diff --git a/include/msgapi.h b/include/msgapi.h index 009a64e..3a7149b 100644 --- a/include/msgapi.h +++ b/include/msgapi.h @@ -35,6 +35,19 @@ EXPORT BOOL MsgAuthVerifyPassword(const char *user, const char *password); EXPORT BOOL MsgAuthSetPassword(const char *user, const char *oldpassword, const char *newpassword); EXPORT BOOL MsgAuthGetUserStore(const char *user, struct sockaddr_in *store); +// Auth / cookie functions + +#define MSGAUTH_COOKIE_LEN 32 + +typedef struct { + char token[MSGAUTH_COOKIE_LEN]; + uint64_t expiration; +} MsgAuthCookie; + +EXPORT BOOL MsgAuthCreateCookie(const char *username, MsgAuthCookie *cookie, uint64_t timeout); +EXPORT int MsgAuthFindCookie(const char *username, const char *token); +EXPORT BOOL MsgAuthDeleteCookie(const char *username, const char *token); + // Misc. util functions EXPORT BOOL MsgSetRecoveryFlag(void); diff --git a/src/agents/store/cookie.c b/src/agents/store/cookie.c index 5f4a553..edffde8 100644 --- a/src/agents/store/cookie.c +++ b/src/agents/store/cookie.c @@ -23,120 +23,17 @@ #include #include #include +#include #include "stored.h" #include "messages.h" -typedef struct { - char token[32]; - uint64_t expirationDate; -} Cookie; - - -static void -GetCookiesPath(StoreClient *client, const char *user, char *path, size_t len) -{ - const char *root; - int n; - - n = snprintf(path, len, "%s/%s/cookies", StoreAgent.store.rootDir, user); -} - - -static int -AddCookie(StoreClient *client, Cookie *cookie) -{ - char path[XPL_MAX_PATH+1]; - FILE *f; - long len; - - GetCookiesPath(client, client->principal.name, path, sizeof(path)); - - f = fopen(path, "a"); - if (!f) { - return -1; - } - - len = ftell(f); - - if (sizeof(Cookie) != fwrite(cookie, 1, sizeof(Cookie), f) || - fclose(f)) - { - fclose(f); - truncate(path, len); - return -1; - } - - return 0; -} - - -/* returns: 1 if cookie found, 0 if not found, -1 on error */ - -static int -FindCookie(StoreClient *client, const char *user, const char *token) -{ - char path[XPL_MAX_PATH+1]; - FILE *f; - Cookie cookie; - int result = 0; - uint64_t now; - int expired = 0; - - now = BongoCalTimeAsUint64(BongoCalTimeNow(NULL)); - - GetCookiesPath(client, user, path, sizeof(path)); - - f = fopen(path, "r"); - if (!f) { - return 0; - } - - while (sizeof(Cookie) == fread(&cookie, 1, sizeof(Cookie), f)) { - if (cookie.expirationDate < now) { - ++expired; - continue; - } - if (!strncmp(cookie.token, token, sizeof(cookie.token))) { - result = 1; - break; - } - } - - if (ferror(f)) { - result = -1; - } - - if (expired > 8) { - /* FIXME: purge expired cookies from the file */ - } - - (void) fclose(f); - - return result; -} - - CCode StoreCommandCOOKIENEW(StoreClient *client, uint64_t timeout) { - Cookie cookie; + MsgAuthCookie cookie; NLockStruct *lock = NULL; CCode ccode = 0; - unsigned char credential[XPLHASH_MD5_LENGTH]; - xpl_hash_context context; - - cookie.expirationDate = timeout; - - XplHashNew(&context, XPLHASH_MD5); - - snprintf(cookie.token, sizeof(cookie.token), "%x%x", - (unsigned int) XplGetThreadID(), - (unsigned int) time(NULL)); - XplHashWrite(&context, cookie.token, strlen(cookie.token)); - XplRandomData(cookie.token, 8); - XplHashWrite(&context, cookie.token, strlen(cookie.token)); - XplHashFinal(&context, XPLHASH_LOWERCASE, cookie.token, XPLHASH_MD5_LENGTH); assert(STORE_PRINCIPAL_USER == client->principal.type); @@ -149,10 +46,10 @@ StoreCommandCOOKIENEW(StoreClient *client, uint64_t timeout) return ConnWriteStr(client->conn, MSG4120USERLOCKED); } - if (AddCookie(client, &cookie)) { - ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR); + if (MsgAuthCreateCookie(client->principal.name, &cookie, timeout)) { + ccode = ConnWriteF(client->conn, "1000 %.32s\r\n", cookie.token); } else { - ccode = ConnWriteF(client->conn, "1000 %.32s\r\n", cookie.token); + ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR); } PurgeNLockRelease(lock); @@ -166,10 +63,7 @@ CCode StoreCommandCOOKIEDELETE(StoreClient *client, const char *token) { CCode ccode = 0; - char path[XPL_MAX_PATH+1]; - FILE *f; NLockStruct *lock = NULL; - Cookie cookie; assert(STORE_PRINCIPAL_USER == client->principal.type); @@ -183,44 +77,12 @@ StoreCommandCOOKIEDELETE(StoreClient *client, const char *token) return ConnWriteStr(client->conn, MSG4120USERLOCKED); } - GetCookiesPath(client, client->principal.name, path, sizeof(path)); - - if (!token) { - if (unlink(path)) { - printf("Couldn't unlink cookie file %s\r\n", path); - ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR); - } else { - ccode = ConnWriteStr(client->conn, MSG1000OK); - } - goto finish; - } - - f = fopen(path, "r+"); - if (!f) { + if (MsgAuthDeleteCookie(client->principal.name, token)) { + ccode = ConnWriteStr(client->conn, MSG1000OK); + } else { ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR); - goto finish; } - while (sizeof(Cookie) == fread(&cookie, 1, sizeof(Cookie), f)) { - if (!strncmp(cookie.token, token, sizeof(cookie.token))) { - /* FIXME: should (atomically) re-generate the cookie file instead of - overwriting the delete cookie with zeroes */ - memset(&cookie, 0, sizeof(Cookie)); - if (fseek(f, -sizeof(Cookie), SEEK_CUR) || - sizeof(Cookie) != fwrite(&cookie, 1, sizeof(Cookie), f)) - { - ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR); - goto finish; - } - } - } - - if (ferror(f)) { - goto finish; - } - - ccode = ConnWriteStr(client->conn, MSG1000OK); - finish: PurgeNLockRelease(lock); ReadNLockRelease(lock); @@ -228,7 +90,8 @@ finish: return ccode; } - +#if 0 +// this looks like multiple-store code CCode TunnelAuthCookie(StoreClient *client, struct sockaddr_in *serv, char *user, char *token, int nouser) @@ -265,7 +128,7 @@ TunnelAuthCookie(StoreClient *client, return ccode; } - +#endif CCode StoreCommandAUTHCOOKIE(StoreClient *client, char *user, char *token, int nouser) @@ -305,7 +168,7 @@ StoreCommandAUTHCOOKIE(StoreClient *client, char *user, char *token, int nouser) return ConnWriteStr(client->conn, MSG4120USERLOCKED); } - switch (FindCookie(client, user, token)) { + switch (MsgAuthFindCookie(user, token)) { case 0: XplDelay(2000); ccode = ConnWriteStr(client->conn, MSG3242BADAUTH); diff --git a/src/libs/msgapi/msgauth.c b/src/libs/msgapi/msgauth.c index e70d145..609c77e 100644 --- a/src/libs/msgapi/msgauth.c +++ b/src/libs/msgapi/msgauth.c @@ -13,6 +13,7 @@ #include #include #include +#include /** * Determine whether or not a user exists in our user database @@ -86,3 +87,174 @@ MsgAuthUserList(BongoArray **list) { *list = userlist; return TRUE; } + +/* Cookie stuff. Cookies are managed by msgapi, so that all agents that know about + * cookies will know which cookies are valid and which aren't. + */ + +void +MsgAuthCookiePath(const char *username, char *path, size_t length) +{ + snprintf(path, length, "%s/cookies/%s", XPL_DEFAULT_DBF_DIR, username); +} + +/** + * Create a 'cookie': a password-substitute token, and an associated expiry + * SECURITY: We shouldn't create cookies for non-authenticated users + * \param user User whose token this will be + * \param cookie Reference to the MsgAuthCookie we're creating + * \param timeout When the token should expire + * \return Whether or not the cookie was created + */ +EXPORT BOOL +MsgAuthCreateCookie(const char *username, MsgAuthCookie *cookie, uint64_t timeout) +{ + char path[XPL_MAX_PATH+1]; + FILE *cookiefile = NULL; + long original_length; + size_t written; + xpl_hash_context context; + + // create the cookie + cookie->expiration = timeout; + + XplHashNew(&context, XPLHASH_MD5); + snprintf(cookie->token, sizeof(cookie->token), "%x%x", + (unsigned int) XplGetThreadID(), + (unsigned int) time(NULL)); + XplHashWrite(&context, cookie->token, strlen(cookie->token)); + XplRandomData(cookie->token, 8); + XplHashWrite(&context, cookie->token, strlen(cookie->token)); + XplHashFinal(&context, XPLHASH_LOWERCASE, cookie->token, XPLHASH_MD5_LENGTH); + + // save it to our cookie list + MsgAuthCookiePath(username, &path, XPL_MAX_PATH); + cookiefile = fopen(path, "a"); + if (! cookiefile) + return FALSE; + + original_length = ftell(cookiefile); + + written = fwrite(cookie, 1, sizeof(MsgAuthCookie), cookiefile); + if ((fclose(cookiefile) != 0) || (sizeof(MsgAuthCookie) != written)) { + truncate(path, original_length); + return FALSE; + } + + return TRUE; +} + +/** + * Find a cookie based on the token. If it exists, it's as good as auth'ing + * SECURITY: This is much like authentication. Be careful to timeout on bad auth? + * \param username User who claims this token is theirs + * \param token Token we want to look for + * \return 0 = bad auth, 1 = good auth, 2 = internal error + */ +EXPORT int +MsgAuthFindCookie(const char *username, const char *token) +{ + char path[XPL_MAX_PATH+1]; + FILE *cookiefile = NULL; + MsgAuthCookie cookie; + int result = 1; + BOOL expired_tokens = FALSE; + uint64_t now; + + now = time(NULL); + + MsgAuthCookiePath(username, &path, XPL_MAX_PATH); + + cookiefile = fopen(path, "r"); + if (! cookiefile) return 2; + + while (fread(&cookie, 1, sizeof(MsgAuthCookie), cookiefile) != sizeof(MsgAuthCookie)) { + if (cookie.expiration < now) { + expired_tokens = TRUE; + continue; + } + if (!strncmp (cookie.token, token, MSGAUTH_COOKIE_LEN)) { + result = 1; + break; + } + } + + if (ferror(cookiefile)) + result = 2; + + fclose(cookiefile); + + if (expired_tokens) + // call this function with no token to remove expired tokens + MsgAuthDeleteCookie(username, ""); + + return result; +} + +/** + * Remove a token from the user's cookie store. Removes expired tokens as a side-effect. + * \param username User whose token this should be + * \param token Token we want rid of. + * \return Whether or not we succeeded. + */ +EXPORT BOOL +MsgAuthDeleteCookie(const char *username, const char *token) +{ + char path[XPL_MAX_PATH + 1]; + FILE *cookiefile = NULL; + long filelen; + int cookiecount, i; + MsgAuthCookie **cookieset; + uint64_t now; + + now = BongoCalTimeAsUint64(BongoCalTimeNow(NULL)); + + MsgAuthCookiePath(username, &path, XPL_MAX_PATH); + + // first, read cookies into memory + cookiefile = fopen(path, "w"); + if (! cookiefile) return FALSE; + + if (fseek(cookiefile, 0, SEEK_END) != 0) { + fclose(cookiefile); + return FALSE; + } + + filelen = ftell(cookiefile); + cookiecount = filelen / sizeof(MsgAuthCookie); + cookieset = MemMalloc(filelen + 1); + if (fread(cookieset, 1, filelen, cookiefile) != filelen) { + fclose(cookiefile); + return FALSE; + } + + // next, clear the existing file + rewind(cookiefile); + ftruncate((int)cookiefile, 0); + + // now, go through memory writing out only the cookies we want to keep + for (i = 0; i < cookiecount; i++) { + MsgAuthCookie *cookie = cookieset[i]; + if (! strncmp(cookie->token, token, MSGAUTH_COOKIE_LEN)) { + // we want to delete this token + cookie->expiration = 0; + } + if (cookie->expiration < now) { + // this token is expired, so we don't want this. + cookie->expiration = 0; + } + if (cookie->expiration != 0) { + // we want to keep this token, so write it out + size_t written; + written = fwrite(cookie, 1, sizeof(MsgAuthCookie), cookiefile); + if (written != sizeof(MsgAuthCookie)) { + // ooops, this is pretty bad... + fclose(cookiefile); + return FALSE; + } + } + } + + fclose(cookiefile); + return TRUE; +}