diff --git a/src/Makefile.am b/src/Makefile.am index da5f525e..eb30be7b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,7 +16,7 @@ CCAN_SOURCES = ccan/build_assert/build_assert.h ccan/check_type/check_type.h \ ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \ http-parser/http_parser.c ipc.h cookies.c worker-tun.c main-misc.c \ - main-config.c \ + main-config.c ip-lease.c ip-lease.h \ vpn.h cookies.h tlslib.h http-parser/http_parser.h log.c tun.c tun.h \ config.c pam.c pam.h worker-resume.c worker.h main-resume.c main.h \ worker-extras.c main-auth.h html.c html.h \ diff --git a/src/common.c b/src/common.c index 29b0e5cd..a0adb771 100644 --- a/src/common.c +++ b/src/common.c @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include ssize_t force_write(int sockfd, const void *buf, size_t len) { @@ -64,3 +67,12 @@ uint8_t * p = buf; return len; } + +int ip_cmp(const struct sockaddr_storage *s1, const struct sockaddr_storage *s2, size_t n) +{ + if (((struct sockaddr*)s1)->sa_family == AF_INET) { + return memcmp(SA_IN_P(s1), SA_IN_P(s2), sizeof(struct in_addr)); + } else { /* inet6 */ + return memcmp(SA_IN6_P(s1), SA_IN6_P(s2), sizeof(struct in6_addr)); + } +} diff --git a/src/common.h b/src/common.h index 1e0db9a3..7adaa924 100644 --- a/src/common.h +++ b/src/common.h @@ -2,6 +2,7 @@ # define COMMON_H ssize_t force_write(int sockfd, const void *buf, size_t len); ssize_t force_read(int sockfd, void *buf, size_t len); +int ip_cmp(const struct sockaddr_storage *s1, const struct sockaddr_storage *s2, size_t n); #endif diff --git a/src/ip-lease.c b/src/ip-lease.c new file mode 100644 index 00000000..c63f4ccf --- /dev/null +++ b/src/ip-lease.c @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2013 Nikos Mavrogiannopoulos + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +static void bignum_add (uint8_t * num, unsigned size, unsigned step) +{ + register int i; + register unsigned tmp, y; + + for (i = size-1; i >= 0; i--) + { + tmp = num[i]; + + num[i] += step; + if (num[i] < tmp) + y = 1; + else + y = 0; + + if (y == 0) + break; + } +} + + +void ip_lease_deinit(struct ip_lease_db_st* db) +{ +struct ip_lease_st * cache; +struct htable_iter iter; + + cache = htable_first(&db->ht, &iter); + while(cache != NULL) { + free(cache); + + cache = htable_next(&db->ht, &iter); + } + + htable_clear(&db->ht); + db->entries = 0; + + return; +} + +static size_t rehash(const void* _e, void* unused) +{ +const struct ip_lease_st * e = _e; + + return hash_stable_8(&e->rip, e->rip_len, 0); +} + +void ip_lease_init(struct ip_lease_db_st* db) +{ + htable_init(&db->ht, rehash, NULL); + db->entries = 0; +} + +static bool ip_lease_cmp(const void* _c1, void* _c2) +{ +const struct ip_lease_st* c1 = _c1; +struct ip_lease_st* c2 = _c2; + + if (c1->rip_len == c2->rip_len && + ip_cmp(&c1->rip, &c2->rip, c2->rip_len) == 0) + return 1; + + return 0; +} + +static int ip_lease_exists(main_server_st* s, struct sockaddr_storage* ip, size_t sockaddrlen) +{ +struct ip_lease_st t; + + t.rip_len = sockaddrlen; + memcpy(&t.rip, ip, sizeof(*ip)); + + if (htable_get(&s->ip_leases.ht, rehash(&t, NULL), ip_lease_cmp, &t) != 0) + return 1; + + return 0; +} + + +#define MAX_IP_TRIES 16 + +static +int get_ipv4_lease(main_server_st* s, struct proc_st* proc) +{ + + struct sockaddr_storage tmp, mask, network, rnd; + unsigned i; + unsigned max_loops = MAX_IP_TRIES; + int ret; + const char* c_network, *c_netmask; + char buf[64]; + + if (proc->config.ipv4_network && proc->config.ipv4_netmask) { + c_network = proc->config.ipv4_network; + c_netmask = proc->config.ipv4_netmask; + } else { + c_network = s->config->network.ipv4; + c_netmask = s->config->network.ipv4_netmask; + } + + if (c_network && c_netmask) { + ret = + inet_pton(AF_INET, c_network, SA_IN_P(&network)); + + if (ret != 1) { + mslog(s, NULL, LOG_ERR, "Error reading IP: %s\n", c_network); + return -1; + } + + ret = + inet_pton(AF_INET, c_netmask, SA_IN_P(&mask)); + + if (ret != 1) { + mslog(s, NULL, LOG_ERR, "Error reading mask: %s\n", c_netmask); + return -1; + } + + proc->ipv4 = calloc(1, sizeof(*proc->ipv4)); + if (proc->ipv4 == NULL) + return ERR_MEM; + + /* mask the network (just in case it is wrong) */ + for (i=0;isin_family = AF_INET; + ((struct sockaddr_in*)&network)->sin_port = 0; + + memcpy(&tmp, &network, sizeof(tmp)); + ((struct sockaddr_in*)&tmp)->sin_family = AF_INET; + ((struct sockaddr_in*)&tmp)->sin_port = 0; + + ((struct sockaddr_in*)&rnd)->sin_family = AF_INET; + ((struct sockaddr_in*)&rnd)->sin_port = 0; + + do { + if (max_loops == 0) { + mslog(s, proc, LOG_ERR, "Could not figure out a valid IPv4 IP.\n"); + ret = ERR_NO_IP; + goto fail; + } + if (max_loops == MAX_IP_TRIES) { + uint32_t t = hash_any(proc->username, strlen(proc->username), 0); + memcpy(SA_IN_U8_P(&rnd), &t, 4); + } else + gnutls_rnd(GNUTLS_RND_NONCE, SA_IN_U8_P(&rnd), sizeof(struct in_addr)); + max_loops--; + + if (SA_IN_U8_P(&rnd)[3] == 255) /* broadcast */ + bignum_add(SA_IN_U8_P(&rnd), sizeof(struct in_addr), 1); + + /* Mask the random number with the netmask */ + for (i=0;iipv4->lip, &rnd, sizeof(struct sockaddr_in)); + proc->ipv4->lip_len = sizeof(struct sockaddr_in); + + /* RIP = LIP + 1 */ + memcpy(&tmp, &proc->ipv4->lip, sizeof(struct sockaddr_in)); + bignum_add(SA_IN_U8_P(&tmp), sizeof(struct in_addr), 1); + + /* check if it exists in the hash table */ + if (ip_lease_exists(s, &tmp, sizeof(struct sockaddr_in)) != 0) + continue; + + memcpy(&proc->ipv4->rip, &tmp, sizeof(struct sockaddr_in)); + proc->ipv4->rip_len = sizeof(struct sockaddr_in); + + /* mask the last IP with the netmask */ + for (i=0;iipv4->lip, proc->ipv4->lip_len, buf, sizeof(buf))); + + if (icmp_ping4(s, (void*)&proc->ipv4->lip, (void*)&proc->ipv4->rip) == 0) + break; + } while(1); + } + + return 0; + +fail: + free(proc->ipv4); + proc->ipv4 = NULL; + + return ret; +} + +static +int get_ipv6_lease(main_server_st* s, struct proc_st* proc) +{ + + struct sockaddr_storage tmp, mask, network, rnd; + unsigned i, max_loops = MAX_IP_TRIES; + int ret; + const char* c_network, *c_netmask; + + if (proc->config.ipv6_network && proc->config.ipv6_netmask) { + c_network = proc->config.ipv6_network; + c_netmask = proc->config.ipv6_netmask; + } else { + c_network = s->config->network.ipv6; + c_netmask = s->config->network.ipv6_netmask; + } + + if (c_network && c_netmask) { + ret = + inet_pton(AF_INET6, c_network, SA_IN6_P(&network)); + + if (ret != 1) { + mslog(s, NULL, LOG_ERR, "Error reading IP: %s\n", c_network); + return -1; + } + + ret = + inet_pton(AF_INET6, c_netmask, SA_IN6_P(&mask)); + + if (ret != 1) { + mslog(s, NULL, LOG_ERR, "Error reading mask: %s\n", c_netmask); + return -1; + } + + proc->ipv6 = calloc(1, sizeof(*proc->ipv6)); + if (proc->ipv6 == NULL) + return ERR_MEM; + + /* mask the network */ + for (i=0;isin6_family = AF_INET6; + ((struct sockaddr_in6*)&network)->sin6_port = 0; + + memcpy(&tmp, &network, sizeof(tmp)); + ((struct sockaddr_in6*)&tmp)->sin6_family = AF_INET6; + ((struct sockaddr_in6*)&tmp)->sin6_port = AF_INET6; + + ((struct sockaddr_in6*)&rnd)->sin6_family = AF_INET6; + ((struct sockaddr_in6*)&rnd)->sin6_port = AF_INET6; + + do { + if (max_loops == 0) { + mslog(s, NULL, LOG_ERR, "Could not figure out a valid IPv6 IP.\n"); + ret = ERR_NO_IP; + goto fail; + } + + if (max_loops == MAX_IP_TRIES) { + uint32_t t = hash_any(proc->username, strlen(proc->username), 0); + memset(SA_IN6_U8_P(&rnd), 0, sizeof(struct in6_addr)); + memcpy(SA_IN6_U8_P(&rnd)+sizeof(struct in6_addr)-5, &t, 4); + } else + gnutls_rnd(GNUTLS_RND_NONCE, SA_IN6_U8_P(&rnd), sizeof(struct in6_addr)); + max_loops--; + + /* Mask the random number with the netmask */ + for (i=0;iipv6->lip_len = sizeof(struct sockaddr_in6); + memcpy(&proc->ipv6->lip, &rnd, proc->ipv6->lip_len); + + /* RIP = LIP + 1 */ + memcpy(&tmp, &proc->ipv6->lip, proc->ipv6->rip_len); + + bignum_add(SA_IN6_U8_P(&tmp), sizeof(struct in6_addr), 1); + + /* check if it exists in the hash table */ + if (ip_lease_exists(s, &tmp, sizeof(struct sockaddr_in6)) != 0) + continue; + + proc->ipv6->rip_len = sizeof(struct sockaddr_in6); + memcpy(&proc->ipv6->rip, &tmp, proc->ipv6->rip_len); + + /* mask the last IP with the netmask */ + for (i=0;iipv6->lip, (void*)&proc->ipv6->rip) == 0) + break; + } while(1); + } + + return 0; +fail: + free(proc->ipv6); + proc->ipv6 = NULL; + + return ret; + +} + +int get_ip_leases(main_server_st* s, struct proc_st* proc) +{ +int ret; + + ret = get_ipv4_lease(s, proc); + if (ret < 0) + return ret; + + ret = get_ipv6_lease(s, proc); + if (ret < 0) + return ret; + + if (proc->ipv4) + htable_add(&s->ip_leases.ht, rehash(proc->ipv4, NULL), proc->ipv4); + + if (proc->ipv6) + htable_add(&s->ip_leases.ht, rehash(proc->ipv6, NULL), proc->ipv6); + + if (proc->ipv4 == 0 && proc->ipv6 == 0) { + mslog(s, NULL, LOG_ERR, "No IPv4 or IPv6 addresses are configured. Cannot obtain lease.\n"); + return -1; + } + + return 0; +} + +void remove_ip_leases(main_server_st* s, struct proc_st* proc) +{ + if (proc->ipv4) { + htable_del(&s->ip_leases.ht, rehash(proc->ipv4, NULL), proc->ipv4); + free(proc->ipv4); + proc->ipv4 = NULL; + } + if (proc->ipv6) { + htable_del(&s->ip_leases.ht, rehash(proc->ipv6, NULL), proc->ipv6); + free(proc->ipv6); + proc->ipv6 = NULL; + } +} diff --git a/src/ip-lease.h b/src/ip-lease.h new file mode 100644 index 00000000..a9634974 --- /dev/null +++ b/src/ip-lease.h @@ -0,0 +1,24 @@ +#ifndef IP_LEASE_H +# define IP_LEASE_H + +#include +#include +#include +#include +#include + +struct ip_lease_st { + struct sockaddr_storage rip; + socklen_t rip_len; + + struct sockaddr_storage lip; + socklen_t lip_len; +}; + +void ip_lease_deinit(struct ip_lease_db_st* db); +void ip_lease_init(struct ip_lease_db_st* db); + +int get_ip_leases(struct main_server_st* s, struct proc_st* proc); +void remove_ip_leases(struct main_server_st* s, struct proc_st* proc); + +#endif diff --git a/src/ipc.h b/src/ipc.h index e2becee3..2e33b525 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -87,8 +87,7 @@ struct __attribute__ ((__packed__)) cmd_auth_reply_st { char vname[IFNAMSIZ]; /* interface name */ char user[MAX_USERNAME_SIZE]; - uint8_t routes_size; /* up to MAX_ROUTES */ - /* routes_size routes of cmd_auth_reply_route_st follow */ + /* additional data follow */ } ok; /* in case of REP_AUTH_MSG */ char msg[MAX_MSG_SIZE]; diff --git a/src/main-auth.c b/src/main-auth.c index 9d7bc136..47b607d1 100644 --- a/src/main-auth.c +++ b/src/main-auth.c @@ -61,8 +61,103 @@ void main_auth_init(main_server_st *s) } } +static int send_str_value_length(main_server_st* s, struct proc_st* proc, char* data) +{ + uint8_t len; + int ret; + + if (data != NULL) { + len = strlen(data); + ret = force_write(proc->fd, &len, 1); + if (ret < 0) + return ret; + + ret = force_write(proc->fd, proc->config.ipv4_dns, len); + if (ret < 0) + return ret; + } else { + len = 0; + ret = force_write(proc->fd, &len, 1); + if (ret < 0) + return ret; + } + + return 0; +} + +static +int serialize_additional_data(main_server_st* s, struct proc_st* proc) +{ +int ret; +unsigned i; +uint8_t len; + + /* IPv4 DNS */ + if (proc->config.ipv4_dns) + mslog(s, proc, LOG_DEBUG, "sending DNS '%s'", proc->config.ipv4_dns); + ret = send_str_value_length(s, proc, proc->config.ipv4_dns); + if (ret < 0) + return ret; + + /* IPv6 DNS */ + if (proc->config.ipv6_dns) + mslog(s, proc, LOG_DEBUG, "sending DNS '%s'", proc->config.ipv6_dns); + ret = send_str_value_length(s, proc, proc->config.ipv6_dns); + if (ret < 0) + return ret; + + /* IPv4 NBNS */ + if (proc->config.ipv4_nbns) + mslog(s, proc, LOG_DEBUG, "sending NBNS '%s'", proc->config.ipv4_nbns); + ret = send_str_value_length(s, proc, proc->config.ipv4_nbns); + if (ret < 0) + return ret; + + /* IPv6 NBNS */ + if (proc->config.ipv6_nbns) + mslog(s, proc, LOG_DEBUG, "sending NBNS '%s'", proc->config.ipv6_nbns); + ret = send_str_value_length(s, proc, proc->config.ipv6_nbns); + if (ret < 0) + return ret; + + /* IPv4 netmask */ + if (proc->config.ipv4_netmask) + mslog(s, proc, LOG_DEBUG, "sending netmask '%s'", proc->config.ipv4_netmask); + ret = send_str_value_length(s, proc, proc->config.ipv4_netmask); + if (ret < 0) + return ret; + + /* IPv6 netmask */ + if (proc->config.ipv6_netmask) + mslog(s, proc, LOG_DEBUG, "sending netmask '%s'", proc->config.ipv6_netmask); + ret = send_str_value_length(s, proc, proc->config.ipv6_netmask); + if (ret < 0) + return ret; + + /* routes */ + len = proc->config.routes_size; + ret = force_write(proc->fd, &len, 1); + if (ret < 0) + return ret; + + for (i=0;iconfig.routes_size;i++) { + len = strlen(proc->config.routes[i]); + + mslog(s, proc, LOG_DEBUG, "sending route '%s'", proc->config.routes[i]); + ret = force_write(proc->fd, &len, 1); + if (ret < 0) + return ret; + + ret = force_write(proc->fd, proc->config.routes[i], len); + if (ret < 0) + return ret; + } + + return 0; +} + int send_auth_reply(main_server_st* s, struct proc_st* proc, - cmd_auth_reply_t r, struct group_cfg_st* cfg) + cmd_auth_reply_t r) { struct iovec iov[2]; uint8_t cmd[2]; @@ -73,12 +168,11 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc, } control_un; struct cmd_auth_reply_st resp; struct cmsghdr *cmptr; - unsigned i; int ret; - if (cfg->routes_size > MAX_ROUTES) { - mslog(s, proc, LOG_INFO, "Note that the routes sent to the client (%d) exceed the maximum allowed (%d). Truncating.", (int)cfg->routes_size, (int)MAX_ROUTES); - cfg->routes_size = MAX_ROUTES; + if (proc->config.routes_size > MAX_ROUTES) { + mslog(s, proc, LOG_INFO, "Note that the routes sent to the client (%d) exceed the maximum allowed (%d). Truncating.", (int)proc->config.routes_size, (int)MAX_ROUTES); + proc->config.routes_size = MAX_ROUTES; } memset(&control_un, 0, sizeof(control_un)); @@ -86,7 +180,7 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc, hdr.msg_iov = iov; - if (r == REP_AUTH_OK && proc->lease != NULL) { + if (r == REP_AUTH_OK && proc->tun_lease.name[0] != 0) { cmd[0] = AUTH_REP; iov[0].iov_base = cmd; @@ -96,15 +190,13 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc, resp.reply = r; memcpy(resp.data.ok.cookie, proc->cookie, COOKIE_SIZE); memcpy(resp.data.ok.session_id, proc->session_id, sizeof(resp.data.ok.session_id)); - memcpy(resp.data.ok.vname, proc->lease->name, sizeof(resp.data.ok.vname)); + memcpy(resp.data.ok.vname, proc->tun_lease.name, sizeof(resp.data.ok.vname)); memcpy(resp.data.ok.user, proc->username, sizeof(resp.data.ok.user)); iov[1].iov_base = &resp; iov[1].iov_len = sizeof(resp); hdr.msg_iovlen++; - resp.data.ok.routes_size = cfg->routes_size; - /* Send the tun fd */ hdr.msg_control = control_un.control; hdr.msg_controllen = sizeof(control_un.control); @@ -113,7 +205,7 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc, cmptr->cmsg_len = CMSG_LEN(sizeof(int)); cmptr->cmsg_level = SOL_SOCKET; cmptr->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmptr), &proc->lease->fd, sizeof(int)); + memcpy(CMSG_DATA(cmptr), &proc->tun_lease.fd, sizeof(int)); } else { cmd[0] = AUTH_REP; cmd[1] = REP_AUTH_FAILED; @@ -128,55 +220,11 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc, return ret; if (r == REP_AUTH_OK) { - uint8_t len; - - if (cfg->ipv4_dns) { - len = strlen(cfg->ipv4_dns); - mslog(s, proc, LOG_INFO, "sending DNS '%s' with size %d", cfg->ipv4_dns, len); - ret = force_write(proc->fd, &len, 1); - if (ret < 0) - return ret; - - ret = force_write(proc->fd, cfg->ipv4_dns, len); - if (ret < 0) - return ret; - } else { - len = 0; - ret = force_write(proc->fd, &len, 1); - if (ret < 0) - return ret; - } - - if (cfg->ipv6_dns) { - len = strlen(cfg->ipv6_dns); - mslog(s, proc, LOG_INFO, "sending DNS '%s' with size %d", cfg->ipv6_dns, len); - ret = force_write(proc->fd, &len, 1); - if (ret < 0) - return ret; - - ret = force_write(proc->fd, cfg->ipv6_dns, len); - if (ret < 0) - return ret; - } else { - len = 0; - ret = force_write(proc->fd, &len, 1); - if (ret < 0) - return ret; - } - - for (i=0;iroutes_size;i++) { - len = strlen(cfg->routes[i]); - - mslog(s, proc, LOG_INFO, "sending route '%s' with size %d", cfg->routes[i], len); - ret = force_write(proc->fd, &len, 1); - if (ret < 0) - return ret; - - ret = force_write(proc->fd, cfg->routes[i], len); - if (ret < 0) - return ret; - } + ret = serialize_additional_data(s, proc); + if (ret < 0) + return ret; } + return 0; } diff --git a/src/main-config.c b/src/main-config.c index 64ae2a8e..d1cce9b0 100644 --- a/src/main-config.c +++ b/src/main-config.c @@ -43,6 +43,12 @@ static struct cfg_options available_options[] = { { .name = "route", .type = OPTION_MULTI_LINE }, { .name = "ipv4-dns", .type = OPTION_STRING }, { .name = "ipv6-dns", .type = OPTION_STRING }, + { .name = "ipv4-nbns", .type = OPTION_STRING }, + { .name = "ipv6-nbns", .type = OPTION_STRING }, + { .name = "ipv4-network", .type = OPTION_STRING }, + { .name = "ipv6-network", .type = OPTION_STRING }, + { .name = "ipv4-netmask", .type = OPTION_STRING }, + { .name = "ipv6-netmask", .type = OPTION_STRING }, }; #define READ_RAW_MULTI_LINE(name, s_name, num) \ @@ -114,6 +120,12 @@ const tOptionValue* val, *prev; READ_RAW_STRING("ipv4-dns", config->ipv4_dns); READ_RAW_STRING("ipv6-dns", config->ipv6_dns); + READ_RAW_STRING("ipv4-nbns", config->ipv4_nbns); + READ_RAW_STRING("ipv6-nbns", config->ipv6_nbns); + READ_RAW_STRING("ipv4-network", config->ipv4_network); + READ_RAW_STRING("ipv6-network", config->ipv6_network); + READ_RAW_STRING("ipv4-netmask", config->ipv4_netmask); + READ_RAW_STRING("ipv6-netmask", config->ipv6_netmask); optionUnloadNested(pov); diff --git a/src/main-misc.c b/src/main-misc.c index bf7aa48f..1212cbe4 100644 --- a/src/main-misc.c +++ b/src/main-misc.c @@ -53,10 +53,10 @@ int fd, ret, e; struct ifreq ifr; const char* name; - if (proc->lease == NULL) + if (proc->tun_lease.name[0] == 0) return -1; - name = proc->lease->name; + name = proc->tun_lease.name; mslog(s, proc, LOG_DEBUG, "setting %s MTU to %u", name, mtu); fd = socket(AF_INET, SOCK_STREAM, 0); @@ -112,119 +112,12 @@ int send_udp_fd(main_server_st* s, struct proc_st * proc, int fd) return(sendmsg(proc->fd, &hdr, 0)); } -static void del_additional_config(struct group_cfg_st* config) -{ -unsigned i; - - for(i=0;iroutes_size;i++) { - free(config->routes[i]); - } - free(config->routes); - free(config->ipv4_dns); - free(config->ipv6_dns); -} - -static int read_additional_config(main_server_st* s, struct proc_st* proc, - struct group_cfg_st *config) -{ -struct group_cfg_st u_cfg; -struct group_cfg_st g_cfg; -char file[_POSIX_PATH_MAX]; -int ret; -unsigned i; - - memset(config, 0, sizeof(*config)); - - if (s->config->per_user_dir != NULL) { - snprintf(file, sizeof(file), "%s/%s", s->config->per_user_dir, proc->username); - - if (access(file, R_OK) == 0) { - mslog(s, proc, LOG_INFO, "Loading user configuration '%s'", file); - - ret = parse_group_cfg_file(s, file, &u_cfg); - if (ret < 0) - return ERR_READ_CONFIG; - - if (u_cfg.routes_size > 0) { - config->routes = u_cfg.routes; - config->routes_size = u_cfg.routes_size; - - u_cfg.routes = NULL; - u_cfg.routes_size = 0; - - } - config->ipv4_dns = u_cfg.ipv4_dns; - u_cfg.ipv4_dns = NULL; - - config->ipv6_dns = u_cfg.ipv6_dns; - u_cfg.ipv6_dns = NULL; - del_additional_config(&u_cfg); - - - } else - mslog(s, proc, LOG_INFO, "No user configuration for '%s'", proc->username); - } - - if (s->config->per_group_dir != NULL) { - snprintf(file, sizeof(file), "%s/%s", s->config->per_group_dir, proc->groupname); - - if (access(file, R_OK) == 0) { - mslog(s, proc, LOG_INFO, "Loading group configuration '%s'", file); - - ret = parse_group_cfg_file(s, file, &g_cfg); - if (ret < 0) - return ERR_READ_CONFIG; - - if (g_cfg.routes_size > 0) { - config->routes = realloc(config->routes, (config->routes_size+g_cfg.routes_size) * sizeof(config->routes[0])); - if (config->routes == NULL) - return ERR_MEM; - - for (i=0;iroutes[config->routes_size] = g_cfg.routes[i]; - g_cfg.routes[i] = NULL; - config->routes_size++; - } - } - - if (config->ipv4_dns == NULL) { - config->ipv4_dns = g_cfg.ipv4_dns; - g_cfg.ipv4_dns = NULL; - } - - if (config->ipv6_dns == NULL) { - config->ipv6_dns = g_cfg.ipv6_dns; - g_cfg.ipv6_dns = NULL; - } - - del_additional_config(&g_cfg); - - } else - mslog(s, proc, LOG_INFO, "No group configuration for '%s'", proc->groupname); - } - - return 0; -} - - int handle_script_exit(main_server_st *s, struct proc_st* proc, int code) { int ret; if (code == 0) { - struct group_cfg_st cfg; - - ret = read_additional_config(s, proc, &cfg); - if (ret < 0) { - mslog(s, proc, LOG_ERR, "error reading additional routes"); - ret = ERR_READ_ROUTES; - goto fail; - } - - ret = send_auth_reply(s, proc, REP_AUTH_OK, &cfg); - - del_additional_config(&cfg); - + ret = send_auth_reply(s, proc, REP_AUTH_OK); if (ret < 0) { mslog(s, proc, LOG_ERR, "could not send reply auth cmd."); ret = ERR_BAD_COMMAND; @@ -232,7 +125,7 @@ int ret; } } else { mslog(s, proc, LOG_INFO, "failed authentication attempt for user '%s'", proc->username); - ret = send_auth_reply( s, proc, REP_AUTH_FAILED, NULL); + ret = send_auth_reply( s, proc, REP_AUTH_FAILED); if (ret < 0) { mslog(s, proc, LOG_ERR, "could not send reply auth cmd."); ret = ERR_BAD_COMMAND; @@ -245,15 +138,141 @@ fail: /* we close the lease tun fd both on success and failure. * The parent doesn't need to keep the tunfd. */ - if (proc->lease) { - if (proc->lease->fd >= 0) - close(proc->lease->fd); - proc->lease->fd = -1; + if (proc->tun_lease.name[0] != 0) { + if (proc->tun_lease.fd >= 0) + close(proc->tun_lease.fd); + proc->tun_lease.fd = -1; } return ret; } +void del_additional_config(struct group_cfg_st* config) +{ +unsigned i; + + for(i=0;iroutes_size;i++) { + free(config->routes[i]); + } + free(config->routes); + free(config->ipv4_dns); + free(config->ipv6_dns); + free(config->ipv4_nbns); + free(config->ipv6_nbns); + free(config->ipv4_network); + free(config->ipv6_network); + free(config->ipv4_netmask); + free(config->ipv6_netmask); +} + +static int read_config_file(main_server_st* s, struct proc_st* proc, const char* file, const char* type) +{ +struct group_cfg_st cfg; +int ret; +unsigned i; + + if (access(file, R_OK) == 0) { + mslog(s, proc, LOG_DEBUG, "Loading %s configuration '%s'", type, file); + + ret = parse_group_cfg_file(s, file, &cfg); + if (ret < 0) + return ERR_READ_CONFIG; + + if (cfg.routes_size > 0) { + if (proc->config.routes == NULL) { + proc->config.routes = cfg.routes; + proc->config.routes_size = cfg.routes_size; + + cfg.routes = NULL; + cfg.routes_size = 0; + } else { + proc->config.routes = realloc(proc->config.routes, (proc->config.routes_size + cfg.routes_size) * sizeof(proc->config.routes[0])); + if (proc->config.routes == NULL) + return ERR_MEM; + + for (i=0;iconfig.routes[proc->config.routes_size] = cfg.routes[i]; + cfg.routes[i] = NULL; + proc->config.routes_size++; + } + } + } + + if (proc->config.ipv4_dns == NULL) { + proc->config.ipv4_dns = cfg.ipv4_dns; + cfg.ipv4_dns = NULL; + } + + if (proc->config.ipv6_dns == NULL) { + proc->config.ipv6_dns = cfg.ipv6_dns; + cfg.ipv6_dns = NULL; + } + + if (proc->config.ipv4_nbns == NULL) { + proc->config.ipv4_nbns = cfg.ipv4_nbns; + cfg.ipv4_nbns = NULL; + } + + if (proc->config.ipv6_nbns == NULL) { + proc->config.ipv6_nbns = cfg.ipv6_nbns; + cfg.ipv6_nbns = NULL; + } + + if (proc->config.ipv4_network == NULL) { + proc->config.ipv4_network = cfg.ipv4_network; + cfg.ipv4_network = NULL; + } + + if (proc->config.ipv6_network == NULL) { + proc->config.ipv6_network = cfg.ipv6_network; + cfg.ipv6_network = NULL; + } + + if (proc->config.ipv4_netmask == NULL) { + proc->config.ipv4_netmask = cfg.ipv4_netmask; + cfg.ipv4_netmask = NULL; + } + + if (proc->config.ipv6_netmask == NULL) { + proc->config.ipv6_netmask = cfg.ipv6_netmask; + cfg.ipv6_netmask = NULL; + } + + del_additional_config(&cfg); + + } else + mslog(s, proc, LOG_DEBUG, "No %s configuration for '%s'", type, proc->username); + + return 0; +} + + +static int read_additional_config(struct main_server_st* s, struct proc_st* proc) +{ +char file[_POSIX_PATH_MAX]; +int ret; + + memset(&proc->config, 0, sizeof(proc->config)); + + if (s->config->per_user_dir != NULL) { + snprintf(file, sizeof(file), "%s/%s", s->config->per_user_dir, proc->username); + + ret = read_config_file(s, proc, file, "user"); + if (ret < 0) + return ret; + } + + if (s->config->per_group_dir != NULL) { + snprintf(file, sizeof(file), "%s/%s", s->config->per_group_dir, proc->groupname); + + ret = read_config_file(s, proc, file, "group"); + if (ret < 0) + return ret; + } + + return 0; +} + static int accept_user(main_server_st *s, struct proc_st* proc, unsigned cmd) { int ret; @@ -269,6 +288,12 @@ const char* group; return ret; } + ret = read_additional_config(s, proc); + if (ret < 0) { + mslog(s, proc, LOG_ERR, "error reading additional configuration"); + return ERR_READ_CONFIG; + } + ret = open_tun(s, proc); if (ret < 0) { return -1; diff --git a/src/main-resume.c b/src/main-resume.c index 57330bb0..18e86898 100644 --- a/src/main-resume.c +++ b/src/main-resume.c @@ -32,12 +32,12 @@ #include #include #include -#include #include "ipc.h" #include #include #include +#include #include int send_resume_fetch_reply(main_server_st* s, struct proc_st * proc, @@ -69,7 +69,7 @@ tls_cache_st* cache; struct htable_iter iter; size_t key; - key = hash_stable_8(req->session_id, req->session_id_size, 0); + key = hash_any(req->session_id, req->session_id_size, 0); cache = htable_firstval(&s->tls_db->ht, &iter, key); while(cache != NULL) { @@ -91,15 +91,6 @@ size_t key; return 0; } -static int ip_cmp(const struct sockaddr_storage *s1, const struct sockaddr_storage *s2, size_t n) -{ - if (((struct sockaddr*)s1)->sa_family == AF_INET) { - return memcmp(SA_IN_P(s1), SA_IN_P(s2), sizeof(struct in_addr)); - } else { /* inet6 */ - return memcmp(SA_IN6_P(s1), SA_IN6_P(s2), sizeof(struct in6_addr)); - } -} - int handle_resume_fetch_req(main_server_st* s, struct proc_st * proc, const struct cmd_resume_fetch_req_st * req, struct cmd_resume_fetch_reply_st * rep) @@ -110,7 +101,7 @@ size_t key; rep->reply = REP_RESUME_FAILED; - key = hash_stable_8(req->session_id, req->session_id_size, 0); + key = hash_any(req->session_id, req->session_id_size, 0); cache = htable_firstval(&s->tls_db->ht, &iter, key); while(cache != NULL) { @@ -155,7 +146,7 @@ unsigned int max; return -1; } - key = hash_stable_8(req->session_id, req->session_id_size, 0); + key = hash_any(req->session_id, req->session_id_size, 0); cache = malloc(sizeof(*cache)); if (cache == NULL) diff --git a/src/main-user.c b/src/main-user.c index 26986d2d..74de2057 100644 --- a/src/main-user.c +++ b/src/main-user.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -66,32 +67,32 @@ const char* script; char local[64]; char remote[64]; - if (proc->lease == NULL) + if (proc->ipv4 == NULL && proc->ipv6 == NULL) exit(1); if (getnameinfo((void*)&proc->remote_addr, proc->remote_addr_len, real, sizeof(real), NULL, 0, NI_NUMERICHOST) != 0) exit(1); - if (proc->lease->lip4_len > 0) { - if (getnameinfo((void*)&proc->lease->lip4, proc->lease->lip4_len, local, sizeof(local), NULL, 0, NI_NUMERICHOST) != 0) + if (proc->ipv4 && proc->ipv4->lip_len > 0) { + if (getnameinfo((void*)&proc->ipv4->lip, proc->ipv4->lip_len, local, sizeof(local), NULL, 0, NI_NUMERICHOST) != 0) exit(1); } else { - if (getnameinfo((void*)&proc->lease->lip6, proc->lease->lip6_len, local, sizeof(local), NULL, 0, NI_NUMERICHOST) != 0) + if (getnameinfo((void*)&proc->ipv6->lip, proc->ipv6->lip_len, local, sizeof(local), NULL, 0, NI_NUMERICHOST) != 0) exit(1); } - if (proc->lease->rip4_len > 0) { - if (getnameinfo((void*)&proc->lease->rip4, proc->lease->rip4_len, remote, sizeof(remote), NULL, 0, NI_NUMERICHOST) != 0) + if (proc->ipv4 && proc->ipv4->rip_len > 0) { + if (getnameinfo((void*)&proc->ipv4->rip, proc->ipv4->rip_len, remote, sizeof(remote), NULL, 0, NI_NUMERICHOST) != 0) exit(1); } else { - if (getnameinfo((void*)&proc->lease->rip6, proc->lease->rip6_len, remote, sizeof(remote), NULL, 0, NI_NUMERICHOST) != 0) + if (getnameinfo((void*)&proc->ipv6->rip, proc->ipv6->rip_len, remote, sizeof(remote), NULL, 0, NI_NUMERICHOST) != 0) exit(1); } setenv("USERNAME", proc->username, 1); setenv("GROUPNAME", proc->groupname, 1); setenv("HOSTNAME", proc->hostname, 1); - setenv("DEVICE", proc->lease->name, 1); + setenv("DEVICE", proc->tun_lease.name, 1); setenv("IP_REAL", real, 1); setenv("IP_LOCAL", local, 1); setenv("IP_REMOTE", remote, 1); @@ -134,7 +135,7 @@ add_utmp_entry(main_server_st *s, struct proc_st* proc) memset(&entry, 0, sizeof(entry)); entry.ut_type = USER_PROCESS; entry.ut_pid = proc->pid; - snprintf(entry.ut_line, sizeof(entry.ut_line), "%s", proc->lease->name); + snprintf(entry.ut_line, sizeof(entry.ut_line), "%s", proc->tun_lease.name); snprintf(entry.ut_user, sizeof(entry.ut_user), "%s", proc->username); #ifdef __linux__ if (proc->remote_addr_len == sizeof(struct sockaddr_in)) @@ -171,8 +172,8 @@ static void remove_utmp_entry(main_server_st *s, struct proc_st* proc) memset(&entry, 0, sizeof(entry)); entry.ut_type = DEAD_PROCESS; - if (proc->lease && proc->lease->name) - snprintf(entry.ut_line, sizeof(entry.ut_line), "%s", proc->lease->name); + if (proc->tun_lease.name[0] != 0) + snprintf(entry.ut_line, sizeof(entry.ut_line), "%s", proc->tun_lease.name); entry.ut_pid = proc->pid; setutxent(); diff --git a/src/main.c b/src/main.c index ecaad9ce..564df7a9 100644 --- a/src/main.c +++ b/src/main.c @@ -49,6 +49,7 @@ #include #include #include +#include #include int syslog_open = 0; @@ -302,11 +303,14 @@ static void remove_proc(main_server_st* s, struct proc_st *proc, unsigned k) proc->fd = -1; proc->pid = -1; + del_additional_config(&proc->config); + if (proc->auth_ctx != NULL) proc_auth_deinit(s, proc); - if (proc->lease) - proc->lease->in_use = 0; + if (proc->ipv4 || proc->ipv6) + remove_ip_leases(s, proc); + list_del(&proc->list); free(proc); s->active_clients--; @@ -459,6 +463,9 @@ void clear_lists(main_server_st *s) } tls_cache_deinit(s->tls_db); + + ip_lease_deinit(&s->ip_leases); + } static void kill_children(main_server_st* s) @@ -628,7 +635,6 @@ int main(int argc, char** argv) int fd, pid, e; struct listener_st *ltmp; struct proc_st *ctmp, *cpos; - struct tun_st tun; fd_set rd; int val, n = 0, ret, flags; struct timeval tv; @@ -643,9 +649,9 @@ int main(int argc, char** argv) list_head_init(&s.clist.head); list_head_init(&s.ban_list.head); list_head_init(&s.script_list.head); - tun_st_init(&tun); tls_cache_init(&s.tls_db); - + ip_lease_init(&s.ip_leases); + ocsignal(SIGINT, handle_term); ocsignal(SIGTERM, handle_term); ocsignal(SIGPIPE, SIG_IGN); @@ -677,7 +683,6 @@ int main(int argc, char** argv) } s.config = &config; - s.tun = &tun; main_auth_init(&s); diff --git a/src/main.h b/src/main.h index 1b1394cb..83881f72 100644 --- a/src/main.h +++ b/src/main.h @@ -55,7 +55,9 @@ struct proc_st { unsigned udp_fd_received; /* if the corresponding process has received a UDP fd */ /* the tun lease this process has */ - struct lease_st* lease; + struct tun_lease_st tun_lease; + struct ip_lease_st *ipv4; + struct ip_lease_st *ipv6; struct sockaddr_storage remote_addr; /* peer address */ socklen_t remote_addr_len; @@ -75,6 +77,13 @@ struct proc_st { void * auth_ctx; /* the context of authentication */ unsigned auth_status; /* PS_AUTH_ */ unsigned auth_reqs; /* the number of requests received */ + + struct group_cfg_st config; /* custom user/group config */ +}; + +struct ip_lease_db_st { + struct htable ht; + unsigned entries; }; struct proc_list_st { @@ -99,7 +108,9 @@ struct ban_list_st { typedef struct main_server_st { struct cfg_st *config; - struct tun_st *tun; + + struct ip_lease_db_st ip_leases; + hash_db_st *tls_db; uint8_t cookie_key[16]; @@ -157,7 +168,7 @@ int set_tun_mtu(main_server_st* s, struct proc_st * proc, unsigned mtu); int send_auth_reply_msg(main_server_st* s, struct proc_st* proc); int send_auth_reply(main_server_st* s, struct proc_st* proc, - cmd_auth_reply_t r, struct group_cfg_st*); + cmd_auth_reply_t r); int handle_auth_cookie_req(main_server_st* s, struct proc_st* proc, const struct cmd_auth_cookie_req_st * req); @@ -179,4 +190,6 @@ void run_sec_mod(main_server_st * s); int parse_group_cfg_file(main_server_st* s, const char* file, struct group_cfg_st *config); +void del_additional_config(struct group_cfg_st* config); + #endif diff --git a/src/ocserv-args.c b/src/ocserv-args.c index 25e3e8ac..33f8a7b3 100644 --- a/src/ocserv-args.c +++ b/src/ocserv-args.c @@ -2,7 +2,7 @@ * * DO NOT EDIT THIS FILE (ocserv-args.c) * - * It has been AutoGen-ed October 28, 2013 at 11:41:39 AM by AutoGen 5.17 + * It has been AutoGen-ed October 28, 2013 at 07:03:52 PM by AutoGen 5.17 * From the definitions ocserv-args.def * and the template file options * diff --git a/src/ocserv-args.def b/src/ocserv-args.def index 5b1da857..685957de 100644 --- a/src/ocserv-args.def +++ b/src/ocserv-args.def @@ -263,7 +263,8 @@ route = 192.168.5.0/255.255.255.0 # Configuration files that will be applied per user connection or # per group. Each file name on these directories must match the username # or the groupname. -# The options allow in such configuration files are ipv?-dns, and route. +# The options allow in such configuration files are ipv?-dns, ipv?-nbns, +# ipv?-network, ipv?-netmask and route. #config-per-user = /etc/ocserv/config-per-user/ #config-per-group = /etc/ocserv/config-per-group/ diff --git a/src/ocserv-args.h b/src/ocserv-args.h index 66a834f1..0d3559c0 100644 --- a/src/ocserv-args.h +++ b/src/ocserv-args.h @@ -2,7 +2,7 @@ * * DO NOT EDIT THIS FILE (ocserv-args.h) * - * It has been AutoGen-ed October 28, 2013 at 11:41:39 AM by AutoGen 5.17 + * It has been AutoGen-ed October 28, 2013 at 07:03:52 PM by AutoGen 5.17 * From the definitions ocserv-args.def * and the template file options * diff --git a/src/tun.c b/src/tun.c index 22009d1f..2b48e348 100644 --- a/src/tun.c +++ b/src/tun.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #ifdef __linux__ # include @@ -45,174 +45,7 @@ #include #include -/* INCREMENT taken from nettle's macros */ -/* Requires that size > 0 */ -#define INCREMENT(size, ctr) \ - do { \ - unsigned increment_i = (size) - 1; \ - if (++(ctr)[increment_i] == 0) \ - while (increment_i > 0 \ - && ++(ctr)[--increment_i] == 0 ) \ - ; \ - } while (0) - -static void bignum_add (uint8_t * num, unsigned size, unsigned step) -{ - register int i; - register unsigned tmp, y; - - for (i = size-1; i >= 0; i--) - { - tmp = num[i]; - - num[i] += step; - if (num[i] < tmp) - y = 1; - else - y = 0; - - if (y == 0) - break; - } -} - -static int get_avail_network_addresses(main_server_st* s, const struct lease_st *last4, - const struct lease_st *last6, struct lease_st* lease) -{ - struct sockaddr_storage tmp, mask, network; - unsigned i; - int ret, step; - - lease->rip4_len = 0; - lease->lip4_len = 0; - lease->rip6_len = 0; - lease->lip6_len = 0; - - memset(&tmp, 0, sizeof(tmp)); - - if (s->config->network.ipv4 && s->config->network.ipv4_netmask) { - ret = - inet_pton(AF_INET, s->config->network.ipv4, SA_IN_P(&network)); - - if (ret != 1) { - mslog(s, NULL, LOG_ERR, "Error reading IP: %s\n", s->config->network.ipv4); - return -1; - } - - ret = - inet_pton(AF_INET, s->config->network.ipv4_netmask, SA_IN_P(&mask)); - - if (ret != 1) { - mslog(s, NULL, LOG_ERR, "Error reading mask: %s\n", s->config->network.ipv4_netmask); - return -1; - } - - /* mask the network (just in case it is wrong) */ - for (i=0;isin_family = AF_INET; - - if (last4 != NULL) { - memcpy(&tmp, &last4->rip4, last4->rip4_len); - } else { - memcpy(&tmp, &network, sizeof(tmp)); - ((struct sockaddr_in*)&tmp)->sin_family = AF_INET; - } - - lease->lip4_len = sizeof(struct sockaddr_in); - memcpy(&lease->lip4, &tmp, sizeof(struct sockaddr_in)); - - step = 1; - do { - bignum_add(SA_IN_U8_P(&lease->lip4), sizeof(struct in_addr), step); - if (SA_IN_U8_P(&lease->lip4)[3] == 255) /* broadcast */ - bignum_add(SA_IN_U8_P(&lease->lip4), sizeof(struct in_addr), step); - - lease->rip4_len = sizeof(struct sockaddr_in); - memcpy(&lease->rip4, &lease->lip4, sizeof(struct sockaddr_in)); - bignum_add(SA_IN_U8_P(&lease->rip4), sizeof(struct in_addr), step); - - /* mask the last IP with the netmask */ - memcpy(&tmp, &lease->rip4, lease->rip4_len); - for (i=0;ilip4, (void*)&lease->rip4)) != 0); - } - - if (s->config->network.ipv6 && s->config->network.ipv6_netmask) { - ret = - inet_pton(AF_INET6, s->config->network.ipv6, SA_IN6_P(&network)); - - if (ret != 1) { - mslog(s, NULL, LOG_ERR, "Error reading IP: %s\n", s->config->network.ipv6); - return -1; - } - - ret = - inet_pton(AF_INET6, s->config->network.ipv6_netmask, SA_IN6_P(&mask)); - - if (ret != 1) { - mslog(s, NULL, LOG_ERR, "Error reading mask: %s\n", s->config->network.ipv6_netmask); - return -1; - } - - /* mask the network */ - for (i=0;isin6_family = AF_INET6; - - if (last6 != NULL) { - memcpy(&tmp, &last6->rip6, last6->rip6_len); - } else { - memcpy(&tmp, &network, sizeof(tmp)); - ((struct sockaddr_in6*)&tmp)->sin6_family = AF_INET6; - } - - lease->lip6_len = sizeof(struct sockaddr_in6); - memcpy(&lease->lip6, &tmp, sizeof(struct sockaddr_in6)); - - step = 1; - do { - bignum_add(SA_IN6_U8_P(&lease->lip6), sizeof(struct in6_addr), step); - - lease->rip6_len = last6->rip6_len; - memcpy(&lease->rip6, &lease->lip6, lease->rip6_len); - bignum_add(SA_IN6_U8_P(&lease->rip6), sizeof(struct in6_addr), step); - - /* mask the last IP with the netmask */ - memcpy(&tmp, &lease->rip6, lease->rip6_len); - for (i=0;ilip6, (void*)&lease->rip6)) != 0); - } - - if (lease->lip6_len == 0 && lease->lip4_len == 0) { - mslog(s, NULL, LOG_ERR, "No IPv4 or IPv6 addresses are configured. Cannot obtain lease.\n"); - return -1; - } - - lease->tun_nr = 0; - if (last4) - lease->tun_nr = MAX(lease->tun_nr, last4->tun_nr+1); - if (last6) - lease->tun_nr = MAX(lease->tun_nr, last6->tun_nr+1); - - return 0; -} - -static int set_network_info( main_server_st* s, const struct lease_st *lease) +static int set_network_info( main_server_st* s, struct proc_st* proc) { struct ifreq ifr; int fd, ret; @@ -221,54 +54,54 @@ static int set_network_info( main_server_st* s, const struct lease_st *lease) if (fd == -1) return -1; - if (lease->lip4_len > 0 && lease->rip4_len > 0) { + if (proc->ipv4 && proc->ipv4->lip_len > 0 && proc->ipv4->rip_len > 0) { memset(&ifr, 0, sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET; - snprintf(ifr.ifr_name, IFNAMSIZ, "%s", lease->name); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", proc->tun_lease.name); - memcpy(&ifr.ifr_addr, &lease->lip4, lease->lip4_len); + memcpy(&ifr.ifr_addr, &proc->ipv4->lip, proc->ipv4->lip_len); ret = ioctl(fd, SIOCSIFADDR, &ifr); if (ret != 0) { - mslog(s, NULL, LOG_ERR, "%s: Error setting IPv4.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Error setting IPv4.\n", proc->tun_lease.name); } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET; - snprintf(ifr.ifr_name, IFNAMSIZ, "%s", lease->name); - memcpy(&ifr.ifr_dstaddr, &lease->rip4, lease->rip4_len); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", proc->tun_lease.name); + memcpy(&ifr.ifr_dstaddr, &proc->ipv4->rip, proc->ipv4->rip_len); ret = ioctl(fd, SIOCSIFDSTADDR, &ifr); if (ret != 0) { - mslog(s, NULL, LOG_ERR, "%s: Error setting DST IPv4.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Error setting DST IPv4.\n", proc->tun_lease.name); } } - if (lease->lip6_len > 0 && lease->rip6_len > 0) { + if (proc->ipv6 && proc->ipv6->lip_len > 0 && proc->ipv6->rip_len > 0) { memset(&ifr, 0, sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET6; - snprintf(ifr.ifr_name, IFNAMSIZ, "%s", lease->name); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", proc->tun_lease.name); - memcpy(&ifr.ifr_addr, &lease->lip6, lease->lip6_len); + memcpy(&ifr.ifr_addr, &proc->ipv6->lip, proc->ipv6->lip_len); ret = ioctl(fd, SIOCSIFADDR, &ifr); if (ret != 0) { - mslog(s, NULL, LOG_ERR, "%s: Error setting IPv6.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Error setting IPv6.\n", proc->tun_lease.name); } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET6; - snprintf(ifr.ifr_name, IFNAMSIZ, "%s", lease->name); - memcpy(&ifr.ifr_dstaddr, &lease->rip6, lease->rip6_len); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", proc->tun_lease.name); + memcpy(&ifr.ifr_dstaddr, &proc->ipv6->rip, proc->ipv6->rip_len); ret = ioctl(fd, SIOCSIFDSTADDR, &ifr); if (ret != 0) { - mslog(s, NULL, LOG_ERR, "%s: Error setting DST IPv6.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Error setting DST IPv6.\n", proc->tun_lease.name); } } - if (lease->lip6_len == 0 && lease->lip4_len == 0) { - mslog(s, NULL, LOG_ERR, "%s: Could not set any IP.\n", lease->name); + if (proc->ipv6 == 0 && proc->ipv4 == 0) { + mslog(s, NULL, LOG_ERR, "%s: Could not set any IP.\n", proc->tun_lease.name); ret = -1; goto cleanup; } @@ -277,11 +110,11 @@ static int set_network_info( main_server_st* s, const struct lease_st *lease) memset(&ifr, 0, sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_flags |= IFF_UP; - snprintf(ifr.ifr_name, IFNAMSIZ, "%s", lease->name); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", proc->tun_lease.name); ret = ioctl(fd, SIOCSIFFLAGS, &ifr); if (ret != 0) { - mslog(s, NULL, LOG_ERR, "%s: Could not bring up interface.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Could not bring up interface.\n", proc->tun_lease.name); ret = -1; } @@ -290,63 +123,18 @@ cleanup: return ret; } +#include int open_tun(main_server_st* s, struct proc_st* proc) { int tunfd, ret, e; struct ifreq ifr; unsigned int t; - struct lease_st *lease = NULL; - struct lease_st *last4, *tmp; - struct lease_st *last6; - unsigned current = s->tun->total; - time_t now = time(0); - last4 = last6 = NULL; - - /* try to re-use an address */ - list_for_each(&s->tun->head, tmp, list) { - /* if the device isn't in use by the server and the IPs - * are free. */ - if (tmp->in_use == 0 && (tmp->available_at < now || (proc->username[0] != 0 && strcmp(proc->username, tmp->username) == 0))) { - if ((tmp->lip6_len != 0 && icmp_ping6(s, (void*)&tmp->lip6, (void*)&tmp->rip6) == 0) || - (tmp->lip4_len != 0 && icmp_ping4(s, (void*)&tmp->lip4, (void*)&tmp->rip4) == 0)) { - lease = tmp; - mslog(s, NULL, LOG_INFO, "reusing tun device %s\n", lease->name); - break; - } - } - } - - /* nothing to re-use */ - if (lease == NULL) { - lease = calloc(1, sizeof(*lease)); - if (lease == NULL) - return -1; - - list_for_each_rev(&s->tun->head, tmp, list) { - if (last4 == NULL && tmp->rip4_len > 0) - last4 = tmp; - - if (last6 == NULL && tmp->rip6_len > 0) - last6 = tmp; - - if (last4 && last6) - break; - } - - ret = get_avail_network_addresses(s, last4, last6, lease); - if (ret < 0) { - free(lease); - return -1; - } - - /* Add into the list */ - list_add_tail( &s->tun->head, &lease->list); - snprintf(lease->name, sizeof(lease->name), "%s%u", s->config->network.name, current); - snprintf(lease->username, sizeof(lease->username), "%s", proc->username); - s->tun->total++; - } + ret = get_ip_leases(s, proc); + if (ret < 0) + return ret; + snprintf(proc->tun_lease.name, sizeof(proc->tun_lease.name), "%s%%d", s->config->network.name); /* No need to free the lease after this point. */ @@ -366,21 +154,24 @@ int open_tun(main_server_st* s, struct proc_st* proc) memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; - memcpy(ifr.ifr_name, lease->name, IFNAMSIZ); + memcpy(ifr.ifr_name, proc->tun_lease.name, IFNAMSIZ); if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0) { e = errno; - mslog(s, NULL, LOG_ERR, "%s: TUNSETIFF: %s\n", lease->name, strerror(e)); + mslog(s, NULL, LOG_ERR, "%s: TUNSETIFF: %s\n", proc->tun_lease.name, strerror(e)); goto fail; } - memcpy(lease->name, ifr.ifr_name, IFNAMSIZ); - mslog(s, NULL, LOG_INFO, "assigning tun device %s\n", lease->name); + memcpy(proc->tun_lease.name, ifr.ifr_name, IFNAMSIZ); + mslog(s, NULL, LOG_INFO, "assigning tun device %s\n", proc->tun_lease.name); +# if 0 + /* we no longer use persistent tun */ if (ioctl(tunfd, TUNSETPERSIST, (void *)0) < 0) { e = errno; - mslog(s, NULL, LOG_ERR, "%s: TUNSETPERSIST: %s\n", lease->name, strerror(e)); + mslog(s, NULL, LOG_ERR, "%s: TUNSETPERSIST: %s\n", proc->tun_lease.name, strerror(e)); goto fail; } +# endif if (s->config->uid != -1) { t = s->config->uid; @@ -388,7 +179,7 @@ int open_tun(main_server_st* s, struct proc_st* proc) if (ret < 0) { e = errno; mslog(s, NULL, LOG_INFO, "%s: TUNSETOWNER: %s\n", - lease->name, strerror(e)); + proc->tun_lease.name, strerror(e)); goto fail; } } @@ -400,7 +191,7 @@ int open_tun(main_server_st* s, struct proc_st* proc) if (ret < 0) { e = errno; mslog(s, NULL, LOG_ERR, "%s: TUNSETGROUP: %s\n", - lease->name, strerror(e)); + proc->tun_lease.name, strerror(e)); goto fail; } } @@ -425,22 +216,19 @@ int open_tun(main_server_st* s, struct proc_st* proc) goto fail; } - snprintf(lease->name, sizeof(lease->name), "%s", devname(st.st_rdev, S_IFCHR)); + snprintf(proc->tun_lease.name, sizeof(proc->tun_lease.name), "%s", devname(st.st_rdev, S_IFCHR)); } - + set_cloexec_flag (tunfd, 1); #endif /* set IP/mask */ - ret = set_network_info(s, lease); + ret = set_network_info(s, proc); if (ret < 0) { goto fail; } - lease->in_use = 1; - lease->available_at = now + s->config->cookie_validity; - lease->fd = tunfd; - proc->lease = lease; + proc->tun_lease.fd = tunfd; return 0; fail: diff --git a/src/tun.h b/src/tun.h index 946bee71..5b1be585 100644 --- a/src/tun.h +++ b/src/tun.h @@ -5,50 +5,12 @@ #include #include -struct lease_st { - struct list_node list; +struct tun_lease_st { char name[IFNAMSIZ]; - char username[MAX_USERNAME_SIZE]; /* owner */ - unsigned int tun_nr; - unsigned int in_use; - time_t available_at; /* when it will be available */ - struct sockaddr_storage rip4; - socklen_t rip4_len; - - struct sockaddr_storage lip4; - socklen_t lip4_len; - - struct sockaddr_storage rip6; - socklen_t rip6_len; - - struct sockaddr_storage lip6; - socklen_t lip6_len; - /* this is used temporarily. */ int fd; }; -struct tun_st { - struct list_head head; - unsigned total; -}; - -inline static void tun_st_init(struct tun_st* ts) -{ - memset(ts, 0, sizeof(*ts)); - list_head_init(&ts->head); -} - -inline static void tun_st_deinit(struct tun_st* ts) -{ - struct lease_st *ltmp, *pos; - - list_for_each_safe(&ts->head, ltmp, pos, list) { - list_del(<mp->list); - ts->total--; - } -} - #endif diff --git a/src/vpn.h b/src/vpn.h index fc47ceda..c2a550c1 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -47,8 +47,8 @@ extern int syslog_open; #define ERR_AUTH_CONTINUE -4 #define ERR_WAIT_FOR_SCRIPT -5 #define ERR_MEM -6 -#define ERR_READ_ROUTES -7 -#define ERR_READ_CONFIG -8 +#define ERR_READ_CONFIG -7 +#define ERR_NO_IP -8 typedef struct { @@ -62,6 +62,12 @@ struct group_cfg_st { char *ipv4_dns; char *ipv6_dns; + char *ipv4_nbns; + char *ipv6_nbns; + char *ipv4_network; + char *ipv6_network; + char *ipv4_netmask; + char *ipv6_netmask; }; struct vpn_st { diff --git a/src/worker-auth.c b/src/worker-auth.c index d1d8e7c5..4d0be27f 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -235,10 +235,105 @@ static int send_auth_cookie_req(int fd, const struct cmd_auth_cookie_req_st* r) return(sendmsg(fd, &hdr, 0)); } +static int read_str_value_length(worker_st *ws, char** res) +{ +int ret; +uint8_t len; + + ret = force_read(ws->cmd_fd, &len, 1); + if (ret != 1) { + oclog(ws, LOG_ERR, "Error receiving length-value from main (%d)", ret); + return ERR_BAD_COMMAND; + } + + if (len > 0) { + *res = malloc(((int)len)+1); + if (*res == NULL) + return ERR_MEM; + + ret = force_read(ws->cmd_fd, *res, len); + if (ret != len) { + oclog(ws, LOG_ERR, "Error receiving value from main (%d)", ret); + return ERR_BAD_COMMAND; + } + (*res)[len] = 0; + } + + return 0; +} + +static +int deserialize_additional_data(worker_st* ws) +{ +int ret; +uint8_t len; +unsigned i; + + /* IPV4 DNS */ + ret = read_str_value_length(ws, &ws->ipv4_dns); + if (ret < 0) + return ret; + + /* IPV6 DNS */ + ret = read_str_value_length(ws, &ws->ipv6_dns); + if (ret < 0) + return ret; + + /* IPV4 NBNS */ + ret = read_str_value_length(ws, &ws->ipv4_nbns); + if (ret < 0) + return ret; + + /* IPV6 NBNS */ + ret = read_str_value_length(ws, &ws->ipv6_nbns); + if (ret < 0) + return ret; + + /* IPV4 netmask */ + ret = read_str_value_length(ws, &ws->ipv4_netmask); + if (ret < 0) + return ret; + + /* IPV6 netmask */ + ret = read_str_value_length(ws, &ws->ipv6_netmask); + if (ret < 0) + return ret; + + /* number of routes */ + ret = force_read(ws->cmd_fd, &len, 1); + if (ret != 1) { + oclog(ws, LOG_ERR, "Error receiving length-value from main (%d)", ret); + return ERR_BAD_COMMAND; + } + ws->routes_size = len; + + /* routes */ + for (i=0;iroutes_size;i++) { + ret = force_read(ws->cmd_fd, &ws->routes[i].size, 1); + if (ret != 1) { + oclog(ws, LOG_ERR, "Error received route size from main (%d)", ret); + return ERR_BAD_COMMAND; + } + + ws->routes[i].route = malloc(ws->routes[i].size+1); + if (ws->routes[i].route == NULL) + return ERR_MEM; + + ret = force_read(ws->cmd_fd, ws->routes[i].route, ws->routes[i].size); + if (ret != ws->routes[i].size) { + oclog(ws, LOG_ERR, "Error received routes from main (%d)", ret); + return ERR_BAD_COMMAND; + } + ws->routes[i].route[ws->routes[i].size] = 0; + } + + return 0; +} + static int recv_auth_reply(worker_st *ws, struct cmd_auth_reply_st *resp) { struct iovec iov[2]; - uint8_t cmd = 0, len; + uint8_t cmd = 0; struct msghdr hdr; int ret, cmdlen; union { @@ -246,7 +341,6 @@ static int recv_auth_reply(worker_st *ws, struct cmd_auth_reply_st *resp) char control[CMSG_SPACE(sizeof(int))]; } control_un; struct cmsghdr *cmptr; - unsigned i; iov[0].iov_base = &cmd; iov[0].iov_len = 1; @@ -295,69 +389,11 @@ static int recv_auth_reply(worker_st *ws, struct cmd_auth_reply_st *resp) memcpy(ws->cookie, resp->data.ok.cookie, sizeof(ws->cookie)); memcpy(ws->session_id, resp->data.ok.session_id, sizeof(ws->session_id)); - ws->routes_size = resp->data.ok.routes_size; - /* Read any additional data */ - /* IPV4 DNS */ - ret = force_read(ws->cmd_fd, &len, 1); - if (ret != 1) { - oclog(ws, LOG_ERR, "Error received route size from main (%d)", ret); - return ERR_BAD_COMMAND; - } - - if (len > 0) { - ws->ipv4_dns = malloc(len+1); - if (ws->ipv4_dns == NULL) - return ERR_MEM; - - ret = force_read(ws->cmd_fd, ws->ipv4_dns, len); - if (ret != len) { - oclog(ws, LOG_ERR, "Error received routes from main (%d)", ret); - return ERR_BAD_COMMAND; - } - ws->ipv4_dns[len] = 0; - } - - /* IPV6 DNS */ - ret = force_read(ws->cmd_fd, &len, 1); - if (ret != 1) { - oclog(ws, LOG_ERR, "Error received route size from main (%d)", ret); - return ERR_BAD_COMMAND; - } - - if (len > 0) { - ws->ipv6_dns = malloc(len+1); - if (ws->ipv6_dns == NULL) - return ERR_MEM; - - ret = force_read(ws->cmd_fd, ws->ipv6_dns, len); - if (ret != len) { - oclog(ws, LOG_ERR, "Error received routes from main (%d)", ret); - return ERR_BAD_COMMAND; - } - ws->ipv6_dns[len] = 0; - } - - /* routes */ - for (i=0;iroutes_size;i++) { - ret = force_read(ws->cmd_fd, &ws->routes[i].size, 1); - if (ret != 1) { - oclog(ws, LOG_ERR, "Error received route size from main (%d)", ret); - return ERR_BAD_COMMAND; - } - ws->routes[i].route = malloc(ws->routes[i].size+1); - if (ws->routes[i].route == NULL) - return ERR_MEM; - - ret = force_read(ws->cmd_fd, ws->routes[i].route, ws->routes[i].size); - if (ret != ws->routes[i].size) { - oclog(ws, LOG_ERR, "Error received routes from main (%d)", ret); - return ERR_BAD_COMMAND; - } - ws->routes[i].route[ws->routes[i].size] = 0; - - } + ret = deserialize_additional_data(ws); + if (ret < 0) + return ret; } else return ERR_AUTH_FAIL; diff --git a/src/worker-tun.c b/src/worker-tun.c index 6dc12f91..79ce534d 100644 --- a/src/worker-tun.c +++ b/src/worker-tun.c @@ -157,22 +157,47 @@ struct ifreq ifr; oclog(ws, LOG_DEBUG, "cannot obtain IPv4 local IP for %s", vinfo->name); #define LOCAL "local" - if (ws->config->network.ipv4_dns && strcmp(ws->config->network.ipv4_dns, LOCAL) == 0) + if (ws->ipv4_dns != NULL) + vinfo->ipv4_dns = ws->ipv4_dns; + else if (ws->config->network.ipv4_dns && strcmp(ws->config->network.ipv4_dns, LOCAL) == 0) vinfo->ipv4_dns = vinfo->ipv4_local; else vinfo->ipv4_dns = ws->config->network.ipv4_dns; - if (ws->config->network.ipv6_dns && strcmp(ws->config->network.ipv6_dns, LOCAL) == 0) + if (ws->ipv6_dns != NULL) + vinfo->ipv6_dns = ws->ipv6_dns; + else if (ws->config->network.ipv6_dns && strcmp(ws->config->network.ipv6_dns, LOCAL) == 0) vinfo->ipv6_dns = vinfo->ipv6_local; else vinfo->ipv6_dns = ws->config->network.ipv6_dns; + if (ws->ipv4_nbns != NULL) + vinfo->ipv4_nbns = ws->ipv4_nbns; + else if (ws->config->network.ipv4_nbns && strcmp(ws->config->network.ipv4_nbns, LOCAL) == 0) + vinfo->ipv4_nbns = vinfo->ipv4_local; + else + vinfo->ipv4_nbns = ws->config->network.ipv4_nbns; + + if (ws->ipv6_nbns != NULL) + vinfo->ipv6_nbns = ws->ipv6_nbns; + else if (ws->config->network.ipv6_nbns && strcmp(ws->config->network.ipv6_nbns, LOCAL) == 0) + vinfo->ipv6_nbns = vinfo->ipv6_local; + else + vinfo->ipv6_nbns = ws->config->network.ipv6_nbns; + vinfo->routes_size = ws->config->network.routes_size; if (ws->config->network.routes_size > 0) vinfo->routes = ws->config->network.routes; - vinfo->ipv4_netmask = ws->config->network.ipv4_netmask; - vinfo->ipv6_netmask = ws->config->network.ipv6_netmask; + if (ws->ipv4_netmask != NULL) + vinfo->ipv4_netmask = ws->ipv4_netmask; + else + vinfo->ipv4_netmask = ws->config->network.ipv4_netmask; + + if (ws->ipv6_netmask != NULL) + vinfo->ipv6_netmask = ws->ipv6_netmask; + else + vinfo->ipv6_netmask = ws->config->network.ipv6_netmask; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET; diff --git a/src/worker-vpn.c b/src/worker-vpn.c index 6d0a5a2a..317c0636 100644 --- a/src/worker-vpn.c +++ b/src/worker-vpn.c @@ -854,13 +854,11 @@ socklen_t sl; SEND_ERR(ret); } - if (ws->ipv4_dns) { - ret = tls_printf(ws->session, "X-CSTP-DNS: %s\r\n", ws->ipv4_dns); - SEND_ERR(ret); - } else if (vinfo.ipv4_dns) { + if (vinfo.ipv4_dns) { ret = tls_printf(ws->session, "X-CSTP-DNS: %s\r\n", vinfo.ipv4_dns); SEND_ERR(ret); } + if (vinfo.ipv4_nbns) { ret = tls_printf(ws->session, "X-CSTP-NBNS: %s\r\n", vinfo.ipv4_nbns); SEND_ERR(ret); @@ -877,19 +875,18 @@ socklen_t sl; SEND_ERR(ret); } - if (ws->ipv6_dns) { - ret = tls_printf(ws->session, "X-CSTP-DNS: %s\r\n", ws->ipv6_dns); - SEND_ERR(ret); - } else if (vinfo.ipv6_dns) { + if (vinfo.ipv6_dns) { ret = tls_printf(ws->session, "X-CSTP-DNS: %s\r\n", vinfo.ipv6_dns); SEND_ERR(ret); } + if (vinfo.ipv6_nbns) { ret = tls_printf(ws->session, "X-CSTP-NBNS: %s\r\n", vinfo.ipv6_nbns); SEND_ERR(ret); } } + for (i=0;ino_ipv6 != 0 && strchr(vinfo.routes[i], ':') != 0) continue; diff --git a/src/worker.h b/src/worker.h index 59b91c21..b6ffc15f 100644 --- a/src/worker.h +++ b/src/worker.h @@ -121,6 +121,10 @@ typedef struct worker_st { /* additional data - received per user or per group */ char *ipv4_dns; char *ipv6_dns; + char *ipv4_nbns; + char *ipv6_nbns; + char *ipv4_netmask; + char *ipv6_netmask; unsigned routes_size; struct route_st routes[MAX_ROUTES];