static int match_version(pool *p, const char *pattern_str, char **error) { #ifdef PR_USE_REGEX pr_regex_t *pre; int res; pre = pr_regexp_alloc(&ifversion_module); res = pr_regexp_compile(pre, pattern_str, REG_EXTENDED|REG_NOSUB|REG_ICASE); if (res != 0) { char errstr[256]; memset(errstr, '\0', sizeof(errstr)); pr_regexp_error(res, pre, errstr, sizeof(errstr)-1); pr_regexp_free(NULL, pre); *error = pstrcat(p, "unable to compile pattern '", pattern_str, "': ", errstr, NULL); return 0; } res = pr_regexp_exec(pre, pr_version_get_str(), 0, NULL, 0, 0, 0); if (res != 0) { *error = pstrcat(p, "server version '", pr_version_get_str(), "' failed to match pattern '", pattern_str, "'", NULL); } pr_regexp_free(NULL, pre); return (res == 0 ? 1 : 0); #else *error = pstrdup(p, "system does not support POSIX regular expressions"); return 0; #endif /* regex support */ }
END_TEST START_TEST (regexp_set_limits_test) { int res; pr_regex_t *pre = NULL; const char *pattern, *str; res = pr_regexp_set_limits(0, 0); fail_unless(res == 0, "Failed to set limits: %s", strerror(errno)); /* Set the limits, and compile/execute a regex. */ res = pr_regexp_set_limits(1, 1); fail_unless(res == 0, "Failed to set limits: %s", strerror(errno)); pre = pr_regexp_alloc(NULL); pattern = "^foo"; res = pr_regexp_compile(pre, pattern, REG_ICASE); fail_unless(res == 0, "Failed to compile regex pattern '%s'", pattern); str = "fooBAR"; (void) pr_regexp_exec(pre, str, 0, NULL, 0, 0, 0); pr_regexp_free(NULL, pre); }
static int af_allow_grent(struct group *grp) { if (!af_group_file) { errno = EPERM; return -1; } /* Check that the grent is within the ID restrictions (if present). */ if (af_group_file->af_restricted_ids) { if (grp->gr_gid < af_group_file->af_min_id.gid) { pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping group '%s': " "GID %lu below the minimum allowed (%lu)", grp->gr_name, (unsigned long) grp->gr_gid, (unsigned long) af_group_file->af_min_id.gid); errno = EINVAL; return -1; } if (grp->gr_gid > af_group_file->af_max_id.gid) { pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping group '%s': " "GID %lu above the maximum allowed (%lu)", grp->gr_name, (unsigned long) grp->gr_gid, (unsigned long) af_group_file->af_max_id.gid); errno = EINVAL; return -1; } } #ifdef PR_USE_REGEX /* Check if the grent has an acceptable name. */ if (af_group_file->af_restricted_names) { int res; res = pr_regexp_exec(af_group_file->af_name_regex, grp->gr_name, 0, NULL, 0, 0, 0); if ((res != 0 && !af_group_file->af_name_regex_inverted) || (res == 0 && af_group_file->af_name_regex_inverted)) { pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping group '%s': " "name '%s' does not meet allowed filter '%s'", grp->gr_name, grp->gr_name, af_group_file->af_name_filter); errno = EINVAL; return -1; } } #endif /* regex support */ return 0; }
END_TEST START_TEST (regexp_exec_test) { pr_regex_t *pre = NULL; int res; char *pattern, *str; res = pr_regexp_exec(NULL, NULL, 0, NULL, 0, 0, 0); fail_unless(res < 0, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); pre = pr_regexp_alloc(NULL); pattern = "^foo"; res = pr_regexp_compile(pre, pattern, 0); fail_unless(res == 0, "Failed to compile regex pattern '%s'", pattern); res = pr_regexp_exec(pre, NULL, 0, NULL, 0, 0, 0); fail_unless(res != 0, "Failed to handle null string"); str = "bar"; res = pr_regexp_exec(pre, str, 0, NULL, 0, 0, 0); fail_unless(res != 0, "Matched string unexpectedly"); str = "foobar"; res = pr_regexp_exec(pre, str, 0, NULL, 0, 0, 0); fail_unless(res == 0, "Failed to match string"); pr_regexp_free(NULL, pre); pre = pr_regexp_alloc(NULL); pattern = "^foo"; res = pr_regexp_compile_posix(pre, pattern, REG_ICASE); fail_unless(res == 0, "Failed to compile regex pattern '%s'", pattern); res = pr_regexp_exec(pre, NULL, 0, NULL, 0, 0, 0); fail_unless(res != 0, "Failed to handle null string"); str = "BAR"; res = pr_regexp_exec(pre, str, 0, NULL, 0, 0, 0); fail_unless(res != 0, "Matched string unexpectedly"); str = "FOOBAR"; res = pr_regexp_exec(pre, str, 0, NULL, 0, 0, 0); fail_unless(res == 0, "Failed to match string"); pr_regexp_free(NULL, pre); }
static int forward_dst_filter(pool *p, const char *hostport) { #ifdef PR_USE_REGEX config_rec *c; pr_regex_t *pre; int negated = FALSE, res; c = find_config(main_server->conf, CONF_PARAM, "ProxyForwardTo", FALSE); if (c == NULL) { return 0; } pre = c->argv[0]; negated = *((int *) c->argv[1]); res = pr_regexp_exec(pre, hostport, 0, NULL, 0, 0, 0); if (res == 0) { /* Pattern matched */ if (negated == TRUE) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "host/port '%.100s' matched ProxyForwardTo !%s, rejecting", hostport, pr_regexp_get_pattern(pre)); errno = EPERM; return -1; } } else { /* Pattern NOT matched */ if (negated == FALSE) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "host/port '%.100s' did not match ProxyForwardTo %s, rejecting", hostport, pr_regexp_get_pattern(pre)); errno = EPERM; return -1; } } #endif /* PR_USE_REGEX */ return 0; }
MODRET site_chgrp(cmd_rec *cmd) { int res; gid_t gid; char *path = NULL, *tmp = NULL, *arg = ""; register unsigned int i = 0; #ifdef PR_USE_REGEX pr_regex_t *pre; #endif if (cmd->argc < 3) { pr_response_add_err(R_500, _("'SITE %s' not understood"), _get_full_cmd(cmd)); return NULL; } /* Construct the target file name by concatenating all the parameters after * the mode, separating them with spaces. */ for (i = 2; i <= cmd->argc-1; i++) arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL); #ifdef PR_USE_REGEX pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) != 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathAllowFilter", cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); return PR_ERROR(cmd); } pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) == 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathDenyFilter", cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); return PR_ERROR(cmd); } #endif path = dir_realpath(cmd->tmp_pool, arg); if (!path) { pr_response_add_err(R_550, "%s: %s", arg, strerror(errno)); return PR_ERROR(cmd); } /* Map the given group argument, if a string, to a GID. If already a * number, pass through as is. */ gid = strtoul(cmd->argv[1], &tmp, 10); if (tmp && *tmp) { /* Try the parameter as a group name. */ gid = pr_auth_name2gid(cmd->tmp_pool, cmd->argv[1]); if (gid == (gid_t) -1) { pr_log_debug(DEBUG9, "SITE CHGRP: Unable to resolve group name '%s' to GID", cmd->argv[1]); pr_response_add_err(R_550, "%s: %s", arg, strerror(EINVAL)); return PR_ERROR(cmd); } } res = core_chgrp(cmd, path, (uid_t) -1, gid); if (res < 0) { int xerrno = errno; (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): " "error chown'ing '%s' to GID %lu: %s", cmd->argv[0], session.user, (unsigned long) session.uid, (unsigned long) session.gid, path, (unsigned long) gid, strerror(xerrno)); pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); errno = xerrno; return PR_ERROR(cmd); } else { pr_response_add(R_200, _("SITE %s command successful"), cmd->argv[0]); } return PR_HANDLED(cmd); }
MODRET site_chmod(cmd_rec *cmd) { int res; mode_t mode = 0; char *dir, *endp, *tmp, *arg = ""; register unsigned int i = 0; #ifdef PR_USE_REGEX pr_regex_t *pre; #endif if (cmd->argc < 3) { pr_response_add_err(R_500, _("'SITE %s' not understood"), _get_full_cmd(cmd)); return NULL; } /* Construct the target file name by concatenating all the parameters after * the mode, separating them with spaces. */ for (i = 2; i <= cmd->argc-1; i++) arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL); #ifdef PR_USE_REGEX pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) != 0) { pr_log_debug(DEBUG2, "'%s %s %s' denied by PathAllowFilter", cmd->argv[0], cmd->argv[1], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); return PR_ERROR(cmd); } pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) == 0) { pr_log_debug(DEBUG2, "'%s %s %s' denied by PathDenyFilter", cmd->argv[0], cmd->argv[1], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); return PR_ERROR(cmd); } #endif dir = dir_realpath(cmd->tmp_pool, arg); if (!dir) { pr_response_add_err(R_550, "%s: %s", arg, strerror(errno)); return PR_ERROR(cmd); } /* If the first character isn't '0', prepend it and attempt conversion. * This will fail if the chmod is a symbolic, but takes care of the * case where an octal number is sent without the leading '0'. */ if (cmd->argv[1][0] != '0') { tmp = pstrcat(cmd->tmp_pool, "0", cmd->argv[1], NULL); } else { tmp = cmd->argv[1]; } mode = strtol(tmp,&endp,0); if (endp && *endp) { /* It's not an absolute number, try symbolic */ char *cp = cmd->argv[1]; int mask = 0, mode_op = 0, curmode = 0, curumask = umask(0); int invalid = 0; char *who, *how, *what; struct stat st; umask(curumask); mode = 0; if (pr_fsio_stat(dir, &st) != -1) curmode = st.st_mode; while (TRUE) { who = pstrdup(cmd->tmp_pool, cp); tmp = strpbrk(who, "+-="); if (tmp != NULL) { how = pstrdup(cmd->tmp_pool, tmp); if (*how != '=') mode = curmode; *tmp = '\0'; } else { invalid++; break; } tmp = strpbrk(how, "rwxXstugo"); if (tmp != NULL) { what = pstrdup(cmd->tmp_pool, tmp); *tmp = '\0'; } else { invalid++; break; } cp = what; while (cp) { switch (*who) { case 'u': mask = 0077; break; case 'g': mask = 0707; break; case 'o': mask = 0770; break; case 'a': mask = 0000; break; case '\0': mask = curumask; break; default: invalid++; break; } if (invalid) break; switch (*how) { case '+': case '-': case '=': break; default: invalid++; } if (invalid) break; switch (*cp) { case 'r': mode_op |= (S_IRUSR|S_IRGRP|S_IROTH); break; case 'w': mode_op |= (S_IWUSR|S_IWGRP|S_IWOTH); break; case 'x': mode_op |= (S_IXUSR|S_IXGRP|S_IXOTH); break; /* 'X' not implemented */ case 's': /* setuid */ mode_op |= S_ISUID; break; case 't': /* sticky */ mode_op |= S_ISVTX; break; case 'o': mode_op |= curmode & S_IRWXO; mode_op |= (curmode & S_IRWXO) << 3; mode_op |= (curmode & S_IRWXO) << 6; break; case 'g': mode_op |= (curmode & S_IRWXG) >> 3; mode_op |= curmode & S_IRWXG; mode_op |= (curmode & S_IRWXG) << 3; break; case 'u': mode_op |= (curmode & S_IRWXO) >> 6; mode_op |= (curmode & S_IRWXO) >> 3; mode_op |= curmode & S_IRWXU; break; case '\0': /* Apply the mode and move on */ switch (*how) { case '+': case '=': mode |= (mode_op & ~mask); break; case '-': mode &= ~(mode_op & ~mask); break; } mode_op = 0; if (*who && *(who+1)) { who++; cp = what; continue; } else cp = NULL; break; default: invalid++; } if (invalid) break; if (cp) cp++; } break; } if (invalid) { pr_response_add_err(R_550, _("'%s': invalid mode"), cmd->argv[1]); return PR_ERROR(cmd); } }
static int af_allow_pwent(struct passwd *pwd) { if (!af_user_file) { errno = EPERM; return -1; } /* Check that the pwent is within the ID restrictions (if present). */ if (af_user_file->af_restricted_ids) { if (pwd->pw_uid < af_user_file->af_min_id.uid) { pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping user '%s': " "UID %lu below the minimum allowed (%lu)", pwd->pw_name, (unsigned long) pwd->pw_uid, (unsigned long) af_user_file->af_min_id.uid); errno = EINVAL; return -1; } if (pwd->pw_uid > af_user_file->af_max_id.gid) { pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping user '%s': " "UID %lu above the maximum allowed (%lu)", pwd->pw_name, (unsigned long) pwd->pw_uid, (unsigned long) af_user_file->af_max_id.uid); errno = EINVAL; return -1; } } #ifdef PR_USE_REGEX /* Check if the pwent has an acceptable name. */ if (af_user_file->af_restricted_names) { int res; res = pr_regexp_exec(af_user_file->af_name_regex, pwd->pw_name, 0, NULL, 0, 0, 0); if ((res != 0 && !af_user_file->af_name_regex_inverted) || (res == 0 && af_user_file->af_name_regex_inverted)) { pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping user '%s': " "name '%s' does not meet allowed filter '%s'", pwd->pw_name, pwd->pw_name, af_user_file->af_name_filter); errno = EINVAL; return -1; } } /* Check if the pwent has an acceptable home directory. */ if (af_user_file->af_restricted_homes) { int res; res = pr_regexp_exec(af_user_file->af_home_regex, pwd->pw_dir, 0, NULL, 0, 0, 0); if ((res != 0 && !af_user_file->af_home_regex_inverted) || (res == 0 && af_user_file->af_home_regex_inverted)) { pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping user '%s': " "home '%s' does not meet allowed filter '%s'", pwd->pw_name, pwd->pw_dir, af_user_file->af_home_filter); errno = EINVAL; return -1; } } #endif /* regex support */ return 0; }
static int check_geoip_filters(geoip_policy_e policy) { int matched_allow_filter = -1, allow_conn = 0; #if PR_USE_REGEX config_rec *c; c = find_config(main_server->conf, CONF_PARAM, "GeoIPAllowFilter", FALSE); while (c) { int filter_id, res; pr_regex_t *filter_re; const char *filter_name, *filter_pattern, *filter_value; pr_signals_handle(); if (matched_allow_filter == -1) { matched_allow_filter = FALSE; } filter_id = *((int *) c->argv[0]); filter_pattern = c->argv[1]; filter_re = c->argv[2]; filter_value = get_geoip_filter_value(filter_id); if (filter_value == NULL) { c = find_config_next(c, c->next, CONF_PARAM, "GeoIPAllowFilter", FALSE); continue; } filter_name = get_geoip_filter_name(filter_id); res = pr_regexp_exec(filter_re, filter_value, 0, NULL, 0, 0, 0); pr_trace_msg(trace_channel, 12, "%s filter value %s %s GeoIPAllowFilter pattern '%s'", filter_name, filter_value, res == 0 ? "matched" : "did not match", filter_pattern); if (res == 0) { matched_allow_filter = TRUE; break; } (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "%s filter value '%s' did not match GeoIPAllowFilter pattern '%s'", filter_name, filter_value, filter_pattern); c = find_config_next(c, c->next, CONF_PARAM, "GeoIPAllowFilter", FALSE); } c = find_config(main_server->conf, CONF_PARAM, "GeoIPDenyFilter", FALSE); while (c) { int filter_id, res; pr_regex_t *filter_re; const char *filter_name, *filter_pattern, *filter_value; pr_signals_handle(); filter_id = *((int *) c->argv[0]); filter_pattern = c->argv[1]; filter_re = c->argv[2]; filter_value = get_geoip_filter_value(filter_id); if (filter_value == NULL) { c = find_config_next(c, c->next, CONF_PARAM, "GeoIPDenyFilter", FALSE); continue; } filter_name = get_geoip_filter_name(filter_id); res = pr_regexp_exec(filter_re, filter_value, 0, NULL, 0, 0, 0); pr_trace_msg(trace_channel, 12, "%s filter value %s %s GeoIPDenyFilter pattern '%s'", filter_name, filter_value, res == 0 ? "matched" : "did not match", filter_pattern); if (res == 0) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "%s filter value '%s' matched GeoIPDenyFilter pattern '%s'", filter_name, filter_value, filter_pattern); return -1; } c = find_config_next(c, c->next, CONF_PARAM, "GeoIPDenyFilter", FALSE); } #endif /* !HAVE_REGEX_H or !HAVE_REGCOMP */ switch (policy) { case GEOIP_POLICY_ALLOW_DENY: allow_conn = 0; break; case GEOIP_POLICY_DENY_ALLOW: if (matched_allow_filter == FALSE) { /* If we have not explicitly matched any allow filters, then * reject the connection. */ allow_conn = -1; } break; } return allow_conn; }
MODRET site_chgrp(cmd_rec *cmd) { int res; gid_t gid; char *path = NULL, *tmp = NULL, *arg = ""; struct stat st; register unsigned int i = 0; #ifdef PR_USE_REGEX pr_regex_t *pre; #endif if (cmd->argc < 3) { pr_response_add_err(R_500, _("'SITE %s' not understood"), _get_full_cmd(cmd)); return NULL; } /* Construct the target file name by concatenating all the parameters after * the mode, separating them with spaces. */ for (i = 2; i <= cmd->argc-1; i++) { char *decoded_path; decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[i], FSIO_DECODE_FL_TELL_ERRORS); if (decoded_path == NULL) { int xerrno = errno; pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", (char *) cmd->argv[i], strerror(xerrno)); pr_response_add_err(R_550, _("SITE %s: Illegal character sequence in command"), (char *) cmd->argv[1]); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", decoded_path, NULL); } #ifdef PR_USE_REGEX pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) != 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathAllowFilter", (char *) cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) == 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathDenyFilter", (char *) cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } #endif if (pr_fsio_lstat(arg, &st) == 0) { if (S_ISLNK(st.st_mode)) { char link_path[PR_TUNABLE_PATH_MAX]; int len; memset(link_path, '\0', sizeof(link_path)); len = dir_readlink(cmd->tmp_pool, arg, link_path, sizeof(link_path)-1, PR_DIR_READLINK_FL_HANDLE_REL_PATH); if (len > 0) { link_path[len] = '\0'; arg = pstrdup(cmd->tmp_pool, link_path); } } } path = dir_realpath(cmd->tmp_pool, arg); if (path == NULL) { int xerrno = errno; pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } /* Map the given group argument, if a string, to a GID. If already a * number, pass through as is. */ gid = strtoul(cmd->argv[1], &tmp, 10); if (tmp && *tmp) { /* Try the parameter as a group name. */ gid = pr_auth_name2gid(cmd->tmp_pool, cmd->argv[1]); if (gid == (gid_t) -1) { int xerrno = EINVAL; pr_log_debug(DEBUG9, "SITE CHGRP: Unable to resolve group name '%s' to GID", (char *) cmd->argv[1]); pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } } res = core_chgrp(cmd, path, (uid_t) -1, gid); if (res < 0) { int xerrno = errno; (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): " "error chown'ing '%s' to GID %s: %s", (char *) cmd->argv[0], session.user, pr_uid2str(cmd->tmp_pool, session.uid), pr_gid2str(cmd->tmp_pool, session.gid), path, pr_gid2str(cmd->tmp_pool, gid), strerror(xerrno)); pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } pr_response_add(R_200, _("SITE %s command successful"), (char *) cmd->argv[0]); return PR_HANDLED(cmd); }
MODRET site_chmod(cmd_rec *cmd) { int res; mode_t mode = 0; char *dir, *endp, *mode_str, *tmp, *arg = ""; struct stat st; register unsigned int i = 0; #ifdef PR_USE_REGEX pr_regex_t *pre; #endif if (cmd->argc < 3) { pr_response_add_err(R_500, _("'SITE %s' not understood"), _get_full_cmd(cmd)); return NULL; } /* Construct the target file name by concatenating all the parameters after * the mode, separating them with spaces. */ for (i = 2; i <= cmd->argc-1; i++) { char *decoded_path; decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[i], FSIO_DECODE_FL_TELL_ERRORS); if (decoded_path == NULL) { int xerrno = errno; pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", (char *) cmd->argv[i], strerror(xerrno)); pr_response_add_err(R_550, _("SITE %s: Illegal character sequence in command"), (char *) cmd->argv[1]); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", decoded_path, NULL); } #ifdef PR_USE_REGEX pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) != 0) { pr_log_debug(DEBUG2, "'%s %s %s' denied by PathAllowFilter", (char *) cmd->argv[0], (char *) cmd->argv[1], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) == 0) { pr_log_debug(DEBUG2, "'%s %s %s' denied by PathDenyFilter", (char *) cmd->argv[0], (char *) cmd->argv[1], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } #endif if (pr_fsio_lstat(arg, &st) == 0) { if (S_ISLNK(st.st_mode)) { char link_path[PR_TUNABLE_PATH_MAX]; int len; memset(link_path, '\0', sizeof(link_path)); len = dir_readlink(cmd->tmp_pool, arg, link_path, sizeof(link_path)-1, PR_DIR_READLINK_FL_HANDLE_REL_PATH); if (len > 0) { link_path[len] = '\0'; arg = pstrdup(cmd->tmp_pool, link_path); } } } dir = dir_realpath(cmd->tmp_pool, arg); if (dir == NULL) { int xerrno = errno; pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } /* If the first character isn't '0', prepend it and attempt conversion. * This will fail if the chmod is a symbolic, but takes care of the * case where an octal number is sent without the leading '0'. */ mode_str = cmd->argv[1]; if (mode_str[0] != '0') { tmp = pstrcat(cmd->tmp_pool, "0", mode_str, NULL); } else { tmp = mode_str; } mode = strtol(tmp, &endp, 0); if (endp && *endp) { /* It's not an absolute number, try symbolic */ char *cp = mode_str; int mask = 0, mode_op = 0, curr_mode = 0, curr_umask = umask(0); int invalid = 0; char *who, *how, *what; umask(curr_umask); mode = 0; if (pr_fsio_stat(dir, &st) != -1) { curr_mode = st.st_mode; } while (TRUE) { pr_signals_handle(); who = pstrdup(cmd->tmp_pool, cp); tmp = strpbrk(who, "+-="); if (tmp != NULL) { how = pstrdup(cmd->tmp_pool, tmp); if (*how != '=') { mode = curr_mode; } *tmp = '\0'; } else { invalid++; break; } tmp = strpbrk(how, "rwxXstugo"); if (tmp != NULL) { what = pstrdup(cmd->tmp_pool, tmp); *tmp = '\0'; } else { invalid++; break; } cp = what; while (cp) { switch (*who) { case 'u': mask = 0077; break; case 'g': mask = 0707; break; case 'o': mask = 0770; break; case 'a': mask = 0000; break; case '\0': mask = curr_umask; break; default: invalid++; break; } if (invalid) break; switch (*how) { case '+': case '-': case '=': break; default: invalid++; } if (invalid) break; switch (*cp) { case 'r': mode_op |= (S_IRUSR|S_IRGRP|S_IROTH); break; case 'w': mode_op |= (S_IWUSR|S_IWGRP|S_IWOTH); break; case 'x': mode_op |= (S_IXUSR|S_IXGRP|S_IXOTH); break; /* 'X' not implemented */ case 's': /* setuid */ mode_op |= S_ISUID; break; case 't': /* sticky */ mode_op |= S_ISVTX; break; case 'o': mode_op |= (curr_mode & S_IRWXO); mode_op |= ((curr_mode & S_IRWXO) << 3); mode_op |= ((curr_mode & S_IRWXO) << 6); break; case 'g': mode_op |= ((curr_mode & S_IRWXG) >> 3); mode_op |= (curr_mode & S_IRWXG); mode_op |= ((curr_mode & S_IRWXG) << 3); break; case 'u': mode_op |= ((curr_mode & S_IRWXU) >> 6); mode_op |= ((curr_mode & S_IRWXU) >> 3); mode_op |= (curr_mode & S_IRWXU); break; case '\0': /* Apply the mode and move on */ switch (*how) { case '+': case '=': mode |= (mode_op & ~mask); break; case '-': mode &= ~(mode_op & ~mask); break; } mode_op = 0; if (*who && *(who+1)) { who++; cp = what; continue; } else { cp = NULL; } break; default: invalid++; } if (invalid) { break; } if (cp) { cp++; } } break; } if (invalid) { pr_response_add_err(R_550, _("'%s': invalid mode"), (char *) cmd->argv[1]); pr_cmd_set_errno(cmd, EINVAL); errno = EINVAL; return PR_ERROR(cmd); } }
static int check_geoip_filters(geoip_policy_e policy) { int allow_conn = 0, matched_allow_filter = -1, matched_deny_filter = -1; #if PR_USE_REGEX config_rec *c; c = find_config(main_server->conf, CONF_PARAM, "GeoIPAllowFilter", FALSE); while (c != NULL) { register unsigned int i; int matched = TRUE; array_header *filters; pr_signals_handle(); if (matched_allow_filter == -1) { matched_allow_filter = FALSE; } filters = c->argv[0]; for (i = 0; i < filters->nelts; i++) { int filter_id, res; struct geoip_filter *filter; pr_regex_t *filter_re; const char *filter_name, *filter_pattern, *filter_value; filter = ((struct geoip_filter **) filters->elts)[i]; filter_id = filter->filter_id; filter_pattern = filter->filter_pattern; filter_re = filter->filter_re; filter_value = get_geoip_filter_value(filter_id); if (filter_value == NULL) { matched = FALSE; break; } filter_name = get_geoip_filter_name(filter_id); res = pr_regexp_exec(filter_re, filter_value, 0, NULL, 0, 0, 0); pr_trace_msg(trace_channel, 12, "%s filter value %s %s GeoIPAllowFilter pattern '%s'", filter_name, filter_value, res == 0 ? "matched" : "did not match", filter_pattern); if (res == 0) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "%s filter value '%s' matched GeoIPAllowFilter pattern '%s'", filter_name, filter_value, filter_pattern); } else { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "%s filter value '%s' did not match GeoIPAllowFilter pattern '%s'", filter_name, filter_value, filter_pattern); matched = FALSE; break; } } if (matched == TRUE) { matched_allow_filter = TRUE; break; } c = find_config_next(c, c->next, CONF_PARAM, "GeoIPAllowFilter", FALSE); } c = find_config(main_server->conf, CONF_PARAM, "GeoIPDenyFilter", FALSE); while (c != NULL) { register unsigned int i; int matched = TRUE; array_header *filters; pr_signals_handle(); if (matched_deny_filter == -1) { matched_deny_filter = FALSE; } filters = c->argv[0]; for (i = 0; i < filters->nelts; i++) { int filter_id, res; struct geoip_filter *filter; pr_regex_t *filter_re; const char *filter_name, *filter_pattern, *filter_value; filter = ((struct geoip_filter **) filters->elts)[i]; filter_id = filter->filter_id; filter_pattern = filter->filter_pattern; filter_re = filter->filter_re; filter_value = get_geoip_filter_value(filter_id); if (filter_value == NULL) { matched = FALSE; break; } filter_name = get_geoip_filter_name(filter_id); res = pr_regexp_exec(filter_re, filter_value, 0, NULL, 0, 0, 0); pr_trace_msg(trace_channel, 12, "%s filter value %s %s GeoIPDenyFilter pattern '%s'", filter_name, filter_value, res == 0 ? "matched" : "did not match", filter_pattern); if (res == 0) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "%s filter value '%s' matched GeoIPDenyFilter pattern '%s'", filter_name, filter_value, filter_pattern); } else { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "%s filter value '%s' did not match GeoIPDenyFilter pattern '%s'", filter_name, filter_value, filter_pattern); matched = FALSE; break; } } if (matched == TRUE) { matched_deny_filter = TRUE; break; } c = find_config_next(c, c->next, CONF_PARAM, "GeoIPDenyFilter", FALSE); } #endif /* !HAVE_REGEX_H or !HAVE_REGCOMP */ switch (policy) { case GEOIP_POLICY_ALLOW_DENY: if (matched_deny_filter == TRUE && matched_allow_filter != TRUE) { /* If we explicitly matched any deny filters AND have NOT explicitly * matched any allow filters, the connection is rejected, otherwise, * it is allowed. */ (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "client matched GeoIPDenyFilter, rejecting connection"); allow_conn = -1; } else { pr_trace_msg(trace_channel, 9, "allowing client connection (policy 'allow,deny')"); } break; case GEOIP_POLICY_DENY_ALLOW: if (matched_allow_filter == FALSE) { /* If we have not explicitly matched any allow filters, then * reject the connection. */ (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "client did not match any GeoIPAllowFilters, rejecting connection"); allow_conn = -1; } else { pr_trace_msg(trace_channel, 9, "allowing client connection (policy 'deny,allow')"); } break; } return allow_conn; }