/* 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: 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); } }