END_TEST START_TEST (add_config_param_set_test) { xaset_t *set = NULL; const char *name = NULL; config_rec *c = NULL; name = "foo"; c = add_config_param_set(NULL, name, 0); fail_unless(c == NULL, "Failed to handle null set argument"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %d (%s)", errno, strerror(errno)); c = add_config_param_set(&set, name, 0); fail_unless(c != NULL, "Failed to add config '%s' to set: %s", name, strerror(errno)); fail_unless(c->config_type == CONF_PARAM, "Expected config_type %d, got %d", CONF_PARAM, c->config_type); fail_unless(c->argc == 0, "Expected argc 0, got %d", c->argc); c = add_config_param_set(&set, name, 2, "bar", "baz"); fail_unless(c != NULL, "Failed to add config '%s' to set: %s", name, strerror(errno)); fail_unless(c->config_type == CONF_PARAM, "Expected config_type %d, got %d", CONF_PARAM, c->config_type); fail_unless(c->argc == 2, "Expected argc 2, got %d", c->argc); fail_unless(strcmp("bar", (char *) c->argv[0]) == 0, "Expected argv[0] to be 'bar', got '%s'", (char *) c->argv[0]); fail_unless(strcmp("baz", (char *) c->argv[1]) == 0, "Expected argv[1] to be 'baz', got '%s'", (char *) c->argv[1]); fail_unless(c->argv[2] == NULL, "Expected argv[2] to be null"); }
END_TEST START_TEST (filter_allow_path_test) { int res; config_rec *c; pr_regex_t *allow_pre, *deny_pre; xaset_t *set = NULL; const char *path = NULL; res = pr_filter_allow_path(NULL, NULL); fail_unless(res < 0, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); mark_point(); c = add_config_param_set(&set, "test", 1, "test"); fail_if(c == NULL, "Failed to add config param: %s", strerror(errno)); path = "/foo/bar"; res = pr_filter_allow_path(set, path); fail_unless(res == 0, "Failed to allow path '%s' with no configured filters", path); /* First, let's add a PathDenyFilter. */ deny_pre = pr_regexp_alloc(NULL); res = pr_regexp_compile(deny_pre, "/bar$", 0); fail_unless(res == 0, "Error compiling deny filter"); c = add_config_param_set(&set, "PathDenyFilter", 1, deny_pre); fail_if(c == NULL, "Failed to add config param: %s", strerror(errno)); mark_point(); res = pr_filter_allow_path(set, path); fail_unless(res == PR_FILTER_ERR_FAILS_DENY_FILTER, "Failed to reject path '%s' with matching PathDenyFilter", path); mark_point(); path = "/foo/baz"; res = pr_filter_allow_path(set, path); fail_unless(res == 0, "Failed to allow path '%s' with non-matching PathDenyFilter", path); /* Now, let's add a PathAllowFilter. */ allow_pre = pr_regexp_alloc(NULL); res = pr_regexp_compile(allow_pre, "/baz$", 0); fail_unless(res == 0, "Error compiling allow filter"); c = add_config_param_set(&set, "PathAllowFilter", 1, allow_pre); fail_if(c == NULL, "Failed to add config param: %s", strerror(errno)); mark_point(); path = "/foo/quxx"; res = pr_filter_allow_path(set, path); fail_unless(res == PR_FILTER_ERR_FAILS_ALLOW_FILTER, "Failed to allow path '%s' with matching PathAllowFilter", path); }
END_TEST START_TEST (config_add_config_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)); name = "foo"; mark_point(); c = add_config(NULL, name); fail_unless(c != NULL, "Failed to add config '%s': %s", name, strerror(errno)); fail_unless(c->config_type == 0, "Expected config_type 0, got %d", c->config_type); mark_point(); pr_config_dump(NULL, s->conf, NULL); c = add_config_param_set(&(c->subset), "bar", 1, "baz"); mark_point(); pr_config_dump(NULL, s->conf, NULL); mark_point(); res = remove_config(s->conf, name, FALSE); fail_unless(res > 0, "Failed to remove config '%s': %s", name, strerror(errno)); }
MODRET pw_getpwnam(cmd_rec *cmd) { struct passwd *pw; const char *name; name = cmd->argv[0]; if (persistent_passwd) { pw = p_getpwnam(name); } else { pw = getpwnam(name); } if (auth_unix_opts & AUTH_UNIX_OPT_MAGIC_TOKEN_CHROOT) { char *home_dir, *ptr; /* Here is where we do the "magic token" chroot monstrosity inflicted * on the world by wu-ftpd. * * If the magic token '/./' appears in the user's home directory, the * directory portion before the token is the directory to use for * the chroot; the directory portion after the token is the directory * to use for the initial chdir. */ home_dir = pstrdup(cmd->tmp_pool, pw->pw_dir); /* We iterate through the home directory string since it is possible * for the '.' character to appear without it being part of the magic * token. */ ptr = strchr(home_dir, '.'); while (ptr != NULL) { pr_signals_handle(); /* If we're at the start of the home directory string, stop looking: * this home directory is not really valid anyway. */ if (ptr == home_dir) { break; } /* Back up one character. */ ptr--; /* If we're at the start of the home directory now, stop looking: * this home directory cannot contain a valid magic token. I.e. * * /./home/foo * * cannot be valid, as there is no directory portion before the * token. */ if (ptr == home_dir) { break; } if (strncmp(ptr, "/./", 3) == 0) { char *default_chdir; config_rec *c; *ptr = '\0'; default_chdir = pstrdup(cmd->tmp_pool, ptr + 2); /* In order to make sure that this user is chrooted to this * directory, we remove all DefaultRoot directives and add a new * one. Same for the DefaultChdir directive. */ (void) remove_config(main_server->conf, "DefaultRoot", FALSE); c = add_config_param_set(&main_server->conf, "DefaultRoot", 1, NULL); c->argv[0] = pstrdup(c->pool, home_dir); (void) remove_config(main_server->conf, "DefaultChdir", FALSE); c = add_config_param_set(&main_server->conf, "DefaultChdir", 1, NULL); c->argv[0] = pstrdup(c->pool, default_chdir); pr_log_debug(DEBUG9, "AuthUnixOption magicTokenChroot: " "found magic token in '%s', using 'DefaultRoot %s' and " "'DefaultChdir %s'", pw->pw_dir, home_dir, default_chdir); /* We need to use a long-lived memory pool for overwriting the * normal home directory. */ pw->pw_dir = pstrdup(session.pool, home_dir); break; } ptr = strchr(ptr + 2, '.'); } } return pw ? mod_create_data(cmd, pw) : PR_DECLINED(cmd); }
END_TEST START_TEST (get_param_ptr_test) { void *res; int count; xaset_t *set = NULL; config_rec *c; const char *name = NULL; res = get_param_ptr(NULL, NULL, FALSE); fail_unless(res == NULL, "Failed to handle null arguments"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); mark_point(); name = "foo"; c = add_config_param_set(&set, name, 1, "bar"); fail_unless(c != NULL, "Failed to add config '%s': %s", name, strerror(errno)); name = "bar"; res = get_param_ptr(set, name, FALSE); fail_unless(res == NULL, "Failed to handle null arguments"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); mark_point(); /* We expect to find "foo", but a 'next' should be empty. Note that we * need to reset the get_param_ptr tree. */ get_param_ptr(NULL, NULL, FALSE); name = "foo"; res = get_param_ptr(set, name, FALSE); fail_unless(res != NULL, "Failed to find config '%s': %s", name, strerror(errno)); mark_point(); res = get_param_ptr_next(name, FALSE); fail_unless(res == NULL, "Found next config unexpectedly"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); /* Now add another config, find "foo" again; this time, a 'next' should * NOT be empty; it should find the 2nd config we added. */ name = "foo2"; c = add_config_param_set(&set, name, 1, "baz"); fail_unless(c != NULL, "Failed to add config '%s': %s", name, strerror(errno)); get_param_ptr(NULL, NULL, FALSE); name = NULL; res = get_param_ptr(set, name, FALSE); fail_unless(res != NULL, "Failed to find any config: %s", strerror(errno)); mark_point(); res = get_param_ptr_next(name, FALSE); fail_unless(res != NULL, "Expected to find another config"); mark_point(); name = "foo"; count = remove_config(set, name, FALSE); fail_unless(count > 0, "Failed to remove config '%s': %s", name, strerror(errno)); mark_point(); res = get_param_ptr(set, name, FALSE); fail_unless(res == NULL, "Found config '%s' unexpectedly", name); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); }
END_TEST START_TEST (find_config2_test) { int res; config_rec *c; xaset_t *set = NULL; const char *name; unsigned long flags = 0; c = find_config2(NULL, -1, NULL, FALSE, flags); fail_unless(c == NULL, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %d (%s)", errno, strerror(errno)); mark_point(); name = "foo"; c = add_config_param_set(&set, name, 0); fail_unless(c != NULL, "Failed to add config '%s': %s", name, strerror(errno)); name = "bar"; c = find_config2(set, -1, name, FALSE, flags); fail_unless(c == NULL, "Failed to handle null arguments"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); mark_point(); /* We expect to find "foo", but a 'next' should be empty. */ name = "foo"; c = find_config2(set, -1, name, FALSE, flags); fail_unless(c != NULL, "Failed to find config '%s': %s", name, strerror(errno)); mark_point(); c = find_config_next2(c, c->next, -1, name, FALSE, flags); fail_unless(c == NULL, "Found next config unexpectedly"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); /* Now add another config, find "foo" again; this time, a 'next' should * NOT be empty; it should find the 2nd config we added. */ name = "foo2"; c = add_config_param_set(&set, name, 0); fail_unless(c != NULL, "Failed to add config '%s': %s", name, strerror(errno)); name = NULL; c = find_config2(set, -1, name, FALSE, flags); fail_unless(c != NULL, "Failed to find any config: %s", strerror(errno)); mark_point(); c = find_config_next2(c, c->next, -1, name, FALSE, flags); fail_unless(c != NULL, "Expected to find another config"); mark_point(); name = "foo"; res = remove_config(set, name, FALSE); fail_unless(res > 0, "Failed to remove config '%s': %s", name, strerror(errno)); mark_point(); c = find_config2(set, -1, name, FALSE, flags); fail_unless(c == NULL, "Found config '%s' unexpectedly", name); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); }
END_TEST START_TEST (config_merge_down_test) { xaset_t *set; config_rec *c, *src, *dst; const char *name; mark_point(); pr_config_merge_down(NULL, FALSE); mark_point(); set = xaset_create(p, NULL); pr_config_merge_down(set, FALSE); name = "foo"; c = add_config_param_set(&set, name, 0); mark_point(); pr_config_merge_down(set, FALSE); name = "bar"; c = add_config_param_set(&set, name, 1, "baz"); c->flags |= CF_MERGEDOWN; mark_point(); pr_config_merge_down(set, FALSE); name = "BAZ"; c = add_config_param_set(&set, name, 2, "quxx", "Quzz"); c->flags |= CF_MERGEDOWN_MULTI; mark_point(); pr_config_merge_down(set, FALSE); /* Add a config to the subsets, with the same name and same args. */ name = "<Anonymous>"; src = add_config_param_set(&set, name, 0); src->config_type = CONF_ANON; mark_point(); pr_config_merge_down(set, FALSE); name = "<Directory>"; dst = add_config_param_set(&set, name, 1, "/baz"); dst->config_type = CONF_DIR; name = "foo"; c = add_config_param_set(&(src->subset), name, 1, "alef"); c->flags |= CF_MERGEDOWN; c = add_config_param_set(&(dst->subset), name, 1, "alef"); c->flags |= CF_MERGEDOWN; mark_point(); pr_config_merge_down(set, FALSE); /* Add a config to the subsets, with the same name and diff args. */ name = "alef"; c = add_config_param_set(&(src->subset), name, 1, "alef"); c->flags |= CF_MERGEDOWN; c = add_config_param_set(&(dst->subset), name, 2, "bet", "vet"); c->flags |= CF_MERGEDOWN; c = add_config_param_set(&(src->subset), "Bet", 3, "1", "2", "3"); c->config_type = CONF_LIMIT; c->flags |= CF_MERGEDOWN; mark_point(); pr_config_merge_down(set, FALSE); }
static int sftppam_driver_open(sftp_kbdint_driver_t *driver, const char *user) { int res; config_rec *c; /* XXX Should we pay attention to AuthOrder here? I.e. if AuthOrder * does not include mod_sftp_pam or mod_auth_pam, should we fail to * open this driver, since the AuthOrder indicates that no PAM check is * desired? For this to work, AuthOrder needs to have been processed * prior to this callback being invoked... */ /* Figure out our default return style: whether or not PAM should allow * other auth modules a shot at this user or not is controlled by adding * '*' to a module name in the AuthOrder directive. By default, auth * modules are not authoritative, and allow other auth modules a chance at * authenticating the user. This is not the most secure configuration, but * it allows things like AuthUserFile to work "out of the box". */ if (sftppam_authtab[0].auth_flags & PR_AUTH_FL_REQUIRED) { sftppam_authoritative = TRUE; } sftppam_userlen = strlen(user) + 1; if (sftppam_userlen > (PAM_MAX_MSG_SIZE + 1)) { sftppam_userlen = PAM_MAX_MSG_SIZE + 1; } #ifdef MAXLOGNAME /* Some platforms' PAM libraries do not handle login strings that exceed * this length. */ if (sftppam_userlen > MAXLOGNAME) { pr_log_pri(PR_LOG_NOTICE, "PAM(%s): Name exceeds maximum login length (%u)", user, MAXLOGNAME); pr_trace_msg(trace_channel, 1, "user name '%s' exceeds maximum login length %u, declining", user, MAXLOGNAME); errno = EPERM; return -1; } #endif sftppam_user = malloc(sftppam_userlen); if (sftppam_user == NULL) { pr_log_pri(PR_LOG_ALERT, MOD_SFTP_PAM_VERSION ": Out of memory!"); exit(1); } memset(sftppam_user, '\0', sftppam_userlen); sstrncpy(sftppam_user, user, sftppam_userlen); c = find_config(main_server->conf, CONF_PARAM, "SFTPPAMOptions", FALSE); while (c != NULL) { unsigned long opts; pr_signals_handle(); opts = *((unsigned long *) c->argv[0]); sftppam_opts |= opts; c = find_config_next(c, c->next, CONF_PARAM, "SFTPPAMOptions", FALSE); } #ifdef SOLARIS2 /* For Solaris environments, the TTY environment will always be set, * in order to workaround a bug (Solaris Bug ID 4250887) where * pam_open_session() will crash unless both PAM_RHOST and PAM_TTY are * set, and the PAM_TTY setting is at least greater than the length of * the string "/dev/". */ sftppam_opts &= ~SFTP_PAM_OPT_NO_TTY; #endif /* SOLARIS2 */ pr_signals_block(); PRIVS_ROOT res = pam_start(sftppam_service, sftppam_user, &sftppam_conv, &sftppam_pamh); if (res != PAM_SUCCESS) { PRIVS_RELINQUISH pr_signals_unblock(); free(sftppam_user); sftppam_user = NULL; sftppam_userlen = 0; switch (res) { case PAM_SYSTEM_ERR: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, "error starting PAM service: %s", strerror(errno)); break; case PAM_BUF_ERR: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, "error starting PAM service: Memory buffer error"); break; } return -1; } pam_set_item(sftppam_pamh, PAM_RUSER, sftppam_user); pam_set_item(sftppam_pamh, PAM_RHOST, session.c->remote_name); if (!(sftppam_opts & SFTP_PAM_OPT_NO_TTY)) { memset(sftppam_tty, '\0', sizeof(sftppam_tty)); snprintf(sftppam_tty, sizeof(sftppam_tty), "/dev/ftpd%02lu", (unsigned long) (session.pid ? session.pid : getpid())); sftppam_tty[sizeof(sftppam_tty)-1] = '\0'; pr_trace_msg(trace_channel, 9, "setting PAM_TTY to '%s'", sftppam_tty); pam_set_item(sftppam_pamh, PAM_TTY, sftppam_tty); } PRIVS_RELINQUISH pr_signals_unblock(); /* We need to disable mod_auth_pam, since both mod_auth_pam and us want * to talk to the PAM API, just in different fashions. */ c = add_config_param_set(&(main_server->conf), "AuthPAM", 1, NULL); c->argv[0] = palloc(c->pool, sizeof(unsigned char)); *((unsigned char *) c->argv[0]) = FALSE; if (pr_auth_remove_auth_only_module("mod_auth_pam.c") < 0) { if (errno != ENOENT) { pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION ": error removing 'mod_auth_pam.c' from the auth-only module list: %s", strerror(errno)); } } if (pr_auth_add_auth_only_module("mod_sftp_pam.c") < 0) { if (errno != EEXIST) { pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION ": error adding 'mod_sftp_pam.c' to the auth-only module list: %s", strerror(errno)); } } sftppam_handle_auth = TRUE; driver->driver_pool = make_sub_pool(permanent_pool); pr_pool_tag(driver->driver_pool, "PAM keyboard-interactive driver pool"); return 0; }