1 #include <config.h>
2 
3 #ifdef WITH_PUREDB
4 
5 #include "ftpd.h"
6 #include "messages.h"
7 #include "log_puredb.h"
8 #include "pure-pw.h"
9 #include "../puredb/src/puredb_read.h"
10 #include "utils.h"
11 
12 #ifdef HAVE_LIBSODIUM
13 # include <sodium.h>
14 #endif
15 
16 #ifdef WITH_DMALLOC
17 # include <dmalloc.h>
18 #endif
19 
20 static char *pdb_filename;
21 
pw_puredb_parse(const char * const file)22 void pw_puredb_parse(const char * const file)
23 {
24     if (file == NULL || *file == 0) {
25         die(421, LOG_ERR, MSG_NO_VIRTUAL_FILE);
26     }
27     if ((pdb_filename = strdup(file)) == NULL) {
28         die_mem();
29     }
30 }
31 
pw_puredb_exit(void)32 void pw_puredb_exit(void)
33 {
34     free(pdb_filename);
35 }
36 
37 /*
38  * The difference between this strtok() and the libc's one is that
39  * this one doesn't skip empty fields, and takes a char instead of a
40  * string as a delimiter.
41  * This strtok2() variant leaves zeroes.
42  */
43 
my_strtok2(char * str,const char delim)44 static char *my_strtok2(char *str, const char delim)
45 {
46     static char *s;
47     static char save;
48 
49     if (str != NULL) {
50         if (*str == 0) {
51             return NULL;
52         }
53         s = str;
54         scan:
55         while (*s != 0 && *s != delim) {
56             s++;
57         }
58         save = *s;
59         *s = 0;
60 
61         return str;
62     }
63     if (s == NULL || save == 0) {
64         return NULL;
65     }
66     s++;
67     str = s;
68 
69     goto scan;
70 }
71 
72 /* Check whether an IP address matches a pattern. 1 = match 0 = nomatch */
73 
access_ip_match(const struct sockaddr_storage * const sa,char * pattern)74 static int access_ip_match(const struct sockaddr_storage * const sa,
75                            char * pattern)
76 {
77     unsigned int ip0, ip1, ip2, ip3;
78     unsigned int netbits;
79     unsigned long ip;
80     unsigned long mask;
81     unsigned long saip;
82     const unsigned char *saip_raw;
83     char *comapoint;
84 
85     if (*pattern == 0) {
86         return 1;
87     }
88     if (STORAGE_FAMILY(*sa) != AF_INET) {
89         return 0;                      /* TODO: IPv6 */
90     }
91     do {
92         if ((comapoint = strchr(pattern, ',')) != NULL) {
93             *comapoint = 0;
94         }
95         if (sscanf(pattern, "%u.%u.%u.%u/%u",    /* IPv4 */
96                    &ip0, &ip1, &ip2, &ip3, &netbits) == 5) {
97             ipcheck:
98             if (STORAGE_FAMILY(*sa) != AF_INET || netbits == 0U) {
99                 return -1;
100             }
101             ip =
102                 ((unsigned long) ip0 << 24) |
103                 ((unsigned long) ip1 << 16) |
104                 ((unsigned long) ip2 << 8) |
105                 (unsigned long) ip3;
106             ipcheck_ipdone:
107             mask = ~((0x80000000 >> (netbits - 1U)) - 1U);
108             saip_raw = (const unsigned char *) &(STORAGE_SIN_ADDR_CONST(*sa));
109             saip =
110                 ((unsigned long) saip_raw[0] << 24) |
111                 ((unsigned long) saip_raw[1] << 16) |
112                 ((unsigned long) saip_raw[2] << 8) |
113                 (unsigned long) saip_raw[3];
114             if ((ip & mask) == (saip & mask)) {
115                 return 1;
116             }
117         } else if (sscanf(pattern, "%u.%u.%u.%u",
118                           &ip0, &ip1, &ip2, &ip3) == 4) {
119             netbits = 32U;
120             goto ipcheck;
121         } else {
122             struct addrinfo hints, *res;
123             int on;
124 
125             memset(&hints, 0, sizeof hints);
126             hints.ai_family = AF_INET;
127             hints.ai_addr = NULL;
128             if ((on = getaddrinfo(pattern, NULL, &hints, &res)) != 0) {
129                 logfile(LOG_WARNING, "puredb: [%s] => [%d]", pattern, on);
130             } else if (res->ai_family != AF_INET) {
131                 freeaddrinfo(res);
132             } else {
133                 const unsigned char * const ip_raw =
134                     (const unsigned char *) &
135                     (((const struct sockaddr_in *) (void *)
136                       (res->ai_addr))->sin_addr.s_addr);
137 
138                 ip =
139                     ((unsigned long) ip_raw[0] << 24) |
140                     ((unsigned long) ip_raw[1] << 16) |
141                     ((unsigned long) ip_raw[2] << 8) |
142                     (unsigned long) ip_raw[3];
143                 netbits = 32U;
144                 freeaddrinfo(res);
145                 goto ipcheck_ipdone;
146             }
147         }
148         if (comapoint == NULL) {
149             break;
150         }
151         *comapoint = ',';
152         pattern = comapoint + 1;
153     } while (*pattern != 0);
154 
155     return 0;
156 }
157 
158 /* IP check. 0 = ok, -1 = denied */
159 
access_ip_check(const struct sockaddr_storage * const sa,char * const allow,char * const deny)160 static int access_ip_check(const struct sockaddr_storage * const sa,
161                            char * const allow, char * const deny)
162 {
163     if (sa == NULL) {
164         return 0;
165     }
166     if (*allow == 0) {
167         if (*deny == 0) {
168             return 0;
169         }
170         if (access_ip_match(sa, deny) != 0) {
171             return -1;
172         }
173         return 0;
174     }
175     if (*deny == 0) {
176         if (access_ip_match(sa, allow) != 0) {
177             return 0;
178         }
179         return -1;
180     }
181     if (access_ip_match(sa, allow) != 0 && access_ip_match(sa, deny) == 0) {
182         return 0;
183     }
184     return -1;
185 }
186 
time_restrictions_check(const char * const restrictions)187 static int time_restrictions_check(const char * const restrictions)
188 {
189     const struct tm *tm;
190     time_t now_t;
191     unsigned int time_begin, time_end;
192     unsigned int now;
193 
194     if (*restrictions == 0) {
195         return 0;
196     }
197     if (sscanf(restrictions, "%u-%u", &time_begin, &time_end) != 2 ||
198         (now_t = time(NULL)) == (time_t) -1 ||
199         (tm = localtime(&now_t)) == NULL) {
200         return 0;
201     }
202     now = (unsigned int) tm->tm_hour * 100U + (unsigned int) tm->tm_min;
203     if (time_begin <= time_end) {
204         if (time_begin <= now && now <= time_end) {
205             return 0;
206         }
207         return -1;
208     }
209     if (now >= time_begin || now <= time_end) {
210         return 0;
211     }
212     return -1;
213 }
214 
pw_puredb_parseline(char * line,const char * const pwd,const struct sockaddr_storage * const sa,const struct sockaddr_storage * const peer,AuthResult * const result)215 static int pw_puredb_parseline(char *line, const char * const pwd,
216                                const struct sockaddr_storage * const sa,
217                                const struct sockaddr_storage * const peer,
218                                AuthResult * const result)
219 {
220     char *allow_local_ip, *deny_local_ip;
221     char *allow_remote_ip, *deny_remote_ip;
222     const char *time_restrictions;
223 
224     if ((line = my_strtok2(line, *PW_LINE_SEP)) == NULL || *line == 0) {   /* pwd */
225         return -1;
226     }
227     {
228         const char *crypted;
229         int         ret = -1;
230 
231 #ifdef crypto_pwhash_STRPREFIX
232         if (crypto_pwhash_str_verify(line, pwd, strlen(pwd)) == 0) {
233             /* pass */
234         } else
235 #endif
236 #ifdef crypto_pwhash_scryptsalsa208sha256_STRPREFIX
237         if (crypto_pwhash_scryptsalsa208sha256_str_verify
238             (line, pwd, strlen(pwd)) == 0) {
239             /* pass */
240         } else
241 #endif
242         {
243             ret = - ((crypted = (const char *) crypt(pwd, line)) == NULL ||
244                      pure_strcmp(line, crypted) != 0);
245             if (ret != 0) {
246                 return -1;
247             }
248         }
249     }
250     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL || *line == 0) {   /* uid */
251         return -1;
252     }
253     result->uid = (uid_t) strtoul(line, NULL, 10);
254     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL || *line == 0) {   /* gid */
255         return -1;
256     }
257     result->gid = (gid_t) strtoul(line, NULL, 10);
258 #ifndef ACCEPT_ROOT_VIRTUAL_USERS
259     if (result->uid <= (uid_t) 0 || result->gid <= (gid_t) 0) {
260         return -1;
261     }
262 #endif
263     if (my_strtok2(NULL, *PW_LINE_SEP) == NULL) {   /* gecos */
264         return -1;
265     }
266     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL || *line == 0) {   /* home */
267         return -1;
268     }
269     if ((result->dir = strdup(line)) == NULL || *result->dir != '/') {
270         return -1;
271     }
272     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* bw_ul */
273         return 0;
274     }
275 #ifdef THROTTLING
276     if (*line != 0) {
277         result->throttling_ul_changed = 1;
278         result->throttling_bandwidth_ul = strtoul(line, NULL, 10);
279     }
280 #endif
281     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* bw_dl */
282         return 0;
283     }
284 #ifdef THROTTLING
285     if (*line != 0) {
286         result->throttling_dl_changed = 1;
287         result->throttling_bandwidth_dl = strtoul(line, NULL, 10);
288     }
289 #endif
290     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* ratio up */
291         return 0;
292     }
293 #ifdef RATIOS
294     if (*line != 0) {
295         result->ratio_upload = (unsigned int) strtoul(line, NULL, 10);
296         if (result->ratio_upload > 0U) {
297             result->ratio_ul_changed = 1;
298         }
299     }
300 #endif
301     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* ratio down */
302         return 0;
303     }
304 #ifdef RATIOS
305     if (*line != 0) {
306         result->ratio_download = (unsigned int) strtoul(line, NULL, 10);
307         if (result->ratio_download > 0U) {
308             result->ratio_dl_changed = 1;
309         }
310     }
311 #endif
312     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* max cnx */
313         return 0;
314     }
315 #ifdef PER_USER_LIMITS
316     if (*line != 0) {
317         result->per_user_max = (unsigned int) strtoull(line, NULL, 10);
318     }
319 #endif
320     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* files quota */
321         return 0;
322     }
323 #ifdef QUOTAS
324     if (*line != 0) {
325         result->quota_files_changed = 1;
326         result->user_quota_files = strtoull(line, NULL, 10);
327     }
328 #endif
329     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* size quota */
330         return 0;
331     }
332 #ifdef QUOTAS
333     if (*line != 0) {
334         result->quota_size_changed = 1;
335         result->user_quota_size = strtoull(line, NULL, 10);
336     }
337 #endif
338     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* allowed local ip */
339         return 0;
340     }
341     allow_local_ip = line;
342     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* denied local ip */
343         return 0;
344     }
345     deny_local_ip = line;
346     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* allowed remote ip */
347         return 0;
348     }
349     allow_remote_ip = line;
350     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* denied remote ip */
351         return 0;
352     }
353     deny_remote_ip = line;
354     if (access_ip_check(sa, allow_local_ip, deny_local_ip) != 0 ||
355         access_ip_check(peer, allow_remote_ip, deny_remote_ip) != 0) {
356         return -1;
357     }
358     if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) {   /* time restrictions */
359         return 0;
360     }
361     time_restrictions = line;
362     if (time_restrictions_check(time_restrictions) != 0) {
363         return -1;
364     }
365 
366     return 0;
367 }
368 
pw_puredb_check(AuthResult * const result,const char * account,const char * password,const struct sockaddr_storage * const sa,const struct sockaddr_storage * const peer)369 void pw_puredb_check(AuthResult * const result,
370                      const char *account, const char *password,
371                      const struct sockaddr_storage * const sa,
372                      const struct sockaddr_storage * const peer)
373 {
374     char *line = NULL;
375     PureDB db;
376     off_t retpos;
377     size_t retlen;
378 
379     result->auth_ok = 0;
380     (void) sa;
381     (void) peer;
382     if (puredb_open(&db, pdb_filename) != 0) {
383         die(421, LOG_ERR, MSG_PDB_BROKEN);
384     }
385     if (puredb_find_s(&db, account, &retpos, &retlen) != 0) {
386         goto bye;
387     }
388     if ((line = puredb_read(&db, retpos, retlen)) == NULL) {
389         goto bye;
390     }
391     result->auth_ok--;
392     if (pw_puredb_parseline(line, password, sa, peer, result) != 0) {
393         goto bye;
394     }
395     result->slow_tilde_expansion = 1;
396     result->auth_ok = -result->auth_ok;
397     bye:
398     puredb_read_free(line);
399     puredb_close(&db);
400 }
401 
402 #endif
403