/* flatfile-auth.c -- Simple authentication plug-in demonstrating its use case * * Copyright (C) 2013 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, either version 3 of the License, or * (at your option) any later version. * * 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, see . * */ /** * @file flatfile-auth.c * @author David Sommerseth * @date 2013-05-29 * * @brief Simple example plug-in authenticating user passwords * against a simple flat file "database" */ #include #include #include #include #include #include #include #include #include static ePluginInfo pluginfo = { .name = "flatfile authentication", .version = "1.0", .copyright = "2013 (C) David Sommerseth ", .pluginType = eptAUTH, .APIversion = 1 }; static eurephiaVALUES *config = NULL; static eurephiaVALUES *passdb = NULL; /** * @copydoc PluginInfo() */ ePluginInfo * PluginInfo() { return &pluginfo; } /** * @copydoc PluginInit() */ int PluginInit(eurephiaCTX *ctx, const char *args) { FILE *fp = NULL; char line[4098]; config = eCreate_value_space(ctx, 80); if( config == NULL ) { eurephia_log(ctx, LOG_FATAL, 0, "Failed to initialise flatfile auth"); return 0; } eAdd_value(ctx, config, "password_file", args); // Load all username/password hashes into memory - *NOT RECOMMENDED* in real production environments fp = fopen(args, "r"); if( fp == NULL ) { eFree_values(ctx, config); eurephia_log(ctx, LOG_FATAL, 0, "flatfile-auth: Failed to open user/password file"); return 0; } passdb = eCreate_value_space(ctx, 81); if( config == NULL ) { eFree_values(ctx, config); fclose(fp); eurephia_log(ctx, LOG_FATAL, 0, "flatfile-auth: Failed to allocate memory to " "username/password database"); return 0; } memset(&line, 0, 4098); while( fgets(line, 4096, fp) ) { char *pw = strstr(line, "|"); if( pw != NULL ) { char *eol = strstr(pw, "\n"); *eol = 0; // Simple strip of \n at end of records *pw = 0; // Split the username and password using a NULL-terminator pw++; eAdd_value(ctx, passdb, line, pw); } else { eurephia_log(ctx, LOG_WARNING, 0, "flatfile-auth: Failed to parse record (no delimiter): %s", line); } memset(&line, 0, 4098); } fclose(fp); eurephia_log(ctx, LOG_INFO, 0, "flatfile auth initialised, using '%s' as user/password database", args); return 1; } /** * @copydoc PluginClose() */ void PluginClose(eurephiaCTX *ctx) { eFree_values(ctx, config); eurephia_log(ctx, LOG_INFO, 0, "flatfile auth stopped"); } /** * @copydoc AuthenticateUser() */ eAuthResult * AuthenticateUser(eurephiaCTX *ctx, const char *username, const char *passwd) { char *pwhash = NULL; eAuthResult *res = NULL; DEBUG(ctx, 10, "flatfile-auth:AuthenticateUser('%s', 'xxxxxx')", username); assert(passdb != NULL); // Allocate result buffer res = malloc_nullsafe(ctx, sizeof(eAuthResult)+2); if( res == NULL ) { return NULL; } // Look up password hash from in-memory password database pwhash = eGet_value(passdb, username); if( pwhash != NULL ) { // If found, verify the password char *crpwd = eurephia_pwd_crypt(ctx, passwd, pwhash); res->status = ((crpwd != NULL) && (strcmp(crpwd, pwhash) == 0) ? eAUTH_SUCCESS : eAUTH_FAILED); if( res->status == eAUTH_FAILED ) { res->msg = strdup("Wrong password"); } free_nullsafe(ctx, crpwd); } else { // If not found - fail res->status = eAUTH_FAILED; res->msg = malloc_nullsafe(ctx, strlen(username)+32); snprintf(res->msg, strlen(username)+30, "flatfile username attempted: %s", username); } return res; } /** * @copydoc ChangePassword() */ eAuthResult * ChangePassword(eurephiaCTX *ctx, const char *username, const char *oldpass, const char *newpass) { eurephiaVALUES *pwentry = NULL, *rec = NULL; char *crpwd = NULL; eAuthResult *res = NULL; FILE *fp = NULL; DEBUG(ctx, 10, "flatfile-auth:ChangePassword('%s', 'xxxOLDxxx', 'xxxNEWxxx')", username); assert((passdb != NULL) && (config != NULL)); // Allocate result buffer res = malloc_nullsafe(ctx, sizeof(eAuthResult)+2); if( res == NULL ) { return NULL; } // Find the password entry pwentry = eGet_valuestruct(passdb, username); if( pwentry == NULL ) { // If not found --> assume new user. eurephiaVALUES *newrec = eCreate_value_space(ctx, 81); if( newrec == NULL ) { res->status = eAUTH_PLGERROR; res->msg = strdup("Internal failure when preparing new user entry"); return res; } newrec->key = strdup(username); newrec->val = eurephia_pwd_crypt(ctx, oldpass, NULL); eAdd_valuestruct(ctx, passdb, newrec); } else { // Verify the password hash for the existing user crpwd = eurephia_pwd_crypt(ctx, oldpass, pwentry->val); if( (crpwd != NULL) && (strcmp(crpwd, pwentry->val) == 0) ) { // Password correct - update database // Get a new password hash free_nullsafe(ctx, crpwd); crpwd = eurephia_pwd_crypt(ctx, newpass, NULL); if( crpwd == NULL ) { res->status = eAUTH_PLGERROR; res->msg = strdup("Failed to generate new password hash"); return res; } } } // Open the database file with write access fp = fopen(eGet_value(config, "password_file"), "w"); if( fp == NULL ) { res->status = eAUTH_PLGERROR; res->msg = strdup("Failed to open password file with write access"); return res; } // Remove the old password hash from the memory database // and replace it with the new one free_nullsafe(ctx, pwentry->val); pwentry->val = crpwd; // Update the database file rewind(fp); for( rec = passdb; rec != NULL; rec = rec->next ) { fprintf(fp, "%s|%s\n", rec->key, rec->val); } fclose(fp); return res; }