diff --git a/TODO b/TODO index 0b86432b..6534bed1 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,3 @@ -* Use a single UDP port * Add a simple username/password back-end * Certificate authentication to the main process (can it be done without moving the TLS handshake over the main thread?) diff --git a/src/config.c b/src/config.c index bfdb7f2e..8fc38daa 100644 --- a/src/config.c +++ b/src/config.c @@ -125,6 +125,7 @@ unsigned j; READ_STRING("listen-host", config->name, 0); READ_NUMERIC("tcp-port", config->port, 1); + READ_NUMERIC("udp-port", config->udp_port, 0); READ_NUMERIC("keepalive", config->keepalive, 0); READ_STRING("server-cert", config->cert, 1); @@ -199,6 +200,9 @@ static void check_cfg( struct cfg_st *config) if (config->keepalive == 0) config->keepalive = 30; + + if (config->udp_port == 0) + config->udp_port = config->port; if (config->priorities == NULL) config->priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT"; diff --git a/src/ipc.h b/src/ipc.h index 9182f130..73c81345 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -18,6 +18,7 @@ typedef enum { RESUME_DELETE_REQ, RESUME_FETCH_REQ, RESUME_FETCH_REP, + CMD_UDP_FD, CMD_TERMINATE, } cmd_request_t; diff --git a/src/log.c b/src/log.c index 4993ecb8..428e34f1 100644 --- a/src/log.c +++ b/src/log.c @@ -92,3 +92,35 @@ void __attribute__ ((format(printf, 3, 4))) return; } + +/* proc is optional */ +void __attribute__ ((format(printf, 4, 5))) + mslog(const main_server_st * s, const struct proc_st* proc, + int priority, const char *fmt, ...) +{ + char buf[1024]; + char ipbuf[128]; + const char* ip = NULL; + va_list args; + + if (priority == LOG_DEBUG && s->config->debug == 0) + return; + + if (proc) { + ip = human_addr((void*)&proc->remote_addr, proc->remote_addr_len, + ipbuf, sizeof(ipbuf)); + } + + buf[1023] = 0; + + va_start(args, fmt); + vsnprintf(buf, 1023, fmt, args); + va_end(args); + + if (ip) + syslog(priority, "%s %s", ip, buf); + else + syslog(priority, "%s", buf); + + return; +} diff --git a/src/main-auth.c b/src/main-auth.c index 278a0e12..501f02b8 100644 --- a/src/main-auth.c +++ b/src/main-auth.c @@ -73,7 +73,7 @@ static int send_auth_reply(main_server_st* s, struct proc_st* proc, hdr.msg_iovlen++; iov[2].iov_base = proc->session_id; - iov[2].iov_len = sizeof(proc->session_id); + iov[2].iov_len = proc->session_id_size; hdr.msg_iovlen++; iov[3].iov_base = lease->name; @@ -114,8 +114,9 @@ struct stored_cookie_st sc; memcpy(proc->cookie, req->cookie, sizeof(proc->cookie)); memcpy(proc->username, sc.username, sizeof(proc->username)); memcpy(proc->session_id, sc.session_id, sizeof(proc->session_id)); + proc->session_id_size = sizeof(proc->session_id); - ret = open_tun(s->config, s->tun, lease); + ret = open_tun(s, lease); if (ret < 0) ret = -1; /* sorry */ @@ -134,6 +135,7 @@ struct stored_cookie_st *sc; ret = gnutls_rnd(GNUTLS_RND_NONCE, proc->session_id, sizeof(proc->session_id)); if (ret < 0) return -2; + proc->session_id_size = sizeof( proc->session_id); sc = calloc(1, sizeof(*sc)); if (sc == NULL) @@ -180,7 +182,7 @@ unsigned username_set = 0; memcpy(proc->username, req->cert_user, MAX_USERNAME_SIZE); else { if (strcmp(proc->username, req->cert_user) != 0) { - syslog(LOG_INFO, "User '%s' presented a certificate from user '%s'", proc->username, req->cert_user); + mslog(s, proc, LOG_INFO, "User '%s' presented a certificate from user '%s'", proc->username, req->cert_user); ret = -1; } } @@ -190,7 +192,7 @@ unsigned username_set = 0; if (req->hostname[0] != 0) memcpy(proc->hostname, req->hostname, MAX_HOSTNAME_SIZE); - ret = open_tun(s->config, s->tun, lease); + ret = open_tun(s, lease); if (ret < 0) ret = -1; /* sorry */ } @@ -232,7 +234,7 @@ int handle_commands(main_server_st *s, struct proc_st* proc) ret = recvmsg( proc->fd, &hdr, 0); if (ret == -1) { e = errno; - syslog(LOG_ERR, "Cannot obtain data from command socket (pid: %d, peer: %s): %s", proc->pid, peer_ip, strerror(e)); + mslog(s, proc, LOG_ERR, "Cannot obtain data from command socket (pid: %d, peer: %s): %s", proc->pid, peer_ip, strerror(e)); return -1; } @@ -245,24 +247,24 @@ int handle_commands(main_server_st *s, struct proc_st* proc) switch(cmd) { case RESUME_STORE_REQ: if (cmd_data_len <= sizeof(cmd_data.sresume)-MAX_SESSION_DATA_SIZE) { - syslog(LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip); return -2; } ret = handle_resume_store_req(s, proc, &cmd_data.sresume); if (ret < 0) { - syslog(LOG_DEBUG, "Could not store resumption data (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_DEBUG, "Could not store resumption data (pid: %d, peer: %s).", proc->pid, peer_ip); } break; case RESUME_DELETE_REQ: if (cmd_data_len != sizeof(cmd_data.fresume)) { - syslog(LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip); return -2; } ret = handle_resume_delete_req(s, proc, &cmd_data.fresume); if (ret < 0) { - syslog(LOG_DEBUG, "Could not delete resumption data (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_DEBUG, "Could not delete resumption data (pid: %d, peer: %s).", proc->pid, peer_ip); } break; @@ -270,19 +272,19 @@ int handle_commands(main_server_st *s, struct proc_st* proc) struct cmd_resume_fetch_reply_st reply; if (cmd_data_len != sizeof(cmd_data.fresume)) { - syslog(LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip); return -2; } ret = handle_resume_fetch_req(s, proc, &cmd_data.fresume, &reply); if (ret < 0) { - syslog(LOG_DEBUG, "Could not fetch resumption data (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_DEBUG, "Could not fetch resumption data (pid: %d, peer: %s).", proc->pid, peer_ip); ret = send_resume_fetch_reply(s, proc, REP_RESUME_FAILED, NULL); } else ret = send_resume_fetch_reply(s, proc, REP_RESUME_OK, &reply); } if (ret < 0) { - syslog(LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip); return -2; } @@ -293,14 +295,14 @@ int handle_commands(main_server_st *s, struct proc_st* proc) if (cmd == AUTH_REQ) { if (cmd_data_len != sizeof(cmd_data.auth)) { - syslog(LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip); return -2; } ret = handle_auth_req(s, proc, &cmd_data.auth, &lease); } else { if (cmd_data_len != sizeof(cmd_data.cauth)) { - syslog(LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip); return -2; } @@ -310,7 +312,7 @@ int handle_commands(main_server_st *s, struct proc_st* proc) if (ret == 0) { ret = user_connected(s, proc, lease); if (ret < 0) { - syslog(LOG_INFO, "User '%s' disconnected due to script", proc->username); + mslog(s, proc, LOG_INFO, "User '%s' disconnected due to script", proc->username); } } @@ -323,10 +325,10 @@ int handle_commands(main_server_st *s, struct proc_st* proc) } - syslog(LOG_INFO, "User '%s' authenticated", proc->username); + mslog(s, proc, LOG_INFO, "User '%s' authenticated", proc->username); ret = send_auth_reply(s, proc, REP_AUTH_OK, lease); if (ret < 0) { - syslog(LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip); return -2; } @@ -336,17 +338,17 @@ int handle_commands(main_server_st *s, struct proc_st* proc) close(lease->fd); lease->fd = -1; } else { - syslog(LOG_INFO, "Failed authentication attempt for user '%s'", proc->username); + mslog(s, proc, LOG_INFO, "Failed authentication attempt for user '%s'", proc->username); ret = send_auth_reply( s, proc, REP_AUTH_FAILED, NULL); if (ret < 0) { - syslog(LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip); + mslog(s, proc, LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip); return -2; } } break; default: - syslog(LOG_ERR, "Unknown CMD 0x%x (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip); + mslog(s, proc, LOG_ERR, "Unknown CMD 0x%x (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip); return -2; } diff --git a/src/main-resume.c b/src/main-resume.c index 3f5d0856..fcd5c33a 100644 --- a/src/main-resume.c +++ b/src/main-resume.c @@ -63,6 +63,42 @@ int send_resume_fetch_reply(main_server_st* s, struct proc_st * proc, return(sendmsg(proc->fd, &hdr, 0)); } +int send_udp_fd(main_server_st* s, struct proc_st * proc, + void* cli_addr, socklen_t cli_addr_size, int fd) +{ + struct iovec iov[2]; + uint8_t cmd = CMD_UDP_FD; + struct msghdr hdr; + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof(int))]; + } control_un; + struct cmsghdr *cmptr; + + + memset(&hdr, 0, sizeof(hdr)); + iov[0].iov_base = &cmd; + iov[0].iov_len = 1; + hdr.msg_iovlen++; + + iov[1].iov_base = cli_addr; + iov[1].iov_len = cli_addr_size; + hdr.msg_iovlen++; + + hdr.msg_iov = iov; + + hdr.msg_control = control_un.control; + hdr.msg_controllen = sizeof(control_un.control); + + cmptr = CMSG_FIRSTHDR(&hdr); + cmptr->cmsg_len = CMSG_LEN(sizeof(int)); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmptr), &fd, sizeof(int)); + + return(sendmsg(proc->fd, &hdr, 0)); +} + int handle_resume_delete_req(main_server_st* s, struct proc_st * proc, const struct cmd_resume_fetch_req_st * req) { diff --git a/src/main.c b/src/main.c index a7edb4ff..256b0582 100644 --- a/src/main.c +++ b/src/main.c @@ -53,46 +53,31 @@ static void tls_log_func(int level, const char *str) syslog(LOG_DEBUG, "TLS[<%d>]: %s", level, str); } -/* Returns 0 on success or negative value on error. - */ -static int -listen_ports(struct cfg_st* config, struct listen_list_st *list, const char *node, - int listen_port, int socktype) +static +int _listen_ports(struct cfg_st* config, struct addrinfo *res, struct listen_list_st *list) { - struct addrinfo hints, *res, *ptr; - char portname[6]; + struct addrinfo *ptr; int s, y; + const char* type = NULL; char buf[512]; struct listener_st *tmp; - list_head_init(&list->head); - list->total = 0; - - snprintf(portname, sizeof(portname), "%d", listen_port); - - memset(&hints, 0, sizeof(hints)); - hints.ai_socktype = socktype; - hints.ai_flags = AI_PASSIVE -#ifdef AI_ADDRCONFIG - | AI_ADDRCONFIG -#endif - ; - - s = getaddrinfo(node, portname, &hints, &res); - if (s != 0) { - fprintf(stderr, "getaddrinfo() failed: %s\n", - gai_strerror(s)); - return -1; - } - for (ptr = res; ptr != NULL; ptr = ptr->ai_next) { #ifndef HAVE_IPV6 if (ptr->ai_family != AF_INET) continue; #endif + + if (ptr->ai_socktype == SOCK_STREAM) + type = "TCP"; + else if (ptr->ai_socktype == SOCK_DGRAM) + type = "UDP"; + else + continue; + if (config->foreground != 0) - fprintf(stderr, "listening on %s...\n", - human_addr(ptr->ai_addr, ptr->ai_addrlen, + fprintf(stderr, "listening (%s) on %s...\n", + type, human_addr(ptr->ai_addr, ptr->ai_addrlen, buf, sizeof(buf))); s = socket(ptr->ai_family, ptr->ai_socktype, @@ -113,7 +98,7 @@ listen_ports(struct cfg_st* config, struct listen_list_st *list, const char *nod } #endif - if (socktype == SOCK_STREAM) { + if (ptr->ai_socktype == SOCK_STREAM) { y = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *) &y, sizeof(y)) < 0) { @@ -122,6 +107,8 @@ listen_ports(struct cfg_st* config, struct listen_list_st *list, const char *nod continue; } } else { + y = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *) &y, sizeof(y)); #if defined(IP_DONTFRAG) y = 1; if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG, @@ -143,25 +130,151 @@ listen_ports(struct cfg_st* config, struct listen_list_st *list, const char *nod continue; } - if (socktype == SOCK_STREAM) { + if (ptr->ai_socktype == SOCK_STREAM) { if (listen(s, 10) < 0) { perror("listen() failed"); - exit(1); + return -1; } } + + tmp = calloc(1, sizeof(struct listener_st)); tmp->fd = s; + tmp->family = ptr->ai_family; + tmp->socktype = ptr->ai_socktype; + tmp->protocol = ptr->ai_protocol; + tmp->addr_len = ptr->ai_addrlen; + memcpy(&tmp->addr, ptr->ai_addr, tmp->addr_len); + list_add(&list->head, &(tmp->list)); list->total++; } fflush(stderr); + + return 0; +} + +/* Returns 0 on success or negative value on error. + */ +static int +listen_ports(struct cfg_st* config, struct listen_list_st *list, const char *node) +{ + struct addrinfo hints, *res; + char portname[6]; + int ret; + + list_head_init(&list->head); + list->total = 0; + + snprintf(portname, sizeof(portname), "%d", config->port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE +#ifdef AI_ADDRCONFIG + | AI_ADDRCONFIG +#endif + ; + + ret = getaddrinfo(node, portname, &hints, &res); + if (ret != 0) { + fprintf(stderr, "getaddrinfo() failed: %s\n", + gai_strerror(ret)); + return -1; + } + + ret = _listen_ports(config, res, list); + if (ret < 0) { + return -1; + } + + freeaddrinfo(res); + + snprintf(portname, sizeof(portname), "%d", config->udp_port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE +#ifdef AI_ADDRCONFIG + | AI_ADDRCONFIG +#endif + ; + + ret = getaddrinfo(node, portname, &hints, &res); + if (ret != 0) { + fprintf(stderr, "getaddrinfo() failed: %s\n", + gai_strerror(ret)); + return -1; + } + + ret = _listen_ports(config, res, list); + if (ret < 0) { + return -1; + } + freeaddrinfo(res); return 0; } +/* This is a hack. I tried to use connect() on the worker + * and use connect() with unspec on the master process but all packets + * were received by master. Reopening the socket seems to resolve + * that. + */ +static +int reopen_udp_port(struct listener_st *l) +{ +int s, y, e; + + close(l->fd); + l->fd = -1; + + s = socket(l->family, l->socktype, l->protocol); + if (s < 0) { + perror("socket() failed"); + return -1; + } + +#if defined(HAVE_IPV6) && !defined(_WIN32) + if (ptr->ai_family == AF_INET6) { + y = 1; + /* avoid listen on ipv6 addresses failing + * because already listening on ipv4 addresses: */ + setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + (const void *) &y, sizeof(y)); + } +#endif + + y = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *) &y, sizeof(y)); + +#if defined(IP_DONTFRAG) + y = 1; + setsockopt(s, IPPROTO_IP, IP_DONTFRAG, + (const void *) &y, sizeof(y)); +#elif defined(IP_MTU_DISCOVER) + y = IP_PMTUDISC_DO; + setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, + (const void *) &y, sizeof(y)); +#endif + set_cloexec_flag (s, 1); + + if (bind(s, (void*)&l->addr, l->addr_len) < 0) { + e = errno; + syslog(LOG_ERR, "bind() failed: %s", strerror(e)); + close(s); + return -1; + } + + l->fd = s; + + return 0; +} + + static void cleanup_children(main_server_st *s) { int status; @@ -171,7 +284,7 @@ pid_t pid; if (WEXITSTATUS(status) != 0 || (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV)) { if (WIFSIGNALED(status)) - syslog(LOG_ERR, "Child %u died with sigsegv\n", (unsigned)pid); + mslog(s, NULL, LOG_ERR, "Child %u died with sigsegv\n", (unsigned)pid); } } need_children_cleanup = 0; @@ -248,36 +361,36 @@ static int verify_certificate_cb(gnutls_session_t session) return 0; } -static void drop_privileges(struct cfg_st *config) +static void drop_privileges(main_server_st* s) { int ret, e; - if (config->chroot_dir) { - ret = chroot(config->chroot_dir); + if (s->config->chroot_dir) { + ret = chroot(s->config->chroot_dir); if (ret != 0) { e = errno; - syslog(LOG_ERR, "Cannot chroot to %s: %s", config->chroot_dir, strerror(e)); + mslog(s, NULL, LOG_ERR, "Cannot chroot to %s: %s", s->config->chroot_dir, strerror(e)); exit(1); } } - if (config->gid != -1 && (getgid() == 0 || getegid() == 0)) { - ret = setgid(config->gid); + if (s->config->gid != -1 && (getgid() == 0 || getegid() == 0)) { + ret = setgid(s->config->gid); if (ret < 0) { e = errno; - syslog(LOG_ERR, "Cannot set gid to %d: %s\n", - (int) config->gid, strerror(e)); + mslog(s, NULL, LOG_ERR, "Cannot set gid to %d: %s\n", + (int) s->config->gid, strerror(e)); exit(1); } } - if (config->uid != -1 && (getuid() == 0 || geteuid() == 0)) { - ret = setuid(config->uid); + if (s->config->uid != -1 && (getuid() == 0 || geteuid() == 0)) { + ret = setuid(s->config->uid); if (ret < 0) { e = errno; - syslog(LOG_ERR, "Cannot set uid to %d: %s\n", - (int) config->uid, strerror(e)); + mslog(s, NULL, LOG_ERR, "Cannot set uid to %d: %s\n", + (int) s->config->uid, strerror(e)); exit(1); } @@ -335,6 +448,76 @@ static void handle_term(int signo) terminate = 1; } +#define RECORD_PAYLOAD_POS 13 +#define HANDSHAKE_SESSION_ID_POS 46 +static int forward_udp_to_owner(main_server_st* s, struct listener_st *listener) +{ +int ret; +struct sockaddr_storage cli_addr; +struct proc_st *ctmp; +socklen_t cli_addr_size; +uint8_t buffer[1024]; +uint8_t *session_id; +int session_id_size; +ssize_t buffer_size; +int connected = 0; + + /* first receive from the correct client and connect socket */ + cli_addr_size = sizeof(cli_addr); + ret = recvfrom(listener->fd, buffer, sizeof(buffer), MSG_PEEK, (void*)&cli_addr, &cli_addr_size); + if (ret < 0) { + mslog(s, NULL, LOG_INFO, "Error receiving in UDP socket"); + return -1; + } + + buffer_size = ret; + + /* obtain the session id */ + if (buffer_size < RECORD_PAYLOAD_POS+HANDSHAKE_SESSION_ID_POS+GNUTLS_MAX_SESSION_ID+2) + goto fail; + + /* check version */ + if (buffer[1] != 254 && (buffer[1] != 1 && buffer[0] != 0)) { + mslog(s, NULL, LOG_ERR, "Unknown DTLS version: %u.%u", (unsigned)buffer[1], (unsigned)buffer[2]); + goto fail; + } + + /* read session_id */ + session_id_size = buffer[RECORD_PAYLOAD_POS+HANDSHAKE_SESSION_ID_POS]; + session_id = &buffer[RECORD_PAYLOAD_POS+HANDSHAKE_SESSION_ID_POS+1]; + + /* search for the IP and the session ID in all procs */ + list_for_each(&s->clist->head, ctmp, list) { + if (ctmp->udp_fd_received == 0 && session_id_size == ctmp->session_id_size && + memcmp(session_id, ctmp->session_id, session_id_size) == 0) { + + ret = send_udp_fd(s, ctmp, (void*)&cli_addr, cli_addr_size, listener->fd); + if (ret < 0) { + mslog(s, ctmp, LOG_ERR, "Error passing UDP socket"); + return -1; + } + ctmp->udp_fd_received = 1; + connected = 1; + + reopen_udp_port(listener); + + break; + } + } + +fail: + if (connected == 0) { + /* received packet from unknown host */ +// mslog(s, NULL, LOG_ERR, "Received UDP packet from unexpected host; discarding it"); + recv(listener->fd, buffer, buffer_size, 0); + + return -1; + } + + return 0; + +} + int main(int argc, char** argv) { int fd, pid, e; @@ -344,13 +527,14 @@ int main(int argc, char** argv) struct listener_st *ltmp; struct proc_st *ctmp, *cpos; struct tun_st tun; + const char* perr; fd_set rd; int val, n = 0, ret; struct timeval tv; int cmd_fd[2]; struct worker_st ws; struct cfg_st config; - unsigned active_clients = 0; + unsigned active_clients = 0, set; main_server_st s; list_head_init(&clist.head); @@ -383,7 +567,7 @@ int main(int argc, char** argv) s.clist = &clist; /* Listen to network ports */ - ret = listen_ports(&config, &llist, config.name, config.port, SOCK_STREAM); + ret = listen_ports(&config, &llist, config.name); if (ret < 0) { fprintf(stderr, "Cannot listen to specified ports\n"); exit(1); @@ -413,35 +597,36 @@ int main(int argc, char** argv) exit(1); } - if (config.ca != NULL) { - ret = - gnutls_certificate_set_x509_trust_file(creds.xcred, - config.ca, - GNUTLS_X509_FMT_PEM); - if (ret < 0) { - fprintf(stderr, "Error setting the CA (%s) file.\n", - config.ca); - exit(1); + if (config.cert_req != GNUTLS_CERT_IGNORE) { + if (config.ca != NULL) { + ret = + gnutls_certificate_set_x509_trust_file(creds.xcred, + config.ca, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + fprintf(stderr, "Error setting the CA (%s) file.\n", + config.ca); + exit(1); + } + + printf("Processed %d CA certificate(s).\n", ret); } - printf("Processed %d CA certificate(s).\n", ret); - } + if (config.crl != NULL) { + ret = + gnutls_certificate_set_x509_crl_file(creds.xcred, + config.crl, + GNUTLS_X509_FMT_PEM); + GNUTLS_FATAL_ERR(ret); + } - if (config.crl != NULL) { - ret = - gnutls_certificate_set_x509_crl_file(creds.xcred, - config.crl, - GNUTLS_X509_FMT_PEM); - GNUTLS_FATAL_ERR(ret); - } - - - if (config.cert_req != GNUTLS_CERT_IGNORE) { gnutls_certificate_set_verify_function(creds.xcred, verify_certificate_cb); } - ret = gnutls_priority_init(&creds.cprio, config.priorities, NULL); + ret = gnutls_priority_init(&creds.cprio, config.priorities, &perr); + if (ret == GNUTLS_E_PARSING_ERROR) + fprintf(stderr, "Error in TLS priority string: %s\n", perr); GNUTLS_FATAL_ERR(ret); memset(&ws, 0, sizeof(ws)); @@ -464,11 +649,14 @@ int main(int argc, char** argv) FD_ZERO(&rd); list_for_each(&llist.head, ltmp, list) { + if (ltmp->fd == -1) continue; + val = fcntl(ltmp->fd, F_GETFL, 0); if ((val == -1) || (fcntl(ltmp->fd, F_SETFL, val | O_NONBLOCK) < 0)) { - perror("fcntl()"); + e = errno; + mslog(&s, NULL, LOG_ERR, "fcntl() error: %s", strerror(e)); exit(1); } @@ -489,18 +677,20 @@ int main(int argc, char** argv) if (ret < 0) { e = errno; - syslog(LOG_ERR, "Error in select(): %s", + mslog(&s, NULL, LOG_ERR, "Error in select(): %s", strerror(e)); exit(1); } /* Check for new connections to accept */ list_for_each(&llist.head, ltmp, list) { - if (FD_ISSET(ltmp->fd, &rd)) { + set = FD_ISSET(ltmp->fd, &rd); + if (set && ltmp->socktype == SOCK_STREAM) { + /* connection on TCP port */ ws.remote_addr_len = sizeof(ws.remote_addr); fd = accept(ltmp->fd, (void*)&ws.remote_addr, &ws.remote_addr_len); if (fd < 0) { - syslog(LOG_ERR, + mslog(&s, NULL, LOG_ERR, "Error in accept(): %s", strerror(errno)); continue; @@ -509,14 +699,14 @@ int main(int argc, char** argv) if (config.max_clients > 0 && active_clients >= config.max_clients) { close(fd); - syslog(LOG_INFO, "Reached maximum client limit (active: %u)", active_clients); + mslog(&s, NULL, LOG_INFO, "Reached maximum client limit (active: %u)", active_clients); break; } /* Create a command socket */ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, cmd_fd); if (ret < 0) { - syslog(LOG_ERR, "Error creating command socket"); + mslog(&s, NULL, LOG_ERR, "Error creating command socket"); close(fd); break; } @@ -525,7 +715,7 @@ int main(int argc, char** argv) if (pid == 0) { /* child */ /* Drop privileges after this point */ - drop_privileges(&config); + drop_privileges(&s); /* close any open descriptors before * running the server @@ -536,6 +726,7 @@ int main(int argc, char** argv) ws.config = &config; ws.cmd_fd = cmd_fd[1]; ws.tun_fd = -1; + ws.udp_fd = -1; ws.conn_fd = fd; ws.creds = &creds; @@ -562,6 +753,12 @@ fork_failed: } close(cmd_fd[1]); close(fd); + } else if (set && ltmp->socktype == SOCK_DGRAM) { + /* connection on UDP port */ + ret = forward_udp_to_owner(&s, ltmp); + if (ret < 0) { + mslog(&s, NULL, LOG_INFO, "Could not determine the owner of received UDP packet"); + } } } @@ -586,7 +783,7 @@ fork_failed: need_maintainance = 0; pid = fork(); if (pid == 0) { /* child */ - syslog(LOG_INFO, "Performing maintainance"); + mslog(&s, NULL, LOG_INFO, "Performing maintainance"); clear_lists(&s); expire_cookies(&s); diff --git a/src/main.h b/src/main.h index 964ac93a..933a4da3 100644 --- a/src/main.h +++ b/src/main.h @@ -14,6 +14,12 @@ int cmd_parser (int argc, char **argv, struct cfg_st* config); struct listener_st { struct list_node list; int fd; + int socktype; + + struct sockaddr_storage addr; /* local socket address */ + socklen_t addr_len; + int family; + int protocol; }; struct listen_list_st { @@ -30,7 +36,10 @@ struct proc_st { char username[MAX_USERNAME_SIZE]; /* the owner */ char hostname[MAX_HOSTNAME_SIZE]; /* the requested hostname */ uint8_t cookie[COOKIE_SIZE]; /* the cookie associated with the session */ + /* The DTLS session ID associated with the TLS session */ uint8_t session_id[GNUTLS_MAX_SESSION_ID]; + unsigned session_id_size; /* would act as a flag if session_id is set */ + unsigned udp_fd_received; /* the tun lease this process has */ struct lease_st* lease; @@ -63,6 +72,8 @@ void expire_tls_sessions(main_server_st *s); int send_resume_fetch_reply(main_server_st* s, struct proc_st* proc, cmd_resume_reply_t r, struct cmd_resume_fetch_reply_st * reply); +int send_udp_fd(main_server_st* s, struct proc_st* proc, void* cli_addr, socklen_t cli_addr_size, int fd); + int handle_resume_delete_req(main_server_st* s, struct proc_st* proc, const struct cmd_resume_fetch_req_st * req); @@ -75,4 +86,11 @@ int handle_resume_store_req(main_server_st* s, struct proc_st *proc, void expire_cookies(main_server_st* s); +void +__attribute__ ((format(printf, 4, 5))) + mslog(const main_server_st * s, const struct proc_st* proc, + int priority, const char *fmt, ...); + +int open_tun(main_server_st* s, struct lease_st** l); + #endif diff --git a/src/ocserv-args.c b/src/ocserv-args.c index 405e95d8..397da1d1 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 February 7, 2013 at 12:56:29 AM by AutoGen 5.16 + * It has been AutoGen-ed February 7, 2013 at 02:45:14 PM by AutoGen 5.16 * From the definitions ocserv-args.def * and the template file options * diff --git a/src/ocserv-args.def b/src/ocserv-args.def index a3ae3ba0..960b4054 100644 --- a/src/ocserv-args.def +++ b/src/ocserv-args.def @@ -74,8 +74,9 @@ auth = "pam" #max-clients = 1024 max-clients = 16 -# TCP port number +# TCP and UDP port number tcp-port = 3333 +udp-port = 3333 # The key and the certificates of the server server-cert = /path/to/cert.pem diff --git a/src/ocserv-args.h b/src/ocserv-args.h index 0d18a5c8..af9bc648 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 February 7, 2013 at 12:56:29 AM by AutoGen 5.16 + * It has been AutoGen-ed February 7, 2013 at 02:45:14 PM by AutoGen 5.16 * From the definitions ocserv-args.def * and the template file options * diff --git a/src/sample.config b/src/sample.config index 70b228a9..cf45fa5a 100644 --- a/src/sample.config +++ b/src/sample.config @@ -17,8 +17,9 @@ max-clients = 4 # to authentication auth-timeout = 40 -# TCP port number +# TCP and UDP port number tcp-port = 3333 +udp-port = 3333 # Keepalive in seconds keepalive = 90 @@ -64,8 +65,8 @@ device = vpns # IP-REAL is the remote IP of the client, # IP-LOCAL is the local IP in the P-t-P connection and IP-REMOTE # is the VPN client IP. -connect-script = /bin/echo -disconnect-script = /bin/echo +#connect-script = /bin/echo +#disconnect-script = /bin/echo # The pool from which the VPN user IPs will be drawn from. ipv4-network = 192.168.1.0 diff --git a/src/tun.c b/src/tun.c index 8c69d112..46ea2946 100644 --- a/src/tun.c +++ b/src/tun.c @@ -31,8 +31,11 @@ #include #include +#include + #include #include +#include #include static int bignum_add1 (uint8_t * num, unsigned size) @@ -60,9 +63,7 @@ static int bignum_add1 (uint8_t * num, unsigned size) static int get_avail_network_addresses(const struct cfg_st *config, const struct lease_st *last4, const struct lease_st *last6, struct lease_st* lease) { - struct sockaddr_storage tmp, mask; - struct sockaddr_in *t4; - struct sockaddr_in6 *t6; + struct sockaddr_storage tmp, mask, network; unsigned i; int ret; @@ -73,13 +74,9 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct memset(&tmp, 0, sizeof(tmp)); - /* read the network */ - if (last4 == NULL && (config->network.ipv4 && config->network.ipv4_netmask)) { - t4 = (void*)&tmp; - t4->sin_family = AF_INET; - + if (config->network.ipv4 && config->network.ipv4_netmask) { ret = - inet_pton(AF_INET, config->network.ipv4, SA_IN_P(t4)); + inet_pton(AF_INET, config->network.ipv4, SA_IN_P(&network)); if (ret != 1) { syslog(LOG_ERR, "Error reading IP: %s\n", config->network.ipv4); @@ -93,40 +90,37 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct syslog(LOG_ERR, "Error reading mask: %s\n", config->network.ipv4_netmask); return -1; } - - /* mask the network */ + + /* mask the network (just in case it is wrong) */ for (i=0;isin_family = AF_INET; + ((struct sockaddr_in*)&network)->sin_family = AF_INET; + } + + + /* read the network */ + if (last4 == NULL && (config->network.ipv4 && config->network.ipv4_netmask)) { /* add one to get local IP */ i = sizeof(struct in_addr)-1; - SA_IN_U8_P(t4)[i]++; + SA_IN_U8_P(&tmp)[i]++; lease->lip4_len = sizeof(struct sockaddr_in); - memcpy(&lease->lip4, t4, lease->lip4_len); + memcpy(&lease->lip4, &tmp, lease->lip4_len); /* add one to get remote IP */ i = sizeof(struct in_addr)-1; - SA_IN_U8_P(t4)[i]++; - + SA_IN_U8_P(&tmp)[i]++; + lease->rip4_len = sizeof(struct sockaddr_in); - memcpy(&lease->rip4, t4, lease->rip4_len); - + memcpy(&lease->rip4, &tmp, lease->rip4_len); } else if (last4 != NULL) { - t4 = (void*)&tmp; - t4->sin_family = AF_INET; - ret = - inet_pton(AF_INET, config->network.ipv4_netmask, SA_IN_P(&mask)); - - if (ret != 1) { - syslog(LOG_ERR, "Error reading mask: %s\n", config->network.ipv4_netmask); - return -1; - } - - /* mask the network */ lease->lip4_len = last4->rip4_len; - memcpy(&lease->lip4, &last4->rip4, lease->rip4_len); + memcpy(&lease->lip4, &last4->rip4, last4->rip4_len); bignum_add1(SA_IN_U8_P(&lease->lip4), sizeof(struct in_addr)); if (SA_IN_U8_P(&lease->lip4)[3] == 255) /* broadcast */ @@ -136,23 +130,21 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct memcpy(&lease->rip4, &lease->lip4, lease->rip4_len); bignum_add1(SA_IN_U8_P(&lease->rip4), sizeof(struct in_addr)); - /* mask the last IP with the complement of netmask */ + /* mask the last IP with the netmask */ memcpy(&tmp, &lease->rip4, lease->rip4_len); for (i=0;irip4, lease->rip4_len) != 0) { - syslog(LOG_ERR, "Reached limit of maximum IPs.\n"); + SA_IN_U8_P(&tmp)[i] &= (SA_IN_U8_P(&mask)[i]); + + /* the result should match the network */ + if (memcmp(SA_IN_U8_P(&network), SA_IN_U8_P(&tmp), sizeof(struct in_addr)) != 0) { + syslog(LOG_ERR, "Reached limit of maximum (v4) IPs.\n"); return -1; } } - if (last6 == NULL && (config->network.ipv6 && config->network.ipv6_netmask)) { - t6 = (void*)&tmp; - t6->sin6_family = AF_INET6; - + if (config->network.ipv6 && config->network.ipv6_netmask) { ret = - inet_pton(AF_INET6, config->network.ipv6, SA_IN6_P(t6)); + inet_pton(AF_INET6, config->network.ipv6, SA_IN6_P(&network)); if (ret != 1) { syslog(LOG_ERR, "Error reading IP: %s\n", config->network.ipv6); @@ -169,49 +161,45 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct /* mask the network */ for (i=0;isin6_family = AF_INET6; + ((struct sockaddr_in6*)&network)->sin6_family = AF_INET6; + } + + if (last6 == NULL && (config->network.ipv6 && config->network.ipv6_netmask)) { /* add one to get local IP */ i = sizeof(struct in6_addr)-1; - SA_IN6_U8_P(t6)[i]++; + SA_IN6_U8_P(&tmp)[i]++; lease->lip6_len = sizeof(struct sockaddr_in6); - memcpy(&lease->lip6, t6, lease->lip6_len); + memcpy(&lease->lip6, &tmp, lease->lip6_len); /* add one to get remote IP */ i = sizeof(struct in6_addr)-1; - SA_IN6_U8_P(t6)[i]++; + SA_IN6_U8_P(&tmp)[i]++; lease->rip6_len = sizeof(struct sockaddr_in6); - memcpy(&lease->rip6, t6, lease->rip6_len); + memcpy(&lease->rip6, &tmp, lease->rip6_len); } else if (last6 != NULL) { - t6 = (void*)&tmp; - t6->sin6_family = AF_INET6; - ret = - inet_pton(AF_INET6, config->network.ipv6_netmask, SA_IN6_P(&mask)); - - if (ret != 1) { - syslog(LOG_ERR, "Error reading mask: %s\n", config->network.ipv6_netmask); - return -1; - } - - /* mask the network */ lease->lip6_len = last6->rip6_len; - memcpy(&lease->lip6, &last6->rip6, lease->rip6_len); + memcpy(&lease->lip6, &last6->rip6, last6->rip6_len); bignum_add1(SA_IN6_U8_P(&lease->lip6), sizeof(struct in6_addr)); lease->rip6_len = last6->rip6_len; memcpy(&lease->rip6, &lease->lip6, lease->rip6_len); bignum_add1(SA_IN6_U8_P(&lease->rip6), sizeof(struct in6_addr)); - /* mask the last IP with the complement of netmask */ + /* mask the last IP with the netmask */ memcpy(&tmp, &lease->rip6, lease->rip6_len); for (i=0;irip6, lease->rip6_len) != 0) { - syslog(LOG_ERR, "Reached limit of maximum IPs.\n"); + /* the result should match the network */ + if (memcmp(SA_IN6_U8_P(&network), SA_IN6_U8_P(&tmp), sizeof(struct in6_addr)) != 0) { + syslog(LOG_ERR, "Reached limit of maximum (v6) IPs.\n"); return -1; } } @@ -230,7 +218,7 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct return 0; } -static int set_network_info( const struct lease_st *lease) +static int set_network_info( main_server_st* s, const struct lease_st *lease) { struct ifreq ifr; int fd, ret; @@ -248,7 +236,7 @@ static int set_network_info( const struct lease_st *lease) ret = ioctl(fd, SIOCSIFADDR, &ifr); if (ret != 0) { - syslog(LOG_ERR, "%s: Error setting IPv4.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Error setting IPv4.\n", lease->name); } memset(&ifr, 0, sizeof(ifr)); @@ -258,7 +246,7 @@ static int set_network_info( const struct lease_st *lease) ret = ioctl(fd, SIOCSIFDSTADDR, &ifr); if (ret != 0) { - syslog(LOG_ERR, "%s: Error setting DST IPv4.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Error setting DST IPv4.\n", lease->name); } } @@ -271,7 +259,7 @@ static int set_network_info( const struct lease_st *lease) ret = ioctl(fd, SIOCSIFADDR, &ifr); if (ret != 0) { - syslog(LOG_ERR, "%s: Error setting IPv6.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Error setting IPv6.\n", lease->name); } memset(&ifr, 0, sizeof(ifr)); @@ -281,12 +269,12 @@ static int set_network_info( const struct lease_st *lease) ret = ioctl(fd, SIOCSIFDSTADDR, &ifr); if (ret != 0) { - syslog(LOG_ERR, "%s: Error setting DST IPv6.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Error setting DST IPv6.\n", lease->name); } } if (lease->lip6_len == 0 && lease->lip4_len == 0) { - syslog(LOG_ERR, "%s: Could not set any IP.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Could not set any IP.\n", lease->name); ret = -1; } else ret = 0; @@ -299,7 +287,7 @@ static int set_network_info( const struct lease_st *lease) ret = ioctl(fd, SIOCSIFFLAGS, &ifr); if (ret != 0) { - syslog(LOG_ERR, "%s: Could not bring up interface.\n", lease->name); + mslog(s, NULL, LOG_ERR, "%s: Could not bring up interface.\n", lease->name); } close(fd); @@ -307,7 +295,7 @@ static int set_network_info( const struct lease_st *lease) } -int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st** l) +int open_tun(main_server_st* s, struct lease_st** l) { int tunfd, ret, e; struct ifreq ifr; @@ -315,27 +303,28 @@ int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st** struct lease_st *lease = NULL; struct lease_st *last4, *tmp; struct lease_st *last6; + unsigned current = s->tun->total; - if (list_empty(&tun->head)) { + if (list_empty(&s->tun->head)) { lease = calloc(1, sizeof(*lease)); if (lease == NULL) return -1; /* find the first IP address */ - ret = get_avail_network_addresses(config, NULL, NULL, lease); + ret = get_avail_network_addresses(s->config, NULL, NULL, lease); if (ret < 0) { free(lease); return -1; } /* Add into the list */ - list_add_tail( &tun->head, &lease->list); - tun->total++; + list_add_tail( &s->tun->head, &lease->list); + s->tun->total++; } else { last4 = last6 = NULL; /* try to re-use an address */ - list_for_each(&tun->head, tmp, list) { + list_for_each(&s->tun->head, tmp, list) { if (tmp->in_use == 0) { lease = tmp; break; @@ -347,26 +336,27 @@ int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st** if (lease == NULL) return -1; - list_for_each_rev(&tun->head, tmp, list) { - if (tmp->rip4_len > 0) + list_for_each_rev(&s->tun->head, tmp, list) { + if (last4 == NULL && tmp->rip4_len > 0) last4 = tmp; - if (tmp->rip6_len > 0) + + if (last6 == NULL && tmp->rip6_len > 0) last6 = tmp; if (last4 && last6) break; } - ret = get_avail_network_addresses(config, last4, last6, lease); + ret = get_avail_network_addresses(s->config, last4, last6, lease); if (ret < 0) { free(lease); return -1; } /* Add into the list */ - list_add_tail( &tun->head, &lease->list); - tun->total++; + list_add_tail( &s->tun->head, &lease->list); + s->tun->total++; } } @@ -376,7 +366,7 @@ int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st** tunfd = open("/dev/net/tun", O_RDWR); if (tunfd < 0) { int e = errno; - syslog(LOG_ERR, "Can't open /dev/net/tun: %s\n", + mslog(s, NULL, LOG_ERR, "Can't open /dev/net/tun: %s\n", strerror(e)); return -1; } @@ -386,39 +376,39 @@ int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st** memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; - snprintf(lease->name, sizeof(lease->name), "%s%u", config->network.name, 0); + snprintf(lease->name, sizeof(lease->name), "%s%u", s->config->network.name, current); memcpy(ifr.ifr_name, lease->name, sizeof(ifr.ifr_name)); if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0) { e = errno; - syslog(LOG_ERR, "TUNSETIFF: %s\n", strerror(e)); + mslog(s, NULL, LOG_ERR, "%s: TUNSETIFF: %s\n", lease->name, strerror(e)); goto fail; } - if (config->uid != -1) { - t = config->uid; + if (s->config->uid != -1) { + t = s->config->uid; ret = ioctl(tunfd, TUNSETOWNER, t); if (ret < 0) { e = errno; - syslog(LOG_INFO, "TUNSETOWNER: %s\n", - strerror(e)); + mslog(s, NULL, LOG_INFO, "%s: TUNSETOWNER: %s\n", + lease->name, strerror(e)); goto fail; } } - if (config->gid != -1) { - t = config->uid; + if (s->config->gid != -1) { + t = s->config->uid; ret = ioctl(tunfd, TUNSETGROUP, t); if (ret < 0) { e = errno; - syslog(LOG_ERR, "TUNSETGROUP: %s\n", - strerror(e)); + mslog(s, NULL, LOG_ERR, "%s: TUNSETGROUP: %s\n", + lease->name, strerror(e)); goto fail; } } /* set IP/mask */ - ret = set_network_info(lease); + ret = set_network_info(s, lease); if (ret < 0) { goto fail; } diff --git a/src/tun.h b/src/tun.h index c229a89e..69f52b27 100644 --- a/src/tun.h +++ b/src/tun.h @@ -48,6 +48,4 @@ inline static void tun_st_deinit(struct tun_st* ts) } } -int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st **lease); - #endif diff --git a/src/vpn.h b/src/vpn.h index 89a2b735..6a03d77f 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -53,6 +53,7 @@ struct vpn_st { struct cfg_st { const char *name; unsigned int port; + unsigned int udp_port; const char *cert; const char *key; const char *ca; @@ -102,4 +103,6 @@ const char *human_addr(const struct sockaddr *sa, socklen_t salen, #define SA_IN_PORT(p) (((struct sockaddr_in *)(p))->sin_port) #define SA_IN6_PORT(p) (((struct sockaddr_in6 *)(p))->sin6_port) +#define SA_IN_P_GENERIC(addr, size) ((size==sizeof(struct sockaddr_in))?SA_IN_U8_P(addr):SA_IN6_U8_P(addr)) +#define SA_IN_SIZE(size) ((size==sizeof(struct sockaddr_in))?sizeof(struct in_addr):sizeof(struct in6_addr)) #endif diff --git a/src/worker-auth.c b/src/worker-auth.c index 26351d93..662376b3 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -157,8 +157,7 @@ static int recv_auth_reply(worker_st *ws) uint8_t cmd = 0; struct cmd_auth_reply_st resp; struct msghdr hdr; - int ret; - + int ret, cmdlen; union { struct cmsghdr cm; char control[CMSG_SPACE(sizeof(int))]; @@ -179,15 +178,25 @@ static int recv_auth_reply(worker_st *ws) hdr.msg_controllen = sizeof(control_un.control); ret = recvmsg( ws->cmd_fd, &hdr, 0); - if (ret < sizeof(resp)+1) { - oclog(ws, LOG_ERR, "Received incorrect data (%d, expected %d) from main", ret, (int)sizeof(resp)+1); + + cmdlen = ret; + + if (cmdlen < 2) { + oclog(ws, LOG_ERR, "Received incorrect data (%d, expected %d) from main", cmdlen, (int)2); return -1; } if (cmd != AUTH_REP) return -1; + + cmdlen--; switch(resp.reply) { case REP_AUTH_OK: + if (cmdlen < sizeof(resp)) { + oclog(ws, LOG_ERR, "Received incorrect data (%d, expected %d) from main", ret, (int)sizeof(resp)+1); + return -1; + } + if ( (cmptr = CMSG_FIRSTHDR(&hdr)) != NULL && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) { if (cmptr->cmsg_level != SOL_SOCKET) return -1; diff --git a/src/worker-vpn.c b/src/worker-vpn.c index 4bd39d54..2731fa65 100644 --- a/src/worker-vpn.c +++ b/src/worker-vpn.c @@ -237,57 +237,11 @@ char* tmp = malloc(length+1); static int setup_dtls_connection(struct worker_st *ws) { -int ret, e; +int ret; gnutls_session_t session; -struct sockaddr_storage cli_addr; -socklen_t cli_addr_size; -uint8_t buffer[512]; -ssize_t buffer_size; gnutls_datum_t master = { ws->master_secret, sizeof(ws->master_secret) }; gnutls_datum_t sid = { ws->session_id, sizeof(ws->session_id) }; -unsigned port; - /* first receive from the correct client and connect socket */ - cli_addr_size = sizeof(cli_addr); - ret = recvfrom(ws->udp_fd, buffer, sizeof(buffer), MSG_PEEK, (void*)&cli_addr, &cli_addr_size); - if (ret < 0) { - oclog(ws, LOG_ERR, "Error receiving in UDP socket"); - return -1; - } - - if (cli_addr_size == sizeof(struct sockaddr_in6)) { - port = SA_IN6_PORT(&cli_addr); - } else { - port = SA_IN_PORT(&cli_addr); - } - port = ntohs(port); - - buffer_size = ret; - - if ( (ws->remote_addr_len == sizeof(struct sockaddr_in) && memcmp(SA_IN_P(&cli_addr), - SA_IN_P(&ws->remote_addr), sizeof(struct in_addr)) == 0) || - (ws->remote_addr_len == sizeof(struct sockaddr_in6) && memcmp(SA_IN6_P(&cli_addr), - SA_IN6_P(&ws->remote_addr), sizeof(struct in6_addr)) == 0)) { - - /* connect to host */ -#if 1 - oclog(ws, LOG_ERR, "Connecting to UDP port %u", port); - ret = connect(ws->udp_fd, (void*)&cli_addr, cli_addr_size); - if (ret < 0) { - e = errno; - oclog(ws, LOG_ERR, "Error connecting: %s", strerror(e)); - return -1; - } -#endif - } else { - /* received packet from unknown host */ - - oclog(ws, LOG_ERR, "Received UDP packet from unexpected host; discarding it"); - recv(ws->udp_fd, buffer, buffer_size, 0); - - return 0; - } - /* DTLS cookie verified. * Initialize session. */ @@ -354,6 +308,10 @@ void vpn_server(struct worker_st* ws) syslog(LOG_INFO, "Accepted connection from %s", human_addr((void*)&ws->remote_addr, ws->remote_addr_len, buf, sizeof(buf))); + if (ws->remote_addr_len == sizeof(struct sockaddr_in)) + ws->proto = AF_INET; + else + ws->proto = AF_INET6; /* initialize the session */ ret = gnutls_init(&session, GNUTLS_SERVER); @@ -631,103 +589,6 @@ fail: return ret; } -static int open_udp_port(worker_st *ws) -{ -int s, e, ret; -struct sockaddr_storage si; -struct sockaddr_storage stcp; -socklen_t len; -int proto; - - len = sizeof(stcp); - ret = getsockname(ws->conn_fd, (void*)&stcp, &len); - if (ret == -1) { - e = errno; - oclog(ws, LOG_ERR, "Error in getsockname: %s", strerror(e)); - return -1; - } - - proto = ((struct sockaddr*)&stcp)->sa_family; - - s = socket(proto, SOCK_DGRAM, IPPROTO_UDP); - if (s == -1) { - e = errno; - oclog(ws, LOG_ERR, "Could not open a UDP socket: %s", strerror(e)); - return -1; - } - - /* listen on the same IP the client connected at */ - memset(&si, 0, sizeof(si)); - ((struct sockaddr*)&si)->sa_family = proto; - - if (proto == AF_INET) { - memcpy(SA_IN_P(&si), SA_IN_P(&stcp), sizeof(*SA_IN_P(&si))); - } else if (proto == AF_INET6) { - memcpy(SA_IN6_P(&si), SA_IN6_P(&stcp), sizeof(*SA_IN6_P(&si))); - } else { - oclog(ws, LOG_ERR, "Unknown protocol family: %d", proto); - goto fail; - } - - /* make sure we don't fragment packets */ -#if defined(IP_DONTFRAG) - ret = 1; - if (setsockopt (s, IPPROTO_IP, IP_DONTFRAG, - (const void *) &ret, sizeof (ret)) < 0) - if (ret < 0) { - e = errno; - oclog(ws, LOG_ERR, "Error in setsockopt (IP_DF): %s", strerror(e)); - goto fail; - } -#elif defined(IP_MTU_DISCOVER) - ret = IP_PMTUDISC_DO; - if (setsockopt (s, IPPROTO_IP, IP_MTU_DISCOVER, - (const void *) &ret, sizeof (ret)) < 0) - if (ret < 0) { - e = errno; - oclog(ws, LOG_ERR, "Error in setsockopt (IP_MTU_DISCOVER): %s", strerror(e)); - goto fail; - } -#endif -#ifdef SO_REUSEPORT - ret = 1; - ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &ret, sizeof(ret)); - if (ret < 0) { - e = errno; - oclog(ws, LOG_ERR, "Error in setsockopt (SO_REUSEPORT): %s", strerror(e)); - goto fail; - } -#endif - ret = bind(s, (void*)&si, len); - if (ret == -1) { - e = errno; - oclog(ws, LOG_ERR, "Could not bind on a UDP port: %s", strerror(e)); - goto fail; - } - - len = sizeof(si); - ret = getsockname(s, (void*)&si, &len); - if (ret == -1) { - e = errno; - oclog(ws, LOG_ERR, "Could not obtain UDP port number: %s", strerror(e)); - goto fail; - } - - if (proto == AF_INET) { - ws->udp_port = ntohs(SA_IN_PORT(&si)); - } else { - ws->udp_port = ntohs(SA_IN6_PORT(&si)); - } - ws->udp_port_proto = proto; - - ws->udp_fd = s; - - return 0; -fail: - close(s); - return -1; -} - static ssize_t tun_write(int sockfd, const void *buf, size_t len) { int left = len; @@ -845,7 +706,7 @@ unsigned mtu_overhead, dtls_mtu = 0; } if (ws->config->network.name == NULL) { - oclog(ws, LOG_ERR, "No networks are configured. Rejecting client"); + oclog(ws, LOG_ERR, "No networks are configured; rejecting client"); tls_puts(ws->session, "HTTP/1.1 503 Service Unavailable\r\n"); tls_puts(ws->session, "X-Reason: Server configuration error\r\n\r\n"); return -1; @@ -869,12 +730,7 @@ unsigned mtu_overhead, dtls_mtu = 0; ws->udp_state = UP_DISABLED; if (req->master_secret_set != 0) { memcpy(ws->master_secret, req->master_secret, TLS_MASTER_SIZE); - - ret = open_udp_port(ws); - if (ret < 0) { - oclog(ws, LOG_NOTICE, "Could not open UDP port"); - } else - ws->udp_state = UP_SETUP; + ws->udp_state = UP_SETUP; } @@ -914,12 +770,13 @@ unsigned mtu_overhead, dtls_mtu = 0; } tls_printf(ws->session, "X-DTLS-Session-ID: %s\r\n", buffer); - tls_printf(ws->session, "X-DTLS-Port: %u\r\n", ws->udp_port); + tls_printf(ws->session, "X-DTLS-Port: %u\r\n", ws->config->udp_port); tls_puts(ws->session, "X-DTLS-ReKey-Time: 86400\r\n"); tls_printf(ws->session, "X-DTLS-Keepalive: %u\r\n", ws->config->keepalive); tls_puts(ws->session, "X-DTLS-CipherSuite: "OPENSSL_CIPHERSUITE"\r\n"); - if (ws->udp_port_proto == AF_INET) + /* assume that if IPv6 is used over TCP then the same would be used over UDP */ + if (ws->proto == AF_INET) mtu_overhead = 20+8; else mtu_overhead = 40+8; @@ -940,7 +797,7 @@ unsigned mtu_overhead, dtls_mtu = 0; max = MAX(ws->cmd_fd,ws->conn_fd); max = MAX(max,ws->tun_fd); - if (ws->udp_state != UP_DISABLED) { + if (ws->udp_state != UP_DISABLED && ws->udp_fd != -1) { FD_SET(ws->udp_fd, &rfds); max = MAX(max,ws->udp_fd); } @@ -1046,7 +903,7 @@ unsigned mtu_overhead, dtls_mtu = 0; } } - if (ws->udp_state != UP_DISABLED && (FD_ISSET(ws->udp_fd, &rfds) || dtls_pending != 0)) { + if (ws->udp_fd != -1 && ws->udp_state != UP_DISABLED && (FD_ISSET(ws->udp_fd, &rfds) || dtls_pending != 0)) { switch (ws->udp_state) { case UP_ACTIVE: @@ -1135,10 +992,16 @@ int handle_worker_commands(struct worker_st *ws) uint8_t cmd; struct msghdr hdr; union { - char x[20]; + char x[32]; + struct sockaddr_storage ss; } cmd_data; - int ret; - /*int cmd_data_len;*/ + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof(int))]; + } control_un; + struct cmsghdr *cmptr; + int ret, e; + int cmd_data_len; memset(&cmd_data, 0, sizeof(cmd_data)); @@ -1151,6 +1014,9 @@ int handle_worker_commands(struct worker_st *ws) memset(&hdr, 0, sizeof(hdr)); hdr.msg_iov = iov; hdr.msg_iovlen = 2; + + hdr.msg_control = control_un.control; + hdr.msg_controllen = sizeof(control_un.control); ret = recvmsg( ws->cmd_fd, &hdr, 0); if (ret == -1) { @@ -1162,11 +1028,39 @@ int handle_worker_commands(struct worker_st *ws) exit(1); } - /*cmd_data_len = ret - 1;*/ + cmd_data_len = ret - 1; switch(cmd) { case CMD_TERMINATE: exit(0); + case CMD_UDP_FD: + if ( (cmptr = CMSG_FIRSTHDR(&hdr)) != NULL && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) { + if (cmptr->cmsg_level != SOL_SOCKET) + return -1; + if (cmptr->cmsg_type != SCM_RIGHTS) + return -1; + memcpy(&ws->udp_fd, CMSG_DATA(cmptr), sizeof(int)); + if (cmd_data_len > 0) { + ret = connect(ws->udp_fd, (void*)&cmd_data.ss, cmd_data_len); + if (ret == -1) { + e = errno; + oclog(ws, LOG_ERR, "connect(): %s", strerror(e)); + goto udp_fd_fail; + } + } else { + oclog(ws, LOG_ERR, "Didn't receive peer's UDP address"); + goto udp_fd_fail; + } + + return 0; +udp_fd_fail: + close(ws->udp_fd); + ws->udp_fd = -1; + return -1; + } else { + return -1; + } + break; default: oclog(ws, LOG_ERR, "Unknown CMD 0x%x", (unsigned)cmd); exit(1); diff --git a/src/worker.h b/src/worker.h index 788bd5dd..50974975 100644 --- a/src/worker.h +++ b/src/worker.h @@ -44,12 +44,11 @@ typedef struct worker_st { struct sockaddr_storage remote_addr; /* peer's address */ socklen_t remote_addr_len; + int proto; /* AF_INET or AF_INET6 */ /* set after authentication */ int udp_fd; udp_port_state_t udp_state; - unsigned int udp_port; - int udp_port_proto; /* for mtu trials */ unsigned last_good_mtu;