END_TEST #endif /* PR_USE_IPV6 */ START_TEST (netaddr_get_ipstr_test) { pr_netaddr_t *addr; const char *res; res = pr_netaddr_get_ipstr(NULL); fail_unless(res == NULL, "Failed to handle null argument"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); addr = pr_netaddr_get_addr(p, "localhost", NULL); fail_unless(addr != NULL, "Failed to get addr for 'localhost': %s", strerror(errno)); res = pr_netaddr_get_ipstr(addr); fail_unless(res != NULL, "Failed to get IP str for addr: %s", strerror(errno)); fail_unless(strcmp(res, "127.0.0.1") == 0, "Expected '%s', got '%s'", "127.0.0.1", res); fail_unless(addr->na_have_ipstr == 1, "addr should have cached IP str"); pr_netaddr_clear(addr); res = pr_netaddr_get_ipstr(addr); fail_unless(res == NULL, "Expected null, got '%s'", res); }
int proxy_forward_sess_init(pool *p, const char *tables_dir, struct proxy_session *proxy_sess) { config_rec *c; int allowed = FALSE; void *enabled = NULL; /* By default, only allow connections from RFC1918 addresses to use * forward proxying. Otherwise, it must be from an explicitly allowed * connection class, via the class notes. */ if (session.conn_class != NULL) { enabled = pr_table_get(session.conn_class->cls_notes, PROXY_FORWARD_ENABLED_NOTE, NULL); } if (enabled != NULL) { allowed = *((int *) enabled); if (allowed == FALSE) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "forward proxying not allowed from client address %s in <Class %s> " "(see ProxyForwardEnabled)", pr_netaddr_get_ipstr(session.c->remote_addr), session.conn_class->cls_name); } } else { if (pr_netaddr_is_rfc1918(session.c->remote_addr) == TRUE) { allowed = TRUE; } else { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "forward proxying not allowed from non-RFC1918 client address %s", pr_netaddr_get_ipstr(session.c->remote_addr)); } } if (allowed == FALSE) { errno = EPERM; return -1; } c = find_config(main_server->conf, CONF_PARAM, "ProxyForwardMethod", FALSE); if (c != NULL) { proxy_method = *((int *) c->argv[0]); } c = find_config(main_server->conf, CONF_PARAM, "ProxyRetryCount", FALSE); if (c != NULL) { forward_retry_count = *((int *) c->argv[0]); } return 0; }
int pr_ipbind_create(server_rec *server, pr_netaddr_t *addr, unsigned int port) { pr_ipbind_t *ipbind = NULL; register unsigned int i = 0; if (server == NULL|| addr == NULL) { errno = EINVAL; return -1; } i = ipbind_hash_addr(addr); /* Make sure the address is not already in use */ for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 && ipbind->ib_port == port) { /* An ipbind already exists for this IP address */ pr_log_pri(PR_LOG_WARNING, "notice: '%s' (%s:%u) already bound to '%s'", server->ServerName, pr_netaddr_get_ipstr(addr), port, ipbind->ib_server->ServerName); errno = EADDRINUSE; return -1; } } if (!binding_pool) { binding_pool = make_sub_pool(permanent_pool); pr_pool_tag(binding_pool, "Bindings Pool"); } ipbind = pcalloc(server->pool, sizeof(pr_ipbind_t)); ipbind->ib_server = server; ipbind->ib_addr = addr; ipbind->ib_port = port; ipbind->ib_namebinds = NULL; ipbind->ib_isdefault = FALSE; ipbind->ib_islocalhost = FALSE; ipbind->ib_isactive = FALSE; pr_trace_msg(trace_channel, 8, "created IP binding for %s#%u, server %p", pr_netaddr_get_ipstr(ipbind->ib_addr), ipbind->ib_port, ipbind->ib_server); /* Add the ipbind to the table. */ if (ipbind_table[i]) { ipbind->ib_next = ipbind_table[i]; } ipbind_table[i] = ipbind; return 0; }
static void dynmasq_refresh(void) { server_rec *s; pr_log_debug(DEBUG2, MOD_DYNMASQ_VERSION ": resolving all MasqueradeAddress directives (could take a little while)"); for (s = (server_rec *) server_list->xas_list; s; s = s->next) { config_rec *c; c = find_config(s->conf, CONF_PARAM, "MasqueradeAddress", FALSE); if (c != NULL) { const char *masq_addr; pr_netaddr_t *na; masq_addr = c->argv[1]; pr_netaddr_clear_ipcache(masq_addr); na = pr_netaddr_get_addr(s->pool, masq_addr, NULL); if (na != NULL) { /* Compare the obtained netaddr with the one already present. * Only update the "live" netaddr if they differ. */ pr_log_debug(DEBUG2, MOD_DYNMASQ_VERSION ": resolved MasqueradeAddress '%s' to IP address %s", masq_addr, pr_netaddr_get_ipstr(na)); if (pr_netaddr_cmp(c->argv[0], na) != 0) { pr_log_pri(PR_LOG_DEBUG, MOD_DYNMASQ_VERSION ": MasqueradeAddress '%s' updated for new address %s (was %s)", masq_addr, pr_netaddr_get_ipstr(na), pr_netaddr_get_ipstr(c->argv[0])); /* Overwrite the old netaddr pointer. Note that this constitutes * a minor memory leak, as there currently isn't a way to free * the memory used by a netaddr object. Hrm. */ c->argv[0] = na; } else { pr_log_debug(DEBUG2, MOD_DYNMASQ_VERSION ": MasqueradeAddress '%s' has not changed addresses", masq_addr); } } else { pr_log_pri(PR_LOG_INFO, MOD_DYNMASQ_VERSION ": unable to resolve '%s', keeping previous address", masq_addr); } } } return; }
MODRET lmd_deny_blacklist_post_pass(cmd_rec *cmd) { /* mod_authを通過するまでは session.userは空の様子 const char *account = session.user; */ const char *account = NULL; const char *remote_ip = NULL; /* return IP unless found hostname */ account = get_param_ptr(cmd->server->conf, "UserName", FALSE); remote_ip = pr_netaddr_get_ipstr(pr_netaddr_get_sess_remote_addr()); if(false == is_set_server) { pr_log_auth(PR_LOG_WARNING, "%s: memcached_server not set", MODULE_NAME); lmd_cleanup(); return PR_DECLINED(cmd); } if(is_allowed_user(cmd, account) == true) { pr_log_auth(PR_LOG_NOTICE, "%s: '%s' is allowed to login. skip last process", MODULE_NAME, account); lmd_cleanup(); return PR_DECLINED(cmd); } /* allow explicily */ if(is_allowed(cmd, session.c->remote_addr) == true) { return PR_DECLINED(cmd); } /* check whether account is registerd in blacklist or not */ if(is_cache_exits(memcached_deny_blacklist_mmc, account) == true) { pr_log_auth(PR_LOG_NOTICE, "%s: denied '%s@%s'. Account found in blacklist(memcached)", MODULE_NAME, account, remote_ip); pr_response_send(R_530, _("Login denied temporary (Account found in blacklist)")); end_login(0); } /* check whether remote IP is registerd in blacklist or not */ if(is_cache_exits(memcached_deny_blacklist_mmc, remote_ip) == true) { pr_log_auth(PR_LOG_NOTICE, "%s: denied '%s@%s'. IP found in blacklist(memcached)", MODULE_NAME, account, remote_ip); pr_response_send(R_530, _("Login denied temporary (IP found in blacklist)")); end_login(0); } pr_log_debug(DEBUG2, "%s: not found in blaclist. '%s@%s' is allowed to Login", MODULE_NAME, account, remote_ip); lmd_cleanup(); return PR_DECLINED(cmd); }
int pr_netaddr_fnmatch(pr_netaddr_t *na, const char *pattern, int flags) { /* Note: I'm still not sure why proftpd bundles an fnmatch(3) * implementation rather than using the system library's implementation. * Needs looking into. * * The FNM_CASEFOLD flag is a GNU extension; perhaps the bundled * implementation was added to make that flag available on other platforms. */ int match_flags = PR_FNM_NOESCAPE|PR_FNM_CASEFOLD; if (!na || !pattern) { errno = EINVAL; return -1; } if (flags & PR_NETADDR_MATCH_DNS) { const char *dnsstr = pr_netaddr_get_dnsstr(na); if (pr_fnmatch(pattern, dnsstr, match_flags) == 0) { pr_trace_msg(trace_channel, 6, "DNS name '%s' matches pattern '%s'", dnsstr, pattern); return TRUE; } } if (flags & PR_NETADDR_MATCH_IP) { const char *ipstr = pr_netaddr_get_ipstr(na); if (pr_fnmatch(pattern, ipstr, match_flags) == 0) { pr_trace_msg(trace_channel, 6, "DNS name '%s' matches pattern '%s'", ipstr, pattern); return TRUE; } } pr_trace_msg(trace_channel, 4, "addr %s does not match pattern '%s'", pr_netaddr_get_ipstr(na), pattern); return FALSE; }
const char *proxy_ftp_msg_fmt_addr(pool *p, const pr_netaddr_t *addr, unsigned short port, int use_masqaddr) { char *addr_str, *msg, *ptr; size_t msglen; if (p == NULL || addr == NULL) { errno = EINVAL; return NULL; } if (use_masqaddr) { config_rec *c; /* TODO What about TLSMasqueradeAddress? */ /* Handle MasqueradeAddress. */ c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE); if (c != NULL) { addr = c->argv[0]; } } addr_str = pstrdup(p, pr_netaddr_get_ipstr(addr)); /* Fixup the address string for use in PORT commands/PASV responses. */ ptr = strrchr(addr_str, ':'); if (ptr != NULL) { addr_str = ptr + 1; } for (ptr = addr_str; *ptr; ptr++) { if (*ptr == '.') { *ptr = ','; } } /* Allocate enough room for 6 numbers (3 digits max each), 5 separators, * and a trailing NUL. */ msglen = (6 * 3) + (5 * 1) + 1; msg = pcalloc(p, msglen); snprintf(msg, msglen-1, "%s,%u,%u", addr_str, (port >> 8) & 255, port & 255); return msg; }
END_TEST START_TEST (netaddr_v6tov4_test) { pr_netaddr_t *addr, *addr2; const char *name, *ipstr; addr = pr_netaddr_v6tov4(NULL, NULL); fail_unless(addr == NULL, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); addr = pr_netaddr_v6tov4(p, NULL); fail_unless(addr == NULL, "Failed to handle null address"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); name = "127.0.0.1"; addr2 = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr2 != NULL, "Failed to resolve '%s': %s", name, strerror(errno)); addr = pr_netaddr_v6tov4(p, addr2); fail_unless(addr == NULL, "Converted '%s' to IPv4 address unexpectedly", name); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); name = "::ffff:127.0.0.1"; addr2 = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr2 != NULL, "Failed to resolve '%s': %s", name, strerror(errno)); addr = pr_netaddr_v6tov4(p, addr2); fail_unless(addr != NULL, "Failed to convert '%s' to IPv4 addres: %s", name, strerror(errno)); fail_unless(pr_netaddr_get_family(addr) == AF_INET, "Expected %d, got %d", AF_INET, pr_netaddr_get_family(addr)); ipstr = pr_netaddr_get_ipstr(addr); fail_unless(strcmp(ipstr, "127.0.0.1") == 0, "Expected '127.0.0.1', got '%s'", ipstr); }
int proxy_conn_connect_timeout_cb(CALLBACK_FRAME) { struct proxy_session *proxy_sess; pr_netaddr_t *server_addr; proxy_sess = pr_table_get(session.notes, "mod_proxy.proxy-session", NULL); server_addr = pr_table_get(session.notes, "mod_proxy.proxy-connect-address", NULL); (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "timed out connecting to %s:%d after %d %s", pr_netaddr_get_ipstr(server_addr), ntohs(pr_netaddr_get_port(server_addr)), proxy_sess->connect_timeout, proxy_sess->connect_timeout != 1 ? "seconds" : "second"); pr_event_generate("mod_proxy.timeout-connect", NULL); pr_log_pri(PR_LOG_NOTICE, "%s", "Connect timed out, disconnected"); pr_session_disconnect(&proxy_module, PR_SESS_DISCONNECT_TIMEOUT, "ProxyTimeoutConnect"); /* Do not restart the timer (should never be reached). */ return 0; }
static int trace_write(const char *channel, int level, const char *msg, int discard) { char buf[PR_TUNABLE_BUFFER_SIZE]; size_t buflen, len; struct tm *tm; int use_conn_ips = FALSE; if (trace_logfd < 0) return 0; memset(buf, '\0', sizeof(buf)); if (!(trace_opts & PR_TRACE_OPT_USE_TIMESTAMP_MILLIS)) { time_t now; now = time(NULL); tm = pr_localtime(NULL, &now); len = strftime(buf, sizeof(buf)-1, "%Y-%m-%d %H:%M:%S", tm); buflen = len; } else { struct timeval now; unsigned long millis; gettimeofday(&now, NULL); tm = pr_localtime(NULL, (const time_t *) &(now.tv_sec)); len = strftime(buf, sizeof(buf)-1, "%Y-%m-%d %H:%M:%S", tm); buflen = len; /* Convert microsecs to millisecs. */ millis = now.tv_usec / 1000; len = snprintf(buf + buflen, sizeof(buf) - buflen, ",%03lu", millis); buflen += len; } if ((trace_opts & PR_TRACE_OPT_LOG_CONN_IPS) && session.c != NULL) { /* We can only support the "+ConnIPs" TraceOption if there actually * is a client connected in this process. We might be the daemon * process, in which there is no client. */ use_conn_ips = TRUE; } if (use_conn_ips == FALSE) { len = snprintf(buf + buflen, sizeof(buf) - buflen, " [%u] <%s:%d>: %s", (unsigned int) (session.pid ? session.pid : getpid()), channel, level, msg); buflen += len; } else { const char *client_ip, *server_ip; int server_port; client_ip = pr_netaddr_get_ipstr(session.c->remote_addr); server_ip = pr_netaddr_get_ipstr(session.c->local_addr); server_port = pr_netaddr_get_port(session.c->local_addr); len = snprintf(buf + buflen, sizeof(buf) - buflen, " [%u] (client %s, server %s:%d) <%s:%d>: %s", (unsigned int) (session.pid ? session.pid : getpid()), client_ip != NULL ? client_ip : "none", server_ip != NULL ? server_ip : "none", server_port, channel, level, msg); buflen += len; } buf[sizeof(buf)-1] = '\0'; if (buflen < (sizeof(buf) - 1)) { buf[buflen] = '\n'; buflen++; } else { buf[sizeof(buf)-2] = '\n'; } pr_log_event_generate(PR_LOG_TYPE_TRACELOG, trace_logfd, level, buf, buflen); if (discard) { /* This log message would not have been written to disk, so just discard * it. The discard value is TRUE when there's a log listener for * TraceLog logging events, and the Trace log level configuration would * otherwise have filtered out this log message. */ return 0; } return write(trace_logfd, buf, buflen); }
static void log_write(int priority, int f, char *s, int discard) { unsigned int max_priority = 0, *ptr = NULL; char serverinfo[PR_TUNABLE_BUFFER_SIZE] = {'\0'}; memset(serverinfo, '\0', sizeof(serverinfo)); if (main_server && main_server->ServerFQDN) { pr_netaddr_t *remote_addr = pr_netaddr_get_sess_remote_addr(); const char *remote_name = pr_netaddr_get_sess_remote_name(); snprintf(serverinfo, sizeof(serverinfo)-1, "%s", main_server->ServerFQDN); serverinfo[sizeof(serverinfo)-1] = '\0'; if (remote_addr && remote_name) { size_t serverinfo_len; serverinfo_len = strlen(serverinfo); snprintf(serverinfo + serverinfo_len, sizeof(serverinfo) - serverinfo_len, " (%s[%s])", remote_name, pr_netaddr_get_ipstr(remote_addr)); serverinfo[sizeof(serverinfo)-1] = '\0'; } } if (!discard && (logstderr || !main_server)) { char buf[LOGBUFFER_SIZE] = {'\0'}; size_t buflen, len; struct timeval now; struct tm *tm = NULL; unsigned long millis; gettimeofday(&now, NULL); tm = pr_localtime(NULL, (const time_t *) &(now.tv_sec)); if (tm == NULL) { return; } len = strftime(buf, sizeof(buf)-1, "%Y-%m-%d %H:%M:%S", tm); buflen = len; buf[sizeof(buf)-1] = '\0'; /* Convert microsecs to millisecs. */ millis = now.tv_usec / 1000; len = snprintf(buf + buflen, sizeof(buf) - len, ",%03lu ", millis); buflen += len; buf[sizeof(buf)-1] = '\0'; if (*serverinfo) { len = snprintf(buf + buflen, sizeof(buf) - buflen, "%s proftpd[%u] %s: %s\n", systemlog_host, (unsigned int) (session.pid ? session.pid : getpid()), serverinfo, s); } else { len = snprintf(buf + buflen, sizeof(buf) - buflen, "%s proftpd[%u]: %s\n", systemlog_host, (unsigned int) (session.pid ? session.pid : getpid()), s); } buflen += len; buf[sizeof(buf)-1] = '\0'; pr_log_event_generate(PR_LOG_TYPE_SYSTEMLOG, STDERR_FILENO, priority, buf, buflen); fprintf(stderr, "%s", buf); return; } if (syslog_discard) { /* Only return now if we don't have any log listeners. */ if (pr_log_event_listening(PR_LOG_TYPE_SYSLOG) <= 0 && pr_log_event_listening(PR_LOG_TYPE_SYSTEMLOG) <= 0) { return; } } ptr = get_param_ptr(main_server->conf, "SyslogLevel", FALSE); if (ptr != NULL) { max_priority = *ptr; } else { /* Default SyslogLevel */ max_priority = default_level; } if (priority > max_priority) { /* Only return now if we don't have any log listeners. */ if (pr_log_event_listening(PR_LOG_TYPE_SYSLOG) <= 0 && pr_log_event_listening(PR_LOG_TYPE_SYSTEMLOG) <= 0) { return; } } if (systemlog_fd != -1) { char buf[LOGBUFFER_SIZE] = {'\0'}; size_t buflen, len; struct timeval now; struct tm *tm; unsigned long millis; gettimeofday(&now, NULL); tm = pr_localtime(NULL, (const time_t *) &(now.tv_sec)); if (tm == NULL) { return; } len = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm); buflen = len; buf[sizeof(buf) - 1] = '\0'; /* Convert microsecs to millisecs. */ millis = now.tv_usec / 1000; len = snprintf(buf + buflen, sizeof(buf) - len, ",%03lu ", millis); buflen += len; buf[sizeof(buf) - 1] = '\0'; if (*serverinfo) { len = snprintf(buf + buflen, sizeof(buf) - buflen, "%s proftpd[%u] %s: %s\n", systemlog_host, (unsigned int) (session.pid ? session.pid : getpid()), serverinfo, s); } else { len = snprintf(buf + buflen, sizeof(buf) - buflen, "%s proftpd[%u]: %s\n", systemlog_host, (unsigned int) (session.pid ? session.pid : getpid()), s); } buflen += len; buf[sizeof(buf)-1] = '\0'; pr_log_event_generate(PR_LOG_TYPE_SYSTEMLOG, systemlog_fd, priority, buf, buflen); /* Now we need to enforce the discard, syslog_discard and SyslogLevel * filtering. */ if (discard) { return; } if (syslog_discard) { return; } if (priority > max_priority) { return; } while (write(systemlog_fd, buf, buflen) < 0) { if (errno == EINTR) { pr_signals_handle(); continue; } return; } return; } pr_log_event_generate(PR_LOG_TYPE_SYSLOG, syslog_sockfd, priority, s, strlen(s)); if (set_facility != -1) f = set_facility; if (!syslog_open) { syslog_sockfd = pr_openlog("proftpd", LOG_NDELAY|LOG_PID, f); if (syslog_sockfd < 0) { (void) pr_trace_msg(trace_channel, 1, "error opening syslog fd: %s", strerror(errno)); return; } syslog_open = TRUE; } else if (f != facility) { /* If this message is to be sent to a different log facility than a * default one (or the facility configured via SyslogFacility), then * OR in the facility with the priority value, as per the syslog(3) * docs. */ priority |= f; } if (*serverinfo) { pr_syslog(syslog_sockfd, priority, "%s - %s\n", serverinfo, s); } else { pr_syslog(syslog_sockfd, priority, "%s\n", s); } }
int main(int argc, char *argv[]) { pool *p; const char *remote_name; pr_netaddr_t *remote_addr; conn_t *client_conn, *ctrl_conn, *data_conn; unsigned int connect_timeout, remote_port; struct proxy_ftp_client *ftp; int res, timerno; char buf[1024]; /* Seed the random number generator. */ /* XXX Use random(3) in the future? */ srand((unsigned int) (time(NULL) * getpid())); init_pools(); init_privs(); init_log(); init_regexp(); init_inet(); init_netio(); init_netaddr(); init_fs(); init_class(); init_config(); init_stash(); pr_log_setdebuglevel(10); log_stderr(TRUE); pr_trace_use_stderr(TRUE); pr_trace_set_levels("DEFAULT", 1, 20); p = make_sub_pool(permanent_pool); pr_pool_tag(p, "FTP Client Pool"); remote_name = "ftp.proftpd.org"; remote_addr = pr_netaddr_get_addr(p, remote_name, NULL); if (remote_addr == NULL) { fprintf(stderr, "Failed to get addr for '%s': %s\n", remote_name, strerror(errno)); destroy_pool(p); return 1; } fprintf(stdout, "Resolved name '%s' to IP address '%s'\n", remote_name, pr_netaddr_get_ipstr(remote_addr)); remote_port = 21; connect_timeout = 5; ftp = proxy_ftp_connect(p, remote_addr, remote_port, connect_timeout, NULL); if (ftp == NULL) { fprintf(stderr, "Error connecting to FTP server: %s\n", strerror(errno)); destroy_pool(p); return 1; } fprintf(stdout, "Successfully connected to %s:%d from %s:%d\n", remote_name, remote_port, pr_netaddr_get_ipstr(client_conn->local_addr), ntohs(pr_netaddr_get_port(client_conn->local_addr))); res = proxy_ftp_disconnect(ftp); if (res < 0) { fprintf(stderr, "Error disconnecting from FTP server: %s\n", strerror(errno)); destroy_pool(p); return 1; } ctrl_conn = pr_inet_openrw(p, client_conn, NULL, PR_NETIO_STRM_OTHR, -1, -1, -1, FALSE); if (ctrl_conn == NULL) { fprintf(stderr, "Error opening control connection: %s\n", strerror(errno)); pr_inet_close(p, client_conn); destroy_pool(p); return 1; } fprintf(stdout, "Reading response from %s:%d\n", remote_name, remote_port); /* Read the response */ memset(buf, '\0', sizeof(buf)); /* XXX We need to write our own version of netio_telnet_gets(), with * the buffering to handle reassembly of a full FTP response out of * multiple TCP packets. Not sure why the existing netio_telnet_gets() * is not sufficient. But we don't need the handling of Telnet codes * in our reading. But DO generate the 'core.ctrl-read' event, so that * any event listeners get a chance to process the data we've received. * (Or maybe use 'mod_proxy.server-read', and differentiate between * client and server reads/writes?) */ if (pr_netio_read(ctrl_conn->instrm, buf, sizeof(buf)-1, 5) < 0) { fprintf(stderr, "Error reading response from server: %s\n", strerror(errno)); } else { fprintf(stdout, "Response: \"%s\"\n", buf); } /* Disconnect */ res = pr_netio_printf(ctrl_conn->outstrm, "%s\r\n", C_QUIT); if (res < 0) { fprintf(stderr, "Error writing command to server: %s", strerror(errno)); } pr_inet_close(p, ctrl_conn); pr_inet_close(p, client_conn); destroy_pool(p); return 0; }
int sftp_auth_password(struct ssh2_packet *pkt, cmd_rec *pass_cmd, const char *orig_user, const char *user, const char *service, unsigned char **buf, uint32_t *buflen, int *send_userauth_fail) { const char *cipher_algo, *mac_algo; char *passwd; int have_new_passwd, res; struct passwd *pw; cipher_algo = sftp_cipher_get_read_algo(); mac_algo = sftp_mac_get_read_algo(); if (strncmp(cipher_algo, "none", 5) == 0 || strncmp(mac_algo, "none", 5) == 0) { if (sftp_opts & SFTP_OPT_ALLOW_INSECURE_LOGIN) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "WARNING: cipher algorithm '%s' or MAC algorithm '%s' INSECURE for " "password authentication (SFTPOption AllowInsecureLogin in effect)", cipher_algo, mac_algo); } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "cipher algorithm '%s' or MAC algorithm '%s' unacceptable for " "password authentication, denying password authentication request", cipher_algo, mac_algo); *send_userauth_fail = TRUE; errno = EPERM; return 0; } } /* XXX We currently don't do anything with this. */ have_new_passwd = sftp_msg_read_bool(pkt->pool, buf, buflen); if (have_new_passwd) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "%s", "client says they have provided a new password; this functionality " "is not currently supported"); } passwd = sftp_msg_read_string(pkt->pool, buf, buflen); passwd = sftp_utf8_decode_str(pkt->pool, passwd); pass_cmd->arg = passwd; if (pr_cmd_dispatch_phase(pass_cmd, PRE_CMD, 0) < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "authentication request for user '%s' blocked by '%s' handler", orig_user, (char *) pass_cmd->argv[0]); pr_cmd_dispatch_phase(pass_cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(pass_cmd, LOG_CMD_ERR, 0); pr_memscrub(passwd, strlen(passwd)); *send_userauth_fail = TRUE; errno = EPERM; return 0; } pw = pr_auth_getpwnam(pkt->pool, user); if (pw == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "no account for user '%s' found", user); pr_log_auth(PR_LOG_NOTICE, "USER %s: no such user found from %s [%s] to %s:%d", user, session.c->remote_name, pr_netaddr_get_ipstr(session.c->remote_addr), pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port); pr_memscrub(passwd, strlen(passwd)); *send_userauth_fail = TRUE; errno = ENOENT; return 0; } res = pr_auth_authenticate(pkt->pool, user, passwd); pr_memscrub(passwd, strlen(passwd)); switch (res) { case PR_AUTH_OK: break; case PR_AUTH_NOPWD: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "password authentication for user '%s' failed: No such user", user); pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): No such user found", user); *send_userauth_fail = TRUE; errno = ENOENT; return 0; case PR_AUTH_BADPWD: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "password authentication for user '%s' failed: Incorrect password", user); pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): Incorrect password", user); *send_userauth_fail = TRUE; errno = EINVAL; return 0; case PR_AUTH_AGEPWD: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "password authentication for user '%s' failed: Password expired", user); pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): Password expired", user); *send_userauth_fail = TRUE; errno = EINVAL; return 0; case PR_AUTH_DISABLEDPWD: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "password authentication for user '%s' failed: Account disabled", user); pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): Account disabled", user); *send_userauth_fail = TRUE; errno = EINVAL; return 0; default: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unknown authentication value (%d), returning error", res); *send_userauth_fail = TRUE; errno = EINVAL; return 0; } return 1; }
conn_t *pr_ipbind_get_listening_conn(server_rec *server, pr_netaddr_t *addr, unsigned int port) { conn_t *l; pool *p; struct listener_rec *lr; if (listening_conn_list) { for (lr = (struct listener_rec *) listening_conn_list->xas_list; lr; lr = lr->next) { int use_elt = FALSE; pr_signals_handle(); if (addr != NULL && lr->addr != NULL) { const char *lr_ipstr = NULL; lr_ipstr = pr_netaddr_get_ipstr(lr->addr); /* Note: lr_ipstr should never be null. If it is, it means that * the lr->addr object never had its IP address resolved/stashed, * and in attempting to do, getnameinfo(3) failed for some reason. * * The IP address on which it's listening, if not available via * lr->addr, should thus be available via lr->conn->local_addr. */ if (lr_ipstr == NULL && lr->conn != NULL) { lr_ipstr = pr_netaddr_get_ipstr(lr->conn->local_addr); } if (lr_ipstr != NULL) { if (strcmp(pr_netaddr_get_ipstr(addr), lr_ipstr) == 0 && port == lr->port) { use_elt = TRUE; } } } else if (addr == NULL && port == lr->port) { use_elt = TRUE; } if (use_elt) { lr->claimed = TRUE; return lr->conn; } } } if (listening_conn_pool == NULL) { listening_conn_pool = make_sub_pool(permanent_pool); pr_pool_tag(listening_conn_pool, "Listening Connection Pool"); listening_conn_list = xaset_create(listening_conn_pool, NULL); } p = make_sub_pool(listening_conn_pool); pr_pool_tag(p, "Listening conn subpool"); l = pr_inet_create_conn(p, -1, addr, port, FALSE); if (l == NULL) { return NULL; } /* Inform any interested listeners that this socket was opened. */ pr_inet_generate_socket_event("core.ctrl-listen", server, l->local_addr, l->listen_fd); lr = pcalloc(p, sizeof(struct listener_rec)); lr->pool = p; lr->conn = l; lr->addr = pr_netaddr_dup(p, addr); if (lr->addr == NULL && errno != EINVAL) { return NULL; } lr->port = port; lr->claimed = TRUE; xaset_insert(listening_conn_list, (xasetmember_t *) lr); return l; }
static void log_write(int priority, int f, char *s) { unsigned int *max_priority = NULL; char serverinfo[PR_TUNABLE_BUFFER_SIZE] = {'\0'}; memset(serverinfo, '\0', sizeof(serverinfo)); if (main_server && main_server->ServerFQDN) { pr_netaddr_t *remote_addr = pr_netaddr_get_sess_remote_addr(); const char *remote_name = pr_netaddr_get_sess_remote_name(); snprintf(serverinfo, sizeof(serverinfo), "%s", main_server->ServerFQDN); serverinfo[sizeof(serverinfo)-1] = '\0'; if (remote_addr && remote_name) { snprintf(serverinfo + strlen(serverinfo), sizeof(serverinfo) - strlen(serverinfo), " (%s[%s])", remote_name, pr_netaddr_get_ipstr(remote_addr)); serverinfo[sizeof(serverinfo)-1] = '\0'; } } if (logstderr || !main_server) { fprintf(stderr, "%s - %s\n", serverinfo, s); return; } if (syslog_discard) return; if (systemlog_fd != -1) { char buf[LOGBUFFER_SIZE] = {'\0'}; time_t tt = time(NULL); struct tm *t; t = localtime(&tt); strftime(buf, sizeof(buf), "%b %d %H:%M:%S ", t); buf[sizeof(buf) - 1] = '\0'; if (*serverinfo) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s proftpd[%u] %s: %s\n", systemlog_host, (unsigned int) getpid(), serverinfo, s); } else { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s proftpd[%u]: %s\n", systemlog_host, (unsigned int) getpid(), s); } buf[sizeof(buf) - 1] = '\0'; write(systemlog_fd, buf, strlen(buf)); return; } if (set_facility != -1) f = set_facility; if (!syslog_open) { syslog_sockfd = pr_openlog("proftpd", LOG_NDELAY|LOG_PID, f); } else if (f != facility) { (void) pr_setlogfacility(f); } max_priority = get_param_ptr(main_server->conf, "SyslogLevel", FALSE); if (max_priority != NULL && priority > *max_priority) return; if (*serverinfo) pr_syslog(syslog_sockfd, priority, "%s - %s\n", serverinfo, s); else pr_syslog(syslog_sockfd, priority, "%s\n", s); if (!syslog_open) { pr_closelog(syslog_sockfd); syslog_sockfd = -1; } else if (f != facility) { (void) pr_setlogfacility(f); } }
int pr_netaddr_ncmp(const pr_netaddr_t *na1, const pr_netaddr_t *na2, unsigned int bitlen) { pool *tmp_pool = NULL; pr_netaddr_t *a, *b; unsigned int nbytes, nbits; const unsigned char *in1, *in2; if (na1 && !na2) return 1; if (!na1 && na2) return -1; if (!na1 && !na2) return 0; if (pr_netaddr_get_family(na1) != pr_netaddr_get_family(na2)) { /* Cannot compare addresses from different families, unless one * of the netaddrs has an AF_INET family, and the other has an * AF_INET6 family AND is an IPv4-mapped IPv6 address. */ if (pr_netaddr_is_v4mappedv6(na1) != TRUE && pr_netaddr_is_v4mappedv6(na2) != TRUE) { errno = EINVAL; return -1; } if (pr_netaddr_is_v4mappedv6(na1) == TRUE) { tmp_pool = make_sub_pool(permanent_pool); /* This case means that na1 is an IPv4-mapped IPv6 address, and * na2 is an IPv4 address. */ a = pr_netaddr_alloc(tmp_pool); pr_netaddr_set_family(a, AF_INET); pr_netaddr_set_port(a, pr_netaddr_get_port(na1)); memcpy(&a->na_addr.v4.sin_addr, get_v4inaddr(na1), sizeof(struct in_addr)); b = (pr_netaddr_t *) na2; pr_trace_msg(trace_channel, 6, "comparing IPv4 address '%s' against " "IPv4-mapped IPv6 address '%s'", pr_netaddr_get_ipstr(b), pr_netaddr_get_ipstr(a)); } else if (pr_netaddr_is_v4mappedv6(na2) == TRUE) { tmp_pool = make_sub_pool(permanent_pool); /* This case means that na is an IPv4 address, and na2 is an * IPv4-mapped IPv6 address. */ a = (pr_netaddr_t *) na1; b = pr_netaddr_alloc(tmp_pool); pr_netaddr_set_family(b, AF_INET); pr_netaddr_set_port(b, pr_netaddr_get_port(na2)); memcpy(&b->na_addr.v4.sin_addr, get_v4inaddr(na2), sizeof(struct in_addr)); pr_trace_msg(trace_channel, 6, "comparing IPv4 address '%s' against " "IPv4-mapped IPv6 address '%s'", pr_netaddr_get_ipstr(a), pr_netaddr_get_ipstr(b)); } else { a = (pr_netaddr_t *) na1; b = (pr_netaddr_t *) na2; } } else { a = (pr_netaddr_t *) na1; b = (pr_netaddr_t *) na2; } switch (pr_netaddr_get_family(a)) { case AF_INET: { /* Make sure that the given number of bits is not more than supported * for IPv4 addresses (32). */ if (bitlen > 32) { errno = EINVAL; return -1; } break; } #ifdef PR_USE_IPV6 case AF_INET6: { if (use_ipv6) { /* Make sure that the given number of bits is not more than supported * for IPv6 addresses (128). */ if (bitlen > 128) { errno = EINVAL; return -1; } break; } } #endif /* PR_USE_IPV6 */ default: errno = EPERM; return -1; } /* Retrieve pointers to the contained in_addrs. */ in1 = (const unsigned char *) pr_netaddr_get_inaddr(a); in2 = (const unsigned char *) pr_netaddr_get_inaddr(b); /* Determine the number of bytes, and leftover bits, in the given * bit length. */ nbytes = bitlen / 8; nbits = bitlen % 8; /* Compare bytes, using memcmp(3), first. */ if (nbytes > 0) { int res = memcmp(in1, in2, nbytes); /* No need to continue comparing the addresses if they differ already. */ if (res != 0) { if (tmp_pool) destroy_pool(tmp_pool); return res; } } /* Next, compare the remaining bits in the addresses. */ if (nbits > 0) { unsigned char mask; /* Get the bytes in the addresses that have not yet been compared. */ unsigned char in1byte = in1[nbytes]; unsigned char in2byte = in2[nbytes]; /* Build up a mask covering the bits left to be checked. */ mask = (0xff << (8 - nbits)) & 0xff; if ((in1byte & mask) > (in2byte & mask)) { if (tmp_pool) destroy_pool(tmp_pool); return 1; } if ((in1byte & mask) < (in2byte & mask)) { if (tmp_pool) destroy_pool(tmp_pool); return -1; } } if (tmp_pool) destroy_pool(tmp_pool); /* If we've made it this far, the addresses match, for the given bit * length. */ return 0; }
static int init_standalone_bindings(void) { int res = 0; server_rec *serv = NULL; unsigned char *default_server = NULL, is_default = FALSE; /* If a port is set to zero, the address/port is not bound to a socket * at all. */ if (main_server->ServerPort) { /* If SocketBindTight is off, then pr_inet_create_conn() will * create and bind to a wildcard socket. However, should it be an * IPv4 or an IPv6 wildcard socket? */ if (!SocketBindTight) { #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { pr_inet_set_default_family(NULL, AF_INET6); } else { int default_family; default_family = pr_netaddr_get_family(main_server->addr); pr_inet_set_default_family(NULL, default_family); } #else pr_inet_set_default_family(NULL, pr_netaddr_get_family(main_server->addr)); #endif /* PR_USE_IPV6 */ } main_server->listen = pr_ipbind_get_listening_conn(main_server, (SocketBindTight ? main_server->addr : NULL), main_server->ServerPort); if (main_server->listen == NULL) { return -1; } } else { main_server->listen = NULL; } default_server = get_param_ptr(main_server->conf, "DefaultServer", FALSE); if (default_server != NULL && *default_server == TRUE) { is_default = TRUE; } if (main_server->ServerPort || is_default) { PR_CREATE_IPBIND(main_server, main_server->addr, main_server->ServerPort); PR_OPEN_IPBIND(main_server->addr, main_server->ServerPort, main_server->listen, is_default, TRUE, TRUE); PR_ADD_IPBINDS(main_server); } for (serv = main_server->next; serv; serv = serv->next) { config_rec *c; int is_namebind = FALSE; /* See if this server is a namebind, to be part of an existing ipbind. */ c = find_config(serv->conf, CONF_PARAM, "ServerAlias", FALSE); while (c != NULL) { pr_signals_handle(); res = pr_namebind_create(serv, c->argv[0], serv->addr, serv->ServerPort); if (res == 0) { is_namebind = TRUE; res = pr_namebind_open(c->argv[0], serv->addr, serv->ServerPort); if (res < 0) { pr_log_pri(PR_LOG_NOTICE, "%s:%d: notice: unable to open namebind '%s': %s", __FILE__, __LINE__, (char *) c->argv[0], strerror(errno)); } } else { pr_log_pri(PR_LOG_NOTICE, "unable to create namebind for '%s' to %s#%u: %s", (char *) c->argv[0], pr_netaddr_get_ipstr(serv->addr), serv->ServerPort, strerror(errno)); } c = find_config_next(c, c->next, CONF_PARAM, "ServerAlias", FALSE); } if (is_namebind == TRUE) { continue; } if (serv->ServerPort != main_server->ServerPort || SocketBindTight || !main_server->listen) { is_default = FALSE; default_server = get_param_ptr(serv->conf, "DefaultServer", FALSE); if (default_server != NULL && *default_server == TRUE) { is_default = TRUE; } if (serv->ServerPort) { if (!SocketBindTight) { #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { pr_inet_set_default_family(NULL, AF_INET6); } else { pr_inet_set_default_family(NULL, pr_netaddr_get_family(serv->addr)); } #else pr_inet_set_default_family(NULL, pr_netaddr_get_family(serv->addr)); #endif /* PR_USE_IPV6 */ } serv->listen = pr_ipbind_get_listening_conn(serv, (SocketBindTight ? serv->addr : NULL), serv->ServerPort); if (serv->listen == NULL) { return -1; } PR_CREATE_IPBIND(serv, serv->addr, serv->ServerPort); PR_OPEN_IPBIND(serv->addr, serv->ServerPort, serv->listen, is_default, FALSE, TRUE); PR_ADD_IPBINDS(serv); } else if (is_default) { serv->listen = NULL; PR_CREATE_IPBIND(serv, serv->addr, serv->ServerPort); PR_OPEN_IPBIND(serv->addr, serv->ServerPort, serv->listen, is_default, FALSE, TRUE); PR_ADD_IPBINDS(serv); } else { serv->listen = NULL; } } else { /* Because this server is sharing the connection with the main server, * we need a cleanup handler to remove the server's reference when the * original connection's pool is destroyed. */ is_default = FALSE; default_server = get_param_ptr(serv->conf, "DefaultServer", FALSE); if (default_server != NULL && *default_server == TRUE) { is_default = TRUE; } serv->listen = main_server->listen; register_cleanup(serv->listen->pool, &serv->listen, server_cleanup_cb, server_cleanup_cb); PR_CREATE_IPBIND(serv, serv->addr, serv->ServerPort); PR_OPEN_IPBIND(serv->addr, serv->ServerPort, NULL, is_default, FALSE, TRUE); PR_ADD_IPBINDS(serv); } } /* Any "unclaimed" listening conns can be removed and closed. */ if (listening_conn_list) { struct listener_rec *lr, *lrn; for (lr = (struct listener_rec *) listening_conn_list->xas_list; lr; lr = lrn) { lrn = lr->next; if (!lr->claimed) { xaset_remove(listening_conn_list, (xasetmember_t *) lr); destroy_pool(lr->pool); } } } return 0; }
int pr_ipbind_listen(fd_set *readfds) { int listen_flags = PR_INET_LISTEN_FL_FATAL_ON_ERROR, maxfd = 0; register unsigned int i = 0; /* sanity check */ if (!readfds) return -1; FD_ZERO(readfds); if (!binding_pool) { binding_pool = make_sub_pool(permanent_pool); pr_pool_tag(binding_pool, "Bindings Pool"); } /* Reset the listener list. */ if (!listener_list) { listener_list = make_array(binding_pool, 1, sizeof(conn_t *)); } else { /* Nasty hack to "clear" the list by making it think it has no * elements. */ listener_list->nelts = 0; } /* Slower than the hash lookup, but...we have to check each and every * ipbind in the table. */ for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) { pr_ipbind_t *ipbind = NULL; for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { /* Skip inactive bindings, but only if SocketBindTight is in effect. */ if (SocketBindTight && !ipbind->ib_isactive) continue; if (ipbind->ib_listener) { if (ipbind->ib_listener->mode == CM_NONE) { pr_inet_listen(ipbind->ib_listener->pool, ipbind->ib_listener, tcpBackLog, listen_flags); } if (ipbind->ib_listener->mode == CM_ACCEPT) { if (pr_inet_resetlisten(ipbind->ib_listener->pool, ipbind->ib_listener) < 0) { pr_trace_msg(trace_channel, 3, "error resetting %s#%u for listening: %s", pr_netaddr_get_ipstr(ipbind->ib_addr), ipbind->ib_port, strerror(errno)); } } if (ipbind->ib_listener->mode == CM_LISTEN) { FD_SET(ipbind->ib_listener->listen_fd, readfds); if (ipbind->ib_listener->listen_fd > maxfd) maxfd = ipbind->ib_listener->listen_fd; /* Add this to the listener list as well. */ *((conn_t **) push_array(listener_list)) = ipbind->ib_listener; } } } } return maxfd; }
server_rec *pr_ipbind_get_server(pr_netaddr_t *addr, unsigned int port) { pr_ipbind_t *ipbind = NULL; pr_netaddr_t wildcard_addr; int addr_family; /* If we've got a binding configured for this exact address, return it * straightaway. */ ipbind = pr_ipbind_find(addr, port, TRUE); if (ipbind != NULL) return ipbind->ib_server; /* Look for a vhost bound to the wildcard address (i.e. INADDR_ANY). * * This allows for "<VirtualHost 0.0.0.0>" configurations, where the * IP address to which the client might connect is not known at * configuration time. (Usually happens when the same config file * is deployed to multiple machines.) */ addr_family = pr_netaddr_get_family(addr); pr_netaddr_clear(&wildcard_addr); pr_netaddr_set_family(&wildcard_addr, addr_family); pr_netaddr_set_sockaddr_any(&wildcard_addr); ipbind = pr_ipbind_find(&wildcard_addr, port, TRUE); if (ipbind != NULL) { pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using " "'%s' listening on wildcard address", pr_netaddr_get_ipstr(addr), port, ipbind->ib_server->ServerName); return ipbind->ib_server; } else { #ifdef PR_USE_IPV6 if (addr_family == AF_INET6 && pr_netaddr_use_ipv6()) { /* The pr_ipbind_find() probably returned NULL because there aren't * any <VirtualHost> sections configured explicitly for the wildcard * IPv6 address of "::", just the IPv4 wildcard "0.0.0.0" address. * * So try the pr_ipbind_find() again, this time using the IPv4 wildcard. */ pr_netaddr_clear(&wildcard_addr); pr_netaddr_set_family(&wildcard_addr, AF_INET); pr_netaddr_set_sockaddr_any(&wildcard_addr); ipbind = pr_ipbind_find(&wildcard_addr, port, TRUE); if (ipbind != NULL) { pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using " "'%s' listening on wildcard address", pr_netaddr_get_ipstr(addr), port, ipbind->ib_server->ServerName); return ipbind->ib_server; } } #endif /* PR_USE_IPV6 */ } /* Use the default server, if set. */ if (ipbind_default_server && ipbind_default_server->ib_isactive) { pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using " "DefaultServer '%s'", pr_netaddr_get_ipstr(addr), port, ipbind_default_server->ib_server->ServerName); return ipbind_default_server->ib_server; } /* Not found in binding list, and no DefaultServer, so see if it's the * loopback address */ if (ipbind_localhost_server && pr_netaddr_is_loopback(addr)) { return ipbind_localhost_server->ib_server; } return NULL; }
/* This differs from pr_netaddr_get_ipstr() in that pr_netaddr_get_ipstr() * returns a string of the numeric form of the given network address, whereas * this function returns a string of the DNS name (if present). */ const char *pr_netaddr_get_dnsstr(pr_netaddr_t *na) { char *name = NULL; char buf[256]; if (!na) { errno = EINVAL; return NULL; } /* If this pr_netaddr_t has already been resolved to an DNS string, return the * cached string. */ if (na->na_have_dnsstr) return na->na_dnsstr; if (reverse_dns) { int res = 0; pr_trace_msg(trace_channel, 3, "verifying DNS name for IP address %s via reverse DNS lookup", pr_netaddr_get_ipstr(na)); memset(buf, '\0', sizeof(buf)); res = pr_getnameinfo(pr_netaddr_get_sockaddr(na), pr_netaddr_get_sockaddr_len(na), buf, sizeof(buf), NULL, 0, NI_NAMEREQD); buf[sizeof(buf)-1] = '\0'; if (res == 0) { char **checkaddr; struct hostent *hent = NULL; unsigned char ok = FALSE; int family = pr_netaddr_get_family(na); void *inaddr = pr_netaddr_get_inaddr(na); #ifdef HAVE_GETHOSTBYNAME2 if (pr_netaddr_is_v4mappedv6(na) == TRUE) { family = AF_INET; inaddr = get_v4inaddr(na); } hent = gethostbyname2(buf, family); #else hent = gethostbyname(buf); #endif /* HAVE_GETHOSTBYNAME2 */ if (hent != NULL) { char **alias; pr_trace_msg(trace_channel, 10, "checking addresses associated with host '%s'", hent->h_name ? hent->h_name : "(null)"); for (alias = hent->h_aliases; *alias; ++alias) { pr_trace_msg(trace_channel, 10, "host '%s' has alias '%s'", hent->h_name ? hent->h_name : "(null)", *alias); } switch (hent->h_addrtype) { case AF_INET: if (family == AF_INET) { for (checkaddr = hent->h_addr_list; *checkaddr; ++checkaddr) { if (memcmp(*checkaddr, inaddr, hent->h_length) == 0) { ok = TRUE; break; } } } break; #ifdef PR_USE_IPV6 case AF_INET6: if (use_ipv6 && family == AF_INET6) { for (checkaddr = hent->h_addr_list; *checkaddr; ++checkaddr) { if (memcmp(*checkaddr, inaddr, hent->h_length) == 0) { ok = TRUE; break; } } } break; #endif /* PR_USE_IPV6 */ } if (ok) { name = buf; pr_trace_msg(trace_channel, 8, "using DNS name '%s' for IP address '%s'", name, pr_netaddr_get_ipstr(na)); } else { name = NULL; pr_trace_msg(trace_channel, 8, "unable to verify any DNS names for IP address '%s'", pr_netaddr_get_ipstr(na)); } } else pr_log_debug(DEBUG1, "notice: unable to resolve '%s': %s", buf, hstrerror(errno)); } } else pr_log_debug(DEBUG10, "UseReverseDNS off, returning IP address instead of DNS name"); if (name) { name = pr_inet_validate(name); } else { name = (char *) pr_netaddr_get_ipstr(na); } /* Copy the string into the pr_netaddr_t cache as well, so we only * have to do this once for this pr_netaddr_t. */ memset(na->na_dnsstr, '\0', sizeof(na->na_dnsstr)); sstrncpy(na->na_dnsstr, name, sizeof(na->na_dnsstr)); na->na_have_dnsstr = TRUE; return na->na_dnsstr; }
int sftp_auth_publickey(struct ssh2_packet *pkt, cmd_rec *pass_cmd, const char *orig_user, const char *user, const char *service, char **buf, uint32_t *buflen, int *send_userauth_fail) { int have_signature, pubkey_type; char *pubkey_algo = NULL, *pubkey_data; const char *fp = NULL; uint32_t pubkey_len; struct passwd *pw; if (pr_cmd_dispatch_phase(pass_cmd, PRE_CMD, 0) < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "authentication request for user '%s' blocked by '%s' handler", orig_user, pass_cmd->argv[0]); pr_cmd_dispatch_phase(pass_cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(pass_cmd, LOG_CMD_ERR, 0); *send_userauth_fail = TRUE; errno = EPERM; return 0; } have_signature = sftp_msg_read_bool(pkt->pool, buf, buflen); if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_HAVE_PUBKEY_ALGO)) { pubkey_algo = sftp_msg_read_string(pkt->pool, buf, buflen); } pubkey_len = sftp_msg_read_int(pkt->pool, buf, buflen); pubkey_data = sftp_msg_read_data(pkt->pool, buf, buflen, pubkey_len); if (pubkey_algo == NULL) { /* The client did not send the string identifying the public key algorithm. * Thus we need to extract the algorithm string from the public key data. */ pubkey_algo = sftp_msg_read_string(pkt->pool, &pubkey_data, &pubkey_len); } pr_trace_msg(trace_channel, 9, "client sent '%s' public key %s", pubkey_algo, have_signature ? "with signature" : "without signature"); if (strncmp(pubkey_algo, "ssh-rsa", 8) == 0) { pubkey_type = EVP_PKEY_RSA; } else if (strncmp(pubkey_algo, "ssh-dss", 8) == 0) { pubkey_type = EVP_PKEY_DSA; /* XXX This is where we would add support for X509 public keys, e.g.: * * x509v3-ssh-dss * x509v3-ssh-rsa * x509v3-sign (older) * */ } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unsupported public key algorithm '%s' requested, rejecting request", pubkey_algo); *send_userauth_fail = TRUE; errno = EINVAL; return 0; } if (sftp_keys_verify_pubkey_type(pkt->pool, pubkey_data, pubkey_len, pubkey_type) != TRUE) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unable to verify that given public key matches given '%s' algorithm", pubkey_algo); *send_userauth_fail = TRUE; errno = EINVAL; return 0; } #ifdef OPENSSL_FIPS if (FIPS_mode()) { fp = sftp_keys_get_fingerprint(pkt->pool, pubkey_data, pubkey_len, SFTP_KEYS_FP_DIGEST_SHA1); if (fp != NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "public key SHA1 fingerprint: %s", fp); } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error obtaining public key SHA1 fingerprint: %s", strerror(errno)); } } else { #endif /* OPENSSL_FIPS */ fp = sftp_keys_get_fingerprint(pkt->pool, pubkey_data, pubkey_len, SFTP_KEYS_FP_DIGEST_MD5); if (fp != NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "public key MD5 fingerprint: %s", fp); } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error obtaining public key MD5 fingerprint: %s", strerror(errno)); } #ifdef OPENSSL_FIPS } #endif /* OPENSSL_FIPS */ pw = pr_auth_getpwnam(pkt->pool, user); if (pw == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "no account for user '%s' found", user); pr_log_auth(PR_LOG_NOTICE, "USER %s: no such user found from %s [%s] to %s:%d", user, session.c->remote_name, pr_netaddr_get_ipstr(session.c->remote_addr), pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port); *send_userauth_fail = TRUE; errno = ENOENT; return 0; } if (!have_signature) { /* We don't perform the actual authentication just yet; we need to * let the client know that the pubkey algorithms are acceptable. */ if (send_pubkey_ok(pubkey_algo, pubkey_data, pubkey_len) < 0) { return -1; } return 0; } else { const unsigned char *id; char *buf2, *ptr2, *signature_data; uint32_t buflen2, bufsz2, id_len, signature_len; /* XXX This should become a more generic "is this key data * usable/acceptable?" check (and take the pubkey_type parameter), so that * that is where we would check the validity/usability of an X509v3 cert * (if a cert), or if the key is on the blacklist (if a key). */ if (sftp_blacklist_reject_key(pkt->pool, pubkey_data, pubkey_len)) { *send_userauth_fail = TRUE; errno = EPERM; return 0; } signature_len = sftp_msg_read_int(pkt->pool, buf, buflen); signature_data = sftp_msg_read_data(pkt->pool, buf, buflen, signature_len); /* The client signed the request as well; we need to authenticate the * user with the given pubkey now. If that succeeds, we use the * signature to verify the request. And if that succeeds, then we're * done authenticating. */ /* XXX Need to pass the pubkey_type here as well, so that the * verification routines can handle different databases of keys/certs. * * For X509v3 certs, we will want a way to enforce/restrict which * user names can be used with the provided cert. Perhaps a database * mapping cert fingerprints to user names/UIDs? Configurable callback * check (HOOK?), for modules to enforce. */ if (sftp_keystore_verify_user_key(pkt->pool, user, pubkey_data, pubkey_len) < 0) { *send_userauth_fail = TRUE; return 0; } /* Make sure the signature matches as well. */ id_len = sftp_session_get_id(&id); /* Make sure to allocate a buffer large enough to hold the publickey * signature and data we want to send back. */ bufsz2 = buflen2 = pubkey_len + 1024; ptr2 = buf2 = sftp_msg_getbuf(pkt->pool, bufsz2); sftp_msg_write_data(&buf2, &buflen2, (char *) id, id_len, TRUE); sftp_msg_write_byte(&buf2, &buflen2, SFTP_SSH2_MSG_USER_AUTH_REQUEST); sftp_msg_write_string(&buf2, &buflen2, orig_user); if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_SERVICE_IN_PUBKEY_SIG)) { sftp_msg_write_string(&buf2, &buflen2, service); } else { sftp_msg_write_string(&buf2, &buflen2, "ssh-userauth"); } if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_HAVE_PUBKEY_ALGO)) { sftp_msg_write_string(&buf2, &buflen2, "publickey"); sftp_msg_write_bool(&buf2, &buflen2, TRUE); sftp_msg_write_string(&buf2, &buflen2, pubkey_algo); } else { sftp_msg_write_bool(&buf2, &buflen2, TRUE); } sftp_msg_write_data(&buf2, &buflen2, pubkey_data, pubkey_len, TRUE); if (sftp_keys_verify_signed_data(pkt->pool, pubkey_algo, pubkey_data, pubkey_len, signature_data, signature_len, (unsigned char *) ptr2, (bufsz2 - buflen2)) < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "failed to verify '%s' signature on public key auth request for " "user '%s'", pubkey_algo, orig_user); *send_userauth_fail = TRUE; return 0; } } /* Make sure the user is authorized to login. Normally this is checked * as part of the password verification process, but in the case of * publickey authentication, there is no password to verify. */ if (pr_auth_authorize(pkt->pool, user) != PR_AUTH_OK) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "authentication for user '%s' failed: User not authorized", user); pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): User not authorized " "for login", user); *send_userauth_fail = TRUE; errno = EACCES; return 0; } return 1; }
void sftp_disconnect_send(uint32_t reason, const char *explain, const char *file, int lineno, const char *func) { struct ssh2_packet *pkt; const pr_netaddr_t *remote_addr; const char *lang = "en-US"; unsigned char *buf, *ptr; uint32_t buflen, bufsz; int sockfd; /* Send the client a DISCONNECT mesg. */ pkt = sftp_ssh2_packet_create(sftp_pool); remote_addr = pr_netaddr_get_sess_remote_addr(); buflen = bufsz = 1024; ptr = buf = palloc(pkt->pool, bufsz); if (explain == NULL) { register unsigned int i; for (i = 0; explanations[i].explain; i++) { if (explanations[i].code == reason) { explain = explanations[i].explain; lang = explanations[i].lang; if (lang == NULL) { lang = "en-US"; } break; } } if (explain == NULL) { explain = "Unknown reason"; } } else { lang = "en-US"; } if (strlen(func) > 0) { pr_trace_msg(trace_channel, 9, "disconnecting (%s) [at %s:%d:%s()]", explain, file, lineno, func); } else { pr_trace_msg(trace_channel, 9, "disconnecting (%s) [at %s:%d]", explain, file, lineno); } sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_MSG_DISCONNECT); sftp_msg_write_int(&buf, &buflen, reason); sftp_msg_write_string(&buf, &buflen, explain); sftp_msg_write_string(&buf, &buflen, lang); pkt->payload = ptr; pkt->payload_len = (bufsz - buflen); (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "disconnecting %s (%s)", pr_netaddr_get_ipstr(remote_addr), explain); /* If we are called very early in the connection lifetime, then the * sftp_conn variable may not have been set yet, thus the conditional here. */ if (sftp_conn != NULL) { sockfd = sftp_conn->wfd; } else { sockfd = session.c->wfd; } /* Explicitly set a short poll timeout of 5 secs. */ sftp_ssh2_packet_set_poll_timeout(5); if (sftp_ssh2_packet_write(sockfd, pkt) < 0) { int xerrno = errno; pr_trace_msg(trace_channel, 12, "error writing DISCONNECT message: %s", strerror(xerrno)); } destroy_pool(pkt->pool); }
END_TEST START_TEST (parse_addr_test) { const pr_netaddr_t *res; const char *msg, *expected, *ip_str; res = proxy_ftp_msg_parse_addr(NULL, NULL, 0); fail_unless(res == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); res = proxy_ftp_msg_parse_addr(p, NULL, 0); fail_unless(res == NULL, "Failed to handle null msg"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); msg = "foo"; res = proxy_ftp_msg_parse_addr(p, msg, 0); fail_unless(res == NULL, "Failed to handle invalid format"); fail_unless(errno == EPERM, "Expected EPERM (%d), got '%s' (%d)", EPERM, strerror(errno), errno); msg = "(a,b,c,d,e,f)"; res = proxy_ftp_msg_parse_addr(p, msg, 0); fail_unless(res == NULL, "Failed to handle invalid format"); fail_unless(errno == EPERM, "Expected EPERM (%d), got '%s' (%d)", EPERM, strerror(errno), errno); msg = "(1000,2000,3000,4000,5000,6000)"; res = proxy_ftp_msg_parse_addr(p, msg, 0); fail_unless(res == NULL, "Failed to handle invalid format"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); msg = "(1,2,3,4,5000,6000)"; res = proxy_ftp_msg_parse_addr(p, msg, 0); fail_unless(res == NULL, "Failed to handle invalid format"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); msg = "(0,0,0,0,1,2)"; res = proxy_ftp_msg_parse_addr(p, msg, 0); fail_unless(res == NULL, "Failed to handle invalid format"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); msg = "(1,2,3,4,0,0)"; res = proxy_ftp_msg_parse_addr(p, msg, 0); fail_unless(res == NULL, "Failed to handle invalid format"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); msg = "(127,0,0,1,8,73)"; res = proxy_ftp_msg_parse_addr(p, msg, 0); fail_unless(res != NULL, "Failed to parse message '%s': %s", msg, strerror(errno)); ip_str = pr_netaddr_get_ipstr(res); expected = "127.0.0.1"; fail_unless(strcmp(ip_str, expected) == 0, "Expected '%s', got '%s'", expected, ip_str); fail_unless(ntohs(pr_netaddr_get_port(res)) == 2121, "Expected 2121, got %u", ntohs(pr_netaddr_get_port(res))); #ifdef PR_USE_IPV6 msg = "(127,0,0,1,8,73)"; res = proxy_ftp_msg_parse_addr(p, msg, AF_INET); fail_unless(res != NULL, "Failed to parse message '%s': %s", msg, strerror(errno)); ip_str = pr_netaddr_get_ipstr(res); expected = "127.0.0.1"; fail_unless(strcmp(ip_str, expected) == 0, "Expected '%s', got '%s'", expected, ip_str); msg = "(127,0,0,1,8,73)"; res = proxy_ftp_msg_parse_addr(p, msg, AF_INET6); fail_unless(res != NULL, "Failed to parse message '%s': %s", msg, strerror(errno)); ip_str = pr_netaddr_get_ipstr(res); expected = "::ffff:127.0.0.1"; fail_unless(strcmp(ip_str, expected) == 0, "Expected '%s', got '%s'", expected, ip_str); pr_netaddr_disable_ipv6(); res = proxy_ftp_msg_parse_addr(p, msg, AF_INET6); fail_unless(res != NULL, "Failed to parse message '%s': %s", msg, strerror(errno)); ip_str = pr_netaddr_get_ipstr(res); expected = "127.0.0.1"; fail_unless(strcmp(ip_str, expected) == 0, "Expected '%s', got '%s'", expected, ip_str); pr_netaddr_enable_ipv6(); #endif /* PR_USE_IPV6 */ }
int pr_ipbind_close(pr_netaddr_t *addr, unsigned int port, unsigned char close_namebinds) { int res = 0; register unsigned int i = 0; if (addr) { pr_ipbind_t *ipbind = NULL; unsigned char have_ipbind = FALSE; i = ipbind_hash_addr(addr); if (ipbind_table[i] == NULL) { pr_log_pri(PR_LOG_NOTICE, "notice: no ipbind found for %s:%d", pr_netaddr_get_ipstr(addr), port); errno = ENOENT; return -1; } for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 && (!ipbind->ib_port || ipbind->ib_port == port)) { have_ipbind = TRUE; break; } } if (!have_ipbind) { pr_log_pri(PR_LOG_NOTICE, "notice: no ipbind found for %s:%d", pr_netaddr_get_ipstr(addr), port); errno = ENOENT; return -1; } /* If already closed, exit now. */ if (!ipbind->ib_isactive) { errno = EPERM; return -1; } /* Close the ipbinding's listen connection, if present. The trick * here is determining whether this binding's listen member is * _the_ listening socket for the master daemon, or whether it's * been created for SocketBindTight, and can be closed. * * Actually, it's not that hard. It's only _the_ listening socket * for the master daemon in inetd mode, in which case virtual servers * can't be shutdown via ftpdctl, anyway. */ if (SocketBindTight && ipbind->ib_listener != NULL) { pr_inet_close(ipbind->ib_server->pool, ipbind->ib_listener); ipbind->ib_listener = ipbind->ib_server->listen = NULL; } /* Mark this ipbind as inactive. For SocketBindTight sockets, the * closing of the listening connection will suffice, from the clients' * point of view. However, this covers the non-SocketBindTight case, * and will prevent this binding from returning its server_rec pointer * on future lookup requests via pr_ipbind_get_server(). */ ipbind->ib_isactive = FALSE; if (close_namebinds && ipbind->ib_namebinds) { register unsigned int j = 0; pr_namebind_t **namebinds = NULL; namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; for (j = 0; j < ipbind->ib_namebinds->nelts; j++) { pr_namebind_t *nb = namebinds[j]; PR_CLOSE_NAMEBIND(nb->nb_name, nb->nb_server->addr, nb->nb_server->ServerPort); } } } else { /* A NULL addr has a special meaning: close _all_ ipbinds in the * list. */ for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) { pr_ipbind_t *ipbind = NULL; for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { if (SocketBindTight && ipbind->ib_listener != NULL) { pr_inet_close(main_server->pool, ipbind->ib_listener); ipbind->ib_listener = ipbind->ib_server->listen = NULL; } /* Note: do not need to check if this ipbind was previously closed, * for the NULL addr is a request to shut down all ipbinds, * regardless of their current state. */ ipbind->ib_isactive = FALSE; if (close_namebinds && ipbind->ib_namebinds) { register unsigned int j = 0; pr_namebind_t **namebinds = NULL; namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; for (j = 0; j < ipbind->ib_namebinds->nelts; j++) { pr_namebind_t *nb = namebinds[j]; PR_CLOSE_NAMEBIND(nb->nb_name, nb->nb_server->addr, nb->nb_server->ServerPort); } } } } } return 0; }
END_TEST START_TEST (parse_ext_addr_test) { const pr_netaddr_t *addr, *res; const char *msg; res = proxy_ftp_msg_parse_ext_addr(NULL, NULL, NULL, 0, NULL); fail_unless(res == NULL, "Failed to handle null pool"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); res = proxy_ftp_msg_parse_ext_addr(p, NULL, NULL, 0, NULL); fail_unless(res == NULL, "Failed to handle null msg"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL, strerror(errno), errno); msg = "foo"; res = proxy_ftp_msg_parse_ext_addr(p, msg, NULL, 0, NULL); fail_unless(res == NULL, "Failed to handle null addr"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); addr = pr_netaddr_get_addr(p, "127.0.0.1", NULL); fail_unless(addr != NULL, "Failed to get address for 127.0.0.1: %s", strerror(errno)); res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, 0, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPROTOTYPE, "Expected EPROTOTYPE (%d), got '%s' (%d)", EPROTOTYPE, strerror(errno), errno); /* EPSV response formats */ res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad EPSV response"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); msg = "(foo"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad EPSV response"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); msg = "(foo)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPROTOTYPE, "Expected EPROTOTYPE (%d), got '%s' (%d)", EPROTOTYPE, strerror(errno), errno); msg = "(1)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPROTOTYPE, "Expected EPROTOTYPE (%d), got '%s' (%d)", EPROTOTYPE, strerror(errno), errno); msg = "(|4)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPROTOTYPE, "Expected EPROTOTYPE (%d), got '%s' (%d)", EPROTOTYPE, strerror(errno), errno); msg = "(|0)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPROTOTYPE, "Expected EPROTOTYPE (%d), got '%s' (%d)", EPROTOTYPE, strerror(errno), errno); msg = "(|1)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|2)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle bad network protocol"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); /* Where the network protocol matches that of the address... */ msg = "(|1|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle badly formatted message"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); msg = "(|1|1.2.3.4)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle badly formatted message"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); msg = "(|1|1.2.3.4|5)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle badly formatted message"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|1|1.2.3.4|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(||1.2.3.4|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(||1.2.3.4|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, "all"); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(||1.2.3.4|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, "1"); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(|||5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); fail_unless( strcmp(pr_netaddr_get_ipstr(addr), pr_netaddr_get_ipstr(res)) == 0, "Expected '%s', got '%s'", pr_netaddr_get_ipstr(addr), pr_netaddr_get_ipstr(res)); /* ...and where the network protocol does not match that of the address. */ msg = "(||::1|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|2|1.2.3.4|5)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); #ifdef PR_USE_IPV6 addr = pr_netaddr_get_addr(p, "::1", NULL); fail_unless(addr != NULL, "Failed to get address for ::1: %s", strerror(errno)); msg = "(|2|1.2.3.4|5)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|1|::1|5)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|2|::1|5)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); msg = "(|2|::1|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(|2|::1|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, "ALL"); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(||::1|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, "2"); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(||::1|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); msg = "(|||5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res != NULL, "Failed to handle formatted message '%s': %s", msg, strerror(errno)); fail_unless( strcmp(pr_netaddr_get_ipstr(addr), pr_netaddr_get_ipstr(res)) == 0, "Expected '%s', got '%s'", pr_netaddr_get_ipstr(addr), pr_netaddr_get_ipstr(res)); msg = "(||1.2.3.4|5|)"; res = proxy_ftp_msg_parse_ext_addr(p, msg, addr, PR_CMD_EPSV_ID, NULL); fail_unless(res == NULL, "Failed to handle network protocol mismatch"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); #endif /* PR_USE_IPV6 */ }
int sftp_auth_hostbased(struct ssh2_packet *pkt, cmd_rec *pass_cmd, const char *orig_user, const char *user, const char *service, unsigned char **buf, uint32_t *buflen, int *send_userauth_fail) { struct passwd *pw; char *hostkey_algo, *host_fqdn, *host_user, *host_user_utf8; const char *fp = NULL; unsigned char *hostkey_data, *signature_data; unsigned char *buf2, *ptr2; const unsigned char *id; uint32_t buflen2, bufsz2, hostkey_datalen, id_len, signature_len; enum sftp_key_type_e pubkey_type; if (pr_cmd_dispatch_phase(pass_cmd, PRE_CMD, 0) < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "authentication request for user '%s' blocked by '%s' handler", orig_user, (char *) pass_cmd->argv[0]); pr_cmd_dispatch_phase(pass_cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(pass_cmd, LOG_CMD_ERR, 0); *send_userauth_fail = TRUE; errno = EPERM; return 0; } hostkey_algo = sftp_msg_read_string(pkt->pool, buf, buflen); if (hostkey_algo == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "missing required host key algorithm, rejecting request"); *send_userauth_fail = TRUE; errno = EINVAL; return 0; } hostkey_datalen = sftp_msg_read_int(pkt->pool, buf, buflen); hostkey_data = sftp_msg_read_data(pkt->pool, buf, buflen, hostkey_datalen); host_fqdn = sftp_msg_read_string(pkt->pool, buf, buflen); host_user_utf8 = sftp_msg_read_string(pkt->pool, buf, buflen); host_user = sftp_utf8_decode_str(pkt->pool, host_user_utf8); signature_len = sftp_msg_read_int(pkt->pool, buf, buflen); signature_data = sftp_msg_read_data(pkt->pool, buf, buflen, signature_len); pr_trace_msg(trace_channel, 9, "client sent '%s' host key, FQDN %s, and remote user '%s'", hostkey_algo, host_fqdn, host_user); if (strncmp(hostkey_algo, "ssh-rsa", 8) == 0) { pubkey_type = SFTP_KEY_RSA; } else if (strncmp(hostkey_algo, "ssh-dss", 8) == 0) { pubkey_type = SFTP_KEY_DSA; #ifdef PR_USE_OPENSSL_ECC } else if (strncmp(hostkey_algo, "ecdsa-sha2-nistp256", 20) == 0) { pubkey_type = SFTP_KEY_ECDSA_256; } else if (strncmp(hostkey_algo, "ecdsa-sha2-nistp256", 20) == 0) { pubkey_type = SFTP_KEY_ECDSA_384; } else if (strncmp(hostkey_algo, "ecdsa-sha2-nistp256", 20) == 0) { pubkey_type = SFTP_KEY_ECDSA_521; #endif /* PR_USE_OPENSSL_ECC */ /* XXX Need to support X509v3 certs here */ } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unsupported host key algorithm '%s' requested, rejecting request", hostkey_algo); *send_userauth_fail = TRUE; errno = EINVAL; return 0; } if (sftp_keys_verify_pubkey_type(pkt->pool, hostkey_data, hostkey_datalen, pubkey_type) != TRUE) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unable to verify that given host key matches given '%s' algorithm", hostkey_algo); *send_userauth_fail = TRUE; errno = EINVAL; return 0; } #ifdef OPENSSL_FIPS if (FIPS_mode()) { fp = sftp_keys_get_fingerprint(pkt->pool, hostkey_data, hostkey_datalen, SFTP_KEYS_FP_DIGEST_SHA1); if (fp != NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "public key SHA1 fingerprint: %s", fp); } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error obtaining public key SHA1 fingerprint: %s", strerror(errno)); } } else { #endif /* OPENSSL_FIPS */ fp = sftp_keys_get_fingerprint(pkt->pool, hostkey_data, hostkey_datalen, SFTP_KEYS_FP_DIGEST_MD5); if (fp != NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "public key MD5 fingerprint: %s", fp); } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error obtaining public key MD5 fingerprint: %s", strerror(errno)); } #ifdef OPENSSL_FIPS } #endif /* OPENSSL_FIPS */ pw = pr_auth_getpwnam(pkt->pool, user); if (pw == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "no account for user '%s' found", user); pr_log_auth(PR_LOG_NOTICE, "USER %s: no such user found from %s [%s] to %s:%d", user, session.c->remote_name, pr_netaddr_get_ipstr(session.c->remote_addr), pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port); *send_userauth_fail = TRUE; errno = ENOENT; return 0; } /* XXX Should we check the given FQDN here against the client's actual * DNS name and/or IP address? Or leave that up to the keystore's * verify_host_key() function? */ if (sftp_blacklist_reject_key(pkt->pool, hostkey_data, hostkey_datalen)) { *send_userauth_fail = TRUE; errno = EACCES; return 0; } /* The client signed the request as well; we need to authenticate the * host with the given key now. If that succeeds, we use the signature to * verify the request. And if that succeeds, then we're done authenticating. */ if (sftp_keystore_verify_host_key(pkt->pool, user, host_fqdn, host_user, hostkey_data, hostkey_datalen) < 0) { *send_userauth_fail = TRUE; errno = EACCES; return 0; } /* Make sure the signature matches as well. */ id_len = sftp_session_get_id(&id); /* XXX Is this buffer large enough? Too large? */ bufsz2 = buflen2 = 2048; ptr2 = buf2 = sftp_msg_getbuf(pkt->pool, bufsz2); sftp_msg_write_data(&buf2, &buflen2, id, id_len, TRUE); sftp_msg_write_byte(&buf2, &buflen2, SFTP_SSH2_MSG_USER_AUTH_REQUEST); sftp_msg_write_string(&buf2, &buflen2, orig_user); if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_SERVICE_IN_HOST_SIG)) { sftp_msg_write_string(&buf2, &buflen2, service); } else { sftp_msg_write_string(&buf2, &buflen2, "ssh-userauth"); } sftp_msg_write_string(&buf2, &buflen2, "hostbased"); sftp_msg_write_string(&buf2, &buflen2, hostkey_algo); sftp_msg_write_data(&buf2, &buflen2, hostkey_data, hostkey_datalen, TRUE); sftp_msg_write_string(&buf2, &buflen2, host_fqdn); sftp_msg_write_string(&buf2, &buflen2, host_user_utf8); if (sftp_keys_verify_signed_data(pkt->pool, hostkey_algo, hostkey_data, hostkey_datalen, signature_data, signature_len, (unsigned char *) ptr2, (bufsz2 - buflen2)) < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "failed to verify '%s' signature on hostbased auth request for " "user '%s', host %s", hostkey_algo, orig_user, host_fqdn); *send_userauth_fail = TRUE; return 0; } /* Make sure the user is authorized to login. Normally this is checked * as part of the password verification process, but in the case of * hostbased authentication, there is no password to verify. */ if (pr_auth_authorize(pkt->pool, user) != PR_AUTH_OK) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "authentication for user '%s' failed: User not authorized", user); pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): User not authorized " "for login", user); *send_userauth_fail = TRUE; errno = EACCES; return 0; } return 1; }
pr_netaddr_t *pr_netaddr_get_addr(pool *p, const char *name, array_header **addrs) { struct sockaddr_in v4; pr_netaddr_t *na = NULL; int res; if (p == NULL || name == NULL) { errno = EINVAL; return NULL; } pr_trace_msg(trace_channel, 10, "resolving name '%s' to IP address", name); /* Attempt to translate the given name into a pr_netaddr_t using * pr_inet_pton() first. * * First, if IPv6 support is enabled, we try to translate the name using * pr_inet_pton(AF_INET6) on the hopes that the given string is a valid * representation of an IPv6 address. If that fails, or if IPv6 support * is not enabled, we try with pr_inet_pton(AF_INET). If that fails, we * assume that the given name is a DNS name, and we call pr_getaddrinfo(). */ na = (pr_netaddr_t *) pcalloc(p, sizeof(pr_netaddr_t)); #ifdef PR_USE_IPV6 if (use_ipv6) { struct sockaddr_in6 v6; memset(&v6, 0, sizeof(v6)); v6.sin6_family = AF_INET6; # ifdef SIN6_LEN v6.sin6_len = sizeof(struct sockaddr_in6); # endif /* SIN6_LEN */ res = pr_inet_pton(AF_INET6, name, &v6.sin6_addr); if (res > 0) { pr_netaddr_set_family(na, AF_INET6); pr_netaddr_set_sockaddr(na, (struct sockaddr *) &v6); if (addrs) *addrs = NULL; pr_trace_msg(trace_channel, 7, "'%s' resolved to IPv6 address %s", name, pr_netaddr_get_ipstr(na)); return na; } } #endif /* PR_USE_IPV6 */ memset(&v4, 0, sizeof(v4)); v4.sin_family = AF_INET; # ifdef SIN_LEN v4.sin_len = sizeof(struct sockaddr_in); # endif /* SIN_LEN */ res = pr_inet_pton(AF_INET, name, &v4.sin_addr); if (res > 0) { pr_netaddr_set_family(na, AF_INET); pr_netaddr_set_sockaddr(na, (struct sockaddr *) &v4); if (addrs) *addrs = NULL; pr_trace_msg(trace_channel, 7, "'%s' resolved to IPv4 address %s", name, pr_netaddr_get_ipstr(na)); return na; } else if (res == 0) { /* If pr_inet_pton() returns 0, it means that name does not represent a * valid network address in the specified address family. Usually, * this means that name is actually a DNS name, not an IP address * string. So we treat it as a DNS name, and use getaddrinfo(3) to * resolve that name to its IP address(es). */ struct addrinfo hints, *info = NULL; int gai_res = 0; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; pr_trace_msg(trace_channel, 7, "attempting to resolve '%s' to IPv4 address via DNS", name); gai_res = pr_getaddrinfo(name, NULL, &hints, &info); if (gai_res != 0) { if (gai_res != EAI_SYSTEM) { pr_trace_msg(trace_channel, 1, "IPv4 getaddrinfo '%s' error: %s", name, pr_gai_strerror(gai_res)); } else { pr_trace_msg(trace_channel, 1, "IPv4 getaddrinfo '%s' system error: [%d] %s", name, errno, strerror(errno)); } return NULL; } if (info) { /* Copy the first returned addr into na, as the return value. */ pr_netaddr_set_family(na, info->ai_family); pr_netaddr_set_sockaddr(na, info->ai_addr); pr_trace_msg(trace_channel, 7, "resolved '%s' to %s address %s", name, info->ai_family == AF_INET ? "IPv4" : "IPv6", pr_netaddr_get_ipstr(na)); pr_freeaddrinfo(info); } #ifdef PR_USE_IPV6 if (use_ipv6 && addrs) { /* Do the call again, this time for IPv6 addresses. * * We make two separate getaddrinfo(3) calls, rather than one * with a hint of AF_UNSPEC, because of certain bugs where the use * of AF_UNSPEC does not function as advertised. (I suspect this * bug was caused by proftpd's calling pattern, but as I could * not track it down, and as there are reports of AF_UNSPEC not * being as fast as AF_INET/AF_INET6, it just seemed easier to * do it this way.) */ gai_res = 0; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; pr_trace_msg(trace_channel, 7, "attempting to resolve '%s' to IPv6 address via DNS", name); gai_res = pr_getaddrinfo(name, NULL, &hints, &info); if (gai_res != 0) { if (gai_res != EAI_SYSTEM) { pr_trace_msg(trace_channel, 1, "IPv6 getaddrinfo '%s' error: %s", name, pr_gai_strerror(gai_res)); } else { pr_trace_msg(trace_channel, 1, "IPv6 getaddrinfo '%s' system error: [%d] %s", name, errno, strerror(errno)); } return na; } if (info) { pr_netaddr_t **elt; *addrs = make_array(p, 0, sizeof(pr_netaddr_t *)); elt = push_array(*addrs); *elt = pcalloc(p, sizeof(pr_netaddr_t)); pr_netaddr_set_family(*elt, info->ai_family); pr_netaddr_set_sockaddr(*elt, info->ai_addr); pr_trace_msg(trace_channel, 7, "resolved '%s' to %s address %s", name, info->ai_family == AF_INET ? "IPv4" : "IPv6", pr_netaddr_get_ipstr(*elt)); pr_freeaddrinfo(info); } } #endif /* PR_USE_IPV6 */ return na; } pr_trace_msg(trace_channel, 8, "failed to resolve '%s' to an IP address", name); return NULL; }
int pr_namebind_create(server_rec *server, const char *name, pr_netaddr_t *addr, unsigned int port) { pr_ipbind_t *ipbind = NULL; pr_namebind_t *namebind = NULL, **namebinds = NULL; if (server == NULL || name == NULL) { errno = EINVAL; return -1; } /* First, find the ipbind to hold this namebind. */ ipbind = pr_ipbind_find(addr, port, FALSE); if (ipbind == NULL) { pr_netaddr_t wildcard_addr; int addr_family; /* If not found, look for the wildcard address. */ addr_family = pr_netaddr_get_family(addr); pr_netaddr_clear(&wildcard_addr); pr_netaddr_set_family(&wildcard_addr, addr_family); pr_netaddr_set_sockaddr_any(&wildcard_addr); ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE); #ifdef PR_USE_IPV6 if (ipbind == FALSE && addr_family == AF_INET6 && pr_netaddr_use_ipv6()) { /* No IPv6 wildcard address found; try the IPv4 wildcard address. */ pr_netaddr_clear(&wildcard_addr); pr_netaddr_set_family(&wildcard_addr, AF_INET); pr_netaddr_set_sockaddr_any(&wildcard_addr); ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE); } #endif /* PR_USE_IPV6 */ } if (ipbind == NULL) { errno = ENOENT; return -1; } /* Make sure we can add this namebind. */ if (!ipbind->ib_namebinds) { ipbind->ib_namebinds = make_array(binding_pool, 0, sizeof(pr_namebind_t *)); } else { register unsigned int i = 0; namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; /* See if there is already a namebind for the given name. */ for (i = 0; i < ipbind->ib_namebinds->nelts; i++) { namebind = namebinds[i]; if (namebind != NULL && namebind->nb_name != NULL) { /* DNS names are case-insensitive, hence the case-insensitive check * here. * * XXX Ideally, we should check whether any existing namebinds which * are globs will match the newly added namebind as well. */ if (strcasecmp(namebind->nb_name, name) == 0) { errno = EEXIST; return -1; } } } } namebind = (pr_namebind_t *) pcalloc(server->pool, sizeof(pr_namebind_t)); namebind->nb_name = name; namebind->nb_server = server; namebind->nb_isactive = FALSE; if (pr_str_is_fnmatch(name) == TRUE) { namebind->nb_iswildcard = TRUE; } pr_trace_msg(trace_channel, 8, "created named binding '%s' for %s#%u, server %p", name, pr_netaddr_get_ipstr(server->addr), server->ServerPort, server->ServerName); /* The given server should already have the following populated: * * server->ServerName * server->ServerAddress * server->ServerFQDN */ /* These TCP socket tweaks will not apply to the control connection (it will * already have been established by the time this named vhost is used), * but WILL apply to any data connections established to this named vhost. */ #if 0 namebind->nb_server->tcp_mss_len = (server->tcp_mss_len ? server->tcp_mss_len : main_server->tcp_mss_len); namebind->nb_server->tcp_rcvbuf_len = (server->tcp_rcvbuf_len ? server->tcp_rcvbuf_len : main_server->tcp_rcvbuf_len); namebind->nb_server->tcp_rcvbuf_override = (server->tcp_rcvbuf_override ? TRUE : main_server->tcp_rcvbuf_override); namebind->nb_server->tcp_sndbuf_len = (server->tcp_sndbuf_len ? server->tcp_sndbuf_len : main_server->tcp_sndbuf_len); namebind->nb_server->tcp_sndbuf_override = (server->tcp_sndbuf_override ? TRUE : main_server->tcp_sndbuf_override); /* XXX Shouldn't need these; the ipbind container handles all of the * connection (listener, port, addr) stuff. */ namebind->nb_server->addr = (server->addr ? server->addr : main_server->addr); namebind->nb_server->ServerPort = (server->ServerPort ? server->ServerPort : main_server->ServerPort); namebind->nb_listener = (server->listen ? server->listen : main_server->listen); #endif *((pr_namebind_t **) push_array(ipbind->ib_namebinds)) = namebind; return 0; }
const char *proxy_ftp_msg_fmt_ext_addr(pool *p, const pr_netaddr_t *addr, unsigned short port, int cmd_id, int use_masqaddr) { const char *addr_str; char delim = '|', *msg; int family = 0; size_t addr_strlen, msglen; if (p == NULL || addr == NULL) { errno = EINVAL; return NULL; } if (use_masqaddr) { config_rec *c; /* Handle MasqueradeAddress. */ c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE); if (c != NULL) { addr = c->argv[0]; } } /* Format is <d>proto<d>ip address<d>port<d> (ASCII in network order), * where <d> is an arbitrary delimiter character. */ switch (pr_netaddr_get_family(addr)) { case AF_INET: family = 1; break; #ifdef PR_USE_IPV6 case AF_INET6: family = 2; break; #endif /* PR_USE_IPV6 */ default: /* Unlikely to happen. */ errno = EINVAL; return NULL; } addr_str = pr_netaddr_get_ipstr(addr); addr_strlen = strlen(addr_str); /* 4 delimiters, the network protocol, the IP address, the port, and a NUL. */ msglen = (4 * 1) + addr_strlen + 6 + 1; msg = pcalloc(p, msglen); switch (cmd_id) { case PR_CMD_EPRT_ID: snprintf(msg, msglen, "%c%d%c%s%c%hu%c", delim, family, delim, addr_str, delim, port, delim); break; case PR_CMD_EPSV_ID: snprintf(msg, msglen-1, "%c%c%c%u%c", delim, delim, delim, port, delim); break; default: pr_trace_msg(trace_channel, 3, "invalid/unsupported command ID: %d", cmd_id); errno = EINVAL; return NULL; } return msg; }
int pr_netaddr_cmp(const pr_netaddr_t *na1, const pr_netaddr_t *na2) { pool *tmp_pool = NULL; pr_netaddr_t *a, *b; int res; if (na1 && !na2) return 1; if (!na1 && na2) return -1; if (!na1 && !na2) return 0; if (pr_netaddr_get_family(na1) != pr_netaddr_get_family(na2)) { /* Cannot compare addresses from different families, unless one * of the netaddrs has an AF_INET family, and the other has an * AF_INET6 family AND is an IPv4-mapped IPv6 address. */ if (pr_netaddr_is_v4mappedv6(na1) != TRUE && pr_netaddr_is_v4mappedv6(na2) != TRUE) { errno = EINVAL; return -1; } if (pr_netaddr_is_v4mappedv6(na1) == TRUE) { tmp_pool = make_sub_pool(permanent_pool); /* This case means that na1 is an IPv4-mapped IPv6 address, and * na2 is an IPv4 address. */ a = pr_netaddr_alloc(tmp_pool); pr_netaddr_set_family(a, AF_INET); pr_netaddr_set_port(a, pr_netaddr_get_port(na1)); memcpy(&a->na_addr.v4.sin_addr, get_v4inaddr(na1), sizeof(struct in_addr)); b = (pr_netaddr_t *) na2; pr_trace_msg(trace_channel, 6, "comparing IPv4 address '%s' against " "IPv4-mapped IPv6 address '%s'", pr_netaddr_get_ipstr(b), pr_netaddr_get_ipstr(a)); } else if (pr_netaddr_is_v4mappedv6(na2) == TRUE) { tmp_pool = make_sub_pool(permanent_pool); /* This case means that na is an IPv4 address, and na2 is an * IPv4-mapped IPv6 address. */ a = (pr_netaddr_t *) na1; b = pr_netaddr_alloc(tmp_pool); pr_netaddr_set_family(b, AF_INET); pr_netaddr_set_port(b, pr_netaddr_get_port(na2)); memcpy(&b->na_addr.v4.sin_addr, get_v4inaddr(na2), sizeof(struct in_addr)); pr_trace_msg(trace_channel, 6, "comparing IPv4 address '%s' against " "IPv4-mapped IPv6 address '%s'", pr_netaddr_get_ipstr(a), pr_netaddr_get_ipstr(b)); } else { a = (pr_netaddr_t *) na1; b = (pr_netaddr_t *) na2; } } else { a = (pr_netaddr_t *) na1; b = (pr_netaddr_t *) na2; } switch (pr_netaddr_get_family(a)) { case AF_INET: res = memcmp(&a->na_addr.v4.sin_addr, &b->na_addr.v4.sin_addr, sizeof(struct in_addr)); if (tmp_pool) destroy_pool(tmp_pool); return res; #ifdef PR_USE_IPV6 case AF_INET6: if (use_ipv6) { res = memcmp(&a->na_addr.v6.sin6_addr, &b->na_addr.v6.sin6_addr, sizeof(struct in6_addr)); if (tmp_pool) destroy_pool(tmp_pool); return res; } #endif /* PR_USE_IPV6 */ } if (tmp_pool) destroy_pool(tmp_pool); errno = EPERM; return -1; }