1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdbool.h>
5 #include <string.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <inttypes.h>
10
11 #include "authfile.h"
12 #include "util.h"
13
14 // TODO: frontend needs a refactor so this can avoid global objects.
15
16 #define MAX_ENTRY_LEN 256
17 // Not supposed to be a huge database!
18 #define MAX_ENTRIES 8
19
20 typedef struct auth_entry {
21 char *user;
22 size_t ulen;
23 char *pass;
24 size_t plen;
25 } auth_t;
26
27 auth_t main_auth_entries[MAX_ENTRIES];
28 int entry_cnt = 0;
29 char *main_auth_data = NULL;
30
authfile_load(const char * file)31 enum authfile_ret authfile_load(const char *file) {
32 struct stat sb;
33 char *auth_data = NULL;
34 auth_t auth_entries[MAX_ENTRIES];
35
36 FILE *pwfile = fopen(file, "r");
37 if (pwfile == NULL) {
38 return AUTHFILE_OPENFAIL;
39 } else if (fstat(fileno(pwfile), &sb)) {
40 fclose(pwfile);
41 return AUTHFILE_STATFAIL;
42 }
43
44 auth_data = calloc(1, sb.st_size + 2);
45
46 char *auth_cur = auth_data;
47 // fgets will stop at EOF or a newline, reading at most one bytes less
48 // than the size limit. If a user supplies a file without an ending
49 // newline we will end up chopping the last character of the password.
50 char *auth_end = auth_data + sb.st_size + 1;
51 auth_t *entry_cur = auth_entries;
52 int used = 0;
53
54 while ((fgets(auth_cur, auth_end - auth_cur < MAX_ENTRY_LEN ? auth_end - auth_cur : MAX_ENTRY_LEN, pwfile)) != NULL) {
55 int x;
56 int found = 0;
57
58 for (x = 0; x < MAX_ENTRY_LEN; x++) {
59 if (!found) {
60 if (auth_cur[x] == '\0') {
61 // The username is malformed - this is either the end of the file or a null byte.
62 break;
63 } else if (auth_cur[x] == ':') {
64 entry_cur->user = auth_cur;
65 entry_cur->ulen = x;
66 entry_cur->pass = &auth_cur[x+1];
67 found = 1;
68 }
69 } else {
70 // Find end of password.
71 if (auth_cur[x] == '\n' ||
72 auth_cur[x] == '\r' ||
73 auth_cur[x] == '\0') {
74 entry_cur->plen = x - (entry_cur->ulen + 1);
75 break;
76 }
77 }
78 }
79
80 // malformed line.
81 if (!found) {
82 (void)fclose(pwfile);
83 free(auth_data);
84 return AUTHFILE_MALFORMED;
85 }
86
87 // FIXME: no silent truncation.
88 if (++used == MAX_ENTRIES) {
89 break;
90 }
91 // EOF
92 if (auth_cur[x] == '\0')
93 break;
94
95 auth_cur += x;
96 entry_cur++;
97 }
98
99 // swap the main pointer out now, so if there's an error reloading we
100 // don't break the existing authentication.
101 if (main_auth_data != NULL) {
102 free(main_auth_data);
103 }
104
105 entry_cnt = used;
106 main_auth_data = auth_data;
107 memcpy(main_auth_entries, auth_entries, sizeof(auth_entries));
108
109 (void)fclose(pwfile);
110
111 return AUTHFILE_OK;
112 }
113
114 // if only loading the file could be this short...
authfile_check(const char * user,const char * pass)115 int authfile_check(const char *user, const char *pass) {
116 size_t ulen = strlen(user);
117 size_t plen = strlen(pass);
118
119 for (int x = 0; x < entry_cnt; x++) {
120 auth_t *e = &main_auth_entries[x];
121 if (ulen == e->ulen && plen == e->plen &&
122 safe_memcmp(user, e->user, e->ulen) &&
123 safe_memcmp(pass, e->pass, e->plen)) {
124 return 1;
125 }
126 }
127
128 return 0;
129 }
130