timekeeper

My first (abandoned unfinished) web application for time tracking
git clone git://jacobedwards.org/timekeeper
Log | Files | Refs | README

user.c (4124B)


      1 #include <assert.h>
      2 
      3 #define const
      4 
      5 #include <sys/types.h>
      6 #include <stdarg.h>
      7 #include <stdint.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <time.h>
     12 #include <kcgi.h>
     13 #include <kcgihtml.h>
     14 
     15 #include <err.h>
     16 #include <pwd.h>
     17 
     18 #include <sqlbox.h>
     19 
     20 #include "page.h"
     21 #include "stmt.h"
     22 #include "user.h"
     23 #include "util.h"
     24 
     25 #include "config.h"
     26 
     27 char *user_errors[] = {
     28 	[LoginValid] = "Login valid",
     29 	[LoginNotFound] = "User not found",
     30 	[LoginInvalid] = "Invalid user credentials",
     31 	[LoginSystem] = "System error",
     32 	[LoginCorrupt] = "User database is corrupt"
     33 };
     34 
     35 int
     36 adduser(struct pagedata *pd, char *name, char *key)
     37 {
     38 	char *salt, *hash;
     39 
     40 	salt = bcrypt_gensalt(12);
     41 	hash = bcrypt(key, salt);
     42 	if (!hash)
     43 		return 1;
     44 
     45 	struct sqlbox_parm p[] = {
     46 		{ .sparm = hash, .type = SQLBOX_PARM_STRING },
     47 		{ .sparm = name, .type = SQLBOX_PARM_STRING }
     48 	};
     49 
     50 	if (sqlbox_exec(pd->db, pd->dbid, StmtAddUser, Len(p), p, 0) != SQLBOX_CODE_OK)
     51 		return 1;
     52 	return 0;
     53 }
     54 
     55 int
     56 deleteuser(struct pagedata *pd, char *hash)
     57 {
     58 	struct sqlbox_parm p = {
     59 		.sparm = hash, .type = SQLBOX_PARM_STRING
     60 	};
     61 
     62 	return sqlbox_exec(pd->db, 0, StmtDeleteUser, 1, &p, 0) != SQLBOX_CODE_OK;
     63 }
     64 
     65 void
     66 freeuser(struct user *user)
     67 {
     68 	if (!user)
     69 		return;
     70 	free(user->hash);
     71 	free(user->name);
     72 	free(user->auth);
     73 }
     74 
     75 enum user_error
     76 getuser(struct user **ruser, struct pagedata *pd, char *field, char *value)
     77 {
     78 	size_t stmtid;
     79 	enum StmtID gets;
     80 	struct user *user;
     81 	struct sqlbox_parmset *res;
     82 	struct sqlbox_parm p[] = {
     83 		{ .sparm = value, .type = SQLBOX_PARM_STRING }
     84 	};
     85 
     86 	if (strcmp(field, "name") == 0)
     87 		gets = StmtGetUserByName;
     88 	else if (strcmp(field, "hash") == 0)
     89 		gets = StmtGetUserByHash;
     90 	else if (strcmp(field, "token") == 0)
     91 		gets = StmtGetUserByToken;
     92 	else
     93 		gets = -1;
     94 
     95 	assert(gets >= 0);
     96 
     97 	if (!(stmtid = sqlbox_prepare_bind(pd->db, pd->dbid, gets, Len(p), p, 0)))
     98 		return LoginSystem;
     99 	if (!(res = sqlbox_step(pd->db, stmtid)))
    100 		return LoginSystem;
    101 
    102 	if (!res->psz && res->code == SQLBOX_CODE_OK) {
    103 		sqlbox_finalise(pd->db, stmtid);
    104 		return LoginNotFound;
    105 	}
    106 
    107 	user = calloc(1, sizeof(*user));
    108 	if (!user)
    109 		return LoginSystem;
    110 
    111 	if (res->psz != 2) {
    112 		freeuser(user);
    113 		return LoginCorrupt;
    114 	}
    115 
    116 	if (!(user->hash = strdup(res->ps[0].sparm)) ||
    117 	    !(user->name = strdup(res->ps[1].sparm))) {
    118 		freeuser(user);
    119 		return LoginSystem;
    120 	}
    121 
    122 	if (!sqlbox_finalise(pd->db, stmtid)) {
    123 		freeuser(user);
    124 		return LoginSystem;
    125 	}
    126 
    127 	assert(ruser);
    128 	*ruser = user;
    129 
    130 	return LoginValid;
    131 }
    132 
    133 enum sqlbox_code
    134 addtoken(struct pagedata *pd, char *hash, char *token, time_t expires)
    135 {
    136 	struct sqlbox_parm params[] = {
    137 		{ .sparm = hash, .type = SQLBOX_PARM_STRING },
    138 		{ .sparm = token, .type = SQLBOX_PARM_STRING },
    139 		{ .iparm = expires, .type = SQLBOX_PARM_INT }
    140 	};
    141 
    142 	return sqlbox_exec(pd->db, pd->dbid, StmtAddAuthToken,
    143 	    Len(params), params, 0);
    144 }
    145 
    146 int
    147 revoketokens(struct pagedata *pd, char *hash)
    148 {
    149 	struct sqlbox_parm param =
    150 		{ .sparm = hash, .type = SQLBOX_PARM_STRING };
    151 
    152 	return sqlbox_exec(pd->db, pd->dbid, StmtRevokeAuthTokens, 1, &param, 0) !=
    153 		SQLBOX_CODE_OK;
    154 }
    155 
    156 char *
    157 newtoken(struct pagedata *pd, char *hash)
    158 {
    159 	time_t expires;
    160 	char *salt, *token;
    161 
    162 	salt = bcrypt_gensalt(12);
    163 	if (!salt) {
    164 		kutil_warn(NULL, NULL, "Unable to generate salt for token");
    165 		return NULL;
    166 	}
    167 
    168 	token = bcrypt(hash, salt);
    169 	if (!token || !(token = strdup(token)))
    170 		return NULL;
    171 
    172 	expires = time(NULL) + TokenTTL;
    173 	if (addtoken(pd, hash, token, expires) != SQLBOX_CODE_OK) {
    174 		free(token);
    175 		return NULL;
    176 	}
    177 	return token;
    178 }
    179 
    180 enum user_error 
    181 loginuser(struct user **ruser, struct pagedata *pd, char *name, char *key)
    182 {
    183 	struct user *user;
    184 	char *testhash;
    185 	enum user_error status;
    186 
    187 	if ((status = getuser(&user, pd, "name", name)))
    188 		return status;
    189 
    190 	testhash = bcrypt(key, user->hash);
    191 	if (!testhash) {
    192 		freeuser(user);
    193 		return LoginSystem;
    194 	}
    195 
    196 	if (strcmp(user->hash, testhash) != 0) {
    197 		freeuser(user);
    198 		return LoginInvalid;
    199 	}
    200 
    201 	user->auth = newtoken(pd, user->hash);
    202 	if (!user->auth) {
    203 		freeuser(user);
    204 		return LoginSystem;
    205 	}
    206 
    207 	if (ruser)
    208 		*ruser = user;
    209 	else
    210 		freeuser(user);
    211 
    212 	return LoginValid;
    213 }