From b1af6f2829f612d5593412500107cffbaa1850de Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Sat, 18 Jan 2014 10:24:47 +0100 Subject: [PATCH] enabling cisco-client-compat allows 'stealing' of processes. This change puts a proc_st that its client has terminated to a "zombie" state. That state will allow a client that connects later using the same TLS session ID to reclaim it. That way clients that try to authenticate by sending their credentials in different sessions can still authenticate with ocserv. That however puts more trust to worker processes (as the main process has no way of telling whether a TLS session is certainly resumed). --- doc/sample.config | 2 +- src/common.c | 2 + src/cookies.c | 6 +- src/ipc.proto | 10 + src/main-auth.c | 104 +++++-- src/main-ctl-handler.c | 10 +- src/main-misc.c | 662 ++++++++++++++++++++++++----------------- src/main-resume.c | 140 +++++---- src/main.c | 18 +- src/main.h | 23 +- src/vpn.h | 1 + src/worker-auth.c | 102 ++++--- src/worker-vpn.c | 17 -- src/worker.h | 7 +- 14 files changed, 666 insertions(+), 438 deletions(-) diff --git a/doc/sample.config b/doc/sample.config index ecd7b2a2..d3866770 100644 --- a/doc/sample.config +++ b/doc/sample.config @@ -228,5 +228,5 @@ route-del-cmd = "ip route delete %R dev %D" # certificate even if they are authenticating via a previously granted # cookie. Legacy CISCO clients do not do that, and thus this option # should be set for them. -#always-require-cert = false +cisco-client-compat = true diff --git a/src/common.c b/src/common.c index 4364b6a9..5c503aee 100644 --- a/src/common.c +++ b/src/common.c @@ -31,6 +31,8 @@ cmd_request_t cmd = _cmd; switch(cmd) { case AUTH_INIT: return "auth init"; + case AUTH_REINIT: + return "auth reinit"; case AUTH_REP: return "auth reply"; case AUTH_REQ: diff --git a/src/cookies.c b/src/cookies.c index 29240b75..54dba1f2 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -129,16 +129,16 @@ int generate_cookie(main_server_st *s, struct proc_st* proc) int ret; struct stored_cookie_st sc; - ret = gnutls_rnd(GNUTLS_RND_NONCE, proc->session_id, sizeof(proc->session_id)); + ret = gnutls_rnd(GNUTLS_RND_NONCE, proc->dtls_session_id, sizeof(proc->dtls_session_id)); if (ret < 0) return -1; - proc->session_id_size = sizeof(proc->session_id); + proc->dtls_session_id_size = sizeof(proc->dtls_session_id); memcpy(sc.username, proc->username, sizeof(proc->username)); memcpy(sc.groupname, proc->groupname, sizeof(proc->groupname)); memcpy(sc.hostname, proc->hostname, sizeof(proc->hostname)); - memcpy(sc.session_id, proc->session_id, sizeof(proc->session_id)); + memcpy(sc.session_id, proc->dtls_session_id, sizeof(proc->dtls_session_id)); sc.expiration = time(0) + s->config->cookie_validity; diff --git a/src/ipc.proto b/src/ipc.proto index 20375c6b..406bac81 100644 --- a/src/ipc.proto +++ b/src/ipc.proto @@ -7,6 +7,16 @@ message auth_init_msg optional string cert_user_name = 4; optional string cert_group_name = 5; optional string hostname = 6; + required bytes session_id = 7; +} + +/* AUTH_REINIT - used in cisco compatible clients, to + * revive open authentication in zombie mode. */ +message auth_reinit_msg +{ + required bool tls_auth_ok = 1 [default = false]; + required string password = 2; + required bytes session_id = 3; } /* AUTH_COOKIE_REQ */ diff --git a/src/main-auth.c b/src/main-auth.c index ccb1b74e..85e45f78 100644 --- a/src/main-auth.c +++ b/src/main-auth.c @@ -80,14 +80,14 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc, msg.has_cookie = 1; msg.cookie.data = proc->cookie; msg.cookie.len = COOKIE_SIZE; - + msg.has_session_id = 1; - msg.session_id.data = proc->session_id; - msg.session_id.len = sizeof(proc->session_id); - + msg.session_id.data = proc->dtls_session_id; + msg.session_id.len = sizeof(proc->dtls_session_id); + msg.vname = proc->tun_lease.name; msg.user_name = proc->username; - + msg.ipv4_dns = proc->config.ipv4_dns; msg.ipv6_dns = proc->config.ipv6_dns; msg.ipv4_nbns = proc->config.ipv4_nbns; @@ -108,13 +108,13 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc, msg.has_net_priority = 1; msg.net_priority = proc->config.net_priority; } - + msg.n_routes = proc->config.routes_size; for (i=0;iconfig.routes_size;i++) { mslog(s, proc, LOG_DEBUG, "sending route '%s'", proc->config.routes[i]); msg.routes = proc->config.routes; } - + ret = send_socket_msg_to_worker(s, proc, AUTH_REP, proc->tun_lease.fd, &msg, (pack_size_func)auth_reply_msg__get_packed_size, @@ -127,7 +127,7 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc, (pack_size_func)auth_reply_msg__get_packed_size, (pack_func)auth_reply_msg__pack); } - + if (ret < 0) { int e = errno; mslog(s, proc, LOG_ERR, "send_msg: %s", strerror(e)); @@ -141,7 +141,7 @@ int send_auth_reply_msg(main_server_st* s, struct proc_st* proc) { AuthReplyMsg msg = AUTH_REPLY_MSG__INIT; char tmp[MAX_MSG_SIZE] = ""; - + int ret; if (proc->auth_ctx == NULL) @@ -172,7 +172,7 @@ static int check_user_group_status(main_server_st *s, struct proc_st* proc, mslog(s, proc, LOG_INFO, "user '%s' presented no certificate", proc->username); return -1; } - + if (tls_auth_ok != 0) { if (proc->username[0] == 0) { memcpy(proc->username, cert_user, sizeof(proc->username)); @@ -192,7 +192,7 @@ static int check_user_group_status(main_server_st *s, struct proc_st* proc, } } } - + return 0; } @@ -212,18 +212,18 @@ time_t now = time(0); if (sc.expiration < now) return -1; - + memcpy(proc->cookie, req->cookie.data, req->cookie.len); memcpy(proc->username, sc.username, sizeof(proc->username)); memcpy(proc->groupname, sc.groupname, sizeof(proc->groupname)); memcpy(proc->hostname, sc.hostname, sizeof(proc->hostname)); - memcpy(proc->session_id, sc.session_id, sizeof(proc->session_id)); - proc->session_id_size = sizeof(proc->session_id); - + memcpy(proc->dtls_session_id, sc.session_id, sizeof(proc->dtls_session_id)); + proc->dtls_session_id_size = sizeof(proc->dtls_session_id); + proc->username[sizeof(proc->username)-1] = 0; proc->groupname[sizeof(proc->groupname)-1] = 0; proc->hostname[sizeof(proc->hostname)-1] = 0; - + memcpy(proc->ipv4_seed, sc.ipv4_seed, sizeof(proc->ipv4_seed)); proc->seeds_are_set = 1; @@ -245,7 +245,12 @@ const char* ip; ipbuf, sizeof(ipbuf)); if (req->user_name == NULL && s->config->auth_types & AUTH_TYPE_USERNAME_PASS) { - mslog(s, proc, LOG_DEBUG, "auth init from '%s' with no username present", ip); + mslog(s, proc, LOG_DEBUG, "auth init from '%s' with no username present", ip); + return -1; + } + + if (req->session_id.len == 0 || req->session_id.len > sizeof(proc->tls_session_id)) { + mslog(s, proc, LOG_DEBUG, "auth init from '%s' with no session ID present", ip); return -1; } @@ -271,20 +276,73 @@ const char* ip; } } + memcpy(proc->tls_session_id, req->session_id.data, req->session_id.len); + proc->tls_session_id_size = req->session_id.len; + ret = check_user_group_status(s, proc, req->tls_auth_ok, req->cert_user_name, req->cert_group_name); if (ret < 0) return ret; - mslog(s, proc, LOG_DEBUG, "auth init for user '%s' from '%s'", proc->username, ip); if (s->config->auth_types & AUTH_TYPE_USERNAME_PASS) { return ERR_AUTH_CONTINUE; } - + return 0; } +int handle_auth_reinit(main_server_st *s, struct proc_st** _proc, + const AuthReinitMsg * req) +{ +char ipbuf[128]; +const char* ip; +struct proc_st *ctmp = NULL; +struct proc_st *proc = *_proc; +unsigned found = 0; + + ip = human_addr((void*)&proc->remote_addr, proc->remote_addr_len, + ipbuf, sizeof(ipbuf)); + + if (req->session_id.len == 0) { + mslog(s, proc, LOG_DEBUG, "auth reinit from '%s' with no session ID present", ip); + return -1; + } + + if (req->password == NULL && s->config->auth_types & AUTH_TYPE_USERNAME_PASS) { + mslog(s, proc, LOG_DEBUG, "auth reinit from '%s' with no password present", ip); + return -1; + } + + /* search all procs for a matching session ID */ + + list_for_each(&s->proc_list.head, ctmp, list) { + if (ctmp->status == PS_AUTH_ZOMBIE && ctmp->tls_session_id_size == req->session_id.len) { + if (memcmp(req->session_id.data, ctmp->tls_session_id, ctmp->tls_session_id_size) == 0) { + /* replace sessions */ + ctmp->pid = proc->pid; + ctmp->fd = proc->fd; + memcpy(&ctmp->remote_addr, &proc->remote_addr, proc->remote_addr_len); + + proc->pid = -1; + proc->fd = -1; + *_proc = proc = ctmp; + found = 1; + break; + } + } + } + + if (found == 0) { + mslog(s, proc, LOG_DEBUG, "auth reinit received from '%s', but does not match any session", ip); + return -1; + } + + mslog(s, proc, LOG_DEBUG, "auth reinit for user '%s' from '%s'", proc->username, ip); + + return module->auth_pass(proc->auth_ctx, req->password, strlen(req->password)); +} + int handle_auth_req(main_server_st *s, struct proc_st* proc, const AuthRequestMsg * req) { @@ -293,7 +351,7 @@ int handle_auth_req(main_server_st *s, struct proc_st* proc, return -1; } mslog(s, proc, LOG_DEBUG, "auth req for user '%s'", proc->username); - + if (req->password == NULL) return -1; @@ -315,7 +373,7 @@ struct proc_st *ctmp = NULL, *cpos; unsigned int entries = 1; /* that one */ list_for_each_safe(&s->proc_list.head, ctmp, cpos, list) { - if (ctmp != proc) { + if (ctmp != proc && ctmp->pid != -1) { if (memcmp(proc->cookie, ctmp->cookie, sizeof(proc->cookie)) == 0) { mslog(s, ctmp, LOG_DEBUG, "disconnecting '%s' due to new cookie connection", ctmp->username); @@ -330,10 +388,10 @@ unsigned int entries = 1; /* that one */ } } } - + if (s->config->max_same_clients && entries > s->config->max_same_clients) return -1; - + return 0; } diff --git a/src/main-ctl-handler.c b/src/main-ctl-handler.c index 5aecfd74..297ca147 100644 --- a/src/main-ctl-handler.c +++ b/src/main-ctl-handler.c @@ -502,12 +502,16 @@ static int append_user_info(DBusMessageIter * subs, struct proc_st *ctmp) return -1; } - if (ctmp->auth_status == PS_AUTH_COMPLETED) + if (ctmp->status == PS_AUTH_COMPLETED) strtmp = "connected"; - else if (ctmp->auth_status == PS_AUTH_INIT) + else if (ctmp->status == PS_AUTH_INIT) strtmp = "auth"; - else + else if (ctmp->status == PS_AUTH_ZOMBIE) + strtmp = "zombie"; + else if (ctmp->status == PS_AUTH_INACTIVE) strtmp = "pre-auth"; + else + strtmp = "unknown"; if (dbus_message_iter_append_basic (subs, DBUS_TYPE_STRING, &strtmp) == 0) { return -1; diff --git a/src/main-misc.c b/src/main-misc.c index f01bac64..580f5941 100644 --- a/src/main-misc.c +++ b/src/main-misc.c @@ -51,11 +51,11 @@ #include #include "pam.h" -int set_tun_mtu(main_server_st* s, struct proc_st * proc, unsigned mtu) +int set_tun_mtu(main_server_st * s, struct proc_st *proc, unsigned mtu) { -int fd, ret, e; -struct ifreq ifr; -const char* name; + int fd, ret, e; + struct ifreq ifr; + const char *name; if (proc->tun_lease.name[0] == 0) return -1; @@ -74,44 +74,50 @@ const char* name; ret = ioctl(fd, SIOCSIFMTU, &ifr); if (ret != 0) { e = errno; - mslog(s, proc, LOG_INFO, "ioctl SIOCSIFMTU error: %s", strerror(e)); + mslog(s, proc, LOG_INFO, "ioctl SIOCSIFMTU error: %s", + strerror(e)); ret = -1; goto fail; } ret = 0; -fail: + fail: close(fd); return ret; } -int handle_script_exit(main_server_st *s, struct proc_st* proc, int code) +int handle_script_exit(main_server_st * s, struct proc_st *proc, int code) { -int ret; + int ret; if (code == 0) { - proc->auth_status = PS_AUTH_COMPLETED; + proc->status = PS_AUTH_COMPLETED; ret = send_auth_reply(s, proc, AUTH_REPLY_MSG__AUTH__REP__OK); if (ret < 0) { - mslog(s, proc, LOG_ERR, "could not send auth reply cmd."); + mslog(s, proc, LOG_ERR, + "could not send auth reply cmd."); ret = ERR_BAD_COMMAND; goto fail; } apply_iroutes(s, proc); } else { - mslog(s, proc, LOG_INFO, "failed authentication attempt for user '%s'", proc->username); - ret = send_auth_reply( s, proc, AUTH_REPLY_MSG__AUTH__REP__FAILED); + mslog(s, proc, LOG_INFO, + "failed authentication attempt for user '%s'", + proc->username); + ret = + send_auth_reply(s, proc, AUTH_REPLY_MSG__AUTH__REP__FAILED); if (ret < 0) { - mslog(s, proc, LOG_ERR, "could not send reply auth cmd."); + mslog(s, proc, LOG_ERR, + "could not send reply auth cmd."); ret = ERR_BAD_COMMAND; goto fail; } } ret = 0; -fail: + fail: /* we close the lease tun fd both on success and failure. * The parent doesn't need to keep the tunfd. */ @@ -124,14 +130,16 @@ fail: return ret; } -static int read_additional_config_file(main_server_st* s, struct proc_st* proc, const char* file, const char* type) +static int read_additional_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; + 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); + mslog(s, proc, LOG_DEBUG, "Loading %s configuration '%s'", type, + file); ret = parse_group_cfg_file(s, file, &cfg); if (ret < 0) @@ -145,12 +153,19 @@ unsigned i; cfg.routes = NULL; cfg.routes_size = 0; } else { - proc->config.routes = safe_realloc(proc->config.routes, (proc->config.routes_size + cfg.routes_size) * sizeof(proc->config.routes[0])); + proc->config.routes = + safe_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]; + for (i = 0; i < cfg.routes_size; i++) { + proc->config.routes[proc->config. + routes_size] = + cfg.routes[i]; cfg.routes[i] = NULL; proc->config.routes_size++; } @@ -225,21 +240,23 @@ unsigned i; del_additional_config(&cfg); } else - mslog(s, proc, LOG_DEBUG, "No %s configuration for '%s'", type, proc->username); + 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) +static int read_additional_config(struct main_server_st *s, + struct proc_st *proc) { -char file[_POSIX_PATH_MAX]; -int ret; + 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); + snprintf(file, sizeof(file), "%s/%s", s->config->per_user_dir, + proc->username); ret = read_additional_config_file(s, proc, file, "user"); if (ret < 0) @@ -247,7 +264,8 @@ int ret; } if (s->config->per_group_dir != NULL && proc->groupname[0] != 0) { - snprintf(file, sizeof(file), "%s/%s", s->config->per_group_dir, proc->groupname); + snprintf(file, sizeof(file), "%s/%s", s->config->per_group_dir, + proc->groupname); ret = read_additional_config_file(s, proc, file, "group"); if (ret < 0) @@ -263,12 +281,12 @@ int ret; /* k: whether to kill the process */ -void remove_proc(main_server_st* s, struct proc_st *proc, unsigned k) +void remove_proc(main_server_st * s, struct proc_st *proc, unsigned k) { list_del(&proc->list); s->active_clients--; - if (k) + if (k && proc->pid != -1) kill(proc->pid, SIGTERM); remove_from_script_list(s, proc); @@ -292,12 +310,24 @@ void remove_proc(main_server_st* s, struct proc_st *proc, unsigned k) free(proc); } +void proc_to_zombie(main_server_st * s, struct proc_st *proc) +{ + proc->status = PS_AUTH_ZOMBIE; + + mslog(s, proc, LOG_INFO, "client disconnected, became zombie"); + + /* close the intercomm fd */ + if (proc->fd >= 0) + close(proc->fd); + proc->fd = -1; + proc->pid = -1; +} /* This is the function after which proc is populated */ -static int accept_user(main_server_st *s, struct proc_st* proc, unsigned cmd) +static int accept_user(main_server_st * s, struct proc_st *proc, unsigned cmd) { -int ret; -const char* group; + int ret; + const char *group; mslog(s, proc, LOG_DEBUG, "accepting user '%s'", proc->username); proc_auth_deinit(s, proc); @@ -305,13 +335,16 @@ const char* group; /* check for multiple connections */ ret = check_multiple_users(s, proc); if (ret < 0) { - mslog(s, proc, LOG_INFO, "user '%s' tried to connect more than %u times", proc->username, s->config->max_same_clients); + mslog(s, proc, LOG_INFO, + "user '%s' tried to connect more than %u times", + proc->username, s->config->max_same_clients); return ret; } ret = read_additional_config(s, proc); if (ret < 0) { - mslog(s, proc, LOG_ERR, "error reading additional configuration"); + mslog(s, proc, LOG_ERR, + "error reading additional configuration"); return ERR_READ_CONFIG; } @@ -325,34 +358,85 @@ const char* group; else group = proc->groupname; - if (cmd == AUTH_REQ || cmd == AUTH_INIT) { + if (cmd == AUTH_REQ || cmd == AUTH_INIT || cmd == AUTH_REINIT) { /* generate cookie */ ret = generate_cookie(s, proc); if (ret < 0) { - return ERR_BAD_COMMAND; + return ret; } - mslog(s, proc, LOG_INFO, "user '%s' of group '%s' authenticated", proc->username, group); + mslog(s, proc, LOG_INFO, + "user '%s' of group '%s' authenticated", proc->username, + group); + } else if (cmd == AUTH_COOKIE_REQ) { + mslog(s, proc, LOG_INFO, + "user '%s' of group '%s' re-authenticated (using cookie)", + proc->username, group); } else { - mslog(s, proc, LOG_INFO, "user '%s' of group '%s' re-authenticated (using cookie)", proc->username, group); + mslog(s, proc, LOG_INFO, + "user '%s' of group '%s' authenticated but from unknown state!", + proc->username, group); + return ERR_BAD_COMMAND; } /* do scripts and utmp */ ret = user_connected(s, proc); if (ret < 0 && ret != ERR_WAIT_FOR_SCRIPT) { - mslog(s, proc, LOG_INFO, "user '%s' disconnected due to script", proc->username); + mslog(s, proc, LOG_INFO, "user '%s' disconnected due to script", + proc->username); } return ret; } -int handle_commands(main_server_st *s, struct proc_st* proc) +static int handle_auth_res(main_server_st * s, struct proc_st *proc, + unsigned cmd, int result, unsigned cont) +{ + int ret; + + if (cont != 0 && result == ERR_AUTH_CONTINUE) { + ret = send_auth_reply_msg(s, proc); + if (ret < 0) { + mslog(s, proc, LOG_ERR, + "could not send reply auth cmd."); + return ret; + } + return 0; /* wait for another command */ + } else if (result == 0) { + ret = accept_user(s, proc, cmd); + if (ret < 0) { + goto auth_ok; + } + proc->status = PS_AUTH_COMPLETED; + goto auth_ok; + } else if (result < 0) { + add_to_ip_ban_list(s, &proc->remote_addr, + proc->remote_addr_len); + return result; + } else { + mslog(s, proc, LOG_ERR, "unexpected auth result: %d\n", result); + return ERR_BAD_COMMAND; + } + + auth_ok: + if (ret == ERR_WAIT_FOR_SCRIPT) + ret = 0; + else { + /* no script was called. Handle it as a successful script call. */ + ret = handle_script_exit(s, proc, ret); + } + + return ret; +} + +int handle_commands(main_server_st * s, struct proc_st *proc) { struct iovec iov[3]; uint8_t cmd; struct msghdr hdr; - AuthInitMsg * auth_init; - AuthCookieRequestMsg * auth_cookie_req; - AuthRequestMsg * auth_req; + AuthInitMsg *auth_init; + AuthReinitMsg *auth_reinit; + AuthCookieRequestMsg *auth_cookie_req; + AuthRequestMsg *auth_req; uint16_t length; uint8_t *raw; int ret, raw_len, e; @@ -367,10 +451,12 @@ int handle_commands(main_server_st *s, struct proc_st* proc) hdr.msg_iov = iov; hdr.msg_iovlen = 2; - ret = recvmsg( proc->fd, &hdr, 0); + ret = recvmsg(proc->fd, &hdr, 0); if (ret == -1) { e = errno; - mslog(s, proc, LOG_ERR, "cannot obtain metadata from command socket: %s", strerror(e)); + mslog(s, proc, LOG_ERR, + "cannot obtain metadata from command socket: %s", + strerror(e)); return ERR_BAD_COMMAND; } @@ -384,8 +470,8 @@ int handle_commands(main_server_st *s, struct proc_st* proc) return ERR_BAD_COMMAND; } - mslog(s, proc, LOG_DEBUG, "main received message '%s' of %u bytes\n", - cmd_request_to_str(cmd), (unsigned)length); + mslog(s, proc, LOG_DEBUG, "main received message '%s' of %u bytes\n", + cmd_request_to_str(cmd), (unsigned)length); raw = malloc(length); if (raw == NULL) { @@ -393,16 +479,18 @@ int handle_commands(main_server_st *s, struct proc_st* proc) return ERR_MEM; } - raw_len = force_read_timeout( proc->fd, raw, length, 2); + raw_len = force_read_timeout(proc->fd, raw, length, 2); if (raw_len != length) { e = errno; - mslog(s, proc, LOG_ERR, "cannot obtain data from command socket: %s", strerror(e)); + mslog(s, proc, LOG_ERR, + "cannot obtain data from command socket: %s", + strerror(e)); ret = ERR_BAD_COMMAND; goto cleanup; } - switch(cmd) { - case CMD_TUN_MTU: { + switch (cmd) { + case CMD_TUN_MTU:{ TunMtuMsg *tmsg; tmsg = tun_mtu_msg__unpack(NULL, raw_len, raw); @@ -416,10 +504,10 @@ int handle_commands(main_server_st *s, struct proc_st* proc) tun_mtu_msg__free_unpacked(tmsg, NULL); - } + } - break; - case CMD_SESSION_INFO: { + break; + case CMD_SESSION_INFO:{ SessionInfoMsg *tmsg; tmsg = session_info_msg__unpack(NULL, raw_len, raw); @@ -430,21 +518,29 @@ int handle_commands(main_server_st *s, struct proc_st* proc) } if (tmsg->tls_ciphersuite) - snprintf(proc->tls_ciphersuite, sizeof(proc->tls_ciphersuite), "%s", tmsg->tls_ciphersuite); + snprintf(proc->tls_ciphersuite, + sizeof(proc->tls_ciphersuite), "%s", + tmsg->tls_ciphersuite); if (tmsg->dtls_ciphersuite) - snprintf(proc->dtls_ciphersuite, sizeof(proc->dtls_ciphersuite), "%s", tmsg->dtls_ciphersuite); + snprintf(proc->dtls_ciphersuite, + sizeof(proc->dtls_ciphersuite), "%s", + tmsg->dtls_ciphersuite); if (tmsg->user_agent) - snprintf(proc->user_agent, sizeof(proc->user_agent), "%s", tmsg->user_agent); + snprintf(proc->user_agent, + sizeof(proc->user_agent), "%s", + tmsg->user_agent); session_info_msg__free_unpacked(tmsg, NULL); - } + } - break; - case RESUME_STORE_REQ: { - SessionResumeStoreReqMsg* smsg; + break; + case RESUME_STORE_REQ:{ + SessionResumeStoreReqMsg *smsg; - smsg = session_resume_store_req_msg__unpack(NULL, raw_len, raw); + smsg = + session_resume_store_req_msg__unpack(NULL, raw_len, + raw); if (smsg == NULL) { mslog(s, proc, LOG_ERR, "error unpacking data"); ret = ERR_BAD_COMMAND; @@ -456,17 +552,20 @@ int handle_commands(main_server_st *s, struct proc_st* proc) session_resume_store_req_msg__free_unpacked(smsg, NULL); if (ret < 0) { - mslog(s, proc, LOG_DEBUG, "could not store resumption data"); + mslog(s, proc, LOG_DEBUG, + "could not store resumption data"); } - } + } - break; + break; - case RESUME_DELETE_REQ: { - SessionResumeFetchMsg* fmsg; + case RESUME_DELETE_REQ:{ + SessionResumeFetchMsg *fmsg; - fmsg = session_resume_fetch_msg__unpack(NULL, raw_len, raw); + fmsg = + session_resume_fetch_msg__unpack(NULL, raw_len, + raw); if (fmsg == NULL) { mslog(s, proc, LOG_ERR, "error unpacking data"); ret = ERR_BAD_COMMAND; @@ -478,17 +577,21 @@ int handle_commands(main_server_st *s, struct proc_st* proc) session_resume_fetch_msg__free_unpacked(fmsg, NULL); if (ret < 0) { - mslog(s, proc, LOG_DEBUG, "could not delete resumption data."); + mslog(s, proc, LOG_DEBUG, + "could not delete resumption data."); } - } + } - break; - case RESUME_FETCH_REQ: { - SessionResumeReplyMsg msg = SESSION_RESUME_REPLY_MSG__INIT; - SessionResumeFetchMsg* fmsg; + break; + case RESUME_FETCH_REQ:{ + SessionResumeReplyMsg msg = + SESSION_RESUME_REPLY_MSG__INIT; + SessionResumeFetchMsg *fmsg; - fmsg = session_resume_fetch_msg__unpack(NULL, raw_len, raw); + fmsg = + session_resume_fetch_msg__unpack(NULL, raw_len, + raw); if (fmsg == NULL) { mslog(s, proc, LOG_ERR, "error unpacking data"); ret = ERR_BAD_COMMAND; @@ -500,183 +603,187 @@ int handle_commands(main_server_st *s, struct proc_st* proc) session_resume_fetch_msg__free_unpacked(fmsg, NULL); if (ret < 0) { - msg.reply = SESSION_RESUME_REPLY_MSG__RESUME__REP__FAILED; - mslog(s, proc, LOG_DEBUG, "could not fetch resumption data."); + msg.reply = + SESSION_RESUME_REPLY_MSG__RESUME__REP__FAILED; + mslog(s, proc, LOG_DEBUG, + "could not fetch resumption data."); } else { - msg.reply = SESSION_RESUME_REPLY_MSG__RESUME__REP__OK; + msg.reply = + SESSION_RESUME_REPLY_MSG__RESUME__REP__OK; } - ret = send_msg_to_worker(s, proc, RESUME_FETCH_REP, &msg, - (pack_size_func)session_resume_reply_msg__get_packed_size, - (pack_func)session_resume_reply_msg__pack); + ret = + send_msg_to_worker(s, proc, RESUME_FETCH_REP, &msg, + (pack_size_func) + session_resume_reply_msg__get_packed_size, + (pack_func) + session_resume_reply_msg__pack); if (ret < 0) { - mslog(s, proc, LOG_ERR, "could not send reply cmd %d.", (unsigned) cmd); + mslog(s, proc, LOG_ERR, + "could not send reply cmd %d.", + (unsigned)cmd); ret = ERR_BAD_COMMAND; goto cleanup; } - } + } - break; + break; - case AUTH_INIT: - - if (proc->auth_status != PS_AUTH_INACTIVE) { - mslog(s, proc, LOG_ERR, "received authentication init when complete."); - ret = ERR_BAD_COMMAND; - goto cleanup; - } - - auth_init = auth_init_msg__unpack(NULL, raw_len, raw); - if (auth_init == NULL) { - mslog(s, proc, LOG_ERR, "error unpacking data"); - ret = ERR_BAD_COMMAND; - goto cleanup; - } - - ret = handle_auth_init(s, proc, auth_init); - - auth_init_msg__free_unpacked(auth_init, NULL); - - if (ret == ERR_AUTH_CONTINUE) { - proc->auth_status = PS_AUTH_INIT; - - ret = send_auth_reply_msg(s, proc); - if (ret < 0) { - mslog(s, proc, LOG_ERR, "could not send reply auth cmd."); - goto cleanup; - } - break; /* wait for another command */ - } else if (ret == 0) { - ret = accept_user(s, proc, cmd); - if (ret < 0) { - goto cleanup_auth; - } - proc->auth_status = PS_AUTH_COMPLETED; - goto cleanup; - } else if (ret < 0) { - add_to_ip_ban_list(s, &proc->remote_addr, proc->remote_addr_len); - goto cleanup; - } - - break; - - case AUTH_REQ: - if (proc->auth_status != PS_AUTH_INIT) { - mslog(s, proc, LOG_ERR, "received authentication request when not initialized."); - ret = ERR_BAD_COMMAND; - goto cleanup_auth; - } - - proc->auth_reqs++; - if (proc->auth_reqs > MAX_AUTH_REQS) { - mslog(s, proc, LOG_ERR, "received too many authentication requests."); - ret = ERR_BAD_COMMAND; - goto cleanup_auth; - } - - auth_req = auth_request_msg__unpack(NULL, raw_len, raw); - if (auth_req == NULL) { - mslog(s, proc, LOG_ERR, "error unpacking data"); - ret = ERR_BAD_COMMAND; - goto cleanup_auth; - } - - ret = handle_auth_req(s, proc, auth_req); - - auth_request_msg__free_unpacked(auth_req, NULL); - - if (ret == ERR_AUTH_CONTINUE) { - ret = send_auth_reply_msg(s, proc); - if (ret < 0) { - mslog(s, proc, LOG_ERR, "could not send reply auth cmd."); - goto cleanup_auth; - } - break; /* wait for another command */ - } else if (ret < 0) { - add_to_ip_ban_list(s, &proc->remote_addr, proc->remote_addr_len); - goto cleanup_auth; - } - - ret = accept_user(s, proc, cmd); - if (ret < 0) { - goto cleanup_auth; - } - proc->auth_status = PS_AUTH_COMPLETED; - goto cleanup_auth; - - case AUTH_COOKIE_REQ: - - if (proc->auth_status != PS_AUTH_INACTIVE) { - mslog(s, proc, LOG_ERR, "received unexpected cookie authentication."); - ret = ERR_BAD_COMMAND; - goto cleanup_auth; - } - - auth_cookie_req = auth_cookie_request_msg__unpack(NULL, raw_len, raw); - if (auth_cookie_req == NULL) { - mslog(s, proc, LOG_ERR, "error unpacking data"); - ret = ERR_BAD_COMMAND; - goto cleanup_auth; - } - - ret = handle_auth_cookie_req(s, proc, auth_cookie_req); - - auth_cookie_request_msg__free_unpacked(auth_cookie_req, NULL); - - if (ret < 0) { - add_to_ip_ban_list(s, &proc->remote_addr, proc->remote_addr_len); - goto cleanup_auth; - } - - ret = accept_user(s, proc, cmd); - if (ret < 0) { - goto cleanup_auth; - } - - proc->auth_status = PS_AUTH_COMPLETED; - -cleanup_auth: - if (ret == ERR_WAIT_FOR_SCRIPT) - ret = 0; - else { - /* no script was called. Handle it as a successful script call. */ - ret = handle_script_exit(s, proc, ret); - } - goto cleanup; - - default: - mslog(s, proc, LOG_ERR, "unknown CMD 0x%x.", (unsigned)cmd); + case AUTH_INIT: + if (proc->status != PS_AUTH_INACTIVE) { + mslog(s, proc, LOG_ERR, + "received authentication init when complete."); ret = ERR_BAD_COMMAND; goto cleanup; + } + + auth_init = auth_init_msg__unpack(NULL, raw_len, raw); + if (auth_init == NULL) { + mslog(s, proc, LOG_ERR, "error unpacking data"); + ret = ERR_BAD_COMMAND; + goto cleanup; + } + + ret = handle_auth_init(s, proc, auth_init); + + auth_init_msg__free_unpacked(auth_init, NULL); + + proc->status = PS_AUTH_INIT; + + ret = handle_auth_res(s, proc, cmd, ret, 1); + if (ret < 0) { + goto cleanup; + } + + break; + + case AUTH_REINIT: + if (proc->status != PS_AUTH_INACTIVE + || s->config->cisco_client_compat == 0) { + mslog(s, proc, LOG_ERR, + "received authentication reinit when complete."); + ret = ERR_BAD_COMMAND; + goto cleanup; + } + + auth_reinit = auth_reinit_msg__unpack(NULL, raw_len, raw); + if (auth_reinit == NULL) { + mslog(s, proc, LOG_ERR, "error unpacking data"); + ret = ERR_BAD_COMMAND; + goto cleanup; + } + + /* note that it may replace proc on success */ + ret = handle_auth_reinit(s, &proc, auth_reinit); + + auth_reinit_msg__free_unpacked(auth_reinit, NULL); + + proc->status = PS_AUTH_INIT; + + ret = handle_auth_res(s, proc, cmd, ret, 1); + if (ret < 0) { + goto cleanup; + } + + case AUTH_REQ: + if (proc->status != PS_AUTH_INIT) { + mslog(s, proc, LOG_ERR, + "received authentication request when not initialized."); + ret = ERR_BAD_COMMAND; + goto cleanup; + } + + proc->auth_reqs++; + if (proc->auth_reqs > MAX_AUTH_REQS) { + mslog(s, proc, LOG_ERR, + "received too many authentication requests."); + ret = ERR_BAD_COMMAND; + goto cleanup; + } + + auth_req = auth_request_msg__unpack(NULL, raw_len, raw); + if (auth_req == NULL) { + mslog(s, proc, LOG_ERR, "error unpacking data"); + ret = ERR_BAD_COMMAND; + goto cleanup; + } + + ret = handle_auth_req(s, proc, auth_req); + + auth_request_msg__free_unpacked(auth_req, NULL); + + proc->status = PS_AUTH_INIT; + + ret = handle_auth_res(s, proc, cmd, ret, 1); + if (ret < 0) { + goto cleanup; + } + + break; + + case AUTH_COOKIE_REQ: + + if (proc->status != PS_AUTH_INACTIVE) { + mslog(s, proc, LOG_ERR, + "received unexpected cookie authentication."); + ret = ERR_BAD_COMMAND; + goto cleanup; + } + + auth_cookie_req = + auth_cookie_request_msg__unpack(NULL, raw_len, raw); + if (auth_cookie_req == NULL) { + mslog(s, proc, LOG_ERR, "error unpacking data"); + ret = ERR_BAD_COMMAND; + goto cleanup; + } + + ret = handle_auth_cookie_req(s, proc, auth_cookie_req); + + auth_cookie_request_msg__free_unpacked(auth_cookie_req, NULL); + + ret = handle_auth_res(s, proc, cmd, ret, 0); + if (ret < 0) { + goto cleanup; + } + + break; + + default: + mslog(s, proc, LOG_ERR, "unknown CMD 0x%x.", (unsigned)cmd); + ret = ERR_BAD_COMMAND; + goto cleanup; } ret = 0; -cleanup: + cleanup: free(raw); return ret; } -int check_if_banned(main_server_st* s, struct sockaddr_storage *addr, socklen_t addr_len) +int check_if_banned(main_server_st * s, struct sockaddr_storage *addr, + socklen_t addr_len) { -time_t now = time(0); -struct banned_st *btmp, *bpos; + time_t now = time(0); + struct banned_st *btmp, *bpos; if (s->config->min_reauth_time == 0) return 0; list_for_each_safe(&s->ban_list.head, btmp, bpos, list) { - if (now-btmp->failed_time > s->config->min_reauth_time) { + if (now - btmp->failed_time > s->config->min_reauth_time) { /* invalid entry. Clean it up */ list_del(&btmp->list); free(btmp); } else { - if (SA_IN_SIZE(btmp->addr_len) == SA_IN_SIZE(addr_len) && - memcmp(SA_IN_P_GENERIC(&btmp->addr, btmp->addr_len), - SA_IN_P_GENERIC(addr, addr_len), - SA_IN_SIZE(btmp->addr_len)) == 0) { + if (SA_IN_SIZE(btmp->addr_len) == SA_IN_SIZE(addr_len) + && + memcmp(SA_IN_P_GENERIC(&btmp->addr, btmp->addr_len), + SA_IN_P_GENERIC(addr, addr_len), + SA_IN_SIZE(btmp->addr_len)) == 0) { return -1; } } @@ -685,17 +792,16 @@ struct banned_st *btmp, *bpos; return 0; } - -void expire_banned(main_server_st* s) +void expire_banned(main_server_st * s) { -time_t now = time(0); -struct banned_st *btmp = NULL, *bpos; + time_t now = time(0); + struct banned_st *btmp = NULL, *bpos; if (s->config->min_reauth_time == 0) return; list_for_each_safe(&s->ban_list.head, btmp, bpos, list) { - if (now-btmp->failed_time > s->config->min_reauth_time) { + if (now - btmp->failed_time > s->config->min_reauth_time) { /* invalid entry. Clean it up */ list_del(&btmp->list); free(btmp); @@ -705,9 +811,10 @@ struct banned_st *btmp = NULL, *bpos; return; } -void add_to_ip_ban_list(main_server_st* s, struct sockaddr_storage *addr, socklen_t addr_len) +void add_to_ip_ban_list(main_server_st * s, struct sockaddr_storage *addr, + socklen_t addr_len) { -struct banned_st *btmp; + struct banned_st *btmp; if (s->config->min_reauth_time == 0) return; @@ -723,31 +830,51 @@ struct banned_st *btmp; list_add(&s->ban_list.head, &(btmp->list)); } +void expire_zombies(main_server_st * s) +{ + time_t now = time(0); + struct proc_st *ctmp = NULL, *cpos; + + if (s->config->cisco_client_compat == 0) + return; + + list_for_each_safe(&s->proc_list.head, ctmp, cpos, list) { + if (ctmp->status == PS_AUTH_ZOMBIE && + now - ctmp->conn_time > MAX_ZOMBIE_SECS) { + remove_proc(s, ctmp, 0); + } + } + + return; +} + void run_sec_mod(main_server_st * s) { -int e; -pid_t pid; -char file[_POSIX_PATH_MAX]; -const char *p; + int e; + pid_t pid; + char file[_POSIX_PATH_MAX]; + const char *p; /* make socket name */ - snprintf(s->socket_file, sizeof(s->socket_file), "%s.%u", s->config->socket_file_prefix, (unsigned)getpid()); + snprintf(s->socket_file, sizeof(s->socket_file), "%s.%u", + s->config->socket_file_prefix, (unsigned)getpid()); p = s->socket_file; if (s->config->chroot_dir != NULL) { - snprintf(file, sizeof(file), "%s/%s.%u", - s->config->chroot_dir, s->config->socket_file_prefix, (unsigned)getpid()); + snprintf(file, sizeof(file), "%s/%s.%u", + s->config->chroot_dir, s->config->socket_file_prefix, + (unsigned)getpid()); p = file; } pid = fork(); - if (pid == 0) { /* child */ + if (pid == 0) { /* child */ clear_lists(s); kill_on_parent_kill(SIGTERM); - setproctitle(PACKAGE_NAME"-secmod"); + setproctitle(PACKAGE_NAME "-secmod"); sec_mod_server(s->config, p); exit(0); - } else if (pid > 0) { /* parent */ + } else if (pid > 0) { /* parent */ s->sec_mod_pid = pid; } else { e = errno; @@ -757,36 +884,40 @@ const char *p; } /* Puts the provided PIN into the config's cgroup */ -void put_into_cgroup(main_server_st * s, const char* _cgroup, pid_t pid) +void put_into_cgroup(main_server_st * s, const char *_cgroup, pid_t pid) { -char* name, *p, *savep; -char cgroup[128]; -char file[_POSIX_PATH_MAX]; -FILE* fd; + char *name, *p, *savep; + char cgroup[128]; + char file[_POSIX_PATH_MAX]; + FILE *fd; if (_cgroup == NULL) return; #ifdef __linux__ - /* format: cpu,memory:cgroup-name */ - snprintf(cgroup, sizeof(cgroup), "%s", _cgroup); - - name = strchr(cgroup, ':'); - if (name == NULL) { - mslog(s, NULL, LOG_ERR, "error parsing cgroup name: %s", cgroup); + /* format: cpu,memory:cgroup-name */ + snprintf(cgroup, sizeof(cgroup), "%s", _cgroup); + + name = strchr(cgroup, ':'); + if (name == NULL) { + mslog(s, NULL, LOG_ERR, "error parsing cgroup name: %s", + cgroup); return; - } - name[0] = 0; - name++; - - p = strtok_r(cgroup, ",", &savep); - while (p != NULL) { - mslog(s, NULL, LOG_DEBUG, "putting process %u to cgroup '%s:%s'", (unsigned)pid, p, name); + } + name[0] = 0; + name++; - snprintf(file, sizeof(file), "/sys/fs/cgroup/%s/%s/tasks", p, name); + p = strtok_r(cgroup, ",", &savep); + while (p != NULL) { + mslog(s, NULL, LOG_DEBUG, + "putting process %u to cgroup '%s:%s'", (unsigned)pid, p, + name); - fd = fopen(file, "w"); - if (fd == NULL) { + snprintf(file, sizeof(file), "/sys/fs/cgroup/%s/%s/tasks", p, + name); + + fd = fopen(file, "w"); + if (fd == NULL) { mslog(s, NULL, LOG_ERR, "cannot open: %s", file); return; } @@ -796,10 +927,11 @@ FILE* fd; } fclose(fd); p = strtok_r(NULL, ",", &savep); - } - - return; + } + + return; #else - mslog(s, NULL, LOG_DEBUG, "Ignoring cgroup option as it is not supported on this system"); + mslog(s, NULL, LOG_DEBUG, + "Ignoring cgroup option as it is not supported on this system"); #endif } diff --git a/src/main-resume.c b/src/main-resume.c index 92ae8ae6..2306cb90 100644 --- a/src/main-resume.c +++ b/src/main-resume.c @@ -38,138 +38,156 @@ #include #include -int handle_resume_delete_req(main_server_st* s, struct proc_st * proc, - const SessionResumeFetchMsg * req) +int handle_resume_delete_req(main_server_st * s, struct proc_st *proc, + const SessionResumeFetchMsg * req) { -tls_cache_st* cache; -struct htable_iter iter; -size_t key; + tls_cache_st *cache; + struct htable_iter iter; + size_t key; key = hash_any(req->session_id.data, req->session_id.len, 0); cache = htable_firstval(&s->tls_db->ht, &iter, key); - while(cache != NULL) { + while (cache != NULL) { if (req->session_id.len == cache->session_id_size && - memcmp (req->session_id.data, cache->session_id, req->session_id.len) == 0) { + memcmp(req->session_id.data, cache->session_id, + req->session_id.len) == 0) { - cache->session_data_size = 0; - cache->session_id_size = 0; - - htable_delval(&s->tls_db->ht, &iter); - free(cache); + cache->session_data_size = 0; + cache->session_id_size = 0; + + htable_delval(&s->tls_db->ht, &iter); + free(cache); s->tls_db->entries--; - return 0; + return 0; } - - cache = htable_nextval(&s->tls_db->ht, &iter, key); - } - return 0; + cache = htable_nextval(&s->tls_db->ht, &iter, key); + } + + return 0; } -int handle_resume_fetch_req(main_server_st* s, struct proc_st * proc, - const SessionResumeFetchMsg * req, - SessionResumeReplyMsg* rep) +int handle_resume_fetch_req(main_server_st * s, struct proc_st *proc, + const SessionResumeFetchMsg * req, + SessionResumeReplyMsg * rep) { -tls_cache_st* cache; -struct htable_iter iter; -size_t key; + tls_cache_st *cache; + struct htable_iter iter; + size_t key; rep->reply = SESSION_RESUME_REPLY_MSG__RESUME__REP__FAILED; key = hash_any(req->session_id.data, req->session_id.len, 0); cache = htable_firstval(&s->tls_db->ht, &iter, key); - while(cache != NULL) { + while (cache != NULL) { if (req->session_id.len == cache->session_id_size && - memcmp (req->session_id.data, cache->session_id, req->session_id.len) == 0) { + memcmp(req->session_id.data, cache->session_id, + req->session_id.len) == 0) { - if (proc->remote_addr_len == cache->remote_addr_len && - ip_cmp(&proc->remote_addr, &cache->remote_addr, proc->remote_addr_len) == 0) { + if (proc->remote_addr_len == cache->remote_addr_len && + ip_cmp(&proc->remote_addr, &cache->remote_addr, + proc->remote_addr_len) == 0) { - rep->reply = SESSION_RESUME_REPLY_MSG__RESUME__REP__OK; - - rep->has_session_data = 1; - rep->session_data.data = (void*)cache->session_data; - rep->session_data.len = cache->session_data_size; + rep->reply = + SESSION_RESUME_REPLY_MSG__RESUME__REP__OK; - return 0; + rep->has_session_data = 1; + + rep->session_data.data = + (void *)cache->session_data; + rep->session_data.len = + cache->session_data_size; + + mslog_hex(s, proc, LOG_DEBUG, "TLS session DB resuming", + req->session_id.data, + req->session_id.len); + + return 0; } } - cache = htable_nextval(&s->tls_db->ht, &iter, key); - } + cache = htable_nextval(&s->tls_db->ht, &iter, key); + } - return 0; + return 0; } -int handle_resume_store_req(main_server_st* s, struct proc_st * proc, - const SessionResumeStoreReqMsg * req) +int handle_resume_store_req(main_server_st * s, struct proc_st *proc, + const SessionResumeStoreReqMsg * req) { -tls_cache_st* cache; -size_t key; -unsigned int max; + tls_cache_st *cache; + size_t key; + unsigned int max; if (req->session_id.len > GNUTLS_MAX_SESSION_ID) return -1; if (req->session_data.len > MAX_SESSION_DATA_SIZE) return -1; - max = MAX(2*s->config->max_clients, DEFAULT_MAX_CACHED_TLS_SESSIONS); + max = MAX(2 * s->config->max_clients, DEFAULT_MAX_CACHED_TLS_SESSIONS); if (s->tls_db->entries >= max) { - mslog(s, NULL, LOG_INFO, "maximum number of stored TLS sessions reached (%u)", max); + mslog(s, NULL, LOG_INFO, + "maximum number of stored TLS sessions reached (%u)", + max); need_maintenance = 1; return -1; } key = hash_any(req->session_id.data, req->session_id.len, 0); - + cache = malloc(sizeof(*cache)); if (cache == NULL) return -1; - + cache->session_id_size = req->session_id.len; cache->session_data_size = req->session_data.len; cache->remote_addr_len = proc->remote_addr_len; memcpy(cache->session_id, req->session_id.data, req->session_id.len); - memcpy(cache->session_data, req->session_data.data, req->session_data.len); + memcpy(cache->session_data, req->session_data.data, + req->session_data.len); memcpy(&cache->remote_addr, &proc->remote_addr, proc->remote_addr_len); - + htable_add(&s->tls_db->ht, key, cache); s->tls_db->entries++; + mslog_hex(s, proc, LOG_DEBUG, "TLS session DB storing", + req->session_id.data, + req->session_id.len); + return 0; } -void expire_tls_sessions(main_server_st *s) +void expire_tls_sessions(main_server_st * s) { -tls_cache_st* cache; -struct htable_iter iter; -time_t now, exp; + tls_cache_st *cache; + struct htable_iter iter; + time_t now, exp; now = time(0); cache = htable_first(&s->tls_db->ht, &iter); - while(cache != NULL) { + while (cache != NULL) { gnutls_datum_t d; - d.data = (void*)cache->session_data; + d.data = (void *)cache->session_data; d.size = cache->session_data_size; exp = gnutls_db_check_entry_time(&d); - if (now-exp > TLS_SESSION_EXPIRATION_TIME) { - cache->session_data_size = 0; - cache->session_id_size = 0; + if (now - exp > TLS_SESSION_EXPIRATION_TIME) { + cache->session_data_size = 0; + cache->session_id_size = 0; - htable_delval(&s->tls_db->ht, &iter); - free(cache); + htable_delval(&s->tls_db->ht, &iter); + free(cache); s->tls_db->entries--; } - cache = htable_next(&s->tls_db->ht, &iter); - } + cache = htable_next(&s->tls_db->ht, &iter); + } - return; + return; } diff --git a/src/main.c b/src/main.c index 44e7ba6b..667bef06 100644 --- a/src/main.c +++ b/src/main.c @@ -657,8 +657,8 @@ time_t now; now = time(0); list_for_each(&s->proc_list.head, ctmp, list) { - if (session_id_size == ctmp->session_id_size && - memcmp(session_id, ctmp->session_id, session_id_size) == 0 && + if (session_id_size == ctmp->dtls_session_id_size && + memcmp(session_id, ctmp->dtls_session_id, session_id_size) == 0 && (now - ctmp->udp_fd_receive_time > UDP_FD_RESEND_TIME)) { UdpFdMsg msg = UDP_FD_MSG__INIT; @@ -744,6 +744,7 @@ unsigned total = 10; need_maintenance = 0; mslog(s, NULL, LOG_INFO, "Performing maintenance"); expire_tls_sessions(s); + expire_zombies(s); expire_banned(s); alarm(MAINTAINANCE_TIME(s)); } @@ -900,8 +901,10 @@ int main(int argc, char** argv) } list_for_each(&s.proc_list.head, ctmp, list) { - FD_SET(ctmp->fd, &rd_set); - n = MAX(n, ctmp->fd); + if (ctmp->fd > 0) { + FD_SET(ctmp->fd, &rd_set); + n = MAX(n, ctmp->fd); + } } list_for_each(&s.ctl_list.head, ctl_tmp, list) { @@ -1046,9 +1049,12 @@ fork_failed: /* Check for any pending commands */ list_for_each_safe(&s.proc_list.head, ctmp, cpos, list) { - if (FD_ISSET(ctmp->fd, &rd_set)) { + if (ctmp->fd >= 0 && FD_ISSET(ctmp->fd, &rd_set)) { ret = handle_commands(&s, ctmp); - if (ret < 0) { + if (ret == ERR_WORKER_TERMINATED && ctmp->status == PS_AUTH_INIT && + s.config->cisco_client_compat != 0) { + proc_to_zombie(&s, ctmp); + } else if (ret < 0) { remove_proc(&s, ctmp, (ret!=ERR_WORKER_TERMINATED)?1:0); } } diff --git a/src/main.h b/src/main.h index 8acaa5ad..dddb644c 100644 --- a/src/main.h +++ b/src/main.h @@ -64,10 +64,12 @@ struct script_wait_st { struct proc_st* proc; }; +#define MAX_ZOMBIE_SECS 240 enum { - PS_AUTH_INACTIVE, - PS_AUTH_INIT, - PS_AUTH_COMPLETED, + PS_AUTH_INACTIVE, /* no comm with worker */ + PS_AUTH_INIT, /* worker has sent an auth init msg */ + PS_AUTH_ZOMBIE, /* in INIT state but worker has disconnected! - only present when cisco-client-compat is set */ + PS_AUTH_COMPLETED, /* successful authentication */ }; /* Each worker process maps to a unique proc_st structure. @@ -88,11 +90,16 @@ struct proc_st { struct sockaddr_storage remote_addr; /* peer address */ socklen_t remote_addr_len; + /* The TLS session ID. + */ + uint8_t tls_session_id[GNUTLS_MAX_SESSION_ID]; + unsigned tls_session_id_size; /* would act as a flag if session_id is set */ + /* The DTLS session ID associated with the TLS session * it is either generated or restored from a cookie. */ - uint8_t session_id[GNUTLS_MAX_SESSION_ID]; - unsigned session_id_size; /* would act as a flag if session_id is set */ + uint8_t dtls_session_id[GNUTLS_MAX_SESSION_ID]; + unsigned dtls_session_id_size; /* would act as a flag if session_id is set */ /* The following are set by the worker process (or by a stored cookie) */ char username[MAX_USERNAME_SIZE]; /* the owner */ @@ -111,7 +118,7 @@ struct proc_st { uint8_t ipv4_seed[4]; void * auth_ctx; /* the context of authentication */ - unsigned auth_status; /* PS_AUTH_ */ + unsigned status; /* PS_AUTH_ */ unsigned auth_reqs; /* the number of requests received */ unsigned applied_iroutes; /* whether the iroutes in the config have been successfully applied */ @@ -193,6 +200,7 @@ int user_connected(main_server_st *s, struct proc_st* cur); void user_disconnected(main_server_st *s, struct proc_st* cur); void expire_tls_sessions(main_server_st *s); +void expire_zombies(main_server_st* s); int send_udp_fd(main_server_st* s, struct proc_st * proc, int fd); @@ -235,6 +243,8 @@ int handle_auth_cookie_req(main_server_st* s, struct proc_st* proc, int generate_cookie(main_server_st *s, struct proc_st* proc); int handle_auth_init(main_server_st *s, struct proc_st* proc, const AuthInitMsg * req); +int handle_auth_reinit(main_server_st *s, struct proc_st** proc, + const AuthReinitMsg * req); int handle_auth_req(main_server_st *s, struct proc_st* proc, const AuthRequestMsg * req); @@ -252,6 +262,7 @@ int parse_group_cfg_file(main_server_st* s, const char* file, struct group_cfg_s void del_additional_config(struct group_cfg_st* config); void remove_proc(main_server_st* s, struct proc_st *proc, unsigned k); +void proc_to_zombie(main_server_st* s, struct proc_st *proc); void put_into_cgroup(main_server_st * s, const char* cgroup, pid_t pid); diff --git a/src/vpn.h b/src/vpn.h index b449a644..88ffc9b7 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -94,6 +94,7 @@ typedef enum { CMD_TUN_MTU = 11, CMD_TERMINATE = 12, CMD_SESSION_INFO = 13, + AUTH_REINIT = 14, } cmd_request_t; typedef struct diff --git a/src/worker-auth.c b/src/worker-auth.c index 1ea48f85..a68bd772 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -65,18 +65,6 @@ static const char login_msg_user[] = "\n" ""; -static const char login_msg_compact[] = - "\n" - "\n" - VERSION_MSG \ - "\n" - "Please enter your username and password\n" - "
\n" - "\n" - "\n" - "
\n" - "
"; - static const char login_msg_no_user[] = "\n" "\n" @@ -90,7 +78,6 @@ static const char login_msg_no_user[] = int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg) { int ret; - struct http_req_st *req = &ws->req; char login_msg[MAX_MSG_SIZE + sizeof(login_msg_user)]; unsigned int lsize; @@ -107,26 +94,17 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg) if (ret < 0) return -1; -#ifdef ANYCONNECT_CLIENT_COMPAT - if (req && req->needs_compact_auth != 0) { - lsize = snprintf(login_msg, sizeof(login_msg), "%s", login_msg_compact); + if (ws->auth_state == S_AUTH_REQ) { + /* only ask password */ + if (pmsg == NULL) + pmsg = "Please enter password"; + lsize = + snprintf(login_msg, sizeof(login_msg), login_msg_no_user, + pmsg); } else { -#endif - if (ws->auth_state == S_AUTH_REQ) { - /* only ask password */ - if (pmsg == NULL) - pmsg = "Please enter password"; - lsize = - snprintf(login_msg, sizeof(login_msg), login_msg_no_user, - pmsg); - } else { - /* ask for username only */ - lsize = snprintf(login_msg, sizeof(login_msg), "%s", login_msg_user); - } - -#ifdef ANYCONNECT_CLIENT_COMPAT + /* ask for username only */ + lsize = snprintf(login_msg, sizeof(login_msg), "%s", login_msg_user); } -#endif ret = tls_printf(ws->session, "Content-Length: %u\r\n", @@ -678,31 +656,64 @@ int post_auth_handler(worker_st * ws, unsigned http_ver) char *password = NULL; char tmp_user[MAX_USERNAME_SIZE]; char tmp_group[MAX_USERNAME_SIZE]; + uint8_t tls_session_id[GNUTLS_MAX_SESSION_ID]; + size_t tls_session_id_size; char msg[MAX_MSG_SIZE]; - unsigned compact_auth = 0; oclog(ws, LOG_HTTP_DEBUG, "POST body: '%.*s'", (int)req->body_length, req->body); -restart: if (ws->auth_state == S_AUTH_INACTIVE) { AuthInitMsg ireq = AUTH_INIT_MSG__INIT; -#ifdef ANYCONNECT_CLIENT_COMPAT - if (req->needs_compact_auth != 0) { - /* the client uses Connection: Close and needs to - * be asked the username and password in one go. - */ - compact_auth = 1; + tls_session_id_size = sizeof(tls_session_id); + ret = gnutls_session_get_id(ws->session, tls_session_id, &tls_session_id_size); + if (ret < 0) { + oclog(ws, LOG_INFO, "failed obtainng session ID"); + goto auth_fail; } -#endif if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) { ret = read_user_pass(ws, req->body, req->body_length, &username, NULL); if (ret < 0) { - oclog(ws, LOG_ERR, "failed reading username"); + /* Try if we need to ReInit */ + if (ws->config->cisco_client_compat != 0 && + gnutls_session_is_resumed(ws->session) != 0) { + AuthReinitMsg rreq = AUTH_REINIT_MSG__INIT; + + /* could it be a client reconnecting and sending + * his password? */ + ret = + read_user_pass(ws, req->body, req->body_length, + NULL, &password); + if (ret < 0) { + oclog(ws, LOG_INFO, "failed reading password as well"); + goto ask_auth; + } + + rreq.tls_auth_ok = ws->cert_auth_ok; + rreq.password = password; + rreq.session_id.data = tls_session_id; + rreq.session_id.len = tls_session_id_size; + + ret = send_msg_to_main(ws, AUTH_REINIT, &rreq, + (pack_size_func)auth_reinit_msg__get_packed_size, + (pack_func)auth_reinit_msg__pack); + free(username); + + if (ret < 0) { + oclog(ws, LOG_ERR, + "failed sending auth reinit message to main"); + goto auth_fail; + } + + ws->auth_state = S_AUTH_INIT; + goto recv_reply; + } + + oclog(ws, LOG_INFO, "failed reading username"); goto ask_auth; } @@ -731,6 +742,8 @@ restart: ireq.cert_group_name = tmp_group; } + ireq.session_id.data = tls_session_id; + ireq.session_id.len = tls_session_id_size; ireq.hostname = req->hostname; ret = send_msg_to_main(ws, AUTH_INIT, @@ -783,18 +796,13 @@ restart: goto auth_fail; } + recv_reply: ret = recv_auth_reply(ws, msg, sizeof(msg)); if (ret == ERR_AUTH_CONTINUE) { oclog(ws, LOG_DEBUG, "continuing authentication for '%s'", ws->username); ws->auth_state = S_AUTH_REQ; -#ifdef ANYCONNECT_CLIENT_COMPAT - if (compact_auth != 0) { - compact_auth = 0; /* avoid infinite loop */ - goto restart; - } -#endif return get_auth_handler2(ws, http_ver, msg); } else if (ret < 0) { oclog(ws, LOG_ERR, "failed authentication for '%s'", diff --git a/src/worker-vpn.c b/src/worker-vpn.c index 2f2564c4..24f484bf 100644 --- a/src/worker-vpn.c +++ b/src/worker-vpn.c @@ -190,7 +190,6 @@ static void value_check(struct worker_st *ws, struct http_req_st *req) uint8_t *p; char *token; char *str; - char strtmp[16]; if (req->value.length <= 0) return; @@ -305,22 +304,6 @@ static void value_check(struct worker_st *ws, struct http_req_st *req) case HEADER_DTLS_MTU: req->dtls_mtu = atoi((char *)req->value.data); break; - case HEADER_CONNECTION: - length = req->value.length; - - if (length > sizeof(strtmp)-1) - break; - - memcpy(strtmp, req->value.data, length); - strtmp[length] = 0; - - if (c_strcasecmp(strtmp, "close") == 0) { - oclog(ws, LOG_INFO, "client needs compact auth"); - req->needs_compact_auth = 1; - } else { - req->needs_compact_auth = 0; - } - break; case HEADER_COOKIE: length = req->value.length; diff --git a/src/worker.h b/src/worker.h index 16edc61f..f22ac230 100644 --- a/src/worker.h +++ b/src/worker.h @@ -79,12 +79,7 @@ struct http_req_st { unsigned int next_header; unsigned char cookie[COOKIE_SIZE]; unsigned int cookie_set; - /* some CISCO clients reconnect for each request - * and is impossible to keep state for them. For - * that we ask username and password in one go for - * them. - */ - unsigned int needs_compact_auth; + unsigned char master_secret[TLS_MASTER_SIZE]; unsigned int master_secret_set;