authentication.c

Go to the documentation of this file.
00001 /* administration.c  --  Functions needed for administration tasks
00002  *
00003  *  GPLv2 only - Copyright (C) 2008 - 2010
00004  *               David Sommerseth <dazo@users.sourceforge.net>
00005  *
00006  *  This program is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU General Public License
00008  *  as published by the Free Software Foundation; version 2
00009  *  of the License.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  *
00020  */
00021 
00031 #include <string.h>
00032 #include <unistd.h>
00033 #include <assert.h>
00034 
00035 #include <libxml/tree.h>
00036 
00040 #ifndef DRIVERAPIVERSION
00041 # define DRIVERAPIVERSION 2
00042 #endif
00043 
00047 #include <sqlite3.h>
00048 
00049 #include <eurephia_nullsafe.h>
00050 #include <eurephia_context.h>
00051 #include <eurephia_log.h>
00052 #include <eurephia_xml.h>
00053 #include <eurephia_values.h>
00054 #include <eurephiadb_session_struct.h>
00055 #include <eurephiadb_mapping.h>
00056 #include <passwd.h>
00057 
00058 #ifndef DRIVER_MODE
00059 #define DRIVER_MODE
00060 #endif
00061 #include <eurephiadb_driver.h>
00062 
00063 #include "../sqlite.h"
00064 
00065 #if (DRIVERAPIVERSION > 1) || defined(DOXYGEN)
00066 /*
00067  *  API Version 2 functions
00068  *
00069  */
00070 
00071 
00085 static xmlDoc *auth_user(eurephiaCTX *ctx, const char *req_access, const char *uname, const char *pwd) {
00086         xmlDoc *res_d = NULL;
00087         xmlNode *info_n = NULL;
00088         dbresult *res = NULL;
00089         char *crpwd = NULL, *dbpwd = NULL;
00090         char *activated = NULL, *deactivated = NULL, *blid = NULL, *uid = NULL;
00091         int access = 0;
00092         char interface;
00093 
00094         DEBUG(ctx, 21, "Function call: auth_user(ctx, '%s, '%s', 'xxxxxxxx')", req_access, uname);
00095 
00096         assert(ctx != NULL);
00097 
00098         switch( ctx->context_type ) {
00099         case ECTX_ADMIN_CONSOLE:
00100                 interface = 'C';
00101                 break;
00102         case ECTX_ADMIN_WEB:
00103                 interface = 'W';
00104                 break;
00105         default:
00106                 eurephia_log(ctx, LOG_ERROR, 0, "Wrong eurephia context type (0x%04x)", ctx->context_type);
00107                 return NULL;
00108         }
00109 
00110         if( (strlen_nullsafe(uname) < 4) || (strlen_nullsafe(pwd) < 4) ) {
00111                 eurephia_log(ctx, LOG_WARNING, 0,
00112                              "Username and/or password is either null or less than 4 bytes");
00113                 return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Username or password is too short");
00114         }
00115 
00116         //
00117         // Authenticate user and password
00118         //
00119         res = sqlite_query(ctx,
00120                            "SELECT activated, deactivated, bl.blid, "
00121                            "       password, uid "
00122                            "  FROM openvpn_users ou"
00123                            "  LEFT JOIN openvpn_blacklist bl USING (username)"
00124                            " WHERE ou.username = '%q'",
00125                            uname);
00126 
00127         if( res == NULL ) {
00128                 eurephia_log(ctx, LOG_FATAL, 0, "Could not authenticate user against the database");
00129                 return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Authentication failed");
00130         }
00131 
00132         if( sqlite_get_numtuples(res) == 1 ) {
00133                 activated   = sqlite_get_value(res, 0, 0);
00134                 deactivated = sqlite_get_value(res, 0, 1);
00135                 blid        = sqlite_get_value(res, 0, 2);
00136                 dbpwd       = sqlite_get_value(res, 0, 3);
00137                 uid         = strdup_nullsafe(sqlite_get_value(res, 0, 4));
00138 
00139                 if( blid != NULL ) {
00140                         eurephia_log(ctx, LOG_WARNING, 0,
00141                                      "User account '%s' is BLACKLISTED.  You have no access.", uname);
00142                         sqlite_free_results(res);
00143                         return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Authentication failed");
00144                 }
00145 
00146                 if( activated == NULL ) {
00147                         eurephia_log(ctx, LOG_WARNING, 0, "User account '%s' is not yet activated.", uname);
00148                         sqlite_free_results(res);
00149                         return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Authentication failed");
00150                 }
00151 
00152                 if( deactivated != NULL ) {
00153                         eurephia_log(ctx, LOG_WARNING, 0, "User account '%s' is deactivated.", uname);
00154                         sqlite_free_results(res);
00155                         return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Authentication failed");
00156                 }
00157 
00158                 if( dbpwd == NULL ) {
00159                         eurephia_log(ctx, LOG_WARNING, 0, "Authentication failed. DB error.");
00160                         sqlite_free_results(res);
00161                         return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Authentication failed");
00162                 } else {
00163                         int pwdok = 0;
00164                         // Verify the password
00165                         crpwd = eurephia_pwd_crypt(ctx, pwd, dbpwd);
00166                         pwdok = ((crpwd != NULL) && (strcmp(crpwd, dbpwd) == 0) ? 1 : 0);
00167                         memset(crpwd, 0, strlen_nullsafe(crpwd));
00168                         memset(dbpwd, 0, strlen_nullsafe(dbpwd));
00169                         free_nullsafe(ctx, crpwd);
00170                         if( pwdok == 0 ) {
00171                                 eurephia_log(ctx, LOG_WARNING, 0, "Authentication failed.");
00172                                 sleep(2);
00173                                 sqlite_free_results(res);
00174                                 return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Authentication failed");
00175                         }
00176                 }
00177                 sqlite_free_results(res);
00178 
00179                 // Check if access level is granted
00180                 // (SQLite do not handle advanced joins so well, so we need to
00181                 //  do this check with an extra query)
00182                 res = sqlite_query(ctx,
00183                                    "SELECT (count(*) = 1) AS access "
00184                                    "  FROM eurephia_adminaccess"
00185                                    " WHERE uid = '%q' AND interface = '%c' AND access = '%q'",
00186                                    uid, interface, req_access);
00187 
00188                 if( res == NULL ) {
00189                         eurephia_log(ctx, LOG_FATAL, 0, "Could not check access level");
00190                         return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL,"Failed to validate access level");
00191                 }
00192                 access = atoi_nullsafe(sqlite_get_value(res, 0, 0));
00193                 sqlite_free_results(res);
00194 
00195                 if( access == 0 ) {
00196                         eurephia_log(ctx, LOG_WARNING, 0,
00197                                      "User account '%s' is lacking privileges for this operation", uname);
00198                         return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Authentication failed");
00199                 }
00200         } else {
00201                 eurephia_log(ctx, LOG_WARNING, 0, "Authentication failed. No unique records found.");
00202                 sqlite_free_results(res);
00203                 sleep(2);
00204                 return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Authentication failed");
00205         }
00206 
00207         // If we reach this place, authentication was successful.  Return users uid
00208         info_n = xmlNewNode(NULL, (xmlChar *) "UserAccount");
00209         assert( info_n != NULL );
00210         xmlNewProp(info_n, (xmlChar *) "uid", (xmlChar *) uid);
00211         res_d =  eurephiaXML_ResultMsg(ctx, exmlRESULT, info_n, "Successful authentication");
00212         xmlFreeNode(info_n);
00213         free_nullsafe(ctx, uid);
00214         return res_d;
00215 }
00216 
00217 
00229 static xmlDoc *auth_session(eurephiaCTX *ctx, const char *sesskey, const char *req_access) {
00230         dbresult *res = NULL;
00231         int valid = 0, access = 0, expire_time = 0;
00232         char interface;
00233 
00234         DEBUG(ctx, 21, "Function call: auth_session(ctx, '%s, '%s')", sesskey, req_access);
00235         assert( (ctx != NULL) && (sesskey != NULL) );
00236 
00237         switch( ctx->context_type ) {
00238         case ECTX_ADMIN_CONSOLE:
00239                 interface = 'C';
00240                 break;
00241         case ECTX_ADMIN_WEB:
00242                 interface = 'W';
00243                 break;
00244         default:
00245                 eurephia_log(ctx, LOG_ERROR, 0, "Wrong eurephia context type (0x%04x)", ctx->context_type);
00246                 return NULL;
00247         }
00248 
00249         // Check if the session is still valid (not expired) and that this session are allowed to access
00250         // the requested access level.
00251         expire_time = (60 * atoi_nullsafe(defaultValue(eGet_value(ctx->dbc->config,
00252                                                                   "eurephiadmin_autologout"),
00253                                                        "10")
00254                                           )
00255                        );
00256         res = sqlite_query(ctx,
00257                            "SELECT (strftime('%%s',CURRENT_TIMESTAMP)-strftime('%%s',last_action)) > %i AS exp,"
00258                            "       (access IS NOT NULL) AS access"
00259                            "  FROM eurephia_adminlog"
00260                            "  LEFT JOIN eurephia_adminaccess USING(uid,interface)"
00261                            " WHERE status IN (1,2)"
00262                            "       AND sessionkey = '%q'"
00263                            "       AND access = '%q'"
00264                            "       AND interface = '%c'",
00265                            expire_time, sesskey, req_access, interface);
00266 
00267         if( (res == NULL) ) {
00268                 eurephia_log(ctx, LOG_FATAL, 0, "Could not validate session");
00269                 return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Session authentication failed");
00270         }
00271 
00272         valid  = (atoi_nullsafe(sqlite_get_value(res, 0, 0)) == 0);
00273         access = (atoi_nullsafe(sqlite_get_value(res, 0, 1)) == 1);
00274         sqlite_free_results(res);
00275 
00276         // If still valid, update last_action
00277         if( valid && access ) {
00278                 res = sqlite_query(ctx,
00279                                    "UPDATE eurephia_adminlog"
00280                                    "   SET last_action = CURRENT_TIMESTAMP, status = 2"
00281                                    " WHERE sessionkey = '%q'", sesskey);
00282                 if( res == NULL ) {
00283                         eurephia_log(ctx, LOG_ERROR, 0, "Could not register session activity");
00284                 }
00285                 sqlite_free_results(res);
00286 
00287         } else {
00288                 // If not valid, register session as auto-logged out
00289                 res = sqlite_query(ctx,
00290                                    "UPDATE eurephia_adminlog"
00291                                    "   SET logout = CURRENT_TIMESTAMP, status = %i"
00292                                    " WHERE sessionkey = '%q'",
00293                                    (access ? 4 : 5), sesskey);
00294                 if( res == NULL ) {
00295                         eurephia_log(ctx, LOG_ERROR, 0, "Could not register old session as logged out");
00296                 }
00297                 sqlite_free_results(res);
00298 
00299                 // Delete session variables
00300                 res = sqlite_query(ctx, "DELETE FROM openvpn_sessions WHERE sessionkey = '%q'",
00301                                    sesskey);
00302                 if( res == NULL ) {
00303                         eurephia_log(ctx, LOG_ERROR, 0,
00304                                      "Could not delete session variables (%s))", sesskey);
00305                 } else if( !access ) {
00306                         eurephia_log(ctx, LOG_WARNING, 0, "User account is lacking privileges");
00307                 }
00308                 sqlite_free_results(res);
00309         }
00310 
00311         if (valid && access) {
00312                 return eurephiaXML_ResultMsg(ctx, exmlRESULT, NULL, "Session authenticated");
00313         } else {
00314                 return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Session authentication failed");
00315 
00316         }
00317 }
00318 
00319 
00330 static xmlDoc *register_login(eurephiaCTX *ctx, const int uid, const char *sesskey) {
00331         dbresult *res = NULL;
00332         char interface;
00333 
00334         DEBUG(ctx, 21, "Function call: register_login(ctx, %i, '%s')", uid, sesskey);
00335         assert( ctx != NULL );
00336 
00337         if( (sesskey == NULL) || (uid < 1) ) {
00338                 return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL, "Invalid data for login registration");
00339         }
00340 
00341         switch( ctx->context_type ) {
00342         case ECTX_ADMIN_CONSOLE:
00343                 interface = 'C'; break;
00344         case ECTX_ADMIN_WEB:
00345                 interface = 'W'; break;
00346         default:
00347                 eurephia_log(ctx, LOG_ERROR, 0, "Wrong eurephia context type (0x%04x)", ctx->context_type);
00348                 return NULL;
00349         }
00350 
00351         // Register login into eurephia_adminlog ... uid, login, interface, sessionkey
00352         res = sqlite_query(ctx,
00353                            "INSERT INTO eurephia_adminlog "
00354                            "       (uid, interface, status, login, last_action, sessionkey) "
00355                            "VALUES ('%i','%c',1,CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, '%q')",
00356                            uid, interface, sesskey);
00357         if( !res ) {
00358                 eurephia_log(ctx, LOG_FATAL, 0, "Failed to register the session in the database");
00359                 return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL,
00360                                              "Failed to register the session in the database");
00361         }
00362         sqlite_free_results(res);
00363         return eurephiaXML_ResultMsg(ctx, exmlRESULT, NULL, "Session is registered as logged in");
00364 }
00365 
00366 
00376 static xmlDoc *register_logout(eurephiaCTX *ctx, const char *sessionkey) {
00377         dbresult *res = NULL;
00378 
00379         DEBUG(ctx, 21, "Function call: register_logout(ctx, '%s')", sessionkey);
00380         assert((ctx != NULL) && (sessionkey != NULL));
00381 
00382         if( (ctx->context_type != ECTX_ADMIN_CONSOLE) && (ctx->context_type != ECTX_ADMIN_WEB) ) {
00383                 eurephia_log(ctx, LOG_CRITICAL, 0,
00384                              "eurephia admin function call attempted with wrong context type");
00385                 return NULL;
00386         }
00387 
00388         // Update session as logged out
00389         res = sqlite_query(ctx,
00390                            "UPDATE eurephia_adminlog "
00391                            "   SET logout = CURRENT_TIMESTAMP, status = 3"
00392                            " WHERE sessionkey = '%q'",
00393                            sessionkey);
00394         if( !res || (sqlite_get_affected_rows(res) == 0) ) {
00395                 eurephia_log(ctx, LOG_FATAL, 0,
00396                              "Failed to register the session as logged out (updated %i rows)",
00397                              sqlite_get_affected_rows(res));
00398                 return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL,
00399                                              "Failed to register the session as logged out");
00400         }
00401         sqlite_free_results(res);
00402 
00403         // Delete session variables
00404         res = sqlite_query(ctx, "DELETE FROM openvpn_sessions WHERE sessionkey = '%q'", sessionkey);
00405         if( !res || (sqlite_get_affected_rows(res) == 0) ) {
00406                 eurephia_log(ctx, LOG_ERROR, 0,
00407                              "Could not delete session variables (%s))", sessionkey);
00408                 return eurephiaXML_ResultMsg(ctx, exmlERROR, NULL,
00409                                              "Could not delete session variables (%s))", sessionkey);
00410         }
00411         sqlite_free_results(res);
00412 
00413         return eurephiaXML_ResultMsg(ctx, exmlRESULT, NULL, "Session is logged out");
00414 }
00415 
00416 
00420 xmlDoc *eDBadminAuthenticate(eurephiaCTX *ctx, xmlDoc *qryxml) {
00421         xmlDoc *res_d = NULL;
00422         xmlNode *qry_n = NULL;
00423         char *mode = NULL;
00424         int type = 0;
00425 
00426         DEBUG(ctx, 20, "Function call: eDBadminAuthenticate(ctx, xmlDoc)");
00427         assert( (ctx != NULL) && (qryxml != NULL) );
00428 
00429         if( (ctx->context_type != ECTX_ADMIN_CONSOLE) && (ctx->context_type != ECTX_ADMIN_WEB) ) {
00430                 eurephia_log(ctx, LOG_CRITICAL, 0,
00431                              "eurephia admin function call attempted with wrong context type");
00432                 return NULL;
00433         }
00434 
00435         qry_n = eurephiaXML_getRoot(ctx, qryxml, "Authenticate", 1);
00436         if( qry_n != NULL ) {
00437                 type = 1;
00438                 goto accept;
00439         }
00440 
00441         qry_n = eurephiaXML_getRoot(ctx, qryxml, "Register", 1);
00442         if( qry_n != NULL ) {
00443                 type = 2;
00444                 goto accept;
00445         }
00446 
00447         eurephia_log(ctx, LOG_ERROR, 0, "Could not find a valid XML request for eDBadminAuthenticate()");
00448         return NULL;
00449 
00450  accept:
00451         mode = xmlGetAttrValue(qry_n->properties, "mode");
00452         if( mode == NULL ) {
00453                 eurephia_log(ctx, LOG_ERROR, 0, "Invalid authentication request");
00454                 return NULL;
00455         }
00456 
00457         switch( type ) {
00458         case 1: // Authenticate tag
00459                 if( strcmp(mode, "user") == 0 ) {
00460                         // const char *req_access, const char *uname, const char *pwd
00461                         const char *reqacc = NULL, *uname = NULL, *pwd = NULL;
00462                         uname  = xmlGetNodeContent(qry_n, "username");
00463                         pwd    = xmlGetNodeContent(qry_n, "password");
00464                         reqacc = xmlGetNodeContent(qry_n, "accesslevel");
00465                         res_d  = auth_user(ctx, reqacc, uname, pwd);
00466                 } else if ( strcmp(mode, "session") == 0 ) {
00467                         const char *sesskey = NULL, *reqacc = NULL;
00468                         sesskey = xmlGetNodeContent(qry_n, "sessionkey");
00469                         reqacc  = xmlGetNodeContent(qry_n, "accesslevel");
00470                         res_d   = auth_session(ctx, sesskey, reqacc);
00471                 }
00472                 break;
00473 
00474         case 2: // Register tag
00475                 if( strcmp(mode, "login") == 0 ) {
00476                         const char *sesskey = NULL;
00477                         unsigned int uid = 0;
00478 
00479                         uid     = atoi_nullsafe(xmlGetAttrValue(qry_n->properties, "uid"));
00480                         sesskey = xmlExtractContent(qry_n);
00481                         res_d   = register_login(ctx, uid, sesskey);
00482                 } else if( strcmp(mode, "logout") == 0 ) {
00483                         const char *sesskey = NULL;
00484 
00485                         sesskey = xmlExtractContent(qry_n);
00486                         res_d   = register_logout(ctx, sesskey);
00487                 }
00488                 break;
00489 
00490         default:
00491                 eurephia_log(ctx, LOG_FATAL, 0, "The unthinkable has just happened (type %i)", type);
00492                 res_d = NULL;
00493                 break;
00494         }
00495         return res_d;
00496 }
00497 
00498 #endif
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines