void * fr_alloc(FRAME *fr) { FRAME *p, *c; void *uptr; if (fr == 0) { return 0; } for (c = fr, p = 0; c != 0; p = c, c = c->next) { if (c->freelist != FR_NULL) { /* take this member out of the free list */ uptr = (char *) c->frbuf + c->freelist; c->freelist = *(int *) uptr; return uptr; } } /* no memory found, add to the chain */ if (p == 0) p = fr; if ((p->next = fr_create(fr->nentry, fr->nsize)) == 0) { no_mem("fr_alloc"); return 0; } p = p->next; uptr = (char *) p->frbuf + p->freelist; p->freelist = *(int *) uptr; return uptr; }
static void init_number() { if ((num_pool = fr_create(NUM_FRSIZE, sizeof(numb_t))) == 0) { no_mem("init_number"); } }
numb_t * create_num() { numb_t *n; if (num_pool == 0) init_number(); if ((n = fr_alloc(num_pool)) == 0) { no_mem("create_num"); } memset(n, 0, sizeof(numb_t)); return n; }
static int do_userdel(const char * const file, const PWInfo * const pwinfo) { char *file2; FILE *fp2; if (pwinfo->login == NULL || *(pwinfo->login) == 0) { fprintf(stderr, "Missing login\n"); return -1; } if (file == NULL) { fprintf(stderr, "Missing passwd file\n"); return PW_ERROR_MISSING_PASSWD_FILE; } if ((file2 = newpasswd_filename(file)) == NULL) { no_mem(); } if ((fp2 = create_newpasswd(file, file2, pwinfo->login, 0, 1)) == NULL) { fprintf(stderr, "Error.\n" "Check that [%s] already exists,\n" "and that [%s] can be written.\n", pwinfo->login, file2); free(file2); return PW_ERROR_USER_ALREADY_EXIST; } fflush(fp2); #ifdef HAVE_FILENO fsync(fileno(fp2)); #endif if (fclose(fp2) != 0) { perror("Unable to close the file"); goto bye2; } if (rename(file2, file) != 0) { perror("Unable to rename the file"); goto bye2; } free(file2); return 0; bye2: unlink(file2); free(file2); return PW_ERROR_UNEXPECTED_ERROR; }
static int do_usermod(const char * const file, const PWInfo *pwinfo) { char *file2; FILE *fp2; PWInfo fetched_info; static char line[LINE_MAX]; if (pwinfo->login == NULL || *(pwinfo->login) == 0) { fprintf(stderr, "Missing login\n"); return PW_ERROR_MISSING_LOGIN; } if (file == NULL) { fprintf(stderr, "Missing passwd file\n"); return PW_ERROR_MISSING_PASSWD_FILE; } if (fetch_pw_account(file, &fetched_info, line, sizeof line, pwinfo->login) != 0) { fprintf(stderr, "Unable to fetch info about user [%s] in file [%s]\n", pwinfo->login, file); return PW_ERROR_UNABLE_TO_FETCH; } if (pwinfo->pwd != NULL) { char *cleartext = pwinfo->pwd; fetched_info.pwd = best_crypt(cleartext); if (*cleartext != 0) { memset(cleartext, 0, strlen(cleartext)); } } if (pwinfo->uid > (uid_t) 0) { fetched_info.uid = pwinfo->uid; } if (pwinfo->gid > (gid_t) 0) { fetched_info.gid = pwinfo->gid; } if (pwinfo->home != NULL) { fetched_info.home = pwinfo->home; } if (pwinfo->gecos != NULL) { fetched_info.gecos = pwinfo->gecos; } if (pwinfo->has_bw_dl != 0) { if (pwinfo->has_bw_dl < 0) { fetched_info.has_bw_dl = 0; } else { fetched_info.has_bw_dl = pwinfo->has_bw_dl; fetched_info.bw_dl = pwinfo->bw_dl; } } if (pwinfo->has_bw_ul != 0) { if (pwinfo->has_bw_ul < 0) { fetched_info.has_bw_ul = 0; } else { fetched_info.has_bw_ul = pwinfo->has_bw_ul; fetched_info.bw_ul = pwinfo->bw_ul; } } if (pwinfo->has_quota_files != 0) { if (pwinfo->has_quota_files < 0) { fetched_info.has_quota_files = 0; } else { fetched_info.has_quota_files = pwinfo->has_quota_files; fetched_info.quota_files = pwinfo->quota_files; } } if (pwinfo->has_quota_size != 0) { if (pwinfo->has_quota_size < 0) { fetched_info.has_quota_size = 0; } else { fetched_info.has_quota_size = pwinfo->has_quota_size; fetched_info.quota_size = pwinfo->quota_size; } } if (pwinfo->has_ul_ratio != 0) { if (pwinfo->has_ul_ratio < 0) { fetched_info.has_ul_ratio = 0; } else { fetched_info.has_ul_ratio = pwinfo->has_ul_ratio; fetched_info.ul_ratio = pwinfo->ul_ratio; } } if (pwinfo->has_dl_ratio != 0) { if (pwinfo->has_dl_ratio < 0) { fetched_info.has_dl_ratio = 0; } else { fetched_info.has_dl_ratio = pwinfo->has_dl_ratio; fetched_info.dl_ratio = pwinfo->dl_ratio; } } if (pwinfo->allow_local_ip != NULL) { fetched_info.allow_local_ip = pwinfo->allow_local_ip; } if (pwinfo->deny_local_ip != NULL) { fetched_info.deny_local_ip = pwinfo->deny_local_ip; } if (pwinfo->allow_client_ip != NULL) { fetched_info.allow_client_ip = pwinfo->allow_client_ip; } if (pwinfo->deny_client_ip != NULL) { fetched_info.deny_client_ip = pwinfo->deny_client_ip; } if (pwinfo->has_time != 0) { if (pwinfo->has_time < 0) { fetched_info.has_time = 0; } else { fetched_info.has_time = pwinfo->has_time; } fetched_info.time_begin = pwinfo->time_begin; fetched_info.time_end = pwinfo->time_end; } if (pwinfo->has_per_user_max != 0) { if (pwinfo->has_per_user_max < 0) { fetched_info.has_per_user_max = 0; } else { fetched_info.has_per_user_max = pwinfo->has_per_user_max; fetched_info.per_user_max = pwinfo->per_user_max; } } if ((file2 = newpasswd_filename(file)) == NULL) { no_mem(); } if ((fp2 = create_newpasswd(file, file2, pwinfo->login, 0, 1)) == NULL) { fprintf(stderr, "Error.\n" "Check that [%s] already exists,\n" "and that [%s] can be written.\n", pwinfo->login, file2); free(file2); return PW_ERROR_USER_ALREADY_EXIST; } if (add_new_pw_line(fp2, &fetched_info) != 0) { fprintf(stderr, "Unable to append a line\n"); goto bye; } fflush(fp2); #ifdef HAVE_FILENO fsync(fileno(fp2)); #endif if (fclose(fp2) != 0) { perror("Unable to close the file"); goto bye2; } if (rename(file2, file) != 0) { perror("Unable to rename the file"); goto bye2; } free(file2); return 0; bye: fclose(fp2); bye2: unlink(file2); free(file2); return PW_ERROR_UNEXPECTED_ERROR; }
static int do_useradd(const char * const file, const PWInfo * const pwinfo_) { char *file2; FILE *fp2; PWInfo pwinfo = *pwinfo_; if (pwinfo.login == NULL || *(pwinfo.login) == 0) { fprintf(stderr, "Missing login\n"); return PW_ERROR_MISSING_LOGIN; } if (file == NULL) { fprintf(stderr, "Missing passwd file\n"); return PW_ERROR_MISSING_PASSWD_FILE; } if (pwinfo.uid <= (uid_t) 0 || pwinfo.gid <= (gid_t) 0) { fprintf(stderr, "You must give (non-root) uid and gid\n"); return PW_ERROR_USERADD_NOT_ROOT; } if (pwinfo.home == NULL) { fprintf(stderr, "Missing home directory\n"); return PW_ERROR_USERADD_MISSING_HOME_DIR; } if (pwinfo.gecos == NULL) { if ((pwinfo.gecos = strdup("")) == NULL) { no_mem(); } } if ((pwinfo.pwd = do_get_passwd()) == NULL) { fprintf(stderr, "Error with entering password - aborting\n"); return PW_ERROR_ENTER_PASSWD_PW_ERROR; } { char *cleartext = pwinfo.pwd; pwinfo.pwd = best_crypt(cleartext); if (*cleartext != 0) { memset(cleartext, 0, strlen(cleartext)); } } if ((file2 = newpasswd_filename(file)) == NULL) { no_mem(); } if ((fp2 = create_newpasswd(file, file2, pwinfo.login, 1, 0)) == NULL) { fprintf(stderr, "Error.\n" "Check that [%s] doesn't already exist,\n" "and that [%s] can be written.\n", pwinfo.login, file2); free(file2); return PW_ERROR_USER_ALREADY_EXIST; } if (add_new_pw_line(fp2, &pwinfo) != 0) { fprintf(stderr, "Unable to append a line\n"); goto bye; } fflush(fp2); #ifdef HAVE_FILENO fsync(fileno(fp2)); #endif if (fclose(fp2) != 0) { perror("Unable to close the file"); goto bye2; } if (rename(file2, file) != 0) { perror("Unable to rename the file"); goto bye2; } free(file2); return 0; bye: fclose(fp2); bye2: unlink(file2); free(file2); return PW_ERROR_UNEXPECTED_ERROR; }
int main(int argc, char *argv[]) { const char *action; char *file = NULL; char *dbfile = NULL; PWInfo pwinfo; int fodder; int ret = 0; int with_chroot = 1; int with_mkdb = 0; if (argc < 2) { help(); } #ifdef HAVE_SETLOCALE # ifdef LC_MESSAGES (void) setlocale(LC_MESSAGES, ""); # endif # ifdef LC_CTYPE (void) setlocale(LC_CTYPE, ""); # endif # ifdef LC_COLLATE (void) setlocale(LC_COLLATE, ""); # endif #endif #ifdef PROBE_RANDOM_AT_RUNTIME pw_zrand_probe(); #endif pwinfo.pwd = NULL; pwinfo.gecos = NULL; pwinfo.home = NULL; pwinfo.allow_local_ip = pwinfo.deny_local_ip = NULL; pwinfo.allow_client_ip = pwinfo.deny_client_ip = NULL; pwinfo.has_bw_dl = 0; pwinfo.has_bw_ul = 0; pwinfo.has_quota_files = 0; pwinfo.has_quota_size = 0; pwinfo.has_ul_ratio = 0; pwinfo.has_dl_ratio = 0; pwinfo.has_time = 0; pwinfo.time_begin = pwinfo.time_end = 0U; pwinfo.has_per_user_max = 0; pwinfo.per_user_max = 0U; #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) pwinfo.uid = (uid_t) 42U; pwinfo.gid = (gid_t) 42U; #else pwinfo.uid = (uid_t) 0U; pwinfo.gid = (gid_t) 0U; #endif argv++; argc--; action = *argv; if (argc > 1) { argv++; argc--; pwinfo.login = *argv; } else { pwinfo.login = NULL; } filter_pw_line_sep(pwinfo.login); while ((fodder = getopt(argc, argv, "c:d:D:f:F:g:hi:I:mn:N:q:Q:r:R:t:T:u:y:z:")) != -1) { switch(fodder) { case 'c' : { if ((pwinfo.gecos = strdup(optarg)) == NULL) { no_mem(); } filter_pw_line_sep(pwinfo.gecos); break; } case 'D' : with_chroot = 0; case 'd' : { char *optarg_copy; size_t sizeof_home; size_t optarg_len; if ((optarg_copy = strdup(optarg)) == NULL) { no_mem(); } again: optarg_len = strlen(optarg_copy); if (optarg_len < (size_t) 1U) { fprintf(stderr, "home directory is missing\n"); exit(EXIT_FAILURE); } if (optarg_copy[optarg_len - 1U] == '/') { optarg_len--; optarg_copy[optarg_len] = 0; goto again; } sizeof_home = optarg_len + sizeof "/./"; if ((pwinfo.home = malloc(sizeof_home)) == NULL) { no_mem(); } snprintf(pwinfo.home, sizeof_home, "%s%s", optarg_copy, with_chroot != 0 ? "/./" : ""); filter_pw_line_sep(pwinfo.home); break; } case 'f' : { if ((file = strdup(optarg)) == NULL) { no_mem(); } break; } case 'F' : { if ((dbfile = strdup(optarg)) == NULL) { no_mem(); } break; } case 'g' : { struct group *gr; if (pwinfo.gid > (gid_t) 0 && pwinfo.uid <= (uid_t) 0) { fprintf(stderr, "You already gave a gid\n"); exit(EXIT_FAILURE); } if ((gr = getgrnam(optarg)) != NULL) { pwinfo.gid = gr->gr_gid; } else { pwinfo.gid = (gid_t) strtoul(optarg, NULL, 10); } break; } case 'h' : { help(); /* doesn't return */ } case 'i' : { if ((pwinfo.allow_local_ip = strdup(optarg)) == NULL) { no_mem(); } break; } case 'I' : { if ((pwinfo.deny_local_ip = strdup(optarg)) == NULL) { no_mem(); } break; } case 'm' : { with_mkdb = 1; break; } case 'n' : { if (*optarg == 0) { pwinfo.has_quota_files = -1; } else { pwinfo.quota_files = strtoull(optarg, NULL, 10); pwinfo.has_quota_files = 1; } break; } case 'N' : { if (*optarg == 0) { pwinfo.has_quota_size = -1; } else { pwinfo.quota_size = strtoull(optarg, NULL, 10) * (1024ULL * 1024ULL); pwinfo.has_quota_size = 1; } break; } case 'q' : { if (*optarg == 0) { pwinfo.has_ul_ratio = -1; } else { pwinfo.ul_ratio = (unsigned int) strtoul(optarg, NULL, 10); if (pwinfo.ul_ratio < 1U) { fprintf(stderr, "Illegal upload ratio\n"); exit(EXIT_FAILURE); } pwinfo.has_ul_ratio = 1; } break; } case 'Q' : { if (*optarg == 0) { pwinfo.has_dl_ratio = -1; } else { pwinfo.dl_ratio = (unsigned int) strtoul(optarg, NULL, 10); if (pwinfo.dl_ratio < 1U) { fprintf(stderr, "Illegal download ratio\n"); exit(EXIT_FAILURE); } pwinfo.has_dl_ratio = 1; } break; } case 'r' : { if ((pwinfo.allow_client_ip = strdup(optarg)) == NULL) { no_mem(); } break; } case 'R' : { if ((pwinfo.deny_client_ip = strdup(optarg)) == NULL) { no_mem(); } break; } case 't' : { if (*optarg == 0) { pwinfo.has_bw_dl = -1; } else { if ((pwinfo.bw_dl = strtoul(optarg, NULL, 10)) > 0UL) { pwinfo.bw_dl *= 1024UL; pwinfo.has_bw_dl = 1; } } break; } case 'T' : { if (*optarg == 0) { pwinfo.has_bw_ul = -1; } else { if ((pwinfo.bw_ul = strtoul(optarg, NULL, 10)) > 0UL) { pwinfo.bw_ul *= 1024UL; pwinfo.has_bw_ul = 1; } } break; } case 'u' : { struct passwd *pw; if (pwinfo.uid > (uid_t) 0) { fprintf(stderr, "You already gave an uid\n"); exit(EXIT_FAILURE); } if ((pw = getpwnam(optarg)) != NULL) { pwinfo.uid = pw->pw_uid; if (pwinfo.gid <= (gid_t) 0) { pwinfo.gid = pw->pw_gid; } } else { pwinfo.uid = (uid_t) strtoul(optarg, NULL, 10); } break; } case 'y' : { if ((pwinfo.per_user_max = (unsigned int) strtoul(optarg, NULL, 10)) <= 0U) { pwinfo.has_per_user_max = -1; } else { pwinfo.has_per_user_max = 1; } break; } case 'z' : { if (sscanf(optarg, "%u-%u", &pwinfo.time_begin, &pwinfo.time_end) == 2 && pwinfo.time_begin < 2360 && (pwinfo.time_begin % 100) < 60 && pwinfo.time_end < 2360 && (pwinfo.time_end % 100) < 60) { pwinfo.has_time = 1; } else if (*optarg != 0) { fprintf(stderr, "Time should be given as hhmm-hhmm\n" "Example : 0900-1800 (9 am to 6 pm)\n"); exit(EXIT_FAILURE); } else { pwinfo.has_time = -1; } break; } case '?' : help(); } } if (file == NULL) { char *file_; if ((file_ = getenv(ENV_DEFAULT_PW_FILE)) != NULL && *file_ != 0) { file = file_; } else if ((file = strdup(DEFAULT_PW_FILE)) == NULL) { no_mem(); } } (void) umask(0177); init_zrand(); if (strcasecmp(action, "useradd") == 0) { ret = do_useradd(file, &pwinfo); if (with_mkdb != 0) { ret |= do_mkdb(dbfile, file); } } else if (strcasecmp(action, "usermod") == 0) { ret = do_usermod(file, &pwinfo); if (with_mkdb != 0) { ret |= do_mkdb(dbfile, file); } } else if (strcasecmp(action, "userdel") == 0) { ret = do_userdel(file, &pwinfo); if (with_mkdb != 0) { ret |= do_mkdb(dbfile, file); } } else if (strcasecmp(action, "passwd") == 0) { ret = do_passwd(file, &pwinfo); if (with_mkdb != 0) { ret |= do_mkdb(dbfile, file); } } else if (strcasecmp(action, "show") == 0) { ret = do_show(file, &pwinfo); } else if (strcasecmp(action, "mkdb") == 0) { ret = do_mkdb(pwinfo.login, file); } else if (strcasecmp(action, "list") == 0) { ret = do_list(file); } else { ret = PW_ERROR_UNEXPECTED_ERROR; help(); } return ret; }
static int do_mkdb(const char *dbfile, const char * const file) { FILE *fp; char *index_dbfile; size_t sizeof_index_dbfile; char *data_dbfile; size_t sizeof_data_dbfile; char *s; PureDBW dbw; int ret = PW_ERROR_UNEXPECTED_ERROR; char line[LINE_MAX]; if (dbfile == NULL || *dbfile == 0) { char *dbfile_; if ((dbfile_ = getenv(ENV_DEFAULT_PW_DB)) != NULL && *dbfile_ != 0) { dbfile = dbfile_; } else { dbfile = DEFAULT_PW_DB; } } if (file == NULL) { fprintf(stderr, "Missing passwd file\n"); return PW_ERROR_MISSING_PASSWD_FILE; } if ((fp = fopen(file, "r")) == NULL) { perror("Unable to open the passwd file"); return PW_ERROR_MISSING_PASSWD_FILE; } sizeof_index_dbfile = strlen(dbfile) + sizeof NEWPASSWD_INDEX_SUFFIX; if ((index_dbfile = ALLOCA(sizeof_index_dbfile)) == NULL) { fclose(fp); no_mem(); } sizeof_data_dbfile = strlen(dbfile) + sizeof NEWPASSWD_DATA_SUFFIX; if ((data_dbfile = ALLOCA(sizeof_data_dbfile)) == NULL) { fclose(fp); ALLOCA_FREE(index_dbfile); no_mem(); } snprintf(index_dbfile, sizeof_index_dbfile, "%s%s", dbfile, NEWPASSWD_INDEX_SUFFIX); snprintf(data_dbfile, sizeof_data_dbfile, "%s%s", dbfile, NEWPASSWD_DATA_SUFFIX); if (puredbw_open(&dbw, index_dbfile, data_dbfile, dbfile) != 0) { perror("Unable to create the database"); goto err; } while (fgets(line, (int) sizeof line - 1U, fp) != NULL) { strip_lf(line); if (*line == PW_LINE_COMMENT) { continue; } if (*line == 0 || (s = strchr(line, *PW_LINE_SEP)) == NULL || s[1] == 0) { continue; } *s++ = 0; if (puredbw_add_s(&dbw, line, s) != 0) { perror("Error while indexing a new entry"); goto err; } } if (puredbw_close(&dbw) != 0) { perror("Unable to close the database"); } else { ret = 0; } err: puredbw_free(&dbw); ALLOCA_FREE(index_dbfile); ALLOCA_FREE(data_dbfile); fclose(fp); return ret; }
static char *best_crypt(const char * const pwd, const unsigned long max_concurrent_logins, const unsigned long long max_auth_memory) { #if defined(crypto_pwhash_STRBYTES) || defined(crypto_pwhash_scryptsalsa208sha256_STRBYTES) # ifndef crypto_pwhash_STRBYTES # define crypto_pwhash_STRBYTES crypto_pwhash_scryptsalsa208sha256_STRBYTES # define crypto_pwhash_str crypto_pwhash_scryptsalsa208sha256_str # endif static char hash[crypto_pwhash_STRBYTES]; struct timeval tv_start, tv_now; struct timezone tz; unsigned long long probe_ops = MIN_AUTH_OPS; unsigned long long ops = 0ULL; unsigned long long auth_memory = max_auth_memory / (unsigned long long) max_concurrent_logins; unsigned long long elapsed; unsigned long long auth_time_ms = DEFAULT_AUTH_TIME_MS; if (max_concurrent_logins > AUTH_CORES) { auth_time_ms /= (unsigned long long) max_concurrent_logins / AUTH_CORES; } if (auth_time_ms < MIN_AUTH_TIME_MS) { auth_time_ms = MIN_AUTH_TIME_MS; } if (auth_memory < MIN_AUTH_MEMORY) { auth_memory = MIN_AUTH_MEMORY; } gettimeofday(&tv_start, &tz); for (;;) { if (crypto_pwhash_str(hash, pwd, strlen(pwd), probe_ops, auth_memory) != 0) { no_mem(); } ops += probe_ops; probe_ops *= 2; gettimeofday(&tv_now, &tz); elapsed = (tv_now.tv_sec * 1000ULL + tv_now.tv_usec / 1000ULL) - (tv_start.tv_sec * 1000ULL + tv_start.tv_usec / 1000ULL); if (elapsed >= auth_time_ms) { break; } } if (ops < MIN_AUTH_OPS) { ops = MIN_AUTH_OPS; } if (crypto_pwhash_str(hash, pwd, strlen(pwd), ops, auth_memory) != 0) { no_mem(); } return hash; #else static const char crcars[64] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; const char *crypted; if ((crypted = (const char *) /* bcrypt */ crypt("test", "$2a$08$1234567890123456789012")) != NULL && strcmp(crypted, "$2a$08$123456789012345678901uBdmsfIXjJcWQwz1wT/IZrWhimJ6xy6a") == 0) { char salt[] = "$2a$10$0000000000000000000000"; int c = 28; do { c--; salt[c] = crcars[alt_arc4random() & 63]; } while (c > 7); return (char *) crypt(pwd, salt); } else if ((crypted = (const char *) /* SHA-512 */ crypt("test", "$6$1234567890123456$")) != NULL && strcmp(crypted, "$6$1234567890123456$d.pgKQFaiD8bRiExg5NesbGR/" "3u51YvxeYaQXPzx4C6oSYREw8VoReiuYZjx0V9OhGVTZF" "qhc6emAxT1RC5BV.") == 0) { char salt[] = "$6$0000000000000000"; int c = 18; do { c--; salt[c] = crcars[alt_arc4random() & 63]; } while (c > 3); return (char *) crypt(pwd, salt); } else if ((crypted = (const char *) /* MD5 */ crypt("test", "$1$12345678$")) != NULL && strcmp(crypted, "$1$12345678$oEitTZYQtRHfNGmsFvTBA/") == 0) { char salt[] = "$1$00000000"; int c = 10; do { c--; salt[c] = crcars[alt_arc4random() & 63]; } while (c > 3); return (char *) crypt(pwd, salt); } else { fprintf(stderr, "No useable password hashing function found\n" "Please install libsodium (https://libsodium.org) and recompile pure-ftpd.\n"); exit(EXIT_FAILURE); } #endif }