aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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);