/* eurephiadb_session.c -- Global API for handling eurephia sessions * * GPLv2 - Copyright (C) 2008 David Sommerseth * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include "eurephia_struct.h" #include "eurephia_nullsafe.h" #include "eurephia_log.h" #include "eurephiadb_session.h" #include "eurephia_values.h" #include "sha512.h" // Also defined in the eurephiadb_driver.h, but not as extern. extern char *(*eDBget_sessionkey_seed) (eurephiaCTX *ctx, const char *sessionseed); extern char *(*eDBget_sessionkey_macaddr) (eurephiaCTX *ctx, const char *macaddr); extern int (*eDBcheck_sessionkey_uniqueness) (eurephiaCTX *ctx, const char *seskey); extern int (*eDBregister_sessionkey) (eurephiaCTX *ctx, const char *seed, const char *seskey); extern eurephiaVALUES *(*eDBload_sessiondata) (eurephiaCTX *ctx, const char *sesskey); extern int eDBstore_session_value(eurephiaCTX *ctx, eurephiaSESSION *session, int mode, const char *key, const char *val); // Adds or updates a key in the eurephiaVALUES stack. Database is updated before the stack is updated. // If database fails, the stack is not updated. int eDBset_session_value(eurephiaCTX *ctx, eurephiaSESSION *session, const char *key, const char *val) { eurephiaVALUES *svals = NULL; if( (session == NULL) || (key == NULL) ) { return 0; } DEBUG(ctx, 30, "Function call: eDBset_session_value(ctx, '%s','%s','%s')", session->sessionkey, key, val); // Create a new session value buffer if it does not exist if( session->sessvals == NULL ) { session->sessvals = eCreate_value_space(ctx, 10); if( session->sessvals == NULL ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for session values"); return 0; } } // Check if the session value exists already. If it does update it, or else add it svals = eGet_valuestruct(session->sessvals, key); if( (svals == NULL) && (val != NULL) ) { DEBUG(ctx, 32, "eDBset_session_value ... New session value: %s = '%s'", key, val); // Add a new session value if( eDBstore_session_value(ctx, session, SESSVAL_NEW, key, val) ) { DEBUG(ctx, 32, "eDBset_session_value ... Adding value to value stack: %s = '%s'", key, val); // Add value to the stack eAdd_value(ctx, session->sessvals, key, val); DEBUG(ctx, 32, "Registered session variable to session '%s': %s = %s", session->sessionkey, key, val); } } else if( svals != NULL ) { if( (val != NULL) && (strcmp(svals->val, val) == 0) ) { DEBUG(ctx, 32, "Session value not changed('%s','%s','%s)", session->sessionkey, key, val); return 1; } // Update the value in the stack if database is updated without errors if( eDBstore_session_value(ctx, session,(val != NULL ? SESSVAL_UPDATE : SESSVAL_DELETE), key,val)){ free_nullsafe(svals->val); svals->val = strdup_nullsafe(val); DEBUG(ctx, 32, "Session variable updated in session '%s': %s = %s", session->sessionkey, key, val); } } else if( (svals == NULL) && (val == NULL ) ) { DEBUG(ctx, 32, "Ignoring saving new session value '%s' == NULL", key); } return 1; } // Generate some random data and return a string. static int rand_init = 0; int get_randstring(eurephiaCTX *ctx, char *rndstr, int len) { int attempts = 0; do { if( !rand_init ) { if( !RAND_load_file("/dev/urandom", 64) ) { eurephia_log(ctx, LOG_FATAL, 0, "Could not load random data from /dev/urandom"); return 0; } rand_init = 1; } if( RAND_pseudo_bytes((unsigned char *) rndstr, len) ) { return 1; } sleep(1); rand_init = 0; } while( attempts++ < 11 ); eurephia_log(ctx, LOG_FATAL, 0, "RAND_pseudo_bytes() could not generate enough random data"); return 0; } // Generates a new session structure. Session key will be created if session seed (input params) are not known. // If session seed is known, the already generated session key will be used. eurephiaSESSION *eDBopen_session_seed(eurephiaCTX *ctx, const char *digest, const char *cname, const char *username, const char *vpnipaddr, const char *vpnipmask, const char *remipaddr, const char *remport) { eurephiaSESSION *new_session = NULL; char *seeddata = NULL, *seed = NULL, *ptr = NULL; SHA512Context sha; uint8_t sha_res[SHA512_HASH_SIZE]; int totlen = 0, i = 0; DEBUG(ctx, 12, "Function call: eDBopen_session_seed(ctx, '%s', '%s', '%s', '%s', '%s', '%s', '%s')", digest, cname, username, vpnipaddr, vpnipmask, remipaddr, remport); new_session = (eurephiaSESSION *) malloc(sizeof(eurephiaSESSION) + 2); if( new_session == NULL ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for a new session"); return NULL; } memset(new_session, 0, sizeof(eurephiaSESSION) + 2); // Build up a string containing all elements for the session seed totlen = strlen_nullsafe(digest) + strlen_nullsafe(cname) + strlen_nullsafe(username) + strlen_nullsafe(vpnipaddr) + strlen_nullsafe(vpnipmask) + strlen_nullsafe(remipaddr) + strlen_nullsafe(remport) + 5; // +5 == len(pid) seeddata = (char *) malloc((totlen * 2) + 4); if( seeddata == NULL ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for a new session key (1)"); free_nullsafe(new_session); return NULL; } memset(seeddata, 0, (totlen * 2) + 4); snprintf((char *)seeddata, totlen, "%s%s%s%s%s%s%s%i", digest, cname, username, vpnipaddr, vpnipmask, remipaddr, remport,getpid()); // Generate a SHA512 version of session seed memset(&sha, 0, sizeof(SHA512Context)); memset(&sha_res, 0, sizeof(sha_res)); SHA512Init(&sha); SHA512Update(&sha, seeddata, totlen); SHA512Final(&sha, sha_res); seed = (char *) malloc((SHA512_HASH_SIZE*2)+3); if( seed == NULL ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for session seed"); free_nullsafe(seeddata); free_nullsafe(new_session); return NULL; } memset(seed, 0, (SHA512_HASH_SIZE*2)+2); ptr = seed; for( i = 0; i < SHA512_HASH_SIZE; i++ ) { sprintf(ptr, "%02x", sha_res[i]); ptr++; } memset(&sha, 0, sizeof(SHA512Context)); memset(&sha_res, 0, sizeof(sha_res)); DEBUG(ctx, 13, "Using session seed '%s'", seed); // Try to retrieve the sessionkey from the database, based on the session seed new_session->sessionkey = eDBget_sessionkey_seed(ctx, seed); if( new_session->sessionkey == NULL ) { // ... if we do not find a sessionkey ... lets generate one int rndlen = 0; char *rndstr = NULL; char *skeydata = NULL; int loop = 0, uniqcheck = 0; DEBUG(ctx, 13, "Unknown session seed, creating new session key"); // Loop until we get a unique sessionkey - don't loop more than 10 times skeydata = (char *) malloc((totlen*2)+4); if( skeydata == NULL ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for new session key data"); free_nullsafe(new_session->sessionkey); free_nullsafe(new_session); free_nullsafe(seeddata); free_nullsafe(seed); return NULL; } do { memset(skeydata, 0, (totlen*2)+4); // FIXME: Validate that we have enough random data for the session key // Append some random data to our session seed rndstr = (char *) malloc((totlen * 2)); if( rndstr == NULL ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for new session key data (2)"); free_nullsafe(new_session->sessionkey); free_nullsafe(new_session); free_nullsafe(seeddata); free_nullsafe(seed); return NULL; } memset(rndstr, 0, (totlen * 2)); rndlen = ((totlen * 2) - strlen_nullsafe(seed) - 2); if( !get_randstring(ctx, rndstr, rndlen) ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not generate enough random data for session key"); free_nullsafe(new_session->sessionkey); free_nullsafe(new_session); free_nullsafe(seeddata); free_nullsafe(seed); return NULL; } // Generate SHA512 version of the session data SHA512Init(&sha); SHA512Update(&sha, rndstr, rndlen); SHA512Final(&sha, sha_res); free_nullsafe(new_session->sessionkey); new_session->sessionkey = (char *) malloc((SHA512_HASH_SIZE*2)+3); if( new_session->sessionkey == NULL ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for new session key"); free_nullsafe(new_session); free_nullsafe(seeddata); free_nullsafe(seed); return NULL; } memset(new_session->sessionkey, 0, (SHA512_HASH_SIZE*2)+3); ptr = new_session->sessionkey; for( i = 0; i < SHA512_HASH_SIZE; i++ ) { sprintf(ptr, "%02x", sha_res[i]); ptr++; } memset(&sha, 0, sizeof(SHA512Context)); memset(&sha_res, 0, sizeof(sha_res)); free_nullsafe(rndstr); loop++; uniqcheck = eDBcheck_sessionkey_uniqueness(ctx, new_session->sessionkey); } while( (uniqcheck == 0) && loop < 11 ); free_nullsafe(skeydata); // If we did not manage to create a unique session key (random data collection must have failed!) if( uniqcheck == 0 ) { eurephia_log(ctx, LOG_FATAL, 0, "Did not manage to create a unique sessionkey after %i attempts", loop-1); free_nullsafe(new_session->sessionkey); free_nullsafe(new_session); free_nullsafe(seeddata); free_nullsafe(seed); return NULL; } // Save this session key in the database and connect it to this session seed if( eDBregister_sessionkey(ctx, seed, new_session->sessionkey) == 0) { eurephia_log(ctx, LOG_FATAL, 0, "Could not register sessionkey"); free_nullsafe(new_session->sessionkey); free_nullsafe(new_session); free_nullsafe(seeddata); free_nullsafe(seed); return NULL; }; new_session->sessionstatus = SESSION_NEW; } else { new_session->sessionstatus = SESSION_EXISTING; DEBUG(ctx, 13, "Session seed found, using sessionkey '%s'", new_session->sessionkey); } free_nullsafe(seed); free_nullsafe(seeddata); // Load session values from the database new_session->sessvals = eDBload_sessiondata(ctx, new_session->sessionkey); // Return struct which contains session key and session variables return new_session; } // Open an existing session based on a MAC address eurephiaSESSION *eDBopen_session_macaddr(eurephiaCTX *ctx, const char *macaddr) { eurephiaSESSION *new_session = NULL; DEBUG(ctx, 12, "Function call: eDBopen_session_mac(ctx, '%s')", macaddr); new_session = (eurephiaSESSION *) malloc(sizeof(eurephiaSESSION) + 2); if( new_session == NULL ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for a new session"); return NULL; } memset(new_session, 0, sizeof(eurephiaSESSION) + 2); // Get the sessionkey from the database new_session->sessionkey = eDBget_sessionkey_macaddr(ctx, macaddr); if( new_session->sessionkey == NULL ) { eurephia_log(ctx, LOG_CRITICAL, 0, "Could not find an active session for MAC address '%s'", macaddr); free_nullsafe(new_session); return NULL; } DEBUG(ctx, 13, "Session seed found, using sessionkey '%s'", new_session->sessionkey); // Load session values from the database new_session->sessvals = eDBload_sessiondata(ctx, new_session->sessionkey); // Return struct which contains the current session return new_session; } // Free up the memory used by a session structure void eDBfree_session_func(eurephiaCTX *ctx, eurephiaSESSION *session) { if( session == NULL ) { return; } DEBUG(ctx, 12, "Function call: eDBfree_session(ctx, '%s')", session->sessionkey); eFree_values(ctx, session->sessvals); free_nullsafe(session->sessionkey); free_nullsafe(session); }