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