123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- #include <config.h>
- #ifdef WITH_PUREDB
- #include "ftpd.h"
- #include "messages.h"
- #include "log_puredb.h"
- #include "pure-pw.h"
- #include "../puredb/src/puredb_read.h"
- #include "utils.h"
- #ifdef HAVE_LIBSODIUM
- # include <sodium.h>
- #endif
- #ifdef WITH_DMALLOC
- # include <dmalloc.h>
- #endif
- static char *pdb_filename;
- void pw_puredb_parse(const char * const file)
- {
- if (file == NULL || *file == 0) {
- die(421, LOG_ERR, MSG_NO_VIRTUAL_FILE);
- }
- if ((pdb_filename = strdup(file)) == NULL) {
- die_mem();
- }
- }
- void pw_puredb_exit(void)
- {
- free(pdb_filename);
- }
- /*
- * The difference between this strtok() and the libc's one is that
- * this one doesn't skip empty fields, and takes a char instead of a
- * string as a delimiter.
- * This strtok2() variant leaves zeroes.
- */
- static char *my_strtok2(char *str, const char delim)
- {
- static char *s;
- static char save;
- if (str != NULL) {
- if (*str == 0) {
- return NULL;
- }
- s = str;
- scan:
- while (*s != 0 && *s != delim) {
- s++;
- }
- save = *s;
- *s = 0;
- return str;
- }
- if (s == NULL || save == 0) {
- return NULL;
- }
- s++;
- str = s;
- goto scan;
- }
- /* Check whether an IP address matches a pattern. 1 = match 0 = nomatch */
- static int access_ip_match(const struct sockaddr_storage * const sa,
- char * pattern)
- {
- unsigned int ip0, ip1, ip2, ip3;
- unsigned int netbits;
- unsigned long ip;
- unsigned long mask;
- unsigned long saip;
- const unsigned char *saip_raw;
- char *comapoint;
- if (*pattern == 0) {
- return 1;
- }
- if (STORAGE_FAMILY(*sa) != AF_INET) {
- return 0; /* TODO: IPv6 */
- }
- do {
- if ((comapoint = strchr(pattern, ',')) != NULL) {
- *comapoint = 0;
- }
- if (sscanf(pattern, "%u.%u.%u.%u/%u", /* IPv4 */
- &ip0, &ip1, &ip2, &ip3, &netbits) == 5) {
- ipcheck:
- if (STORAGE_FAMILY(*sa) != AF_INET || netbits == 0U) {
- return -1;
- }
- ip =
- ((unsigned long) ip0 << 24) |
- ((unsigned long) ip1 << 16) |
- ((unsigned long) ip2 << 8) |
- (unsigned long) ip3;
- ipcheck_ipdone:
- mask = ~((0x80000000 >> (netbits - 1U)) - 1U);
- saip_raw = (const unsigned char *) &(STORAGE_SIN_ADDR_CONST(*sa));
- saip =
- ((unsigned long) saip_raw[0] << 24) |
- ((unsigned long) saip_raw[1] << 16) |
- ((unsigned long) saip_raw[2] << 8) |
- (unsigned long) saip_raw[3];
- if ((ip & mask) == (saip & mask)) {
- return 1;
- }
- } else if (sscanf(pattern, "%u.%u.%u.%u",
- &ip0, &ip1, &ip2, &ip3) == 4) {
- netbits = 32U;
- goto ipcheck;
- } else {
- struct addrinfo hints, *res;
- int on;
- memset(&hints, 0, sizeof hints);
- hints.ai_family = AF_INET;
- hints.ai_addr = NULL;
- if ((on = getaddrinfo(pattern, NULL, &hints, &res)) != 0) {
- logfile(LOG_WARNING, "puredb: [%s] => [%d]", pattern, on);
- } else if (res->ai_family != AF_INET) {
- freeaddrinfo(res);
- } else {
- const unsigned char * const ip_raw =
- (const unsigned char *) &
- (((const struct sockaddr_in *) (void *)
- (res->ai_addr))->sin_addr.s_addr);
- ip =
- ((unsigned long) ip_raw[0] << 24) |
- ((unsigned long) ip_raw[1] << 16) |
- ((unsigned long) ip_raw[2] << 8) |
- (unsigned long) ip_raw[3];
- netbits = 32U;
- freeaddrinfo(res);
- goto ipcheck_ipdone;
- }
- }
- if (comapoint == NULL) {
- break;
- }
- *comapoint = ',';
- pattern = comapoint + 1;
- } while (*pattern != 0);
- return 0;
- }
- /* IP check. 0 = ok, -1 = denied */
- static int access_ip_check(const struct sockaddr_storage * const sa,
- char * const allow, char * const deny)
- {
- if (sa == NULL) {
- return 0;
- }
- if (*allow == 0) {
- if (*deny == 0) {
- return 0;
- }
- if (access_ip_match(sa, deny) != 0) {
- return -1;
- }
- return 0;
- }
- if (*deny == 0) {
- if (access_ip_match(sa, allow) != 0) {
- return 0;
- }
- return -1;
- }
- if (access_ip_match(sa, allow) != 0 && access_ip_match(sa, deny) == 0) {
- return 0;
- }
- return -1;
- }
- static int time_restrictions_check(const char * const restrictions)
- {
- const struct tm *tm;
- time_t now_t;
- unsigned int time_begin, time_end;
- unsigned int now;
- if (*restrictions == 0) {
- return 0;
- }
- if (sscanf(restrictions, "%u-%u", &time_begin, &time_end) != 2 ||
- (now_t = time(NULL)) == (time_t) -1 ||
- (tm = localtime(&now_t)) == NULL) {
- return 0;
- }
- now = (unsigned int) tm->tm_hour * 100U + (unsigned int) tm->tm_min;
- if (time_begin <= time_end) {
- if (time_begin <= now && now <= time_end) {
- return 0;
- }
- return -1;
- }
- if (now >= time_begin || now <= time_end) {
- return 0;
- }
- return -1;
- }
- static int pw_puredb_parseline(char *line, const char * const pwd,
- const struct sockaddr_storage * const sa,
- const struct sockaddr_storage * const peer,
- AuthResult * const result)
- {
- char *allow_local_ip, *deny_local_ip;
- char *allow_remote_ip, *deny_remote_ip;
- const char *time_restrictions;
- if ((line = my_strtok2(line, *PW_LINE_SEP)) == NULL || *line == 0) { /* pwd */
- return -1;
- }
- {
- const char *crypted;
- int ret = -1;
- #ifdef crypto_pwhash_STRPREFIX
- if (crypto_pwhash_str_verify(line, pwd, strlen(pwd)) == 0) {
- /* pass */
- } else
- #endif
- #ifdef crypto_pwhash_scryptsalsa208sha256_STRPREFIX
- if (crypto_pwhash_scryptsalsa208sha256_str_verify
- (line, pwd, strlen(pwd)) == 0) {
- /* pass */
- } else
- #endif
- {
- ret = - ((crypted = (const char *) crypt(pwd, line)) == NULL ||
- pure_strcmp(line, crypted) != 0);
- if (ret != 0) {
- return -1;
- }
- }
- }
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL || *line == 0) { /* uid */
- return -1;
- }
- result->uid = (uid_t) strtoul(line, NULL, 10);
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL || *line == 0) { /* gid */
- return -1;
- }
- result->gid = (gid_t) strtoul(line, NULL, 10);
- #ifndef ACCEPT_ROOT_VIRTUAL_USERS
- if (result->uid <= (uid_t) 0 || result->gid <= (gid_t) 0) {
- return -1;
- }
- #endif
- if (my_strtok2(NULL, *PW_LINE_SEP) == NULL) { /* gecos */
- return -1;
- }
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL || *line == 0) { /* home */
- return -1;
- }
- if ((result->dir = strdup(line)) == NULL || *result->dir != '/') {
- return -1;
- }
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* bw_ul */
- return 0;
- }
- #ifdef THROTTLING
- if (*line != 0) {
- result->throttling_ul_changed = 1;
- result->throttling_bandwidth_ul = strtoul(line, NULL, 10);
- }
- #endif
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* bw_dl */
- return 0;
- }
- #ifdef THROTTLING
- if (*line != 0) {
- result->throttling_dl_changed = 1;
- result->throttling_bandwidth_dl = strtoul(line, NULL, 10);
- }
- #endif
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* ratio up */
- return 0;
- }
- #ifdef RATIOS
- if (*line != 0) {
- result->ratio_upload = (unsigned int) strtoul(line, NULL, 10);
- if (result->ratio_upload > 0U) {
- result->ratio_ul_changed = 1;
- }
- }
- #endif
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* ratio down */
- return 0;
- }
- #ifdef RATIOS
- if (*line != 0) {
- result->ratio_download = (unsigned int) strtoul(line, NULL, 10);
- if (result->ratio_download > 0U) {
- result->ratio_dl_changed = 1;
- }
- }
- #endif
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* max cnx */
- return 0;
- }
- #ifdef PER_USER_LIMITS
- if (*line != 0) {
- result->per_user_max = (unsigned int) strtoull(line, NULL, 10);
- }
- #endif
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* files quota */
- return 0;
- }
- #ifdef QUOTAS
- if (*line != 0) {
- result->quota_files_changed = 1;
- result->user_quota_files = strtoull(line, NULL, 10);
- }
- #endif
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* size quota */
- return 0;
- }
- #ifdef QUOTAS
- if (*line != 0) {
- result->quota_size_changed = 1;
- result->user_quota_size = strtoull(line, NULL, 10);
- }
- #endif
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* allowed local ip */
- return 0;
- }
- allow_local_ip = line;
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* denied local ip */
- return 0;
- }
- deny_local_ip = line;
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* allowed remote ip */
- return 0;
- }
- allow_remote_ip = line;
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* denied remote ip */
- return 0;
- }
- deny_remote_ip = line;
- if (access_ip_check(sa, allow_local_ip, deny_local_ip) != 0 ||
- access_ip_check(peer, allow_remote_ip, deny_remote_ip) != 0) {
- return -1;
- }
- if ((line = my_strtok2(NULL, *PW_LINE_SEP)) == NULL) { /* time restrictions */
- return 0;
- }
- time_restrictions = line;
- if (time_restrictions_check(time_restrictions) != 0) {
- return -1;
- }
- return 0;
- }
- void pw_puredb_check(AuthResult * const result,
- const char *account, const char *password,
- const struct sockaddr_storage * const sa,
- const struct sockaddr_storage * const peer)
- {
- char *line = NULL;
- PureDB db;
- off_t retpos;
- size_t retlen;
- result->auth_ok = 0;
- (void) sa;
- (void) peer;
- if (puredb_open(&db, pdb_filename) != 0) {
- die(421, LOG_ERR, MSG_PDB_BROKEN);
- }
- if (puredb_find_s(&db, account, &retpos, &retlen) != 0) {
- goto bye;
- }
- if ((line = puredb_read(&db, retpos, retlen)) == NULL) {
- goto bye;
- }
- result->auth_ok--;
- if (pw_puredb_parseline(line, password, sa, peer, result) != 0) {
- goto bye;
- }
- result->slow_tilde_expansion = 1;
- result->auth_ok = -result->auth_ok;
- bye:
- puredb_read_free(line);
- puredb_close(&db);
- }
- #endif
|