END_TEST START_TEST (config_add_config_param_test) { int res; const char *name = NULL; config_rec *c = NULL; server_rec *s = NULL; s = pr_parser_server_ctxt_open("127.0.0.1"); fail_unless(s != NULL, "Failed to open server context: %s", strerror(errno)); c = add_config_param(NULL, 0, NULL); fail_unless(c == NULL, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); name = "foo"; mark_point(); c = add_config_param(name, 1, "bar"); fail_unless(c != NULL, "Failed to add config '%s': %s", name, strerror(errno)); fail_unless(c->config_type == CONF_PARAM, "Expected config_type %d, got %d", CONF_PARAM, c->config_type); mark_point(); pr_config_dump(NULL, s->conf, NULL); mark_point(); res = pr_config_remove(s->conf, name, PR_CONFIG_FL_PRESERVE_ENTRY, FALSE); fail_unless(res > 0, "Failed to remove config '%s': %s", name, strerror(errno)); }
/* usage: MemcacheConnectFailures count */ MODRET set_memcacheconnectfailures(cmd_rec *cmd) { char *ptr = NULL; config_rec *c; uint64_t count = 0; CHECK_ARGS(cmd, 1); CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); #ifdef HAVE_STRTOULL count = strtoull(cmd->argv[1], &ptr, 10); #else count = strtoul(cmd->argv[1], &ptr, 10); #endif /* HAVE_STRTOULL */ if (ptr && *ptr) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad connect failures parameter: ", cmd->argv[1], NULL)); } c = add_config_param(cmd->argv[0], 1, NULL); c->argv[0] = palloc(c->pool, sizeof(uint64_t)); *((uint64_t *) c->argv[0]) = count; return PR_HANDLED(cmd); }
/* usage: SFTPPAMOptions opt1 ... */ MODRET set_sftppamoptions(cmd_rec *cmd) { config_rec *c = NULL; register unsigned int i = 0; unsigned long opts = 0UL; if (cmd->argc-1 == 0) CONF_ERROR(cmd, "wrong number of parameters"); CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); c = add_config_param(cmd->argv[0], 1, NULL); for (i = 1; i < cmd->argc; i++) { if (strcmp(cmd->argv[i], "NoTTY") == 0) { opts |= SFTP_PAM_OPT_NO_TTY; } else if (strcmp(cmd->argv[i], "NoInfoMsgs") == 0) { opts |= SFTP_PAM_OPT_NO_INFO_MSGS; } else if (strcmp(cmd->argv[i], "NoRadioMsgs") == 0) { opts |= SFTP_PAM_OPT_NO_RADIO_MSGS; } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown SFTPPAMOption: '", cmd->argv[i], "'", NULL)); } } c->argv[0] = pcalloc(c->pool, sizeof(unsigned long)); *((unsigned long *) c->argv[0]) = opts; return PR_HANDLED(cmd); }
/* usage: MemcacheOptions opt1 opt2 ... */ MODRET set_memcacheoptions(cmd_rec *cmd) { config_rec *c = NULL; register unsigned int i = 0; unsigned long opts = 0UL; if (cmd->argc-1 == 0) { CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); c = add_config_param(cmd->argv[0], 1, NULL); for (i = 1; i < cmd->argc; i++) { if (strcmp(cmd->argv[i], "NoBinaryProtocol") == 0) { opts |= PR_MEMCACHE_FL_NO_BINARY_PROTOCOL; } else if (strcmp(cmd->argv[i], "NoRandomReplicaReads") == 0) { opts |= PR_MEMCACHE_FL_NO_RANDOM_REPLICA_READ; } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown MemcacheOption '", cmd->argv[i], "'", NULL)); } } c->argv[0] = pcalloc(c->pool, sizeof(unsigned long)); *((unsigned long *) c->argv[0]) = opts; return PR_HANDLED(cmd); }
MODRET add_lmd_allow_from(cmd_rec *cmd) { config_rec *c; int i; array_header *allowed_acls; if(cmd->argc < 2 ) CONF_ERROR(cmd, "argument missing"); CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL); /* argv => LMDMemcachedHost 127.0.0.1 192.168.0.1 ... */ c = find_config(main_server->conf, CONF_PARAM, "LMDBAllow", FALSE); if(c && c->argv[0]) { allowed_acls = c->argv[0]; } else { c = add_config_param(cmd->argv[0], 0, NULL); c->argv[0] = allowed_acls = make_array(cmd->server->pool, 0, sizeof(char *)); } for(i=1; i < cmd->argc; i++) { char *entry = cmd->argv[i]; if (strcasecmp(entry, "all") == 0 || strcasecmp(entry, "none") == 0) { break; } pr_netacl_t *acl = pr_netacl_create(cmd->server->pool, entry); *((pr_netacl_t **) push_array(allowed_acls)) = acl; pr_log_debug(DEBUG2, "%s: add LMDBAllow[%d] %s", MODULE_NAME, i, entry); } return PR_HANDLED(cmd); }
/* usage: * CounterMaxReaders max * CounterMaxWriters max */ MODRET set_countermaxreaderswriters(cmd_rec *cmd) { int count; config_rec *c; CHECK_ARGS(cmd, 1); CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR); /* A count of zero means that an unlimited number of readers (or writers), * as is the default without this module, is in effect. */ count = atoi(cmd->argv[1]); if (count < 0 || count > INT_MAX) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid number: ", cmd->argv[1], NULL)); } c = add_config_param(cmd->argv[0], 1, NULL); c->argv[0] = pcalloc(c->pool, sizeof(int)); *((int *) c->argv[0]) = count; c->flags |= CF_MERGEDOWN; return PR_HANDLED(cmd); }
/* usage: FailureLog path [logfmt-name] */ MODRET set_failurelog(cmd_rec *cmd) { config_rec *c; const char *path; char *log_fmt = NULL; if (cmd->argc < 2 || cmd->argc > 3) { CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); path = cmd->argv[1]; if (*path != '/') { CONF_ERROR(cmd, "must be an absolute path"); } if (cmd->argc == 3) { const char *fmt_name; fmt_name = cmd->argv[2]; /* Double-check that logfmt-name is valid, defined, etc. Look up the * format string, and stash a pointer to that in the config_rec (but NOT * a copy of the format string; don't need to use that much memory). */ c = find_config(cmd->server->conf, CONF_PARAM, "LogFormat", FALSE); while (c != NULL) { if (strcmp(c->argv[0], fmt_name) == 0) { log_fmt = c->argv[1]; break; } log_fmt = NULL; c = find_config_next(c, c->next, CONF_PARAM, "LogFormat", FALSE); } if (log_fmt == NULL) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "no such LogFormat '", cmd->argv[2], "' configured", NULL)); } } c = add_config_param(cmd->argv[0], 2, NULL, NULL); c->argv[0] = pstrdup(c->pool, path); c->argv[1] = log_fmt; return PR_HANDLED(cmd); }
/* usage: CopyEngine on|off */ MODRET set_copyengine(cmd_rec *cmd) { int engine = -1; config_rec *c; CHECK_ARGS(cmd, 1); CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); engine = get_boolean(cmd, 1); if (engine == -1) { CONF_ERROR(cmd, "expected Boolean parameter"); } c = add_config_param(cmd->argv[0], 1, NULL); c->argv[0] = palloc(c->pool, sizeof(int)); *((int *) c->argv[0]) = engine; return PR_HANDLED(cmd); }
/* usage: QoSOptions */ MODRET set_qosoptions(cmd_rec *cmd) { register unsigned int i; config_rec *c; int ctrlqos = 0, dataqos = 0; CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL); /* Make sure we have the right number of parameters. */ if ((cmd->argc-1) % 2 != 0) { CONF_ERROR(cmd, "bad number of parameters"); } for (i = 1; i < cmd->argc; i++) { if (strcasecmp(cmd->argv[i], "dataqos") == 0) { dataqos = qos_get_int(cmd->argv[++i]); if (dataqos == -1) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown dataqos parameter '", cmd->argv[i-1], "'", NULL)); } } else if (strcasecmp(cmd->argv[i], "ctrlqos") == 0) { ctrlqos = qos_get_int(cmd->argv[++i]); if (ctrlqos == -1) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown ctrlqos parameter '", cmd->argv[i-1], "'", NULL)); } } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown QoS option: '", cmd->argv[i], "'", NULL)); } } c = add_config_param(cmd->argv[0], 2, NULL, NULL); c->argv[0] = pcalloc(c->pool, sizeof(int)); *((int *) c->argv[0]) = ctrlqos; c->argv[1] = pcalloc(c->pool, sizeof(int)); *((int *) c->argv[1]) = dataqos; return PR_HANDLED(cmd); }
/* usage: MemcacheServers host1[:port1] ... */ MODRET set_memcacheservers(cmd_rec *cmd) { register unsigned int i; config_rec *c; char *str = ""; int ctxt; memcached_server_st *memcache_servers = NULL; if (cmd->argc-1 < 1) { CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); c = add_config_param(cmd->argv[0], 1, NULL); for (i = 1; i < cmd->argc; i++) { str = pstrcat(cmd->pool, str, *str ? ", " : "", cmd->argv[i], NULL); } memcache_servers = memcached_servers_parse(str); if (memcache_servers == NULL) { CONF_ERROR(cmd, "unable to parse server parameters"); } ctxt = (cmd->config && cmd->config->config_type != CONF_PARAM ? cmd->config->config_type : cmd->server->config_type ? cmd->server->config_type : CONF_ROOT); if (ctxt == CONF_ROOT) { /* If we're the "server config" context, set the server list now. This * would let mod_memcache talk to those servers for e.g. ftpdctl actions. */ memcache_set_servers(memcache_servers); } c->argv[0] = memcache_servers; /* Add the libmemcached-allocated pointer to a list, for later freeing. */ *((memcached_server_st **) push_array(memcache_server_lists)) = memcache_servers; return PR_HANDLED(cmd); }
MODRET add_lmd_allow_user(cmd_rec *cmd) { config_rec *c; int i; pr_table_t *explicit_users; if(cmd->argc < 2) CONF_ERROR(cmd, "missing argument"); CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL); /* argv => LMDBAllowedUser nobody nobody1 nobody2 */ c = find_config(main_server->conf, CONF_PARAM, "LMDBAllowedUser", FALSE); if(c && c->argv[0]) { explicit_users = c->argv[0]; } else { c = add_config_param(cmd->argv[0], 0, NULL); c->argv[0] = explicit_users = pr_table_alloc(main_server->pool, 0); } for(i=1; i < cmd->argc; i++) { const char *account = pstrdup(main_server->pool, cmd->argv[i]); if(pr_table_exists(explicit_users, account) > 0) { pr_log_debug(DEBUG2, "%s: %s is already registerd", MODULE_NAME, account); continue; } if(pr_table_add_dup(explicit_users, account, "y", 0) < 0){ pr_log_pri(PR_LOG_ERR, "%s: failed pr_table_add_dup(): %s", MODULE_NAME, strerror(errno)); exit(1); } pr_log_debug(DEBUG2, "%s: add LMDBAllowedUser[%d] %s", MODULE_NAME, i, account); } return PR_HANDLED(cmd); }
MODRET add_lmd_allow_user_regex(cmd_rec *cmd) { array_header *list; pr_regex_t *pre; int i, res; config_rec *c; if(cmd->argc < 2) CONF_ERROR(cmd, "missing argument"); CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL); /* argv => LMDBAllowUserRegex ^test */ c = find_config(cmd->server->conf, CONF_PARAM, "LMDBAllowedUserRegex", FALSE); if(c && c->argv[0]) { list = c->argv[0]; } else { c = add_config_param(cmd->argv[0], 0, NULL); c->argv[0] = list = make_array(cmd->server->pool, 0, sizeof(regex_t *)); } for(i=1; i < cmd->argc; i++) { pre = pr_regexp_alloc(&libmemcached_deny_blacklist_module); res = pr_regexp_compile(pre, cmd->argv[i], REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", cmd->argv[i], "' failed " "regex compilation: ", errstr, NULL)); } *((pr_regex_t **) push_array(list)) = pre; pr_log_debug(DEBUG2, "%s: add LMDBAllowedUserRegex[%d] %s", MODULE_NAME, i, cmd->argv[i]); } return PR_HANDLED(cmd); }
/* This function was copied, almost verbatim, from the set_sysloglevel() * function in modules/mod_core.c. I hereby cite the source for this code * as MacGuyver <*****@*****.**>. =) */ MODRET set_tcpaccesssysloglevels(cmd_rec *cmd) { config_rec *c = NULL; int allow_level, deny_level; CHECK_ARGS(cmd, 2); CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL); if (!strcasecmp(cmd->argv[1], "emerg")) { allow_level = PR_LOG_EMERG; } else if (!strcasecmp(cmd->argv[1], "alert")) { allow_level = PR_LOG_ALERT; } else if (!strcasecmp(cmd->argv[1], "crit")) { allow_level = PR_LOG_CRIT; } else if (!strcasecmp(cmd->argv[1], "error")) { allow_level = PR_LOG_ERR; } else if (!strcasecmp(cmd->argv[1], "warn")) { allow_level = PR_LOG_WARNING; } else if (!strcasecmp(cmd->argv[1], "notice")) { allow_level = PR_LOG_NOTICE; } else if (!strcasecmp(cmd->argv[1], "info")) { allow_level = PR_LOG_INFO; } else if (!strcasecmp(cmd->argv[1], "debug")) { allow_level = PR_LOG_DEBUG; } else { CONF_ERROR(cmd, "TCPAccessSyslogLevels requires \"allow\" level keyword: " "one of emerg/alert/crit/error/warn/notice/info/debug"); } if (!strcasecmp(cmd->argv[2], "emerg")) { deny_level = PR_LOG_EMERG; } else if(!strcasecmp(cmd->argv[2], "alert")) { deny_level = PR_LOG_ALERT; } else if(!strcasecmp(cmd->argv[2], "crit")) { deny_level = PR_LOG_CRIT; } else if(!strcasecmp(cmd->argv[2], "error")) { deny_level = PR_LOG_ERR; } else if(!strcasecmp(cmd->argv[2], "warn")) { deny_level = PR_LOG_WARNING; } else if(!strcasecmp(cmd->argv[2], "notice")) { deny_level = PR_LOG_NOTICE; } else if(!strcasecmp(cmd->argv[2], "info")) { deny_level = PR_LOG_INFO; } else if(!strcasecmp(cmd->argv[2], "debug")) { deny_level = PR_LOG_DEBUG; } else { CONF_ERROR(cmd, "TCPAccessSyslogLevels requires \"deny\" level keyword: " "one of emerg/alert/crit/error/warn/notice/info/debug"); } c = add_config_param(cmd->argv[0], 2, (void *) allow_level, (void *) deny_level); c->flags |= CF_MERGEDOWN; return HANDLED(cmd); }
MODRET add_tcpgroupaccessfiles(cmd_rec *cmd) { int group_argc = 1; char **group_argv = NULL; array_header *group_acl = NULL; config_rec *c = NULL; /* assume use of the standard TCP wrappers installation locations */ char *allow_filename = NULL, *deny_filename = NULL; CHECK_ARGS(cmd, 3); CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); /* use the user-given files, checking to make sure that they exist and * are readable. */ allow_filename = cmd->argv[2]; deny_filename = cmd->argv[3]; /* if the filenames begin with a '~', AND this is not immediately followed * by a '/' (ie '~/'), expand it out for checking and storing for later * lookups. If the filenames DO begin with '~/', do the expansion later, * after authenication. In other words, do checking of static filenames * now, and checking of dynamic (user-authentication-based) filenames * later. */ if (allow_filename[0] == '/') { /* it's an absolute path, so the filename will be checked as is */ if (!wrap_is_usable_file(allow_filename)) return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool, cmd->argv[0], ": '", allow_filename, "' must be a usable file", NULL)); } else if (allow_filename[0] == '~' && allow_filename[1] != '/') { char *allow_real_file = NULL; allow_real_file = dir_realpath(cmd->pool, allow_filename); if (allow_real_file == NULL || !wrap_is_usable_file(allow_real_file)) return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool, cmd->argv[0], ": '", allow_filename, "' must be a usable file", NULL)); allow_filename = allow_real_file; } else if (allow_filename[0] != '~' && allow_filename[0] != '/') { /* no relative paths allowed */ return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool, cmd->argv[0], ": '", allow_filename, "' must start with \"/\" or \"~\"", NULL)); } else { /* it's a determine-at-login-time filename -- check it later */ ; } if (deny_filename[0] == '/') { /* it's an absolute path, so the filename will be checked as is */ if (!wrap_is_usable_file(deny_filename)) return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool, cmd->argv[0], ": '", deny_filename, "' must be a usable file", NULL)); } else if (deny_filename[0] == '~' && deny_filename[1] != '/') { char *deny_real_file = NULL; deny_real_file = dir_realpath(cmd->pool, deny_filename); if (deny_real_file == NULL || !wrap_is_usable_file(deny_real_file)) return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool, cmd->argv[0], ": '", deny_filename, "' must be a usable file", NULL)); deny_filename = deny_real_file; } else if (deny_filename[0] != '~' && deny_filename[0] != '/') { /* no relative paths allowed */ return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool, cmd->argv[0], ": '", deny_filename, "' must start with \"/\" or \"~\"", NULL)); } else { /* it's a determine-at-login-time filename -- check it later */ ; } c = add_config_param(cmd->argv[0], 0); group_acl = pr_expr_create(cmd->tmp_pool, &group_argc, &cmd->argv[0]); /* build the desired config_rec manually */ c->argc = group_argc + 2; c->argv = pcalloc(c->pool, (group_argc + 3) * sizeof(char *)); group_argv = (char **) c->argv; /* the access files are the first two arguments */ *group_argv++ = pstrdup(c->pool, allow_filename); *group_argv++ = pstrdup(c->pool, deny_filename); /* and the group names follow */ if (group_argc && group_acl) while (group_argc--) { *group_argv++ = pstrdup(c->pool, *((char **) group_acl->elts)); group_acl->elts = ((char **) group_acl->elts) + 1; } /* don't forget to NULL-terminate */ *group_argv = NULL; c->flags |= CF_MERGEDOWN; /* done */ return HANDLED(cmd); }
/* usage: AuthGroupFile path [id <min-max>] [name <regex>] */ MODRET set_authgroupfile(cmd_rec *cmd) { config_rec *c = NULL; authfile_file_t *file = NULL; int flags = 0; #ifdef PR_USE_REGEX if (cmd->argc-1 < 1 || cmd->argc-1 > 5) { #else if (cmd->argc-1 < 1 || cmd->argc-1 > 2) { #endif /* regex support */ CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); if (*(cmd->argv[1]) != '/') { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use relative path for ", cmd->argv[0], " '", cmd->argv[1], "'.", NULL)); } /* Make sure the configured file has the correct permissions. Note that * AuthGroupFiles, unlike AuthUserFiles, do not contain any sensitive * information, and can thus be world-readable. */ flags = PR_AUTH_FILE_FL_ALLOW_WORLD_READABLE; if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use ", cmd->argv[1], ": ", strerror(errno), NULL)); } c = add_config_param(cmd->argv[0], 1, NULL); file = pcalloc(c->pool, sizeof(authfile_file_t)); file->af_path = pstrdup(c->pool, cmd->argv[1]); c->argv[0] = (void *) file; /* Check for restrictions */ if (cmd->argc-1 != 1) { register unsigned int i = 0; for (i = 2; i < cmd->argc; i++) { if (strncmp(cmd->argv[i], "id", 3) == 0) { gid_t min, max; char *sep = NULL, *tmp = NULL; /* The range restriction parameter is of the form "min-max", where max * must be >= min. */ sep = strchr(cmd->argv[++i], '-'); if (sep == NULL) { CONF_ERROR(cmd, "badly formatted ID restriction parameter"); } *sep = '\0'; min = strtol(cmd->argv[i], &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted minimum ID"); } tmp = NULL; max = strtol(sep+1, &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted maximum ID"); } if (min > max) { CONF_ERROR(cmd, "minimum cannot be larger than maximum"); } file->af_min_id.gid = min; file->af_max_id.gid = max; file->af_restricted_ids = TRUE; #ifdef PR_USE_REGEX } else if (strncmp(cmd->argv[i], "name", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_name_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_name_filter = pstrdup(c->pool, cmd->argv[i]); file->af_name_regex = pre; file->af_restricted_names = TRUE; #endif /* regex support */ } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '", cmd->argv[i], "'", NULL)); } } } return PR_HANDLED(cmd); } /* usage: AuthUserFile path [home <regexp>] [id <min-max>] [name <regex>] */ MODRET set_authuserfile(cmd_rec *cmd) { config_rec *c = NULL; authfile_file_t *file = NULL; int flags = 0; #ifdef PR_USE_REGEX if (cmd->argc-1 < 1 || cmd->argc-1 > 7) { #else if (cmd->argc-1 < 1 || cmd->argc-1 > 2) { #endif /* regex support */ CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); if (*(cmd->argv[1]) != '/') { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use relative path for ", cmd->argv[0], " '", cmd->argv[1], "'.", NULL)); } /* Make sure the configured file has the correct permissions. Note that * AuthUserFiles, unlike AuthGroupFiles, DO contain any sensitive * information, and thus CANNOT be world-readable. */ flags = 0; if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use ", cmd->argv[1], ": ", strerror(errno), NULL)); } c = add_config_param(cmd->argv[0], 1, NULL); file = pcalloc(c->pool, sizeof(authfile_file_t)); file->af_path = pstrdup(c->pool, cmd->argv[1]); c->argv[0] = (void *) file; /* Check for restrictions */ if (cmd->argc-1 != 1) { register unsigned int i = 0; for (i = 2; i < cmd->argc; i++) { if (strncmp(cmd->argv[i], "id", 3) == 0) { uid_t min, max; char *sep = NULL, *tmp = NULL; /* The range restriction parameter is of the form "min-max", where max * must be >= min. */ sep = strchr(cmd->argv[++i], '-'); if (sep == NULL) { CONF_ERROR(cmd, "badly formatted ID restriction parameter"); } *sep = '\0'; min = strtol(cmd->argv[i], &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted minimum ID"); } tmp = NULL; max = strtol(sep+1, &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted maximum ID"); } if (min > max) { CONF_ERROR(cmd, "minimum cannot be larger than maximum"); } file->af_min_id.uid = min; file->af_max_id.uid = max; file->af_restricted_ids = TRUE; #ifdef PR_USE_REGEX } else if (strncmp(cmd->argv[i], "home", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_home_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_home_filter = pstrdup(c->pool, cmd->argv[i]); file->af_home_regex = pre; file->af_restricted_homes = TRUE; } else if (strncmp(cmd->argv[i], "name", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_name_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_name_filter = pstrdup(c->pool, cmd->argv[i]); file->af_name_regex = pre; file->af_restricted_names = TRUE; #endif /* regex support */ } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '", cmd->argv[i], "'", NULL)); } } } return PR_HANDLED(cmd); } /* Command handlers */ MODRET authfile_post_host(cmd_rec *cmd) { /* If the HOST command changed the main_server pointer, reinitialize * ourselves. */ if (session.prev_server != NULL) { int res; af_user_file = NULL; af_group_file = NULL; res = authfile_sess_init(); if (res < 0) { pr_session_disconnect(&auth_file_module, PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL); } } return PR_DECLINED(cmd); } /* Initialization routines */ static int authfile_init(void) { const char *key, *salt, *hash; /* On some Unix platforms, giving crypt(3) an empty string for the salt, * no matter what the input key, results in an empty string being returned. * (The salt string is what is obtained from the AuthUserFile that has been * configured.) * * On other platforms, given crypt(3) a real key and an empty string for * the salt returns in a real string. (I'm looking at you, Mac OSX.) * * Thus in order to handle the edge case of an AuthUserFile with a passwd * field being empty the same on such differing platforms, we perform a * runtime check (at startup), to see how crypt(3) behaves -- and then * preserve the principle of least surprise appropriately. */ key = "key"; salt = ""; hash = crypt(key, salt); if (hash != NULL) { if (strcmp(hash, "") != 0) { /* We're probably on a Mac OSX or similar platform. */ handle_empty_salt = TRUE; } } return 0; } static int authfile_sess_init(void) { config_rec *c = NULL; c = find_config(main_server->conf, CONF_PARAM, "AuthUserFile", FALSE); if (c) { af_user_file = c->argv[0]; } c = find_config(main_server->conf, CONF_PARAM, "AuthGroupFile", FALSE); if (c) { af_group_file = c->argv[0]; } return 0; }
/* usage: MemcacheTimeouts conn-timeout read-timeout write-timeout * [ejected-timeout] */ MODRET set_memcachetimeouts(cmd_rec *cmd) { config_rec *c; unsigned long conn_millis, read_millis, write_millis, ejected_sec = 0; char *ptr = NULL; if (cmd->argc-1 < 3 || cmd->argc-1 > 4) { CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); conn_millis = strtoul(cmd->argv[1], &ptr, 10); if (ptr && *ptr) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "badly formatted connect timeout value: ", cmd->argv[1], NULL)); } ptr = NULL; read_millis = strtoul(cmd->argv[2], &ptr, 10); if (ptr && *ptr) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "badly formatted read timeout value: ", cmd->argv[2], NULL)); } ptr = NULL; write_millis = strtoul(cmd->argv[3], &ptr, 10); if (ptr && *ptr) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "badly formatted write timeout value: ", cmd->argv[3], NULL)); } if (cmd->argc-1 == 4) { ptr = NULL; ejected_sec = strtoul(cmd->argv[4], &ptr, 10); if (ptr && *ptr) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "badly formatted retry timeout value: ", cmd->argv[4], NULL)); } } #if 0 /* XXX If we're the "server config" context, set the timeouts now. * This would let mod_memcache talk to those servers for e.g. ftpdctl * actions. */ memcache_set_timeouts(conn_timeout, read_timeout, write_timeout, ejected_sec); #endif c = add_config_param(cmd->argv[0], 4, NULL, NULL, NULL, NULL); c->argv[0] = palloc(c->pool, sizeof(unsigned long)); *((unsigned long *) c->argv[0]) = conn_millis; c->argv[1] = palloc(c->pool, sizeof(unsigned long)); *((unsigned long *) c->argv[1]) = read_millis; c->argv[2] = palloc(c->pool, sizeof(unsigned long)); *((unsigned long *) c->argv[2]) = write_millis; c->argv[3] = palloc(c->pool, sizeof(unsigned long)); *((unsigned long *) c->argv[3]) = ejected_sec; return PR_HANDLED(cmd); }
/* usage: AuthGroupFile path [id <min-max>] [name <regex>] */ MODRET set_authgroupfile(cmd_rec *cmd) { config_rec *c = NULL; authfile_file_t *file = NULL; #ifdef PR_USE_REGEX if (cmd->argc-1 < 1 || cmd->argc-1 > 5) #else if (cmd->argc-1 < 1 || cmd->argc-1 > 2) #endif /* regex support */ CONF_ERROR(cmd, "wrong number of parameters"); CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); if (*(cmd->argv[1]) != '/') CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use relative path for ", cmd->argv[0], " '", cmd->argv[1], "'.", NULL)); c = add_config_param(cmd->argv[0], 1, NULL); file = pcalloc(c->pool, sizeof(authfile_file_t)); file->af_path = pstrdup(c->pool, cmd->argv[1]); c->argv[0] = (void *) file; /* Check for restrictions */ if (cmd->argc-1 != 1) { register unsigned int i = 0; for (i = 2; i < cmd->argc; i++) { if (strcmp(cmd->argv[i], "id") == 0) { gid_t min, max; char *sep = NULL, *tmp = NULL; /* The range restriction parameter is of the form "min-max", where max * must be >= min. */ sep = strchr(cmd->argv[++i], '-'); if (sep == NULL) CONF_ERROR(cmd, "badly formatted ID restriction parameter"); *sep = '\0'; min = strtol(cmd->argv[i], &tmp, 10); if (tmp && *tmp) CONF_ERROR(cmd, "badly formatted minimum ID"); tmp = NULL; max = strtol(sep+1, &tmp, 10); if (tmp && *tmp) CONF_ERROR(cmd, "badly formatted maximum ID"); if (min > max) CONF_ERROR(cmd, "minimum cannot be larger than maximum"); file->af_min_id.gid = min; file->af_max_id.gid = max; file->af_restricted_ids = TRUE; #ifdef PR_USE_REGEX } else if (strcmp(cmd->argv[i], "name") == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_name_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_name_filter = pstrdup(c->pool, cmd->argv[i]); file->af_name_regex = pre; file->af_restricted_names = TRUE; #endif /* regex support */ } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '", cmd->argv[i], "'", NULL)); } } } return PR_HANDLED(cmd); }
/* usage: AuthGroupFile path [id <min-max>] [name <regex>] */ MODRET set_authgroupfile(cmd_rec *cmd) { config_rec *c = NULL; authfile_file_t *file = NULL; int flags = 0; char *path; #ifdef PR_USE_REGEX if (cmd->argc-1 < 1 || cmd->argc-1 > 5) { #else if (cmd->argc-1 < 1 || cmd->argc-1 > 2) { #endif /* regex support */ CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); path = cmd->argv[1]; if (*path != '/') { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use relative path for ", (char *) cmd->argv[0], " '", path, "'.", NULL)); } /* Make sure the configured file has the correct permissions. Note that * AuthGroupFiles, unlike AuthUserFiles, do not contain any sensitive * information, and can thus be world-readable. */ flags = PR_AUTH_FILE_FL_ALLOW_WORLD_READABLE; if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use ", path, ": ", strerror(errno), NULL)); } c = add_config_param(cmd->argv[0], 1, NULL); file = pcalloc(c->pool, sizeof(authfile_file_t)); file->af_path = pstrdup(c->pool, path); c->argv[0] = (void *) file; /* Check for restrictions */ if (cmd->argc-1 != 1) { register unsigned int i = 0; for (i = 2; i < cmd->argc; i++) { if (strncmp(cmd->argv[i], "id", 3) == 0) { gid_t min, max; char *sep = NULL, *tmp = NULL; /* The range restriction parameter is of the form "min-max", where max * must be >= min. */ sep = strchr(cmd->argv[++i], '-'); if (sep == NULL) { CONF_ERROR(cmd, "badly formatted ID restriction parameter"); } *sep = '\0'; min = strtol(cmd->argv[i], &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted minimum ID"); } tmp = NULL; max = strtol(sep+1, &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted maximum ID"); } if (min > max) { CONF_ERROR(cmd, "minimum cannot be larger than maximum"); } file->af_min_id.gid = min; file->af_max_id.gid = max; file->af_restricted_ids = TRUE; #ifdef PR_USE_REGEX } else if (strncmp(cmd->argv[i], "name", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_name_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_name_filter = pstrdup(c->pool, cmd->argv[i]); file->af_name_regex = pre; file->af_restricted_names = TRUE; #endif /* regex support */ } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '", cmd->argv[i], "'", NULL)); } } } return PR_HANDLED(cmd); } /* usage: AuthUserFile path [home <regexp>] [id <min-max>] [name <regex>] */ MODRET set_authuserfile(cmd_rec *cmd) { config_rec *c = NULL; authfile_file_t *file = NULL; int flags = 0; char *path; #ifdef PR_USE_REGEX if (cmd->argc-1 < 1 || cmd->argc-1 > 7) { #else if (cmd->argc-1 < 1 || cmd->argc-1 > 2) { #endif /* regex support */ CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); path = cmd->argv[1]; if (*path != '/') { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use relative path for ", (char *) cmd->argv[0], " '", path, "'.", NULL)); } /* Make sure the configured file has the correct permissions. Note that * AuthUserFiles, unlike AuthGroupFiles, DO contain any sensitive * information, and thus CANNOT be world-readable. */ flags = 0; if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use ", path, ": ", strerror(errno), NULL)); } c = add_config_param(cmd->argv[0], 1, NULL); file = pcalloc(c->pool, sizeof(authfile_file_t)); file->af_path = pstrdup(c->pool, path); c->argv[0] = (void *) file; /* Check for restrictions */ if (cmd->argc-1 != 1) { register unsigned int i = 0; for (i = 2; i < cmd->argc; i++) { if (strncmp(cmd->argv[i], "id", 3) == 0) { uid_t min, max; char *sep = NULL, *tmp = NULL; /* The range restriction parameter is of the form "min-max", where max * must be >= min. */ sep = strchr(cmd->argv[++i], '-'); if (sep == NULL) { CONF_ERROR(cmd, "badly formatted ID restriction parameter"); } *sep = '\0'; min = strtol(cmd->argv[i], &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted minimum ID"); } tmp = NULL; max = strtol(sep+1, &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted maximum ID"); } if (min > max) { CONF_ERROR(cmd, "minimum cannot be larger than maximum"); } file->af_min_id.uid = min; file->af_max_id.uid = max; file->af_restricted_ids = TRUE; #ifdef PR_USE_REGEX } else if (strncmp(cmd->argv[i], "home", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_home_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_home_filter = pstrdup(c->pool, cmd->argv[i]); file->af_home_regex = pre; file->af_restricted_homes = TRUE; } else if (strncmp(cmd->argv[i], "name", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_name_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_name_filter = pstrdup(c->pool, cmd->argv[i]); file->af_name_regex = pre; file->af_restricted_names = TRUE; #endif /* regex support */ } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '", cmd->argv[i], "'", NULL)); } } } return PR_HANDLED(cmd); } /* Event listeners */ static void authfile_sess_reinit_ev(const void *event_data, void *user_data) { int res; /* A HOST command changed the main_server pointer, reinitialize ourselves. */ pr_event_unregister(&auth_file_module, "core.session-reinit", authfile_sess_reinit_ev); af_user_file = NULL; af_group_file = NULL; res = authfile_sess_init(); if (res < 0) { pr_session_disconnect(&auth_file_module, PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL); } }