/* eurephiadb_session.c -- Functions for handling sessions from eurephia-auth * * GPLv2 only - Copyright (C) 2008 - 2012 * 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. * */ /** * @file eurephiadb_session.c * @author David Sommerseth * @date 2008-08-06 * * @brief Handles creating user sessions, which is unique per openvpn connection. * */ #include #include #include #include #define EUREPHIA_FWINTF #include #include #include "eurephia_nullsafe.h" #include "eurephia_log.h" #include "eurephiadb_session.h" #include #include #include #include // Also defined in the eurephiadb_driver.h, but not as extern. extern char *(*eDBget_sessionkey_seed) (eurephiaCTX *ctx, sessionType type, 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); /** * Generates a new eurephia session. 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. * * @param ctx eurephiaCTX * @param digest Contains the clients SHA1 fingerprint / digest * @param cname Contains the clients X.509 Common Name field * @param username The user name of the client * @param vpnipaddr The IP address of the VPN connection of the client * @param vpnipmask The IP address' network mask * @param remipaddr The public IP address the client is connecting from * @param remport The remote port of the client connection * * @return returns a eurephiaSESSION pointer on success, otherwise NULL. */ 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]; size_t 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_nullsafe(ctx, sizeof(eurephiaSESSION) + 2); if( new_session == NULL ) { return NULL; } // Session type is stSESSION if we do have VPN address and/or netmask new_session->type = ((vpnipaddr == NULL) && (vpnipmask == NULL) ? stAUTHENTICATION : stSESSION); // Build up a string containing all elements for the session seed totlen = MAXLEN_TLSDIGEST + MAXLEN_CNAME + MAXLEN_USERNAME + MAXLEN_POOLIPADDR + MAXLEN_TRUSTEDIP + MAXLEN_TRUSTEDPORT + 5 + 15; // max length of: digest + cname + username + vpnipaddr + vpnipmask // + remipaddr + remport + pid + extra buffer seeddata = (char *) malloc_nullsafe(ctx, totlen); if( seeddata == NULL ) { free_nullsafe(ctx, new_session); return NULL; } snprintf(seeddata, totlen, "%.60s%.64s%.34s%.34s%.34s%.6s%05i", digest, cname, username, vpnipaddr, 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_nullsafe(ctx, (SHA512_HASH_SIZE*2)+3); if( seed == NULL ) { free_nullsafe(ctx, seeddata); free_nullsafe(ctx, new_session); return NULL; } ptr = seed; for( i = 0; i < SHA512_HASH_SIZE; i++ ) { sprintf(ptr, "%02x", sha_res[i]); ptr += 2; } 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, new_session->type, 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_nullsafe(ctx, (totlen*2)+4); if( skeydata == NULL ) { free_nullsafe(ctx, new_session->sessionkey); free_nullsafe(ctx, new_session); free_nullsafe(ctx, seeddata); free_nullsafe(ctx, 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_nullsafe(ctx, (totlen * 2)); if( rndstr == NULL ) { free_nullsafe(ctx, new_session->sessionkey); free_nullsafe(ctx, new_session); free_nullsafe(ctx, seeddata); free_nullsafe(ctx, seed); return NULL; } rndlen = ((totlen * 2) - strlen_nullsafe(seed) - 2); if( !eurephia_randstring(ctx, rndstr, rndlen) ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not generate enough random data for session key"); free_nullsafe(ctx, new_session->sessionkey); free_nullsafe(ctx, new_session); free_nullsafe(ctx, seeddata); free_nullsafe(ctx, seed); return NULL; } // Generate SHA512 version of the session data SHA512Init(&sha); SHA512Update(&sha, rndstr, rndlen); SHA512Final(&sha, sha_res); free_nullsafe(ctx, new_session->sessionkey); new_session->sessionkey = (char *) malloc_nullsafe(ctx, (SHA512_HASH_SIZE*2)+3); if( new_session->sessionkey == NULL ) { free_nullsafe(ctx, new_session); free_nullsafe(ctx, seeddata); free_nullsafe(ctx, seed); return NULL; } ptr = new_session->sessionkey; for( i = 0; i < SHA512_HASH_SIZE; i++ ) { sprintf(ptr, "%02x", sha_res[i]); ptr += 2; } memset(&sha, 0, sizeof(SHA512Context)); memset(&sha_res, 0, sizeof(sha_res)); free_nullsafe(ctx, rndstr); loop++; uniqcheck = eDBcheck_sessionkey_uniqueness(ctx, new_session->sessionkey); } while( (uniqcheck == 0) && loop < 11 ); free_nullsafe(ctx, 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(ctx, new_session->sessionkey); free_nullsafe(ctx, new_session); free_nullsafe(ctx, seeddata); free_nullsafe(ctx, 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(ctx, new_session->sessionkey); free_nullsafe(ctx, new_session); free_nullsafe(ctx, seeddata); free_nullsafe(ctx, 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(ctx, seed); free_nullsafe(ctx, 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; } /** * Load a session based on a known session key * * @param ctx eurephiaCTX * @param sesskey A string containing the session key * @param sesstype What kind of type the session data should be opend as (sessionType) * * @return Returns a pointer to the corresponding eurephiaSESSION struct on success, otherwise NULL. */ eurephiaSESSION *eDBsession_load(eurephiaCTX *ctx, const char *sesskey, sessionType sesstype) { eurephiaSESSION *new_session = NULL; DEBUG(ctx, 12, "Function call: eDBsession_load(ctx, '%s')", sesskey); new_session = (eurephiaSESSION *) malloc_nullsafe(ctx, sizeof(eurephiaSESSION) + 2); if( new_session == NULL ) { return NULL; } new_session->type = sesstype; // Get the sessionkey from the database new_session->sessionkey = strdup_nullsafe(sesskey); if( new_session->sessionkey == NULL ) { eurephia_log(ctx, LOG_CRITICAL, 0, "Failed to set the session key to '%s'", sesskey); free_nullsafe(ctx, new_session); return NULL; } // 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; } /** * Open an existing eurephia session based on a MAC address. This function is only used * when there is not enough information to generate a session seed and when the MAC address * is available. Usually this only happens when the client has disconnected and the session * is about to be marked as closed. * * @param ctx eurephiaCTX * @param macaddr MAC address of the client * * @return returns a eurephiaSESSION pointer on success, otherwise NULL. */ eurephiaSESSION *eDBopen_session_macaddr(eurephiaCTX *ctx, const char *macaddr) { char *sesskey = NULL; DEBUG(ctx, 12, "Function call: eDBopen_session_mac(ctx, '%s')", macaddr); // Get the sessionkey from the database sesskey = eDBget_sessionkey_macaddr(ctx, macaddr); if( sesskey == NULL ) { eurephia_log(ctx, LOG_CRITICAL, 0, "Could not find an active session for MAC address '%s'", macaddr); return NULL; } DEBUG(ctx, 13, "Session seed found, using sessionkey '%s'", sesskey); // Open and load the session from the database return eDBsession_load(ctx, sesskey, stSESSION); }