diff --git a/src/Makefile.am b/src/Makefile.am index be2c14e7..fa3e6f43 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,9 +5,10 @@ EXTRA_DIST = ocserv.1 bin_PROGRAMS = ocserv -ocserv_SOURCES = main.c worker-vpn.c worker-auth.c tlslib.c cookies.c http-parser/http_parser.c \ +ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \ + cookies.c http-parser/http_parser.c \ vpn.h cookies.h tlslib.h http-parser/http_parser.h log.c tun.c tun.h \ - config.c worker-auth.h + config.c worker-auth.h ocserv_SOURCES += ocserv-args.def ocserv-args.c ocserv-args.h diff --git a/src/config.c b/src/config.c index 59e62b63..6d8f0bc2 100644 --- a/src/config.c +++ b/src/config.c @@ -85,9 +85,8 @@ static void parse_cfg_file(const char* file, struct cfg_st *config) { tOptionValue const * pov; const tOptionValue* val; -char tmpstr[256]; -char** auth; -unsigned auth_size; +char** auth = NULL; +unsigned auth_size = 0; unsigned j; pov = configFileLoad(file); @@ -127,6 +126,7 @@ unsigned j; READ_STRING("chroot-dir", config->chroot_dir, 0); READ_NUMERIC("cookie-validity", config->cookie_validity, 1); + READ_NUMERIC("auth-timeout", config->auth_timeout, 0); READ_STRING("cookie-db", config->db_file, 1); val = optionGetValue(pov, "run-as-user"); \ diff --git a/src/cookies.c b/src/cookies.c index 0b99be77..a4f86eb8 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -38,7 +38,7 @@ /* All the functions return zero on success and a negative value on error */ -int store_cookie(worker_st *server, const void* cookie, unsigned cookie_size, +int store_cookie(const struct cfg_st *config, const void* cookie, unsigned cookie_size, const struct stored_cookie_st* sc) { GDBM_FILE dbf; @@ -46,9 +46,9 @@ datum key; datum data; int ret; - dbf = gdbm_open((char*)server->config->db_file, 0, GDBM_WRCREAT, S_IRUSR|S_IWUSR, NULL); + dbf = gdbm_open((char*)config->db_file, 0, GDBM_WRCREAT, S_IRUSR|S_IWUSR, NULL); if (dbf == NULL) { - oclog(server, LOG_ERR, "Cannot open cookie database: %s", server->config->db_file); + syslog(LOG_ERR, "Cannot open cookie database: %s", config->db_file); return -1; } @@ -70,7 +70,7 @@ finish: return ret; } -int retrieve_cookie(worker_st *server, const void* cookie, unsigned cookie_size, +int retrieve_cookie(const struct cfg_st *config, const void* cookie, unsigned cookie_size, struct stored_cookie_st* sc) { GDBM_FILE dbf; @@ -78,9 +78,9 @@ datum key; datum data; int ret; - dbf = gdbm_open((char*)server->config->db_file, 0, GDBM_READER, 0, NULL); + dbf = gdbm_open((char*)config->db_file, 0, GDBM_READER, 0, NULL); if (dbf == NULL) { - oclog(server, LOG_ERR, "Cannot open cookie database: %s", server->config->db_file); + syslog(LOG_ERR, "Cannot open cookie database: %s", config->db_file); return -1; } @@ -104,7 +104,7 @@ finish: return ret; } -void expire_cookies(struct cfg_st *cfg) +void expire_cookies(const struct cfg_st *cfg) { GDBM_FILE dbf; datum key; diff --git a/src/cookies.h b/src/cookies.h index 1a30cad4..f3f182a2 100644 --- a/src/cookies.h +++ b/src/cookies.h @@ -5,15 +5,14 @@ struct __attribute__ ((__packed__)) stored_cookie_st { char username[MAX_USERNAME_SIZE]; - char tun_name[IFNAMSIZ]; time_t expiration; }; -int store_cookie(worker_st *server, const void* cookie, unsigned cookie_size, +int store_cookie(const struct cfg_st *, const void* cookie, unsigned cookie_size, const struct stored_cookie_st* sc); -void expire_cookies(struct cfg_st *cfg); +void expire_cookies(const struct cfg_st *cfg); -int retrieve_cookie(worker_st *server, const void* cookie, unsigned cookie_size, +int retrieve_cookie(const struct cfg_st *, const void* cookie, unsigned cookie_size, struct stored_cookie_st* sc); #endif diff --git a/src/log.c b/src/log.c index ef883e10..0892e46e 100644 --- a/src/log.c +++ b/src/log.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -63,7 +64,7 @@ const char *human_addr(const struct sockaddr *sa, socklen_t salen, return save_buf; } -int __attribute__ ((format(printf, 3, 4))) +void __attribute__ ((format(printf, 3, 4))) oclog(const worker_st * ws, int priority, const char *fmt, ...) { char buf[1024]; @@ -83,5 +84,7 @@ int __attribute__ ((format(printf, 3, 4))) if (ip) syslog(priority, "%s %s", ip, buf); else - syslog(priority, "[unknown] %s", ip, buf); + syslog(priority, "[unknown] %s", buf); + + return; } diff --git a/src/main-auth.c b/src/main-auth.c new file mode 100644 index 00000000..7ffdc4d0 --- /dev/null +++ b/src/main-auth.c @@ -0,0 +1,261 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static int send_auth_reply(cmd_auth_reply_t r, struct proc_list_st* proc, struct lease_st* lease) +{ + struct iovec iov[4]; + uint8_t cmd[2]; + struct msghdr hdr; + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof(int))]; + } control_un; + struct cmsghdr *cmptr; + + memset(&control_un, 0, sizeof(control_un)); + memset(&hdr, 0, sizeof(hdr)); + + cmd[0] = AUTH_REP; + cmd[1] = r; + + iov[0].iov_base = cmd; + iov[0].iov_len = 2; + hdr.msg_iovlen++; + + hdr.msg_iov = iov; + + if (r == REP_AUTH_OK && lease != NULL) { + iov[1].iov_base = proc->cookie; + iov[1].iov_len = sizeof(proc->cookie); + hdr.msg_iovlen++; + + iov[2].iov_base = lease->name; + iov[2].iov_len = sizeof(lease->name); + hdr.msg_iovlen++; + + iov[3].iov_base = proc->username; + iov[3].iov_len = MAX_USERNAME_SIZE; + hdr.msg_iovlen++; + + /* Send the tun fd */ + 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), &lease->fd, sizeof(int)); + } + + return(sendmsg(proc->fd, &hdr, 0)); +} + +static int handle_auth_cookie_req(const struct cfg_st *config, struct tun_st *tun, + const struct cmd_auth_cookie_req_st * req, struct lease_st **lease, + char username[MAX_USERNAME_SIZE]) +{ +int ret; +struct stored_cookie_st sc; + + ret = retrieve_cookie(config, req->cookie, sizeof(req->cookie), &sc); + if (ret < 0) { + return -1; + } + + ret = 0; /* cookie was found and valid */ + + memcpy(username, sc.username, MAX_USERNAME_SIZE); + + ret = open_tun(config, tun, lease); + if (ret < 0) + ret = -1; /* sorry */ + + return ret; +} + +static +int generate_and_store_cookie(const struct cfg_st* config, uint8_t *cookie, unsigned cookie_size) +{ +int ret; +struct stored_cookie_st sc; + + ret = gnutls_rnd(GNUTLS_RND_RANDOM, cookie, cookie_size); + if (ret < 0) + return -2; + + memset(&sc, 0, sizeof(sc)); + sc.expiration = time(0) + config->cookie_validity; + + ret = store_cookie(config, cookie, cookie_size, &sc); + if (ret < 0) + return -1; + + return 0; +} + +static int handle_auth_req(const struct cfg_st *config, struct tun_st *tun, + const struct cmd_auth_req_st * req, struct lease_st **lease, + char username[MAX_USERNAME_SIZE]) +{ +int ret; +#warning fix auth + if (strcmp(req->user, "test") == 0 && strcmp(req->pass, "test") == 0) + ret = 0; + else + ret = -1; + + memcpy(username, req->user, MAX_USERNAME_SIZE); + + if (ret == 0) { /* open tun */ + ret = open_tun(config, tun, lease); + if (ret < 0) + ret = -1; /* sorry */ + } + + return ret; +} + +int handle_commands(const struct cfg_st *config, struct tun_st *tun, + struct proc_list_st* proc) +{ + struct iovec iov[2]; + char buf[128]; + uint8_t cmd; + struct msghdr hdr; + struct lease_st *lease; + union { + struct cmd_auth_req_st auth; + struct cmd_auth_cookie_req_st cauth; + } cmd_data; + int ret, cmd_data_len; + const char* peer_ip; + + peer_ip = human_addr((void*)&proc->remote_addr, proc->remote_addr_len, buf, sizeof(buf)); + + memset(&cmd_data, 0, sizeof(cmd_data)); + + iov[0].iov_base = &cmd; + iov[0].iov_len = 1; + + iov[1].iov_base = &cmd_data; + iov[1].iov_len = sizeof(cmd_data); + + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_iov = iov; + hdr.msg_iovlen = 2; + + ret = recvmsg( proc->fd, &hdr, 0); + if (ret == -1) { + syslog(LOG_ERR, "Cannot obtain data from command socket (pid: %d, peer: %s).", proc->pid, peer_ip); + return -1; + } + + if (ret == 0) { + return -1; + } + + cmd_data_len = ret - 1; + + switch(cmd) { + case AUTH_REQ: + case AUTH_COOKIE_REQ: + + 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); + return -2; + } + + ret = handle_auth_req(config, tun, &cmd_data.auth, &lease, proc->username); + } 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); + return -2; + } + + ret = handle_auth_cookie_req(config, tun, &cmd_data.cauth, &lease, proc->username); + } + + if (ret == 0) { + if (cmd == AUTH_REQ) { + /* generate and store cookie */ + ret = generate_and_store_cookie(config, + proc->cookie, + COOKIE_SIZE); + if (ret < 0) + return -2; + } else { /* copy cookie */ + memcpy(proc->cookie, cmd_data.cauth.cookie, + COOKIE_SIZE); + } + + syslog(LOG_INFO, "User '%s' authenticated", proc->username); + ret = send_auth_reply(REP_AUTH_OK, proc, lease); + if (ret < 0) { + syslog(LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip); + return -2; + } + + proc->lease = lease; + proc->lease->in_use = 1; + if (lease->fd >= 0) + close(lease->fd); + lease->fd = -1; + } else { + syslog(LOG_INFO, "Failed authentication attempt for user '%s'", proc->username); + ret = send_auth_reply( REP_AUTH_FAILED, proc, NULL); + if (ret < 0) { + syslog(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); + return -2; + } + + return 0; +} diff --git a/src/main.c b/src/main.c index 7b8dcfcc..3f8a69b5 100644 --- a/src/main.c +++ b/src/main.c @@ -49,20 +49,6 @@ struct listen_list_st { int fd; }; -struct proc_list_st { - struct list_head list; - int fd; - pid_t pid; - struct sockaddr_storage remote_addr; /* peer address */ - socklen_t remote_addr_len; - - /* the tun lease this process has */ - struct lease_st* lease; -}; - -static int handle_commands(const struct cfg_st *config, struct tun_st *tun, - struct proc_list_st* proc); - static void tls_log_func(int level, const char *str) { syslog(LOG_DEBUG, "Debug[<%d>]: %s", level, str); @@ -209,8 +195,7 @@ worker_st * ws; static int verify_certificate_cb(gnutls_session_t session) { unsigned int status; - int ret, type; - gnutls_datum_t out; + int ret; worker_st * ws; ws = gnutls_session_get_ptr(session); @@ -310,7 +295,7 @@ static void clear_proc_list(struct proc_list_st* clist) int main(int argc, char** argv) { - int fd, pid, e; + int fd, pid; struct tls_st creds; struct listen_list_st llist; struct proc_list_st clist; @@ -326,9 +311,6 @@ int main(int argc, char** argv) struct worker_st ws; struct cfg_st config; - struct sockaddr_storage tmp_addr; - socklen_t tmp_addr_len; - INIT_LIST_HEAD(&clist.list); tun_st_init(&tun); @@ -417,7 +399,7 @@ int main(int argc, char** argv) daemon(0, 0); alarm(config.cookie_validity + 300); - openlog("ocserv", LOG_PID, LOG_LOCAL0); + openlog("ocserv", LOG_PID|LOG_NDELAY, LOG_LOCAL0); syslog_open = 1; for (;;) { @@ -500,15 +482,14 @@ int main(int argc, char** argv) } else if (pid == -1) { fork_failed: close(cmd_fd[0]); - free(ctmp); } else { /* parent */ ctmp = calloc(1, sizeof(struct proc_list_st)); if (ctmp == NULL) { kill(pid, SIGTERM); goto fork_failed; } - memcpy(&ctmp->remote_addr, &tmp_addr, tmp_addr_len); - ctmp->remote_addr_len = tmp_addr_len; + memcpy(&ctmp->remote_addr, &ws.remote_addr, ws.remote_addr_len); + ctmp->remote_addr_len = ws.remote_addr_len; ctmp->pid = pid; ctmp->fd = cmd_fd[0]; @@ -544,17 +525,8 @@ fork_failed: need_to_expire_cookies = 0; pid = fork(); if (pid == 0) { /* child */ - /* Drop privileges after this point */ - drop_privileges(&config); - - list_for_each(pos, &llist.list) { - ltmp = - list_entry(pos, - struct - listen_list_st, - list); - close(ltmp->fd); - } + clear_listen_list(&llist); + clear_proc_list(&clist); expire_cookies(&config); exit(0); @@ -566,144 +538,3 @@ fork_failed: return 0; } -static int send_auth_reply(int fd, cmd_auth_reply_t r, struct lease_st* lease) -{ - struct iovec iov[2]; - uint8_t cmd[2]; - struct msghdr hdr; - union { - struct cmd_auth_req_st auth; - } cmd_data; - int ret; - union { - struct cmsghdr cm; - char control[CMSG_SPACE(sizeof(int))]; - } control_un; - struct cmsghdr *cmptr; - - memset(&control_un, 0, sizeof(control_un)); - memset(&hdr, 0, sizeof(hdr)); - - cmd[0] = AUTH_REP; - cmd[1] = r; - - iov[0].iov_base = cmd; - iov[0].iov_len = 2; - hdr.msg_iovlen++; - - hdr.msg_iov = iov; - - if (r == REP_AUTH_OK && lease != NULL) { - iov[1].iov_base = lease->name; - iov[1].iov_len = sizeof(lease->name); - hdr.msg_iovlen++; - - /* Send the tun fd */ - 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; - *((int *) CMSG_DATA(cmptr)) = lease->fd; - } - - return(sendmsg(fd, &hdr, 0)); -} - -static int handle_auth_req(const struct cfg_st *config, struct tun_st *tun, - const struct cmd_auth_req_st * req, struct lease_st **lease) -{ -int ret; -#warning fix auth - if (strcmp(req->user, "test") == 0 && strcmp(req->pass, "test") == 0) - ret = 0; - else - ret = -1; - - if (ret == 0) { /* open tun */ - ret = open_tun(config, tun, lease); - if (ret < 0) - ret = -1; /* sorry */ - } - - return ret; -} - -static int handle_commands(const struct cfg_st *config, struct tun_st *tun, - struct proc_list_st* proc) -{ - struct iovec iov[2]; - char buf[128]; - uint8_t cmd; - struct msghdr hdr; - struct lease_st *lease; - union { - struct cmd_auth_req_st auth; - } cmd_data; - int ret, cmd_data_len; - const char* peer_ip; - - peer_ip = human_addr((void*)&proc->remote_addr, proc->remote_addr_len, buf, sizeof(buf)); - - memset(&cmd_data, 0, sizeof(cmd_data)); - - iov[0].iov_base = &cmd; - iov[0].iov_len = 1; - - iov[1].iov_base = &cmd_data; - iov[1].iov_len = sizeof(cmd_data); - - memset(&hdr, 0, sizeof(hdr)); - hdr.msg_iov = iov; - hdr.msg_iovlen = 2; - - ret = recvmsg( proc->fd, &hdr, 0); - if (ret == -1) { - syslog(LOG_ERR, "Cannot obtain data from command socket (pid: %d, peer: %s).", proc->pid, peer_ip); - return -1; - } - - if (ret == 0) { - return -1; - } - - cmd_data_len = ret - 1; - - switch(cmd) { - case 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); - return -2; - } - - ret = handle_auth_req(config, tun, &cmd_data.auth, &lease); - if (ret == 0) { - ret = send_auth_reply(proc->fd, REP_AUTH_OK, lease); - if (ret < 0) { - syslog(LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip); - return -2; - } - - proc->lease = lease; - proc->lease->in_use = 1; - if (lease->fd >= 0) - close(lease->fd); - lease->fd = -1; - } else { - ret = send_auth_reply(proc->fd, REP_AUTH_FAILED, NULL); - if (ret < 0) { - syslog(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); - return -2; - } - - return 0; -} diff --git a/src/ocserv-args.c b/src/ocserv-args.c index 4d2f178d..16421af6 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 January 30, 2013 at 05:50:16 PM by AutoGen 5.16 + * It has been AutoGen-ed January 30, 2013 at 08:53:35 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 85bf33ac..cb195cbe 100644 --- a/src/ocserv-args.def +++ b/src/ocserv-args.def @@ -85,6 +85,10 @@ tls-priorities = "PERFORMANCE:%SERVER_PRECEDENCE" # The default server directory #chroot-dir = /path/to/chroot +# The time (in seconds) that a client is allowed to stay connected prior +# to authentication +auth-timeout = 40 + # Cookie validity time (in seconds) # Once a client is authenticated he's provided a cookie with # which he can reconnect. This option sets the maximum lifetime diff --git a/src/ocserv-args.h b/src/ocserv-args.h index 730e7248..7bc18219 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 January 30, 2013 at 05:50:16 PM by AutoGen 5.16 + * It has been AutoGen-ed January 30, 2013 at 08:53:35 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 bc8b4923..2e9667f2 100644 --- a/src/sample.config +++ b/src/sample.config @@ -6,6 +6,10 @@ auth = "pam" # Use listen-host to limit to specific IPs or to the IPs of a provided hostname. # listen-host = [IP|HOSTNAME] +# The time (in seconds) that a client is allowed to stay connected prior +# to authentication +auth-timeout = 40 + # TCP port number tcp-port = 3333 diff --git a/src/tun.c b/src/tun.c index c2699e34..31abb59e 100644 --- a/src/tun.c +++ b/src/tun.c @@ -112,6 +112,9 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct memcpy(&lease->rip4, t4, 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)); @@ -181,6 +184,9 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct lease->rip6_len = sizeof(struct sockaddr_in6); memcpy(&lease->rip6, t6, 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)); @@ -227,7 +233,6 @@ static int set_network_info( const struct lease_st *lease) { struct ifreq ifr; int fd, ret; - void* p; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) @@ -296,7 +301,6 @@ static int set_network_info( const struct lease_st *lease) syslog(LOG_ERR, "%s: Could not bring up interface.\n", lease->name); } -fail: close(fd); return ret; } diff --git a/src/vpn.h b/src/vpn.h index 8510b2d9..7233b702 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -56,6 +56,7 @@ struct cfg_st { const char *priorities; const char *chroot_dir; /* where the xml files are served from */ time_t cookie_validity; /* in seconds */ + unsigned auth_timeout; /* timeout of HTTP auth */ const char *db_file; unsigned foreground; @@ -68,6 +69,10 @@ struct cfg_st { #include +#define MAX_USERNAME_SIZE 64 +#define MAX_PASSWORD_SIZE 64 +#define COOKIE_SIZE 32 + struct tls_st { gnutls_certificate_credentials_t xcred; gnutls_priority_t cprio; @@ -75,8 +80,6 @@ struct tls_st { typedef struct worker_st { gnutls_session_t session; - char tun_name[IFNAMSIZ]; - int tun_fd; int cmd_fd; int conn_fd; @@ -85,11 +88,15 @@ typedef struct worker_st { struct sockaddr_storage remote_addr; /* peer's address */ socklen_t remote_addr_len; + + /* the following are set only if authentication is complete */ + char tun_name[IFNAMSIZ]; + char username[MAX_USERNAME_SIZE]; + uint8_t cookie[COOKIE_SIZE]; + unsigned auth_ok; + int tun_fd; } worker_st; -#define MAX_USERNAME_SIZE 64 -#define MAX_PASSWORD_SIZE 64 -#define COOKIE_SIZE 32 enum { HEADER_COOKIE = 1, @@ -110,11 +117,27 @@ void vpn_server(struct worker_st* ws, struct tls_st *creds); const char *human_addr(const struct sockaddr *sa, socklen_t salen, void *buf, size_t buflen); -int __attribute__ ((format(printf, 3, 4))) +void __attribute__ ((format(printf, 3, 4))) oclog(const worker_st * server, int priority, const char *fmt, ...); int cmd_parser (int argc, char **argv, struct cfg_st* config); +struct proc_list_st { + struct list_head list; + int fd; + pid_t pid; + struct sockaddr_storage remote_addr; /* peer address */ + socklen_t remote_addr_len; + char username[MAX_USERNAME_SIZE]; /* the owner */ + uint8_t cookie[COOKIE_SIZE]; /* the cookie associate with the session */ + + /* the tun lease this process has */ + struct lease_st* lease; +}; + +int handle_commands(const struct cfg_st *config, struct tun_st *tun, + struct proc_list_st* proc); + /* Helper casts */ #define SA_IN_P(p) (&((struct sockaddr_in *)(p))->sin_addr) #define SA_IN_U8_P(p) ((uint8_t*)(&((struct sockaddr_in *)(p))->sin_addr)) diff --git a/src/worker-auth.c b/src/worker-auth.c index cec54ded..d6290ca8 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -55,12 +55,10 @@ const char login_msg[] = "\r\n" int get_auth_handler(worker_st *ws) { -int ret; - tls_puts(ws->session, "HTTP/1.1 200 OK\r\n"); tls_puts(ws->session, "Connection: close\r\n"); tls_puts(ws->session, "Content-Type: text/xml\r\n"); - tls_printf(ws->session, "Content-Length: %u\r\n", sizeof(login_msg)-1); + tls_printf(ws->session, "Content-Length: %u\r\n", (unsigned int)sizeof(login_msg)-1); tls_puts(ws->session, "X-Transcend-Version: 1\r\n"); tls_puts(ws->session, "\r\n"); @@ -109,12 +107,6 @@ static int send_auth_req(int fd, const struct cmd_auth_req_st* r) struct iovec iov[2]; uint8_t cmd; struct msghdr hdr; - int ret; - union { - struct cmsghdr cm; - char control[CMSG_SPACE(sizeof(int))]; - } control_un; - struct cmsghdr *cmptr; memset(&hdr, 0, sizeof(hdr)); @@ -132,11 +124,33 @@ static int send_auth_req(int fd, const struct cmd_auth_req_st* r) return(sendmsg(fd, &hdr, 0)); } +static int send_auth_cookie_req(int fd, const struct cmd_auth_cookie_req_st* r) +{ + struct iovec iov[2]; + uint8_t cmd; + struct msghdr hdr; + + memset(&hdr, 0, sizeof(hdr)); + + cmd = AUTH_COOKIE_REQ; + + iov[0].iov_base = &cmd; + iov[0].iov_len = 1; + + iov[1].iov_base = (void*)r; + iov[1].iov_len = sizeof(*r); + + hdr.msg_iov = iov; + hdr.msg_iovlen = 2; + + return(sendmsg(fd, &hdr, 0)); +} + static int recv_auth_reply(worker_st *ws) { - struct iovec iov[3]; + struct iovec iov[2]; uint8_t cmd = 0; - struct cmd_auth_resp_st resp; + struct cmd_auth_reply_st resp; struct msghdr hdr; int ret; @@ -149,21 +163,19 @@ static int recv_auth_reply(worker_st *ws) iov[0].iov_base = &cmd; iov[0].iov_len = 1; - iov[1].iov_base = &resp.reply; - iov[1].iov_len = 1; - - iov[2].iov_base = resp.vname; - iov[2].iov_len = sizeof(resp.vname); + iov[1].iov_base = &resp; + iov[1].iov_len = sizeof(resp); memset(&hdr, 0, sizeof(hdr)); hdr.msg_iov = iov; - hdr.msg_iovlen = 3; + 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 <= 0) { + if (ret < sizeof(resp)+1) { + oclog(ws, LOG_ERR, "Received incorrect data (%d, expected %d) from main", ret, (int)sizeof(resp)+1); return -1; } if (cmd != AUTH_REP) @@ -177,8 +189,11 @@ static int recv_auth_reply(worker_st *ws) if (cmptr->cmsg_type != SCM_RIGHTS) return -1; - ws->tun_fd = *((int *) CMSG_DATA(cmptr)); + memcpy(&ws->tun_fd, CMSG_DATA(cmptr), sizeof(int)); memcpy(ws->tun_name, resp.vname, sizeof(ws->tun_name)); + memcpy(ws->username, resp.user, sizeof(ws->username)); + memcpy(ws->cookie, resp.cookie, sizeof(ws->cookie)); + ws->auth_ok = 1; } else return -1; break; @@ -189,21 +204,9 @@ static int recv_auth_reply(worker_st *ws) return 0; } -/* sends an authentication request to main thread and waits for - * a reply */ -static int auth_user(worker_st *ws, const struct cmd_auth_req_st* areq) -{ -int ret; - - ret = send_auth_req(ws->cmd_fd, areq); - if (ret < 0) - return ret; - - return recv_auth_reply(ws); -} - +/* grabs the username from the session certificate */ static -int get_cert_info(worker_st *ws, struct cmd_auth_req_st *areq, const char** reason) +int get_cert_info(worker_st *ws, char* user, unsigned user_size) { const gnutls_datum_t * cert; unsigned int ncerts; @@ -214,16 +217,13 @@ int ret; cert = gnutls_certificate_get_peers (ws->session, &ncerts); if (cert == NULL) { - *reason = "No certificate found"; return -1; } - areq->tls_auth_ok = 1; if (ws->config->cert_user_oid) { /* otherwise certificate username is ignored */ - ret = get_cert_username(ws, cert, areq->cert_user, sizeof(areq->cert_user)); + ret = get_cert_username(ws, cert, user, user_size); if (ret < 0) { oclog(ws, LOG_ERR, "Cannot get username (%s) from certificate", ws->config->cert_user_oid); - *reason = "No username in certificate"; return -1; } } @@ -231,30 +231,75 @@ int ret; return 0; } +/* sends an authentication request to main thread and waits for + * a reply. + * Returns 0 on success. + */ +static int auth_user(worker_st *ws, struct cmd_auth_req_st* areq) +{ +int ret; + + if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) { + ret = get_cert_info(ws, areq->cert_user, sizeof(areq->cert_user)); + if (ret < 0) + return -1; + + areq->tls_auth_ok = 1; + } + + oclog(ws, LOG_DEBUG, "Sending authentication request"); + ret = send_auth_req(ws->cmd_fd, areq); + if (ret < 0) + return ret; + + return recv_auth_reply(ws); +} + +/* sends a cookie authentication request to main thread and waits for + * a reply. + * Returns 0 on success. + */ +int auth_cookie(worker_st *ws, void* cookie, size_t cookie_size) +{ +int ret; +struct cmd_auth_cookie_req_st areq; + + if (cookie_size != sizeof(areq.cookie)) + return -1; + + if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) { + ret = get_cert_info(ws, areq.cert_user, sizeof(areq.cert_user)); + if (ret < 0) + return -1; + + areq.tls_auth_ok = 1; + } + + memcpy(areq.cookie, cookie, sizeof(areq.cookie)); + + oclog(ws, LOG_DEBUG, "Sending cookie authentication request"); + ret = send_auth_cookie_req(ws->cmd_fd, &areq); + if (ret < 0) + return ret; + + return recv_auth_reply(ws); +} + + int post_old_auth_handler(worker_st *ws) { int ret; struct req_data_st *req = ws->parser->data; const char* reason = "Authentication failed"; -unsigned char cookie[COOKIE_SIZE]; char str_cookie[2*COOKIE_SIZE+1]; char * username = NULL; char * password = NULL; char *p; unsigned int i; -struct stored_cookie_st sc; struct cmd_auth_req_st areq; memset(&areq, 0, sizeof(areq)); - if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) { - ret = get_cert_info(ws, &areq, &reason); - if (ret < 0) - goto auth_fail; - - username = areq.cert_user; - } - if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) { /* body should be "username=test&password=test" */ username = strstr(req->body, "username="); @@ -296,32 +341,20 @@ struct cmd_auth_req_st areq; } ret = auth_user(ws, &areq); - if (ret < 0) + if (ret < 0) { + if (username) + oclog(ws, LOG_INFO, "Failed authentication attempt for '%s'", username); + else + oclog(ws, LOG_INFO, "Failed authentication attempt"); goto auth_fail; - - oclog(ws, LOG_INFO, "User '%s' logged in\n", username); - - /* generate cookie */ - ret = gnutls_rnd(GNUTLS_RND_RANDOM, cookie, sizeof(cookie)); - GNUTLS_FATAL_ERR(ret); - - p = str_cookie; - for (i=0;iconfig->cookie_validity; - if (username) - snprintf(sc.username, sizeof(sc.username), "%s", username); - memcpy(sc.tun_name, ws->tun_name, sizeof(sc.tun_name)); + oclog(ws, LOG_INFO, "User '%s' logged in\n", ws->username); - /* store cookie */ - ret = store_cookie(ws, cookie, sizeof(cookie), &sc); - if (ret < 0) { - reason = "Storage issue"; - goto auth_fail; + p = str_cookie; + for (i=0;icookie);i++) { + sprintf(p, "%.2x", (unsigned int)ws->cookie[i]); + p+=2; } /* reply */ @@ -355,24 +388,14 @@ struct req_data_st *req = ws->parser->data; const char* reason = "Authentication failed"; unsigned char cookie[COOKIE_SIZE]; char str_cookie[2*COOKIE_SIZE+1]; -char cert_username[MAX_USERNAME_SIZE]; char * username = NULL; char * password = NULL; char *p; unsigned int i; -struct stored_cookie_st sc; struct cmd_auth_req_st areq; memset(&areq, 0, sizeof(areq)); - if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) { - ret = get_cert_info(ws, &areq, &reason); - if (ret < 0) - goto auth_fail; - - username = areq.cert_user; - } - if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) { /* body should contain testtest */ username = strstr(req->body, XMLUSER); @@ -414,10 +437,15 @@ struct cmd_auth_req_st areq; } ret = auth_user(ws, &areq); - if (ret < 0) + if (ret < 0) { + if (username) + oclog(ws, LOG_INFO, "Failed authentication attempt for '%s'", username); + else + oclog(ws, LOG_INFO, "Failed authentication attempt"); goto auth_fail; + } - oclog(ws, LOG_INFO, "User '%s' logged in\n", username); + oclog(ws, LOG_INFO, "User '%s' logged in\n", ws->username); /* generate cookie */ ret = gnutls_rnd(GNUTLS_RND_RANDOM, cookie, sizeof(cookie)); @@ -429,19 +457,6 @@ struct cmd_auth_req_st areq; p+=2; } - memset(&sc, 0, sizeof(sc)); - sc.expiration = time(0) + ws->config->cookie_validity; - if (username) - snprintf(sc.username, sizeof(sc.username), "%s", username); - memcpy(sc.tun_name, ws->tun_name, sizeof(sc.tun_name)); - - /* store cookie */ - ret = store_cookie(ws, cookie, sizeof(cookie), &sc); - if (ret < 0) { - reason = "Storage issue"; - goto auth_fail; - } - /* reply */ tls_puts(ws->session, "HTTP/1.1 200 OK\r\n"); diff --git a/src/worker-auth.h b/src/worker-auth.h new file mode 100644 index 00000000..4e21cef7 --- /dev/null +++ b/src/worker-auth.h @@ -0,0 +1,54 @@ +#ifndef WORKER_AUTH_H +#define WORKER_AUTH_H + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + AUTH_REQ = 1, + AUTH_COOKIE_REQ = 2, + AUTH_REP = 3, +} cmd_request_t; + +typedef enum { + REP_AUTH_OK = 0, + REP_AUTH_FAILED = 1, +} cmd_auth_reply_t; + +/* AUTH_COOKIE_REQ */ +struct __attribute__ ((__packed__)) cmd_auth_cookie_req_st { + uint8_t cookie[COOKIE_SIZE]; + uint8_t tls_auth_ok; + char cert_user[MAX_USERNAME_SIZE]; +}; + +/* AUTH_REQ */ +struct __attribute__ ((__packed__)) cmd_auth_req_st { + uint8_t user_pass_present; + char user[MAX_USERNAME_SIZE]; + char pass[MAX_PASSWORD_SIZE]; + uint8_t tls_auth_ok; + char cert_user[MAX_USERNAME_SIZE]; +}; + +/* AUTH_REP */ +struct __attribute__ ((__packed__)) cmd_auth_reply_st { + uint8_t reply; + uint8_t cookie[COOKIE_SIZE]; + char vname[IFNAMSIZ]; /* interface name */ + char user[MAX_USERNAME_SIZE]; +}; + +int auth_cookie(worker_st *ws, void* cookie, size_t cookie_size); + +int get_auth_handler(worker_st *server); +int post_old_auth_handler(worker_st *server); +int post_new_auth_handler(worker_st *server); + +#endif diff --git a/src/worker-vpn.c b/src/worker-vpn.c index a31c5cb7..b97133ae 100644 --- a/src/worker-vpn.c +++ b/src/worker-vpn.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,11 @@ /* HTTP requests prior to disconnection */ #define MAX_HTTP_REQUESTS 8 +static void handle_alarm(int signo) +{ + exit(1); +} + static int connect_handler(worker_st *ws); typedef int (*url_handler_fn)(worker_st*); @@ -195,6 +201,11 @@ void vpn_server(struct worker_st* ws, struct tls_st *creds) url_handler_fn fn; int requests_left = MAX_HTTP_REQUESTS; + signal(SIGALRM, handle_alarm); + + if (ws->config->auth_timeout) + alarm(ws->config->auth_timeout); + syslog(LOG_INFO, "Accepted connection from %s", human_addr((void*)&ws->remote_addr, ws->remote_addr_len, buf, sizeof(buf))); @@ -245,7 +256,10 @@ restart: /* parse as we go */ do { nrecvd = tls_recv(session, buf, sizeof(buf)); - GNUTLS_FATAL_ERR(nrecvd); + if (nrecvd <= 0) { + oclog(ws, LOG_INFO, "Error receiving client data"); + exit(1); + } nparsed = http_parser_execute(&parser, &settings, (void*)buf, nrecvd); if (nparsed == 0) { @@ -308,10 +322,10 @@ finish: static int get_ip(struct worker_st* ws, int fd, int family, unsigned int local, struct vpn_st* vinfo, char** buffer, size_t* buffer_size) { -void* *ptr; +void* ptr; const void* p; struct ifreq ifr; -unsigned int i, flags; +unsigned int flags; int ret, e; memset(&ifr, 0, sizeof(ifr)); @@ -374,10 +388,8 @@ fail: static int get_rt_vpn_info(worker_st * ws, struct vpn_st* vinfo, char* buffer, size_t buffer_size) { -unsigned int i; int fd, ret; struct ifreq ifr; -const char* p; memset(vinfo, 0, sizeof(*vinfo)); vinfo->name = ws->tun_name; @@ -453,7 +465,6 @@ fd_set rfds; int l, pktlen; int tls_fd, max; unsigned i; -struct stored_cookie_st sc; struct vpn_st vinfo; char* buffer; unsigned int buffer_size; @@ -466,14 +477,21 @@ unsigned int buffer_size; exit(1); } - ret = retrieve_cookie(ws, req->cookie, sizeof(req->cookie), &sc); - if (ret < 0) { - oclog(ws, LOG_INFO, "Connect request without authentication"); - tls_puts(ws->session, "HTTP/1.1 503 Service Unavailable\r\n\r\n"); - tls_fatal_close(ws->session, GNUTLS_A_ACCESS_DENIED); - exit(1); + if (ws->auth_ok == 0) { + /* authentication didn't occur in this session. Use the + * cookie */ + ret = auth_cookie(ws, req->cookie, sizeof(req->cookie)); + if (ret < 0) { + oclog(ws, LOG_INFO, "Failed cookie authentication attempt"); + tls_puts(ws->session, "HTTP/1.1 503 Service Unavailable\r\n\r\n"); + tls_fatal_close(ws->session, GNUTLS_A_ACCESS_DENIED); + exit(1); + } } - memcpy(ws->tun_name, sc.tun_name, sizeof(ws->tun_name)); + + /* turn of the alarm */ + if (ws->config->auth_timeout) + alarm(0); if (strcmp(req->url, "/CSCOSSLC/tunnel") != 0) { oclog(ws, LOG_INFO, "Bad connect request: '%s'\n", req->url); @@ -489,8 +507,6 @@ unsigned int buffer_size; return -1; } - oclog(ws, LOG_INFO, "User '%s' connected.\n", sc.username); - buffer_size = 2048; buffer = malloc(buffer_size); if (buffer == NULL) {