diff -r e528d9e3cdc8 src/auth/auth-request.c --- a/src/auth/auth-request.c Sat May 31 14:27:27 2008 +0300 +++ b/src/auth/auth-request.c Sun Jun 01 21:07:12 2008 +0300 @@ -868,38 +868,12 @@ return request->requested_login_user != NULL; } -static int is_ip_in_network(const char *network, const struct ip_addr *ip) -{ - struct ip_addr src_ip, net_ip; - const char *p; - unsigned int max_bits, bits; - - if (net_ipv6_mapped_ipv4_convert(ip, &src_ip) == 0) - ip = &src_ip; - - max_bits = IPADDR_IS_V4(ip) ? 32 : 128; - p = strchr(network, '/'); - if (p == NULL) { - /* full IP address must match */ - bits = max_bits; - } else { - /* get the network mask */ - network = t_strdup_until(network, p); - bits = strtoul(p+1, NULL, 10); - if (bits > max_bits) - bits = max_bits; - } - - if (net_addr2ip(network, &net_ip) < 0) - return -1; - - return net_is_in_network(ip, &net_ip, bits); -} - static void auth_request_validate_networks(struct auth_request *request, const char *networks) { const char *const *net; + struct ip_addr net_ip; + unsigned int bits; bool found = FALSE; if (request->remote_ip.family == 0) { @@ -913,15 +887,14 @@ for (net = t_strsplit_spaces(networks, ", "); *net != NULL; net++) { auth_request_log_debug(request, "auth", "allow_nets: Matching for network %s", *net); - switch (is_ip_in_network(*net, &request->remote_ip)) { - case 1: - found = TRUE; - break; - case -1: + + if (net_parse_range(*net, &net_ip, &bits) < 0) { auth_request_log_info(request, "passdb", "allow_nets: Invalid network '%s'", *net); - break; - default: + } + + if (net_is_in_network(&request->remote_ip, &net_ip, bits)) { + found = TRUE; break; } } diff -r e528d9e3cdc8 src/imap-login/client-authenticate.c --- a/src/imap-login/client-authenticate.c Sat May 31 14:27:27 2008 +0300 +++ b/src/imap-login/client-authenticate.c Sun Jun 01 21:07:12 2008 +0300 @@ -91,12 +91,16 @@ string_t *reply; unsigned int port = 143; bool proxy = FALSE, temp = FALSE, nologin = !success, proxy_self; + enum imap_proxy_flags proxy_flags = 0; for (; *args != NULL; args++) { if (strcmp(*args, "nologin") == 0) nologin = TRUE; else if (strcmp(*args, "proxy") == 0) proxy = TRUE; + else if (strcmp(*args, "xforward") == 0 || + strncmp(*args, "xforward=", 9) == 0) + proxy_flags |= IMAP_PROXY_FLAG_XFORWARD; else if (strcmp(*args, "temp") == 0) temp = TRUE; else if (strncmp(*args, "reason=", 7) == 0) @@ -124,7 +128,8 @@ proxy host=.. [port=..] [destuser=..] pass=.. */ if (!success) return FALSE; - if (imap_proxy_new(client, host, port, destuser, pass) < 0) + if (imap_proxy_new(client, host, port, destuser, pass, + proxy_flags) < 0) client_destroy_internal_failure(client); return TRUE; } diff -r e528d9e3cdc8 src/imap-login/client.c --- a/src/imap-login/client.c Sat May 31 14:27:27 2008 +0300 +++ b/src/imap-login/client.c Sun Jun 01 21:07:12 2008 +0300 @@ -15,6 +15,8 @@ #include "auth-client.h" #include "ssl-proxy.h" #include "imap-proxy.h" + +#include /* max. size of one parameter in line, or max reply length in SASL authentication */ @@ -216,6 +218,35 @@ return 1; } +static int cmd_xforward(struct imap_client *client, const struct imap_arg *args) +{ + const char *key, *value; + + if (!client_has_xforward_access(&client->common)) { + client_send_tagline(client, "NO Permission denied."); + return 1; + } + for (; args->type != IMAP_ARG_EOL; args += 2) { + if (args->type != IMAP_ARG_ATOM || + !IMAP_ARG_TYPE_IS_STRING(args[1].type)) { + client_send_tagline(client, "BAD Invalid args."); + return 1; + } + key = IMAP_ARG_STR_NONULL(&args[0]); + value = IMAP_ARG_STR_NONULL(&args[1]); + if (strcmp(key, "rip") == 0) + (void)net_addr2ip(value, &client->common.ip); + else if (strcmp(key, "lip") == 0) + (void)net_addr2ip(value, &client->common.local_ip); + else if (strcmp(key, "rport") == 0) + client->common.remote_port = atoi(value); + else if (strcmp(key, "lport") == 0) + client->common.local_port = atoi(value); + } + client_send_tagline(client, "OK"); + return 1; +} + static int client_command_execute(struct imap_client *client, const char *cmd, const struct imap_arg *args) { @@ -232,6 +263,8 @@ return cmd_noop(client); if (strcmp(cmd, "LOGOUT") == 0) return cmd_logout(client); + if (strcmp(cmd, "XFORWARD") == 0) + return cmd_xforward(client, args); return -1; } diff -r e528d9e3cdc8 src/imap-login/client.h --- a/src/imap-login/client.h Sat May 31 14:27:27 2008 +0300 +++ b/src/imap-login/client.h Sun Jun 01 21:07:12 2008 +0300 @@ -4,6 +4,10 @@ #include "network.h" #include "master.h" #include "client-common.h" + +enum imap_proxy_flags { + IMAP_PROXY_FLAG_XFORWARD = 0x01 +}; struct imap_client { struct client common; @@ -19,6 +23,7 @@ struct login_proxy *proxy; char *proxy_user, *proxy_password; + enum imap_proxy_flags proxy_flags; unsigned int bad_counter; diff -r e528d9e3cdc8 src/imap-login/imap-proxy.c --- a/src/imap-login/imap-proxy.c Sat May 31 14:27:27 2008 +0300 +++ b/src/imap-login/imap-proxy.c Sun Jun 01 21:07:12 2008 +0300 @@ -29,8 +29,18 @@ return -1; } + str = t_str_new(128); + if ((client->proxy_flags & IMAP_PROXY_FLAG_XFORWARD) != 0) { + /* Send XFORWARD command */ + str_printfa(str, "F XFORWARD rip %s rport %u " + "lip %s lport %u\r\n", + net_ip2addr(&client->common.ip), + client->common.remote_port, + net_ip2addr(&client->common.local_ip), + client->common.local_port); + } + /* send LOGIN command */ - str = t_str_new(128); str_append(str, "P LOGIN "); imap_quote_append_string(str, client->proxy_user, FALSE); str_append_c(str, ' '); @@ -146,7 +156,8 @@ } int imap_proxy_new(struct imap_client *client, const char *host, - unsigned int port, const char *user, const char *password) + unsigned int port, const char *user, const char *password, + enum imap_proxy_flags flags) { i_assert(user != NULL); i_assert(!client->destroyed); @@ -173,6 +184,7 @@ client->proxy_login_sent = FALSE; client->proxy_user = i_strdup(user); client->proxy_password = i_strdup(password); + client->proxy_flags = flags; /* disable input until authentication is finished */ if (client->io != NULL) diff -r e528d9e3cdc8 src/imap-login/imap-proxy.h --- a/src/imap-login/imap-proxy.h Sat May 31 14:27:27 2008 +0300 +++ b/src/imap-login/imap-proxy.h Sun Jun 01 21:07:12 2008 +0300 @@ -4,6 +4,7 @@ #include "login-proxy.h" int imap_proxy_new(struct imap_client *client, const char *host, - unsigned int port, const char *user, const char *password); + unsigned int port, const char *user, const char *password, + enum imap_proxy_flags flags); #endif diff -r e528d9e3cdc8 src/lib/network.c --- a/src/lib/network.c Sat May 31 14:27:27 2008 +0300 +++ b/src/lib/network.c Sun Jun 01 21:07:12 2008 +0300 @@ -5,6 +5,7 @@ #include "fd-set-nonblock.h" #include "network.h" +#include #include #include #include @@ -704,12 +705,45 @@ return TRUE; } +int net_parse_range(const char *network, struct ip_addr *ip_r, + unsigned int *bits_r) +{ + const char *p; + int bits, max_bits; + + p = strchr(network, '/'); + if (p != NULL) + network = t_strdup_until(network, p++); + + if (net_addr2ip(network, ip_r) < 0) + return -1; + + max_bits = IPADDR_IS_V4(ip_r) ? 32 : 128; + if (p == NULL) { + /* full IP address must match */ + bits = max_bits; + } else { + /* get the network mask */ + bits = atoi(p); + if (bits < 0 || bits > max_bits) + return -1; + } + *bits_r = bits; + return 0; +} + bool net_is_in_network(const struct ip_addr *ip, const struct ip_addr *net_ip, unsigned int bits) { + struct ip_addr tmp_ip; const uint32_t *ip1, *ip2; uint32_t mask, i1, i2; unsigned int pos, i; + + if (net_ipv6_mapped_ipv4_convert(ip, &tmp_ip) == 0) { + /* IPv4 address mapped disguised as IPv6 address */ + ip = &tmp_ip; + } if (IPADDR_IS_V4(ip) != IPADDR_IS_V4(net_ip)) { /* one is IPv6 and one is IPv4 */ diff -r e528d9e3cdc8 src/lib/network.h --- a/src/lib/network.h Sat May 31 14:27:27 2008 +0300 +++ b/src/lib/network.h Sun Jun 01 21:07:12 2008 +0300 @@ -110,7 +110,11 @@ bool is_ipv4_address(const char *addr); bool is_ipv6_address(const char *addr); -/* Returns TRUE if ip is in net_ip/bits network. */ +/* Parse network as ip/bits. Returns 0 if successful, -1 if invalid input. */ +int net_parse_range(const char *network, struct ip_addr *ip_r, + unsigned int *bits_r); +/* Returns TRUE if ip is in net_ip/bits network. IPv6 mapped IPv4 addresses + are converted to plain IPv4 addresses before matching. */ bool net_is_in_network(const struct ip_addr *ip, const struct ip_addr *net_ip, unsigned int bits); diff -r e528d9e3cdc8 src/login-common/client-common.c --- a/src/login-common/client-common.c Sat May 31 14:27:27 2008 +0300 +++ b/src/login-common/client-common.c Sun Jun 01 21:07:12 2008 +0300 @@ -151,3 +151,25 @@ client_syslog_real(client, msg); } T_END; } + +bool client_has_xforward_access(struct client *client) +{ + const char *const *net; + struct ip_addr net_ip; + unsigned int bits; + + if (xforward_networks == NULL) + return FALSE; + + net = t_strsplit_spaces(xforward_networks, ", "); + for (; *net != NULL; net++) { + if (net_parse_range(*net, &net_ip, &bits) < 0) { + i_error("xforward: Invalid network '%s'", *net); + break; + } + + if (net_is_in_network(&client->ip, &net_ip, bits)) + return TRUE; + } + return FALSE; +} diff -r e528d9e3cdc8 src/login-common/client-common.h --- a/src/login-common/client-common.h Sat May 31 14:27:27 2008 +0300 +++ b/src/login-common/client-common.h Sun Jun 01 21:07:12 2008 +0300 @@ -42,6 +42,7 @@ unsigned int clients_get_count(void); void client_syslog(struct client *client, const char *msg); +bool client_has_xforward_access(struct client *client); void clients_notify_auth_connected(void); void client_destroy_oldest(void); diff -r e528d9e3cdc8 src/login-common/common.h --- a/src/login-common/common.h Sat May 31 14:27:27 2008 +0300 +++ b/src/login-common/common.h Sun Jun 01 21:07:12 2008 +0300 @@ -18,6 +18,7 @@ extern const char *greeting, *log_format; extern const char *const *log_format_elements; extern const char *capability_string; +extern const char *xforward_networks; extern unsigned int max_connections; extern unsigned int login_process_uid; extern struct auth_client *auth_client; diff -r e528d9e3cdc8 src/login-common/main.c --- a/src/login-common/main.c Sat May 31 14:27:27 2008 +0300 +++ b/src/login-common/main.c Sun Jun 01 21:07:12 2008 +0300 @@ -23,6 +23,7 @@ bool verbose_proctitle, verbose_ssl, verbose_auth; const char *greeting, *log_format; const char *const *log_format_elements; +const char *xforward_networks; unsigned int max_connections; unsigned int login_process_uid; struct auth_client *auth_client; @@ -320,6 +321,8 @@ if (log_format == NULL) log_format = "%$: %s"; + xforward_networks = getenv("XFORWARD_NETWORKS"); + value = getenv("PROCESS_UID"); if (value == NULL) i_fatal("BUG: PROCESS_UID environment not given"); diff -r e528d9e3cdc8 src/master/login-process.c --- a/src/master/login-process.c Sat May 31 14:27:27 2008 +0300 +++ b/src/master/login-process.c Sun Jun 01 21:07:12 2008 +0300 @@ -580,6 +580,10 @@ set->imap_capability : set->imap_generated_capability, NULL)); } + if (*set->xforward_networks != '\0') { + env_put(t_strconcat("XFORWARD_NETWORKS=", + set->xforward_networks, NULL)); + } } static pid_t create_login_process(struct login_group *group) diff -r e528d9e3cdc8 src/master/master-settings-defs.c --- a/src/master/master-settings-defs.c Sat May 31 14:27:27 2008 +0300 +++ b/src/master/master-settings-defs.c Sun Jun 01 21:07:12 2008 +0300 @@ -114,6 +114,7 @@ DEF_STR(imap_capability), DEF_STR(imap_client_workarounds), DEF_STR(imap_logout_format), + DEF_STR(xforward_networks), /* pop3 */ DEF_BOOL(pop3_no_flag_updates), diff -r e528d9e3cdc8 src/master/master-settings.c --- a/src/master/master-settings.c Sat May 31 14:27:27 2008 +0300 +++ b/src/master/master-settings.c Sun Jun 01 21:07:12 2008 +0300 @@ -280,6 +280,7 @@ MEMBER(imap_capability) "", MEMBER(imap_client_workarounds) "", MEMBER(imap_logout_format) "bytes=%i/%o", + MEMBER(xforward_networks) "", /* pop3 */ MEMBER(pop3_no_flag_updates) FALSE, diff -r e528d9e3cdc8 src/master/master-settings.h --- a/src/master/master-settings.h Sat May 31 14:27:27 2008 +0300 +++ b/src/master/master-settings.h Sun Jun 01 21:07:12 2008 +0300 @@ -126,6 +126,7 @@ const char *imap_capability; const char *imap_client_workarounds; const char *imap_logout_format; + const char *xforward_networks; /* pop3 */ bool pop3_no_flag_updates;