aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/sledjchisl/sledjchisl.c
diff options
context:
space:
mode:
authoronefang2020-03-18 23:44:23 +1000
committeronefang2020-03-18 23:44:23 +1000
commit0dd0082560332995ba8e6ed86b3fd6b92ba783e8 (patch)
tree4b417ed9c5a5d5916cf29ba3e2734e8a0b4cda20 /src/sledjchisl/sledjchisl.c
parentInclude all stuff in a session file. (diff)
downloadopensim-SC-0dd0082560332995ba8e6ed86b3fd6b92ba783e8.zip
opensim-SC-0dd0082560332995ba8e6ed86b3fd6b92ba783e8.tar.gz
opensim-SC-0dd0082560332995ba8e6ed86b3fd6b92ba783e8.tar.bz2
opensim-SC-0dd0082560332995ba8e6ed86b3fd6b92ba783e8.tar.xz
Major rewrite of the validation code.
Other things might have snuck into this mess. Make sure errors cause pages to reload with the data that was entered, so they can easily correct it.
Diffstat (limited to '')
-rw-r--r--src/sledjchisl/sledjchisl.c576
1 files changed, 304 insertions, 272 deletions
diff --git a/src/sledjchisl/sledjchisl.c b/src/sledjchisl/sledjchisl.c
index 06c6187..8bcf076 100644
--- a/src/sledjchisl/sledjchisl.c
+++ b/src/sledjchisl/sledjchisl.c
@@ -305,18 +305,18 @@ qhashtbl_t *HTMLfileCache = NULL;
305typedef struct _reqData reqData; 305typedef struct _reqData reqData;
306 306
307 307
308typedef int (*fieldValidFunc) (reqData *Rd, qhashtbl_t *data); 308typedef int (*fieldValidFunc) (reqData *Rd, qhashtbl_t *data, char *name);
309typedef struct _validFunc validFunc; 309typedef struct _validFunc validFunc;
310struct _validFunc 310struct _validFunc
311{ 311{
312 char *name; 312 char *name, *title;
313 fieldValidFunc func; 313 fieldValidFunc func;
314}; 314};
315qlisttbl_t *fieldValidFuncs = NULL; 315qlisttbl_t *fieldValidFuncs = NULL;
316static void newValidFunc(char *name, fieldValidFunc func) 316static void newValidFunc(char *name, char *title, fieldValidFunc func)
317{ 317{
318 validFunc *vf = xmalloc(sizeof(validFunc)); 318 validFunc *vf = xmalloc(sizeof(validFunc));
319 vf->name = name; vf->func = func; 319 vf->name = name; vf->title = title; vf->func = func;
320 fieldValidFuncs->put(fieldValidFuncs, vf->name, vf, sizeof(validFunc)); 320 fieldValidFuncs->put(fieldValidFuncs, vf->name, vf, sizeof(validFunc));
321} 321}
322 322
@@ -402,7 +402,7 @@ struct _sesh
402struct _reqData 402struct _reqData
403{ 403{
404 lua_State *L; 404 lua_State *L;
405 qhashtbl_t *configs, *queries, *body, *cookies, *headers, *stuff, *database, *Rcookies, *Rheaders; 405 qhashtbl_t *configs, *queries, *body, *cookies, *headers, *valid, *stuff, *database, *Rcookies, *Rheaders;
406 char *Scheme, *Host, *Method, *Script, *RUri, *doit; 406 char *Scheme, *Host, *Method, *Script, *RUri, *doit;
407 sesh shs, *lnk; 407 sesh shs, *lnk;
408 MYSQL *db; 408 MYSQL *db;
@@ -2727,6 +2727,96 @@ NOTE - storing a pepper on the same RAID array as everything else will be a prob
2727https://stackoverflow.com/questions/16891729/best-practices-salting-peppering-passwords 2727https://stackoverflow.com/questions/16891729/best-practices-salting-peppering-passwords
2728*/ 2728*/
2729 2729
2730
2731static void bitch(reqData *Rd, char *message, char *log)
2732{
2733 addStrL(Rd->errors, message);
2734 E("%s %s %s %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), getStrH(Rd->stuff, "UUID"), getStrH(Rd->stuff, "name"), message, log);
2735}
2736
2737/* "A token cookie that references a non-existent session, its value should be replaced immediately to prevent session fixation."
2738https://owasp.org/www-community/attacks/Session_fixation
2739 Which describes the problem, but offers no solution.
2740 See https://stackoverflow.com/questions/549/the-definitive-guide-to-form-based-website-authentication?rq=1.
2741I think this means send a new cookie.
2742 I clear out the cookies and send blank ones with -1 maxAge, so they should get deleted.
2743*/
2744static void bitchSession(reqData *Rd, char *message, char *log)
2745{
2746 addStrL(Rd->errors, message);
2747 C("%s %s %s %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), getStrH(Rd->stuff, "UUID"), getStrH(Rd->stuff, "name"), message, log);
2748 Rd->vegOut = TRUE;
2749}
2750
2751
2752int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, struct stat *st, struct timespec *now, char *type)
2753{
2754 struct timespec then;
2755
2756 if (-1 == clock_gettime(CLOCK_REALTIME, &then))
2757 perror_msg("Unable to get the time.");
2758 I("Reading %s file %s", type, file);
2759 if (0 != stat(file, st))
2760 {
2761 D("No %s file.", file);
2762 perror_msg("Unable to stat %s", file);
2763 ret++;
2764 }
2765 else
2766 {
2767 int status = luaL_loadfile(Rd->L, file), result;
2768
2769 if (status)
2770 {
2771 bitchSession(Rd, "No such thing.", "Can't load file.");
2772 E("Couldn't load file: %s", lua_tostring(Rd->L, -1));
2773 ret++;
2774 }
2775 else
2776 {
2777 result = lua_pcall(Rd->L, 0, LUA_MULTRET, 0);
2778
2779 if (result)
2780 {
2781 bitchSession(Rd, "Broken thing.", "Can't run file.");
2782 E("Failed to run script: %s", lua_tostring(Rd->L, -1));
2783 ret++;
2784 }
2785 else
2786 {
2787 lua_getglobal(Rd->L, var);
2788 lua_pushnil(Rd->L);
2789
2790 while(lua_next(Rd->L, -2) != 0)
2791 {
2792 char *n = (char *) lua_tostring(Rd->L, -2);
2793
2794 if (lua_isstring(Rd->L, -1))
2795 {
2796 tnm->putstr(tnm, n, (char *) lua_tostring(Rd->L, -1));
2797//d("Reading %s = %s", n, getStrH(tnm, n));
2798 }
2799 else
2800 {
2801 char *v = (char *) lua_tostring(Rd->L, -1);
2802 W("Unknown Lua variable type for %s = %s", n, v);
2803 }
2804 lua_pop(Rd->L, 1);
2805 }
2806
2807 if (-1 == clock_gettime(CLOCK_REALTIME, now))
2808 perror_msg("Unable to get the time.");
2809 double n = (now->tv_sec * 1000000000.0) + now->tv_nsec;
2810 double t = (then.tv_sec * 1000000000.0) + then.tv_nsec;
2811 T("Reading %s file took %lf seconds", type, (n - t) / 1000000000.0);
2812 }
2813 }
2814 }
2815
2816 return ret;
2817}
2818
2819
2730static void freeSesh(reqData *Rd, boolean linky, boolean wipe) 2820static void freeSesh(reqData *Rd, boolean linky, boolean wipe)
2731{ 2821{
2732 char *file = NULL; 2822 char *file = NULL;
@@ -2933,26 +3023,6 @@ static void createUser(reqData *Rd)
2933 free(file); 3023 free(file);
2934} 3024}
2935 3025
2936
2937static void bitch(reqData *Rd, char *message, char *log)
2938{
2939 addStrL(Rd->errors, message);
2940 E("%s %s %s %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), getStrH(Rd->stuff, "UUID"), getStrH(Rd->stuff, "name"), message, log);
2941}
2942
2943/* "A token cookie that references a non-existent session, its value should be replaced immediately to prevent session fixation."
2944https://owasp.org/www-community/attacks/Session_fixation
2945 Which describes the problem, but offers no solution.
2946 See https://stackoverflow.com/questions/549/the-definitive-guide-to-form-based-website-authentication?rq=1.
2947I think this means send a new cookie.
2948 I clear out the cookies and send blank ones with -1 maxAge, so they should get deleted.
2949*/
2950static void bitchSession(reqData *Rd, char *message, char *log)
2951{
2952 addStrL(Rd->errors, message);
2953 C("%s %s %s %s %s", getStrH(Rd->headers, "REMOTE_ADDR"), getStrH(Rd->stuff, "UUID"), getStrH(Rd->stuff, "name"), message, log);
2954 Rd->vegOut = TRUE;
2955}
2956static sesh *newSesh(reqData *Rd, boolean linky) 3026static sesh *newSesh(reqData *Rd, boolean linky)
2957{ 3027{
2958 unsigned char *md5hash = xzalloc(17); 3028 unsigned char *md5hash = xzalloc(17);
@@ -2961,7 +3031,7 @@ static sesh *newSesh(reqData *Rd, boolean linky)
2961 uuid_t binuuid; 3031 uuid_t binuuid;
2962 sesh *ret = &Rd->shs; 3032 sesh *ret = &Rd->shs;
2963 3033
2964W("New sesh"); 3034d("New sesh");
2965 if (linky) 3035 if (linky)
2966 { 3036 {
2967 Rd->lnk = xzalloc(sizeof(sesh)); 3037 Rd->lnk = xzalloc(sizeof(sesh));
@@ -3044,132 +3114,72 @@ char *checkLinky(reqData *Rd)
3044} 3114}
3045 3115
3046 3116
3047boolean prevalidate(qhashtbl_t *data, char *name)
3048{
3049 if ('\0' != getStrH(data, name)[0])
3050 {
3051 I("Already validated %s.", name);
3052 return TRUE;
3053 }
3054 return FALSE;
3055}
3056
3057boolean badBoy(int ret, reqData *Rd, qhashtbl_t *data, char *name, char *value) 3117boolean badBoy(int ret, reqData *Rd, qhashtbl_t *data, char *name, char *value)
3058{ 3118{
3059 if (NULL == value) 3119 if (NULL == value)
3060 value = getStrH(data, name); 3120 value = getStrH(data, name);
3061
3062 if (0 != ret) 3121 if (0 != ret)
3063 { 3122 {
3064 char *t = xmprintf("BAD - %s", name); 3123 Rd->valid->putint(Rd->valid, name, -1);
3065 3124W("Bad boy %s = %s", name, value);
3066 Rd->stuff->putstr(Rd->stuff, t, value);
3067 Rd->stuff->remove(data, name);
3068 free(t);
3069 return TRUE; 3125 return TRUE;
3070 } 3126 }
3071 data->putstr(data, name, value); 3127 Rd->stuff->putstr(Rd->stuff, name, value);
3128 Rd->valid->putint(Rd->valid, name, 1);
3072 return FALSE; 3129 return FALSE;
3073} 3130}
3074 3131
3075char *months[] = 3132int validateThings(reqData *Rd, char *doit, char *name, qhashtbl_t *things)
3076{
3077 "january",
3078 "february",
3079 "march",
3080 "april",
3081 "may",
3082 "june",
3083 "july",
3084 "august",
3085 "september",
3086 "october",
3087 "november",
3088 "december"
3089};
3090
3091
3092
3093int LuaToHash(reqData *Rd, char *file, char *var, qhashtbl_t *tnm, int ret, struct stat *st, struct timespec *now, char *type)
3094{ 3133{
3095 struct timespec then; 3134 int e = 0;
3135 qlisttbl_obj_t obj;
3096 3136
3097 if (-1 == clock_gettime(CLOCK_REALTIME, &then)) 3137 D("For function %s, start of %s validation.", doit, name);
3098 perror_msg("Unable to get the time."); 3138 memset((void *) &obj, 0, sizeof(obj));
3099 I("Reading %s file %s", type, file); 3139 fieldValidFuncs->lock(fieldValidFuncs);
3100 if (0 != stat(file, st)) 3140 while(fieldValidFuncs->getnext(fieldValidFuncs, &obj, NULL, false) == true)
3101 {
3102 bitchSession(Rd, "No such thing.", "No file.");
3103 perror_msg("Unable to stat %s", file);
3104 ret++;
3105 }
3106 else
3107 { 3141 {
3108 int status = luaL_loadfile(Rd->L, file), result; 3142 char *t = things->getstr(things, obj.name, false);
3109 3143
3110 if (status) 3144 if (NULL != t)
3111 {
3112 bitchSession(Rd, "No such thing.", "Can't load file.");
3113 E("Couldn't load file: %s", lua_tostring(Rd->L, -1));
3114 ret++;
3115 }
3116 else
3117 { 3145 {
3118 result = lua_pcall(Rd->L, 0, LUA_MULTRET, 0); 3146 char *nm = obj.name;
3147 int valid = Rd->valid->getint(Rd->valid, nm);
3148 validFunc *vf = (validFunc *) obj.data;
3119 3149
3120 if (result) 3150 if (0 != valid) // Is it in the valid qhashtbl?
3121 { 3151 {
3122 bitchSession(Rd, "Broken thing.", "Can't run file."); 3152 if (0 < valid) // Positive valid means it's valid, negative means it's invald.
3123 E("Failed to run script: %s", lua_tostring(Rd->L, -1)); 3153 D("Already validated %s - %s.", vf->title, nm);
3124 ret++; 3154 else
3155 D("Already invalidated %s - %s.", vf->title, nm);
3125 } 3156 }
3126 else 3157 else
3127 { 3158 {
3128 lua_getglobal(Rd->L, var); 3159 D("Validating %s - %s.", vf->title, nm);
3129 lua_pushnil(Rd->L); 3160 if (vf->func)
3130 3161 e += vf->func(Rd, things, nm);
3131 while(lua_next(Rd->L, -2) != 0) 3162 else
3132 { 3163 E("No validation function for %s - %s", vf->title, nm);
3133 char *n = (char *) lua_tostring(Rd->L, -2);
3134
3135 if (lua_isstring(Rd->L, -1))
3136 {
3137 tnm->putstr(tnm, n, (char *) lua_tostring(Rd->L, -1));
3138//d("Reading %s %s", n, getStrH(tnm, n));
3139 }
3140 else
3141 {
3142 char *v = (char *) lua_tostring(Rd->L, -1);
3143 W("Unknown Lua variable type for %s = %s", n, v);
3144 }
3145 lua_pop(Rd->L, 1);
3146 }
3147
3148 if (-1 == clock_gettime(CLOCK_REALTIME, now))
3149 perror_msg("Unable to get the time.");
3150 double n = (now->tv_sec * 1000000000.0) + now->tv_nsec;
3151 double t = (then.tv_sec * 1000000000.0) + then.tv_nsec;
3152 T("Reading %s file took %lf seconds", type, (n - t) / 1000000000.0);
3153 } 3164 }
3154 } 3165 }
3155 } 3166 }
3156 3167 fieldValidFuncs->unlock(fieldValidFuncs);
3157 return ret; 3168 return e;
3158} 3169}
3159 3170
3160static int validateSesh(reqData *Rd, qhashtbl_t *data) 3171
3172static int validateSesh(reqData *Rd, qhashtbl_t *data, char *name)
3161{ 3173{
3162 int ret = 0; 3174 int ret = 0;
3163 boolean linky = FALSE; 3175 boolean linky = FALSE;
3164 3176
3165 if ('\0' != Rd->shs.leaf[0]) 3177 if ('\0' != Rd->shs.leaf[0])
3166 { 3178 {
3167 I("Already validated session."); 3179 d("Already validated session.");
3168 return ret; 3180 return ret;
3169 } 3181 }
3170 3182
3171 I("Validating session.");
3172
3173 char *toke_n_munchie = "", *munchie = "", *hashish = "", 3183 char *toke_n_munchie = "", *munchie = "", *hashish = "",
3174 *leaf, *timeStamp = "", *seshion = "", *seshID = "", 3184 *leaf, *timeStamp = "", *seshion = "", *seshID = "",
3175 *t0, *t1; 3185 *t0, *t1;
@@ -3324,15 +3334,25 @@ W("Validated session linky.");
3324 Rd->stuff->putstr(Rd->stuff, "linky-hashish", t0); 3334 Rd->stuff->putstr(Rd->stuff, "linky-hashish", t0);
3325 } 3335 }
3326 } 3336 }
3327 Rd->stuff->putstr(Rd->stuff, "name", tnm->getstr(tnm, "name", true)); 3337
3328 Rd->stuff->putstr(Rd->stuff, "UUID", tnm->getstr(tnm, "UUID", true)); 3338 qhashtbl_obj_t obj;
3329 Rd->stuff->putstr(Rd->stuff, "level", tnm->getstr(tnm, "level", true)); 3339
3330 Rd->stuff->putstr(Rd->stuff, "passwordSalt", tnm->getstr(tnm, "passwordSalt", true)); 3340 memset((void*)&obj, 0, sizeof(obj));
3331 Rd->stuff->putstr(Rd->stuff, "passwordHash", tnm->getstr(tnm, "passwordHash", true)); 3341 tnm->lock(tnm);
3342 while(tnm->getnext(tnm, &obj, false) == true)
3343 {
3344 char *n = obj.name;
3345
3346 if ((strcmp("salt", n) != 0) && (strcmp("seshID", n) != 0))
3347 {
3348t("Lua %s = %s", n, (char *) obj.data);
3349 Rd->stuff->putstr(Rd->stuff, obj.name, (char *) obj.data);
3350 }
3351 }
3352 tnm->unlock(tnm);
3332 Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", tnm->getstr(tnm, "UUID", true)); 3353 Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", tnm->getstr(tnm, "UUID", true));
3333 } 3354 }
3334 } 3355 }
3335
3336 } 3356 }
3337 } 3357 }
3338 } 3358 }
@@ -3342,15 +3362,48 @@ W("Validated session linky.");
3342 return ret; 3362 return ret;
3343} 3363}
3344 3364
3345static int validateDoB(reqData *Rd, qhashtbl_t *data) 3365static int validateAboutMe(reqData *Rd, qhashtbl_t *data, char *name)
3366{
3367 int ret = 0;
3368 char *about = getStrH(data, "aboutMe");
3369
3370 if ((strcmp("confirm", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0))
3371 return ret;
3372
3373 if ('\0' == about[0])
3374 {
3375 bitch(Rd, "Please fill in the 'About me' section.", "None supplied.");
3376 ret++;
3377 }
3378
3379 badBoy(ret, Rd, data, "aboutMe", about);
3380 return ret;
3381}
3382
3383
3384char *months[] =
3385{
3386 "january",
3387 "february",
3388 "march",
3389 "april",
3390 "may",
3391 "june",
3392 "july",
3393 "august",
3394 "september",
3395 "october",
3396 "november",
3397 "december"
3398};
3399static int validateDoB(reqData *Rd, qhashtbl_t *data, char *name)
3346{ 3400{
3347 int ret = 0, i; 3401 int ret = 0, i;
3348 char *t; 3402 char *t;
3349 3403
3350 if (prevalidate(Rd->stuff, "year")) return ret; 3404 if ((strcmp("confirm", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0))
3351 if (prevalidate(Rd->stuff, "month")) return ret; 3405 return ret;
3352 3406
3353 I("Validating DoB.");
3354 t = getStrH(data, "year"); 3407 t = getStrH(data, "year");
3355 if ((NULL == t) || ('\0' == t[0])) 3408 if ((NULL == t) || ('\0' == t[0]))
3356 { 3409 {
@@ -3392,19 +3445,15 @@ static int validateDoB(reqData *Rd, qhashtbl_t *data)
3392 return ret; 3445 return ret;
3393} 3446}
3394 3447
3395static int validateEmail(reqData *Rd, qhashtbl_t *data) 3448static int validateEmail(reqData *Rd, qhashtbl_t *data, char *name)
3396{ 3449{
3397 int ret = 0; 3450 int ret = 0;
3398 char *email = getStrH(data, "email"); 3451 char *email = getStrH(data, "email");
3399 char *emayl = getStrH(data, "emayl"); 3452 char *emayl = getStrH(data, "emayl");
3400 3453
3401 if ((strcmp("create", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0)) 3454 if ((strcmp("confirm", Rd->doit) != 0) && (strcmp("update", Rd->doit) != 0))
3402 return ret; 3455 return ret;
3403 3456
3404 if (prevalidate(Rd->stuff, "email")) return ret;
3405 if (prevalidate(Rd->stuff, "emayl")) return ret;
3406
3407 I("Validating email.");
3408 if ((NULL == email) || (NULL == emayl) || ('\0' == email[0]) || ('\0' == emayl[0])) 3457 if ((NULL == email) || (NULL == emayl) || ('\0' == email[0]) || ('\0' == emayl[0]))
3409 { 3458 {
3410 bitch(Rd, "Please supply an email address.", "None supplied."); 3459 bitch(Rd, "Please supply an email address.", "None supplied.");
@@ -3425,25 +3474,20 @@ static int validateEmail(reqData *Rd, qhashtbl_t *data)
3425// TODO - do other email checks - does the domain exist, .. 3474// TODO - do other email checks - does the domain exist, ..
3426 } 3475 }
3427 3476
3428 badBoy(ret, Rd, data, "email", email); 3477 badBoy(ret, Rd, data, "email", NULL);
3429 badBoy(ret, Rd, data, "emayl", emayl); 3478 badBoy(ret, Rd, data, "emayl", NULL);
3430 return ret; 3479 return ret;
3431} 3480}
3432 3481
3433static int validateLegal(reqData *Rd, qhashtbl_t *data) 3482static int validateLegal(reqData *Rd, qhashtbl_t *data, char *name)
3434{ 3483{
3435 int ret = 0; 3484 int ret = 0;
3436 char *t; 3485 char *t;
3437 3486
3438 if (prevalidate(Rd->stuff, "adult")) return ret;
3439 if (prevalidate(Rd->stuff, "agree")) return ret;
3440
3441
3442 I("Validating legal.");
3443 t = getStrH(data, "adult"); 3487 t = getStrH(data, "adult");
3444 if ((NULL == t) || (strcmp("on", t) != 0)) 3488 if ((NULL == t) || (strcmp("on", t) != 0))
3445 { 3489 {
3446 bitch(Rd, "You must be an adult to enter this world.", ""); 3490 bitch(Rd, "You must be an adult to enter this site.", "");
3447 ret++; 3491 ret++;
3448 } 3492 }
3449 t = getStrH(data, "agree"); 3493 t = getStrH(data, "agree");
@@ -3458,19 +3502,16 @@ static int validateLegal(reqData *Rd, qhashtbl_t *data)
3458 return ret; 3502 return ret;
3459} 3503}
3460 3504
3461static int validateName(reqData *Rd, qhashtbl_t *data) 3505static int validateName(reqData *Rd, qhashtbl_t *data, char *nm)
3462{ 3506{
3463 boolean login = strcmp("login", Rd->doit) == 0; 3507 boolean login = strcmp("login", Rd->doit) == 0;
3464 int ret = 0; 3508 int ret = 0;
3465 unsigned char *name = data->getstr(data, "name", true); // We have to be unsigned coz of isalnum(). 3509 unsigned char *name = data->getstr(data, "name", true); // We have to be unsigned coz of isalnum().
3466 char *where = NULL; 3510 char *where = NULL;
3467 3511
3468 if (strcmp("logout", Rd->doit) == 0) 3512 if ((strcmp("cancel", Rd->doit) == 0) || (strcmp("logout", Rd->doit) == 0))
3469 return ret; 3513 return ret;
3470 3514
3471 if (prevalidate(Rd->database, "UserAccounts.UserLevel")) return ret;
3472
3473 I("Validating name.");
3474 if ((NULL == name) || ('\0' == name[0])) 3515 if ((NULL == name) || ('\0' == name[0]))
3475 { 3516 {
3476 bitch(Rd, "Please supply an account name.", "None supplied."); 3517 bitch(Rd, "Please supply an account name.", "None supplied.");
@@ -3557,102 +3598,105 @@ static int validateName(reqData *Rd, qhashtbl_t *data)
3557 } 3598 }
3558 dbDoSomething(acnts, FALSE, name, s); 3599 dbDoSomething(acnts, FALSE, name, s);
3559 rowData *rows = acnts->rows; 3600 rowData *rows = acnts->rows;
3601 int c = 0;
3602
3560 if (rows) 3603 if (rows)
3561 { 3604 c = rows->rows->size(rows->rows);
3562 int i = rows->rows->size(rows->rows);
3563 3605
3564 if (login) 3606 if (login)
3607 {
3608 if ((1 != c) && (rt))
3565 { 3609 {
3566 if (rt) 3610 if (rt)
3567 { 3611 {
3568 bitch(Rd, "Login failed.", "Could not read user Lua file."); 3612 W("Could not read user Lua file.");
3569 ret += rt; 3613 ret += rt;
3570 if (i == 0) 3614 }
3571 { 3615 if (0 == c)
3572 bitch(Rd, "Login failed.", "No UserAccounts record with that name."); 3616 {
3573 ret++; 3617 W("No UserAccounts record with that name.");
3574 } 3618 ret++;
3575 else
3576 {
3577 if (i != 1)
3578 {
3579 bitch(Rd, "Login failed.", "More than one UserAccounts record with that name.");
3580 ret++;
3581 }
3582 else
3583 {
3584 dbPull(Rd, "UserAccounts", rows);
3585 Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(getStrH(Rd->database, "UserAccounts.PrincipalID")));
3586 Rd->stuff->putstr(Rd->stuff, "level", xstrdup(getStrH(Rd->database, "UserAccounts.Userlevel")));
3587 if (s) {s--; *s = ' '; s++;}
3588 Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name));
3589 }
3590 }
3591 } 3619 }
3592 else 3620 else
3593 { 3621 {
3622 W("More than one UserAccounts record with that name.");
3623 ret++;
3624 }
3625 bitch(Rd, "Login failed.", "Could not find user record.");
3626 }
3627 else
3628 {
3629 if (1 == c)
3630 dbPull(Rd, "UserAccounts", rows);
3631 else
3632 {
3594 Rd->database->putstr(Rd->database, "UserAccounts.FirstName", name); 3633 Rd->database->putstr(Rd->database, "UserAccounts.FirstName", name);
3595 Rd->database->putstr(Rd->database, "UserAccounts.LastName", s); 3634 Rd->database->putstr(Rd->database, "UserAccounts.LastName", s);
3596 Rd->database->putstr(Rd->database, "UserAccounts.Email", getStrH(tnm, "email")); 3635 Rd->database->putstr(Rd->database, "UserAccounts.Email", getStrH(tnm, "email"));
3597 Rd->database->putstr(Rd->database, "UserAccounts.Created", getStrH(tnm, "created")); 3636 Rd->database->putstr(Rd->database, "UserAccounts.Created", getStrH(tnm, "created"));
3598 Rd->database->putstr(Rd->database, "UserAccounts.PrincipleID", getStrH(tnm, "UUID")); 3637 Rd->database->putstr(Rd->database, "UserAccounts.PrincipleID", getStrH(tnm, "UUID"));
3599 Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", getStrH(tnm, "level")); 3638 Rd->database->putstr(Rd->database, "UserAccounts.UserLevel", getStrH(tnm, "level"));
3600 Rd->database->putstr(Rd->database, "UserAccounts.UserFlags", getStrH(tnm, "flags")); 3639 Rd->database->putstr(Rd->database, "UserAccounts.UserFlags", getStrH(tnm, "flags"));
3601 Rd->database->putstr(Rd->database, "UserAccounts.UserTitle", getStrH(tnm, "title")); 3640 Rd->database->putstr(Rd->database, "UserAccounts.UserTitle", getStrH(tnm, "title"));
3602 Rd->database->putstr(Rd->database, "UserAccounts.active", getStrH(tnm, "active")); 3641 Rd->database->putstr(Rd->database, "UserAccounts.active", getStrH(tnm, "active"));
3603 Rd->database->putstr(Rd->database, "auth.passwordSalt", getStrH(tnm, "passwordSalt")); 3642 Rd->database->putstr(Rd->database, "auth.passwordSalt", getStrH(tnm, "passwordSalt"));
3604 Rd->database->putstr(Rd->database, "auth.passwordHash", getStrH(tnm, "passwordHash")); 3643 Rd->database->putstr(Rd->database, "auth.passwordHash", getStrH(tnm, "passwordHash"));
3605 3644 tnm->free(tnm);
3606 Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(getStrH(Rd->database, "UserAccounts.PrincipalID")));
3607 Rd->stuff->putstr(Rd->stuff, "level", xstrdup(getStrH(Rd->database, "UserAccounts.Userlevel")));
3608 if (s) {s--; *s = ' '; s++;}
3609 Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name));
3610 if (s) {s--; *s = '\0'; s++;}
3611 } 3645 }
3646 Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(getStrH(Rd->database, "UserAccounts.PrincipalID")));
3647 Rd->stuff->putstr(Rd->stuff, "level", xstrdup(getStrH(Rd->database, "UserAccounts.Userlevel")));
3648 if (s) {s--; *s = ' '; s++;}
3649 Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name));
3650 if (s) {s--; *s = '\0'; s++;}
3612 } 3651 }
3613 else if (strcmp("create", Rd->doit) == 0) 3652 }
3653 else if (strcmp("create", Rd->doit) == 0)
3654 {
3655 if ((0 != c) && (!rt))
3656 {
3657 bitch(Rd, "Pick a different name.", "An existing Lua user file or UserAccounts record matched that name.");
3658 ret++;
3659 }
3660 else
3614 { 3661 {
3615 if (i != 0)
3616 {
3617 bitch(Rd, "Pick a different name.", "An existing UserAccounts record matched that name.");
3618 ret++;
3619 }
3620 else
3621 {
3622// TODO - compare first, last, and fullname against god names, complain and fail if there's a match. 3662// TODO - compare first, last, and fullname against god names, complain and fail if there's a match.
3623 { 3663 // Generate a UUID, check it isn't already being used.
3624 // Generate a UUID, check it isn't already being used. 3664 char uuid[37];
3625 char uuid[37]; 3665 uuid_t binuuid;
3626 uuid_t binuuid; 3666 my_ulonglong users = 0;
3627 my_ulonglong users = 0;
3628 3667
3629 do // UserAccounts.PrincipalID is a unique primary index anyway, but we want the user creation process to be a little on the slow side. 3668 do // UserAccounts.PrincipalID is a unique primary index anyway, but we want the user creation process to be a little on the slow side.
3630 { 3669 {
3631 uuid_generate_random(binuuid); 3670 struct stat st;
3632 uuid_unparse_lower(binuuid, uuid); 3671
3633 where = xmprintf("UserAccounts.PrincipalID = '%s'", uuid); 3672 uuid_generate_random(binuuid);
3634 D("Trying new UUID %s.", where); 3673 uuid_unparse_lower(binuuid, uuid);
3635 users = dbCount(Rd->db, "UserAccounts", where); 3674 // Try Lua user file.
3636 free(where); 3675 where = xmprintf("%s/users/%s.lua", scData, uuid);
3637 } while (users != 0); 3676 c = stat(where, &st);
3677 if (c)
3678 users = 1;
3679 free(where);
3680 // Try database.
3681 where = xmprintf("UserAccounts.PrincipalID = '%s'", uuid);
3682 D("Trying new UUID %s.", where);
3683 users = dbCount(Rd->db, "UserAccounts", where);
3684 free(where);
3685 } while (users != 0);
3638// TODO - perhaps create a place holder UserAccounts record? Then we'll have to deal with deleting them later. 3686// TODO - perhaps create a place holder UserAccounts record? Then we'll have to deal with deleting them later.
3639 3687 Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(uuid));
3640 Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(uuid)); 3688 Rd->stuff->putstr(Rd->stuff, "level", xstrdup("-200"));
3641 Rd->stuff->putstr(Rd->stuff, "level", xstrdup("-200")); 3689 Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID",xstrdup(uuid));
3642 Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", xstrdup(uuid)); 3690 Rd->database->putstr(Rd->database, "UserAccounts.Userlevel", xstrdup("-200"));
3643 Rd->database->putstr(Rd->database, "UserAccounts.Userlevel", xstrdup("-200")); 3691 Rd->database->putstr(Rd->database, "UserAccounts.firstName", xstrdup(name));
3644 Rd->database->putstr(Rd->database, "UserAccounts.firstName", xstrdup(name)); 3692 Rd->database->putstr(Rd->database, "UserAccounts.lastName", xstrdup(s));
3645 Rd->database->putstr(Rd->database, "UserAccounts.lastName", xstrdup(s)); 3693 if (s) {s--; *s = ' '; s++;}
3646 if (s) {s--; *s = ' '; s++;} 3694 Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name));
3647 Rd->stuff->putstr(Rd->stuff, "name", xstrdup(name));
3648 }
3649 }
3650 } 3695 }
3651 free(rows->rows);
3652 free(rows->fieldNames);
3653 free(rows);
3654 } 3696 }
3655 3697 free(rows->rows);
3698 free(rows->fieldNames);
3699 free(rows);
3656 if (s) {s--; *s = ' '; s++;} 3700 if (s) {s--; *s = ' '; s++;}
3657 } 3701 }
3658 } 3702 }
@@ -3661,7 +3705,7 @@ static int validateName(reqData *Rd, qhashtbl_t *data)
3661 return ret; 3705 return ret;
3662} 3706}
3663 3707
3664static int validatePassword(reqData *Rd, qhashtbl_t *data) 3708static int validatePassword(reqData *Rd, qhashtbl_t *data, char *name)
3665{ 3709{
3666 boolean login = strcmp("login", Rd->doit) == 0; 3710 boolean login = strcmp("login", Rd->doit) == 0;
3667 boolean create = strcmp("create", Rd->doit) == 0; 3711 boolean create = strcmp("create", Rd->doit) == 0;
@@ -3670,13 +3714,13 @@ static int validatePassword(reqData *Rd, qhashtbl_t *data)
3670 char *psswrdH = getStrH(Rd->stuff, "passwordHash"); 3714 char *psswrdH = getStrH(Rd->stuff, "passwordHash");
3671 char *psswrdS = getStrH(Rd->stuff, "passwordSalt"); 3715 char *psswrdS = getStrH(Rd->stuff, "passwordSalt");
3672 3716
3673 if (prevalidate(Rd->database, "auth.passwordSalt")) return ret;
3674
3675 I("Validating password.");
3676 if (login) 3717 if (login)
3677 { 3718 {
3678 char *UUID = getStrH(Rd->database, "UserAccounts.PrincipalID"); 3719 char *UUID = getStrH(Rd->database, "UserAccounts.PrincipalID");
3679 static dbRequest *auth = NULL; 3720 static dbRequest *auth = NULL;
3721
3722 if ('\0' == UUID[0]) // If the name validation didn't find us a UUID, then this user doesn't exist, so can't log them on.
3723 return 1;
3680 if (NULL == auth) 3724 if (NULL == auth)
3681 { 3725 {
3682 static char *szi[] = {"UUID", NULL}; 3726 static char *szi[] = {"UUID", NULL};
@@ -3851,16 +3895,19 @@ static int validatePassword(reqData *Rd, qhashtbl_t *data)
3851 else 3895 else
3852 D("Account created%s.", password); 3896 D("Account created%s.", password);
3853 3897
3898 badBoy(ret, Rd, data, "auth.passwordSalt", NULL);
3899
3854 return ret; 3900 return ret;
3855} 3901}
3856 3902
3857 3903
3858static int validateUUID(reqData *Rd, qhashtbl_t *data) 3904static int validateUUID(reqData *Rd, qhashtbl_t *data, char *name)
3859{ 3905{
3860 int ret = 0, i; 3906 int ret = 0, i;
3861 char uuid[37], *t; 3907 char uuid[37], *t;
3862 rowData *rows = NULL; 3908 rowData *rows = NULL;
3863 static dbRequest *uuids = NULL; 3909 static dbRequest *uuids = NULL;
3910
3864 if (NULL == uuids) 3911 if (NULL == uuids)
3865 { 3912 {
3866 static char *szi[] = {"PrincipalID", NULL}; 3913 static char *szi[] = {"PrincipalID", NULL};
@@ -3873,14 +3920,15 @@ static int validateUUID(reqData *Rd, qhashtbl_t *data)
3873 uuids->where = "PrincipalID=?"; 3920 uuids->where = "PrincipalID=?";
3874 } 3921 }
3875 3922
3876 I("Validating UUID."); 3923 if ((strcmp("cancel", Rd->doit) == 0) || (strcmp("logout", Rd->doit) == 0))
3924 return ret;
3925
3926 uuid[0] = '\0';
3877 if (strcmp("create", Rd->doit) == 0) 3927 if (strcmp("create", Rd->doit) == 0)
3878 { 3928 {
3879 // Generate a UUID, check it isn't already being used, and totally ignore whatever UUID is in body. 3929 // Generate a UUID, check it isn't already being used, and totally ignore whatever UUID is in body.
3880 uuid_t binuuid; 3930 uuid_t binuuid;
3881 3931
3882 if (prevalidate(Rd->stuff, "UUID")) return ret;
3883
3884 do // UserAccounts.PrincipalID is a unique primary index anyway, but we want the user creation process to be a little on the slow side. 3932 do // UserAccounts.PrincipalID is a unique primary index anyway, but we want the user creation process to be a little on the slow side.
3885 { 3933 {
3886 uuid_generate_random(binuuid); 3934 uuid_generate_random(binuuid);
@@ -3903,7 +3951,7 @@ static int validateUUID(reqData *Rd, qhashtbl_t *data)
3903 if (0 == ret) 3951 if (0 == ret)
3904 { 3952 {
3905 data->putstr(data, "UUID", xstrdup(uuid)); 3953 data->putstr(data, "UUID", xstrdup(uuid));
3906 data->putstr(data, "NEW - UUID", uuid); 3954 Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(uuid));
3907 } 3955 }
3908 rows = NULL; 3956 rows = NULL;
3909 } 3957 }
@@ -3958,41 +4006,13 @@ static int validateUUID(reqData *Rd, qhashtbl_t *data)
3958 { 4006 {
3959 Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", xstrdup(uuid)); 4007 Rd->database->putstr(Rd->database, "UserAccounts.PrincipalID", xstrdup(uuid));
3960 } 4008 }
4009 Rd->stuff->putstr(Rd->stuff, "UUID", xstrdup(uuid));
3961 } 4010 }
3962 4011
3963 return ret; 4012 return ret;
3964} 4013}
3965 4014
3966 4015
3967
3968int validateThings(reqData *Rd, char *doit, char *name, qhashtbl_t *things)
3969{
3970 int e = 0;
3971
3972 W("%s start of %s validation.", doit, name);
3973 qlisttbl_obj_t obj;
3974 memset((void *) &obj, 0, sizeof(obj));
3975 fieldValidFuncs->lock(fieldValidFuncs);
3976 while(fieldValidFuncs->getnext(fieldValidFuncs, &obj, NULL, false) == true)
3977 {
3978 char *t = getStrH(things, obj.name);
3979
3980 if ('\0' != t[0])
3981 {
3982 validFunc *vf = (validFunc *) obj.data;
3983
3984W("Validating %s", obj.name);
3985 if (vf->func)
3986 e += vf->func(Rd, things);
3987 else
3988 E("No validation function for %s", obj.name);
3989 }
3990 }
3991 fieldValidFuncs->unlock(fieldValidFuncs);
3992 return e;
3993}
3994
3995
3996void loginPage(reqData *Rd, char *message) 4016void loginPage(reqData *Rd, char *message)
3997{ 4017{
3998 char *name = xstrdup(getStrH(Rd->stuff, "name")); 4018 char *name = xstrdup(getStrH(Rd->stuff, "name"));
@@ -4022,11 +4042,14 @@ void loginPage(reqData *Rd, char *message)
4022 4042
4023void accountCreationPage(reqData *Rd, char *message) 4043void accountCreationPage(reqData *Rd, char *message)
4024{ 4044{
4025 char *name = getStrH(Rd->stuff, "name"); 4045 char *name = getStrH(Rd->body, "name");
4026 char *toke_n_munchie = getCookie(Rd->Rcookies, "toke_n_munchie"); 4046 char *toke_n_munchie = getCookie(Rd->Rcookies, "toke_n_munchie");
4027 char *tmp = xmalloc(16), *t; 4047 char *tmp = xmalloc(16), *t;
4028 int i, d; 4048 int i, d;
4029 4049
4050 if ('\0' == name[0])
4051 name = getStrH(Rd->stuff, "name");
4052
4030// TODO - eww lots of memory leaks here. 4053// TODO - eww lots of memory leaks here.
4031// TODO - need to check if qlibc does it's own free() calls, and fill in the gaps for when it doesn't. 4054// TODO - need to check if qlibc does it's own free() calls, and fill in the gaps for when it doesn't.
4032 HTMLheader(Rd->reply, "<!--#echo var=\"grid\" --> account manager"); 4055 HTMLheader(Rd->reply, "<!--#echo var=\"grid\" --> account manager");
@@ -4050,7 +4073,7 @@ void accountCreationPage(reqData *Rd, char *message)
4050 Rd->reply->addstr(Rd->reply, "<p>A validation email will be sent to this email address, you will need to click on the link in it to continue your account creation.</p>\n"); 4073 Rd->reply->addstr(Rd->reply, "<p>A validation email will be sent to this email address, you will need to click on the link in it to continue your account creation.</p>\n");
4051 Rd->reply->addstr(Rd->reply, "<table><tr><td>\n"); 4074 Rd->reply->addstr(Rd->reply, "<table><tr><td>\n");
4052 HTMLselect(Rd->reply, "Date of birth", "year"); 4075 HTMLselect(Rd->reply, "Date of birth", "year");
4053 t = getStrH(Rd->stuff, "year"); 4076 t = getStrH(Rd->body, "year");
4054 if (NULL == t) 4077 if (NULL == t)
4055 d = -1; 4078 d = -1;
4056 else 4079 else
@@ -4068,7 +4091,7 @@ void accountCreationPage(reqData *Rd, char *message)
4068 HTMLselectEnd(Rd->reply); 4091 HTMLselectEnd(Rd->reply);
4069 Rd->reply->addstr(Rd->reply, "</tt><td>"); 4092 Rd->reply->addstr(Rd->reply, "</tt><td>");
4070 HTMLselect(Rd->reply, NULL, "month"); 4093 HTMLselect(Rd->reply, NULL, "month");
4071 t = getStrH(Rd->stuff, "month"); 4094 t = getStrH(Rd->body, "month");
4072 HTMLoption(Rd->reply, xstrdup(""), FALSE); 4095 HTMLoption(Rd->reply, xstrdup(""), FALSE);
4073 for (i = 0; i <= 11; i++) 4096 for (i = 0; i <= 11; i++)
4074 { 4097 {
@@ -4177,17 +4200,18 @@ void account_html(char *file, reqData *Rd, HTMLfile *thisFile)
4177 if (NULL == fieldValidFuncs) 4200 if (NULL == fieldValidFuncs)
4178 { 4201 {
4179 fieldValidFuncs = qlisttbl(QLISTTBL_LOOKUPFORWARD | QLISTTBL_THREADSAFE | QLISTTBL_UNIQUE); 4202 fieldValidFuncs = qlisttbl(QLISTTBL_LOOKUPFORWARD | QLISTTBL_THREADSAFE | QLISTTBL_UNIQUE);
4180 newValidFunc("hashish", (fieldValidFunc) validateSesh); 4203 newValidFunc("hashish", "session", (fieldValidFunc) validateSesh);
4181 newValidFunc("toke_n_munchie", (fieldValidFunc) validateSesh); 4204 newValidFunc("toke_n_munchie", "session", (fieldValidFunc) validateSesh);
4182 newValidFunc("UUID", (fieldValidFunc) validateUUID); 4205 newValidFunc("UUID", "UUID", (fieldValidFunc) validateUUID);
4183 newValidFunc("name", (fieldValidFunc) validateName); 4206 newValidFunc("name", "name", (fieldValidFunc) validateName);
4184 newValidFunc("password", (fieldValidFunc) validatePassword); 4207 newValidFunc("password", "password", (fieldValidFunc) validatePassword);
4185 newValidFunc("email", (fieldValidFunc) validateEmail); 4208 newValidFunc("email", "email", (fieldValidFunc) validateEmail);
4186 newValidFunc("emayl", (fieldValidFunc) validateEmail); 4209 newValidFunc("emayl", "email", (fieldValidFunc) validateEmail);
4187 newValidFunc("year", (fieldValidFunc) validateDoB); 4210 newValidFunc("year", "DoB", (fieldValidFunc) validateDoB);
4188 newValidFunc("month", (fieldValidFunc) validateDoB); 4211 newValidFunc("month", "DoB", (fieldValidFunc) validateDoB);
4189 newValidFunc("adult", (fieldValidFunc) validateLegal); 4212 newValidFunc("adult", "legal", (fieldValidFunc) validateLegal);
4190 newValidFunc("agree", (fieldValidFunc) validateLegal); 4213 newValidFunc("agree", "legal", (fieldValidFunc) validateLegal);
4214 newValidFunc("aboutMe", "about me", (fieldValidFunc) validateAboutMe);
4191 } 4215 }
4192 if (NULL == buildPages) 4216 if (NULL == buildPages)
4193 { 4217 {
@@ -4329,6 +4353,12 @@ void account_html(char *file, reqData *Rd, HTMLfile *thisFile)
4329 freeSesh(Rd, FALSE, TRUE); 4353 freeSesh(Rd, FALSE, TRUE);
4330 newSesh(Rd, FALSE); 4354 newSesh(Rd, FALSE);
4331 } 4355 }
4356 else if ((0 != e)) // So we can reload details into a broken form, so the user doesn't have to retype everything.
4357 {
4358 freeSesh(Rd, FALSE, FALSE);
4359 newSesh(Rd, FALSE);
4360 }
4361
4332 Rd->func(Rd, ""); 4362 Rd->func(Rd, "");
4333 } 4363 }
4334 4364
@@ -4726,6 +4756,7 @@ jit library is loaded or the JIT compiler will not be activated.
4726// Rd->body = qhashtbl(0, 0); // Inited in toknize below. 4756// Rd->body = qhashtbl(0, 0); // Inited in toknize below.
4727// Rd->cookies = qhashtbl(0, 0); // Inited in toknize below. 4757// Rd->cookies = qhashtbl(0, 0); // Inited in toknize below.
4728 Rd->headers = qhashtbl(0, 0); 4758 Rd->headers = qhashtbl(0, 0);
4759 Rd->valid = qhashtbl(0, 0);
4729 Rd->stuff = qhashtbl(0, 0); 4760 Rd->stuff = qhashtbl(0, 0);
4730 Rd->database = qhashtbl(0, 0); 4761 Rd->database = qhashtbl(0, 0);
4731 Rd->Rcookies = qhashtbl(0, 0); 4762 Rd->Rcookies = qhashtbl(0, 0);
@@ -4983,6 +5014,7 @@ fcgiDone:
4983 qhashtbl_free(Rd->Rcookies); 5014 qhashtbl_free(Rd->Rcookies);
4984 qhashtbl_free(Rd->database); 5015 qhashtbl_free(Rd->database);
4985 qhashtbl_free(Rd->stuff); 5016 qhashtbl_free(Rd->stuff);
5017 qhashtbl_free(Rd->valid);
4986 qhashtbl_free(Rd->headers); 5018 qhashtbl_free(Rd->headers);
4987 qhashtbl_free(Rd->cookies); 5019 qhashtbl_free(Rd->cookies);
4988 qhashtbl_free(Rd->body); 5020 qhashtbl_free(Rd->body);