Lovely new sqlite-db backed cookie database

This commit is contained in:
alexhudson
2010-07-31 15:33:32 +00:00
parent 54d595d9f0
commit 44ee056858
12 changed files with 231 additions and 334 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ include(cmake/Legacy.cmake)
# output header files etc. specifically configured for this build
configure_file(config.h.cmake include/config.h @ONLY)
configure_file(src/agents/store/sql/create-store.s.cmake src/agents/store/sql/createstore.s @ONLY)
configure_file(src/agents/store/sql/create-store-1.s.cmake src/agents/store/sql/createstore-1.s @ONLY)
configure_file(src/agents/store/sql/create-cookie-1.s.cmake src/agents/store/sql/createcookie-1.s @ONLY)
# tell compiler where to find Bongo's header files
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include/)
-13
View File
@@ -42,19 +42,6 @@ EXPORT int MsgAuthAddUser(const char *user);
EXPORT int MsgAuthUserList(char **list[]);
EXPORT void MsgAuthUserListFree(char **list[]);
// Auth / cookie functions
#define MSGAUTH_COOKIE_LEN XPLHASH_MD5_LENGTH + 1
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);
// SQL Routines
typedef struct _MsgSQLStatement {
+1 -1
View File
@@ -6,7 +6,7 @@ add_executable(bongostore
auth.c
calendar.c
sql/createstore.s
sql/createstore-1.s
sql/createcookie-1.s
command.c
command-parsing.c
contacts.c
+3
View File
@@ -571,6 +571,7 @@ StoreCommandLoop(StoreClient *client)
case STORE_COMMAND_COOKIE:
/* COOKIE [BAKE | NEW] <timeout> */
/* COOKIE [CRUMBLE | DELETE] [<token>] */
/* COOKIE LIST */
if (TOKEN_OK != (ccode = RequireUser(client)) ||
TOKEN_OK != (ccode = CheckTokC(client, n, 2, 3)))
@@ -591,6 +592,8 @@ StoreCommandLoop(StoreClient *client)
!XplStrCaseCmp(tokens[1], "DELETE"))
{
ccode = StoreCommandCOOKIEDELETE(client, 3 == n ? tokens[2] : NULL);
} else if (!XplStrCaseCmp(tokens[1], "LIST")) {
ccode = StoreCommandCOOKIELIST(client);
} else {
ccode = ConnWriteStr(client->conn, MSG3000UNKNOWN);
}
+2
View File
@@ -153,6 +153,8 @@ CCode StoreCommandCOOKIENEW(StoreClient *client, uint64_t timeout);
CCode StoreCommandCOOKIEDELETE(StoreClient *client, const char *token);
CCode StoreCommandCOOKIELIST(StoreClient *client);
CCode StoreCommandCOPY(StoreClient *client, StoreObject *object, StoreObject *collection);
CCode StoreCommandCREATE(StoreClient *client, char *collection, uint64_t guid);
+211 -118
View File
@@ -1,150 +1,243 @@
/****************************************************************************
* <Novell-copyright>
* Copyright (c) 2006 Novell, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail, you
* may find current contact information at www.novell.com.
* </Novell-copyright>
****************************************************************************/
#include <config.h>
#include <xpl.h>
#include <nmlib.h>
#include <msgapi.h>
#include <assert.h>
#include "stored.h"
#include "messages.h"
extern const char *sql_create_cookie_1[]; // defined in sql/create-cookie-1.s.cmake
static MsgSQLHandle *
OpenCookieDB(StoreClient *client)
{
char path[XPL_MAX_PATH + 1];
int current_version = 0;
snprintf(path, XPL_MAX_PATH, "%s/cookies.db", MsgGetDir(MSGAPI_DIR_DBF, NULL, 0));
MsgSQLStatement stmt;
memset(&stmt, 0, sizeof(MsgSQLStatement));
MsgSQLStatement *schema = NULL;
MsgSQLHandle *cdb = MsgSQLOpen(path, &client->memstack, 3000);
if (MsgSQLBeginTransaction(cdb)) return NULL;
schema = MsgSQLPrepare(cdb, "PRAGMA user_version;", &stmt);
if (schema == NULL) goto aborttran;
if (! MsgSQLResults(cdb, schema)) goto aborttran;
current_version = MsgSQLResultInt(&stmt, 0);
MsgSQLEndStatement(&stmt);
MsgSQLFinalize(&stmt);
if (current_version > 1) {
Log(LOG_ERROR, "Cookie DB created with newer version of store, cannot open");
}
if (current_version < 1) {
if (MsgSQLQuickExecute(cdb, (const char*)sql_create_cookie_1))
goto aborttran;
MsgSQLReset(cdb);
}
if (MsgSQLCommitTransaction(cdb)) goto aborttran;
return cdb;
aborttran:
MsgSQLFinalize(&stmt);
MsgSQLAbortTransaction(cdb);
return NULL;
}
CCode
StoreCommandCOOKIENEW(StoreClient *client, uint64_t timeout)
{
MsgAuthCookie cookie;
CCode ccode = 0;
assert(STORE_PRINCIPAL_USER == client->principal.type);
if (MsgAuthCreateCookie(client->principal.name, &cookie, timeout)) {
ccode = ConnWriteF(client->conn, "1000 %.32s\r\n", cookie.token);
} else {
ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR);
}
return ccode;
}
MsgSQLStatement stmt;
MsgSQLStatement *ret;
memset(&stmt, 0, sizeof(MsgSQLStatement));
char token[50];
xpl_hash_context context;
MsgSQLHandle *db = NULL;
XplHashNew(&context, XPLHASH_MD5);
snprintf(token, sizeof(token) - 1, "%x%x",
(unsigned int) XplGetThreadID(), (unsigned int) time(NULL));
XplHashWrite(&context, token, strlen(token));
XplRandomData(token, 8);
XplHashWrite(&context, token, strlen(token));
XplHashFinal(&context, XPLHASH_LOWERCASE, token, XPLHASH_MD5_LENGTH);
uint64_t expiration = time(NULL) + timeout;
db = OpenCookieDB(client);
if (db == NULL) goto abort;
if (MsgSQLBeginTransaction(db)) goto abort;
ret = MsgSQLPrepare(db, "INSERT INTO cookies (user, cookie, expiration) VALUES (?, ?, ?);", &stmt);
MsgSQLBindString(&stmt, 1, client->principal.name, FALSE);
MsgSQLBindString(&stmt, 2, token, FALSE);
MsgSQLBindInt64(&stmt, 3, expiration);
if (MsgSQLExecute(db, &stmt)) goto abort;
MsgSQLFinalize(&stmt);
if (MsgSQLCommitTransaction(db))
goto abort;
MsgSQLClose(db);
return ConnWriteF(client->conn, "1000 %.32s\r\n", token);
abort:
MsgSQLFinalize(&stmt);
MsgSQLAbortTransaction(db);
if (db) MsgSQLClose(db);
return ConnWriteStr(client->conn, MSG5004INTERNALERR);
}
CCode
StoreCommandCOOKIEDELETE(StoreClient *client, const char *token)
{
CCode ccode = 0;
assert(STORE_PRINCIPAL_USER == client->principal.type);
assert(STORE_PRINCIPAL_USER == client->principal.type);
MsgSQLStatement stmt;
MsgSQLStatement *ret;
memset(&stmt, 0, sizeof(MsgSQLStatement));
if (MsgAuthDeleteCookie(client->principal.name, token)) {
ccode = ConnWriteStr(client->conn, MSG1000OK);
} else {
ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR);
}
return ccode;
MsgSQLHandle *db = OpenCookieDB(client);
if (db == NULL) goto abort;
if (MsgSQLBeginTransaction(db)) goto abort;
if (token != NULL) {
ret = MsgSQLPrepare(db, "DELETE FROM cookies WHERE (expiration < ?) OR (user=? AND cookie=?);", &stmt);
MsgSQLBindString(&stmt, 3, token, FALSE);
} else {
ret = MsgSQLPrepare(db, "DELETE FROM cookies WHERE expiration < ? OR user=?;", &stmt);
}
MsgSQLBindInt64(&stmt, 1, time(NULL));
MsgSQLBindString(&stmt, 2, client->principal.name, FALSE);
if (MsgSQLExecute(db, &stmt)) goto abort;
MsgSQLFinalize(&stmt);
if (MsgSQLCommitTransaction(db))
goto abort;
MsgSQLClose(db);
return ConnWriteStr(client->conn, MSG1000OK);
abort:
MsgSQLFinalize(&stmt);
MsgSQLAbortTransaction(db);
if (db) MsgSQLClose(db);
return ConnWriteStr(client->conn, MSG5004INTERNALERR);
}
#if 0
// this looks like multiple-store code
CCode
TunnelAuthCookie(StoreClient *client,
struct sockaddr_in *serv, char *user, char *token, int nouser)
{
Connection *conn = NULL;
char buffer[1024];
CCode ccode;
conn = NMAPConnect(NULL, serv);
if (!conn) {
return ConnWriteStr(client->conn, MSG3242BADAUTH);
}
ccode = NMAPAuthenticateWithCookie(conn, user, token, buffer, sizeof(buffer));
switch (ccode) {
case 1000:
ccode = SelectUser(client, user, NULL, nouser);
break;
case -1:
break;
default:
if (ccode >= 1000 && ccode < 6000) {
ccode = ConnWriteF(client->conn, "%d %s\r\n", ccode, buffer);
} else {
XplConsolePrintf("Unexpected response from remote store: %s\r\n",
buffer);
ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR);
}
break;
}
NMAPQuit(conn);
ConnFree(conn);
return ccode;
}
#endif
CCode
StoreCommandAUTHCOOKIE(StoreClient *client, char *user, char *token, int nouser)
{
CCode ccode;
// struct sockaddr_in serv;
CCode ccode;
if (StoreAgent.installMode) {
// don't allow cookie logins in installation mode. FIXME: better error message?
return ConnWriteStr(client->conn, MSG3242BADAUTH);
}
if (StoreAgent.installMode)
// don't allow cookie logins in installation mode. FIXME: better error message?
return ConnWriteStr(client->conn, MSG3242BADAUTH);
if (0 != MsgAuthFindUser(user)) {
XplConsolePrintf("Couldn't find user object for %s\r\n", user);
ccode = ConnWriteStr(client->conn, MSG3242BADAUTH);
XplDelay(2000);
goto finish;
}
if (0 != MsgAuthFindUser(user)) {
Log(LOG_INFO, "Couldn't find user object for %s\r\n", user);
XplDelay(2000);
return ConnWriteStr(client->conn, MSG3242BADAUTH);
}
// FIXME: This checks for a non-local store. For Bongo 1.0, we assume a single store.
#if 0
if (serv.sin_addr.s_addr != MsgGetHostIPAddress()) {
/* non-local store, need to verify against user's server */
ccode = TunnelAuthCookie(client, &serv, user, token, nouser);
goto finish;
}
#endif
MsgSQLStatement stmt;
MsgSQLStatement *find = NULL;
int result;
MsgSQLHandle *db = OpenCookieDB(client);
if (MsgSQLBeginTransaction(db)) return -2;
memset(&stmt, 0, sizeof(MsgSQLStatement));
find = MsgSQLPrepare(db, "SELECT id FROM cookies WHERE user = ? AND cookie = ? AND expiration > ?;", &stmt);
if (find == NULL) goto abort;
MsgSQLBindString(find, 1, user, FALSE);
MsgSQLBindString(find, 2, token, FALSE);
MsgSQLBindInt64(find, 3, time(NULL));
result = MsgSQLResults(db, find);
if (result < 0) goto abort;
MsgSQLFinalize(&stmt);
if (MsgSQLCommitTransaction(db))
goto abort;
switch (MsgAuthFindCookie(user, token)) {
case 0:
XplDelay(2000);
ccode = ConnWriteStr(client->conn, MSG3242BADAUTH);
break;
case 1:
ccode = SelectUser(client, user, NULL, nouser);
break;
default:
ccode = ConnWriteStr(client->conn, MSG5004INTERNALERR);
break;
}
MsgSQLClose(db);
finish:
return ccode;
if (result == 0) {
XplDelay(2000);
ccode = ConnWriteStr(client->conn, MSG3242BADAUTH);
} else {
ccode = SelectUser(client, user, NULL, nouser);
}
return ccode;
abort:
MsgSQLFinalize(&stmt);
MsgSQLAbortTransaction(db);
if (db) MsgSQLClose(db);
return ConnWriteStr(client->conn, MSG5004INTERNALERR);
}
CCode
StoreCommandCOOKIELIST(StoreClient *client)
{
MsgSQLStatement stmt;
MsgSQLStatement *find = NULL;
MsgSQLHandle *db = OpenCookieDB(client);
if (MsgSQLBeginTransaction(db)) return -2;
memset(&stmt, 0, sizeof(MsgSQLStatement));
find = MsgSQLPrepare(db, "SELECT cookie, expiration FROM cookies WHERE user = ?", &stmt);
if (find == NULL) goto abort;
MsgSQLBindString(find, 1, client->principal.name, FALSE);
// loop results
while (MsgSQLResults(db, &stmt) > 0) {
char *token;
MsgSQLResultTextPtr(&stmt, 0, &token);
uint64_t expiration = MsgSQLResultInt64(&stmt, 1);
ConnWriteF(client->conn, "2001 %s " FMT_UINT64_DEC "\r\n", token, expiration);
}
MsgSQLFinalize(&stmt);
if (MsgSQLCommitTransaction(db))
goto abort;
MsgSQLClose(db);
return ConnWriteStr(client->conn, MSG1000OK);
abort:
MsgSQLFinalize(&stmt);
MsgSQLAbortTransaction(db);
if (db) MsgSQLClose(db);
return ConnWriteStr(client->conn, MSG5004INTERNALERR);
}
+2 -3
View File
@@ -27,7 +27,6 @@
#include "messages.h"
extern const char *sql_create_store[]; // defined in sql/create-store.s.cmake
extern const char *sql_create_store_1[]; // defined in sql/create-store-1.s.cmake
extern const StorePropValName StorePropTable[]; // defined in properties.c
int ACLCheckOnGUID(StoreClient *client, uint64_t guid, int prop);
@@ -151,8 +150,8 @@ StoreObjectDBCheckSchema(StoreClient *client, BOOL new_install)
// deliberate fall-through to upgrade to next version
case 0:
// current database schema
if (MsgSQLQuickExecute(client->storedb, (const char*)sql_create_store_1))
goto abort;
//if (MsgSQLQuickExecute(client->storedb, (const char*)sql_create_store_1))
// goto abort;
// current version, nothing to do
break;
default:
@@ -0,0 +1,8 @@
.section ".note.GNU-stack","",%progbits
.section ".rodata"
.globl sql_create_cookie_1
.type sql_create_cookie_1,@object
sql_create_cookie_1:
.incbin "@CMAKE_CURRENT_SOURCE_DIR@/src/agents/store/sql/create-cookie-1.sql"
.byte 0
.size sql_create_cookie_1, .-sql_create_cookie_1
@@ -1,10 +1,12 @@
CREATE TABLE cookies (
guid INTEGER NOT NULL UNIQUE,
id INTEGER PRIMARY KEY AUTOINCREMENT,
user TEXT DEFAULT NULL,
cookie TEXT DEFAULT NULL,
expiration INTEGER DEFAULT 0
);
CREATE INDEX cookies_lookup ON cookies (user, cookie);
CREATE INDEX cookies_lookup_user ON cookies (user);
CREATE INDEX cookies_expiry ON cookies (expiration);
PRAGMA user_version = 1;
@@ -1,8 +0,0 @@
.section ".note.GNU-stack","",%progbits
.section ".rodata"
.globl sql_create_store_1
.type sql_create_store_1,@object
sql_create_store_1:
.incbin "@CMAKE_CURRENT_SOURCE_DIR@/src/agents/store/sql/create-store-1.sql"
.byte 0
.size sql_create_store_1, .-sql_create_store_1
-1
View File
@@ -1,7 +1,6 @@
#StrictCompile()
add_library(bongomsgapi SHARED
cookie.c
msgaddr.c
msgapi.c
msgauth.c
-188
View File
@@ -1,188 +0,0 @@
/* Bongo Project licensing applies to this file, see COPYING
* (C) 2007 Alex Hudson
*/
/** \file
* Single cookie implementation for all agents; this ensures any cookie
* is valid with any agent which recognises them.
*/
#include <config.h>
#include <xpl.h>
#include <sqlite3.h>
#include <bongoutil.h>
#include <msgapi.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
// Internal function: path to the cookie file for this user.
void
MsgAuthCookiePath(const char *username, char *path, size_t length)
{
snprintf(path, length, "%s/%s",
MsgGetDir(MSGAPI_DIR_COOKIE, path, XPL_MAX_PATH),
username);
}
/**
* Create a 'cookie': a password-substitute token, and an associated expiry
* SECURITY: We shouldn't create cookies for non-authenticated users
* \param username 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 = time(NULL) + 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 = 0;
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 = time(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(fileno(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;
}