summaryrefslogtreecommitdiffstats
path: root/auth/flatfile/flatfile-auth.c
blob: 141fcd3808cd79188d022cbec44b3d152e43d6a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*  flatfile-auth.c  -- Simple authentication plug-in demonstrating its use case
 *
 *  Copyright (C) 2013          David Sommerseth <dazo@users.sourceforge.net>
 *
 *  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 <http://www.gnu.org/licenses/>.
 *
 */

/**
 * @file   flatfile-auth.c
 * @author David Sommerseth <dazo@users.sourceforge.net>
 * @date   2013-05-29
 *
 * @brief  Simple example plug-in authenticating user passwords
 *         against a simple flat file "database"
 */

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <eurephia_nullsafe.h>
#include <eurephia_context.h>
#include <eurephia_log.h>
#include <eurephia_values.h>
#include <eurephia_authplugin_driver.h>
#include <passwd.h>

static ePluginInfo pluginfo = { .name = "flatfile authentication",
                                .version = "1.0",
                                .copyright = "2013 (C) David Sommerseth <dazo@users.sourceforge.net>",
                                .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;
}