MODRET wrap_handle_request(cmd_rec *cmd) { /* these variables are names expected to be set by the TCP wrapper code */ struct request_info request; char *user = NULL; config_rec *conf = NULL, *access_conf = NULL, *syslog_conf = NULL; hosts_allow_table = NULL; hosts_deny_table = NULL; /* hide passwords */ session.hide_password = TRUE; /* Sneaky...found in mod_auth.c's cmd_pass() function. Need to find the * login UID in order to resolve the possibly-login-dependent filename. */ user = (char *) get_param_ptr(cmd->server->conf, C_USER, FALSE); /* It's possible that a PASS command came before USER. This is a PRE_CMD * handler, so it won't be protected from this case; we'll need to do * it manually. */ if (!user) return DECLINED(cmd); /* Use mod_auth's _auth_resolve_user() [imported for use here] to get the * right configuration set, since the user may be logging in anonymously, * and the session struct hasn't yet been set for that yet (thus short- * circuiting the easiest way to get the right context...the macros. */ conf = wrap_resolve_user(cmd->pool, &user); /* Search first for user-specific access files. Multiple TCPUserAccessFiles * directives are allowed. */ if ((access_conf = find_config(conf ? conf->subset : CURRENT_CONF, CONF_PARAM, "TCPUserAccessFiles", FALSE)) != NULL) { int matched = FALSE; array_header *user_array = NULL; while (access_conf) { user_array = make_array(cmd->tmp_pool, 0, sizeof(char *)); *((char **) push_array(user_array)) = pstrdup(cmd->tmp_pool, user); /* Check the user expression -- don't forget the offset, to skip * the access file name strings in argv */ if (wrap_eval_expression(((char **) access_conf->argv) + 2, user_array)) { pr_log_debug(DEBUG4, MOD_WRAP_VERSION ": matched TCPUserAccessFiles expression"); matched = TRUE; break; } access_conf = find_config_next(access_conf, access_conf->next, CONF_PARAM, "TCPUserAccessFiles", FALSE); } if (!matched) access_conf = NULL; } /* Next, search for group-specific access files. Multiple * TCPGroupAccessFiles directives are allowed. */ if (!access_conf && (access_conf = find_config(conf ? conf->subset : CURRENT_CONF, CONF_PARAM, "TCPGroupAccessFiles", FALSE)) != NULL) { unsigned char matched = FALSE; /* NOTE: this gid_array is only necessary until Bug#1461 is fixed */ array_header *gid_array = make_array(cmd->pool, 0, sizeof(gid_t)); array_header *group_array = make_array(cmd->pool, 0, sizeof(char *)); while (access_conf) { if (pr_auth_getgroups(cmd->pool, user, &gid_array, &group_array) < 1) { pr_log_debug(DEBUG3, MOD_WRAP_VERSION ": no supplemental groups found for user '%s'", user); } else { /* Check the group expression -- don't forget the offset, to skip * the access file names strings in argv */ if (wrap_eval_expression(((char **) access_conf->argv) + 2, group_array)) { pr_log_debug(DEBUG4, MOD_WRAP_VERSION ": matched TCPGroupAccessFiles expression"); matched = TRUE; break; } } access_conf = find_config_next(access_conf, access_conf->next, CONF_PARAM, "TCPGroupAccessFiles", FALSE); } if (!matched) access_conf = NULL; } /* Finally for globally-applicable access files. Only one such directive * is allowed. */ if (!access_conf) { access_conf = find_config(conf ? conf->subset : CURRENT_CONF, CONF_PARAM, "TCPAccessFiles", FALSE); } if (access_conf) { hosts_allow_table = (char *) access_conf->argv[0]; hosts_deny_table = (char *) access_conf->argv[1]; } /* Now, check the retrieved filename, and see if it requires a login-time * file. */ if (hosts_allow_table != NULL && hosts_allow_table[0] == '~' && hosts_allow_table[1] == '/') { char *allow_real_table = NULL; allow_real_table = wrap_get_user_table(cmd, user, hosts_allow_table); if (!wrap_is_usable_file(allow_real_table)) { pr_log_pri(PR_LOG_WARNING, MOD_WRAP_VERSION ": configured TCPAllowFile %s is unusable", hosts_allow_table); hosts_allow_table = NULL; } else hosts_allow_table = allow_real_table; } if (hosts_deny_table != NULL && hosts_deny_table[0] == '~' && hosts_deny_table[1] == '/') { char *deny_real_table = NULL; deny_real_table = dir_realpath(cmd->pool, hosts_deny_table); if (!wrap_is_usable_file(deny_real_table)) { pr_log_pri(PR_LOG_WARNING, MOD_WRAP_VERSION ": configured TCPDenyFile %s is unusable", hosts_deny_table); hosts_deny_table = NULL; } else hosts_deny_table = deny_real_table; } /* Make sure that _both_ allow and deny TCPAccessFiles are present. * If not, log the missing file, and by default allow request to succeed. */ if (hosts_allow_table != NULL && hosts_deny_table != NULL) { /* Most common case...nothing more necessary */ } else if (hosts_allow_table == NULL && hosts_deny_table != NULL) { /* Log the missing file */ pr_log_pri(PR_LOG_INFO, MOD_WRAP_VERSION ": no usable allow access file -- " "allowing connection"); return DECLINED(cmd); } else if (hosts_allow_table != NULL && hosts_deny_table == NULL) { /* log the missing file */ pr_log_pri(PR_LOG_INFO, MOD_WRAP_VERSION ": no usable deny access file -- " "allowing connection"); return DECLINED(cmd); } else { /* Neither set -- assume the admin hasn't configured these directives * at all. */ return DECLINED(cmd); } /* Log the names of the allow/deny files being used. */ pr_log_pri(PR_LOG_DEBUG, MOD_WRAP_VERSION ": using access files: %s, %s", hosts_allow_table, hosts_deny_table); /* retrieve the user-defined syslog priorities, if any. Fall back to the * defaults as seen in tcpd.h if not defined. */ syslog_conf = find_config(main_server->conf, CONF_PARAM, "TCPAccessSyslogLevels", FALSE); if (syslog_conf) { allow_severity = (int) syslog_conf->argv[1]; deny_severity = (int) syslog_conf->argv[2]; } else { allow_severity = PR_LOG_INFO; deny_severity = PR_LOG_WARNING; } pr_log_debug(DEBUG4, MOD_WRAP_VERSION ": checking under service name '%s'", wrap_service_name); request_init(&request, RQ_DAEMON, wrap_service_name, RQ_FILE, session.c->rfd, 0); fromhost(&request); if (STR_EQ(eval_hostname(request.client), paranoid) || !hosts_access(&request)) { char *denymsg = NULL; /* log the denied connection */ wrap_log_request_denied(deny_severity, &request); /* check for AccessDenyMsg */ if ((denymsg = (char *) get_param_ptr(TOPLEVEL_CONF, "AccessDenyMsg", FALSE)) != NULL) denymsg = sreplace(cmd->tmp_pool, denymsg, "%u", user, NULL); if (denymsg) return ERROR_MSG(cmd, R_530, denymsg); else return ERROR_MSG(cmd, R_530, "Access denied."); } /* If request is allowable, return DECLINED (for engine to act as if this * handler was never called, else ERROR (for engine to abort processing and * deny request. */ wrap_log_request_allowed(allow_severity, &request); return DECLINED(cmd); }
int main(int argc, char *argv[]) { struct request_info request; char path[MAXPATHNAMELEN]; /* Attempt to prevent the creation of world-writable files. */ #ifdef DAEMON_UMASK umask(DAEMON_UMASK); #endif /* * If argv[0] is an absolute path name, ignore REAL_DAEMON_DIR, and strip * argv[0] to its basename. */ if (argv[0][0] == '/') { strlcpy(path, argv[0], sizeof path); argv[0] = strrchr(argv[0], '/') + 1; } else { snprintf(path, sizeof path, "%s/%s", REAL_DAEMON_DIR, argv[0]); } /* * Open a channel to the syslog daemon. Older versions of openlog() * require only two arguments. */ #ifdef LOG_MAIL (void) openlog(argv[0], LOG_PID, FACILITY); #else (void) openlog(argv[0], LOG_PID); #endif /* * Find out the endpoint addresses of this conversation. Host name * lookups and double checks will be done on demand. */ request_init(&request, RQ_DAEMON, argv[0], RQ_FILE, STDIN_FILENO, 0); fromhost(&request); /* * Optionally look up and double check the remote host name. Sites * concerned with security may choose to refuse connections from hosts * that pretend to have someone elses host name. */ #ifdef PARANOID if (STR_EQ(eval_hostname(request.client), paranoid)) refuse(&request); #endif /* * The BSD rlogin and rsh daemons that came out after 4.3 BSD disallow * socket options at the IP level. They do so for a good reason. * Unfortunately, we cannot use this with SunOS 4.1.x because the * getsockopt() system call can panic the system. */ #ifdef KILL_IP_OPTIONS fix_options(&request); #endif /* * Check whether this host can access the service in argv[0]. The * access-control code invokes optional shell commands as specified in * the access-control tables. */ #ifdef HOSTS_ACCESS if (!hosts_access(&request)) refuse(&request); #endif /* Report request and invoke the real daemon program. */ syslog(allow_severity, "connect from %s", eval_client(&request)); closelog(); (void) execv(path, argv); syslog(LOG_ERR, "error: cannot execute %s: %m", path); clean_exit(&request); /* NOTREACHED */ }