Password authentication is now delegated to sec-mod.

That prevents any memory from the authentication modules to be leaked
to a worker process. As a result, the status zombie and dead no longer
exists.
This commit is contained in:
Nikos Mavrogiannopoulos
2014-05-13 10:44:10 +02:00
parent df7b124df4
commit 09704b8819
24 changed files with 1514 additions and 955 deletions

6
TODO
View File

@@ -1,11 +1,5 @@
Short term items:
* Move PAM (and other authentication methods) to sec-mod, to prevent
any leakage to the worker process. Alternatively that could be
prevented in a fork-then-exec mode switch, but unfortunately that
requires the reload of all certificates/parameters on the worker
process, and imposes a quite high penalty (see the 'exec' branch).
* Think for ways for ocserv to co-exist on the same system with
an HTTPS server (while sharing the same port).

View File

@@ -51,14 +51,19 @@ ocserv-args.c: $(srcdir)/ocserv-args.def
$(AUTOGEN) $<
ocserv-args.h: ocserv-args.c
# Files common to ocserv and occtl.
COMMON=common.c common.h system.c system.h setproctitle.c setproctitle.h
ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \
cookies.c main-misc.c group-config.c ip-lease.c ip-lease.h \
vpn.h cookies.h tlslib.h log.c tun.c tun.h \
config.c pam.c pam.h worker-resume.c worker.h main-resume.c main.h \
worker-extras.c main-auth.h html.c html.h \
main-user.c worker-misc.c setproctitle.h route-add.c route-add.h \
setproctitle.c worker-privs.c plain.c plain.h common.h common.c \
sec-mod.c sec-mod.h script-list.h system.c system.h icmp-ping.c icmp-ping.h \
main-user.c worker-misc.c route-add.c route-add.h \
worker-privs.c plain.c plain.h \
sec-mod.c sec-mod-db.c sec-mod-auth.c sec-mod.h sec-mod-ban.c \
script-list.h $(COMMON) \
icmp-ping.c icmp-ping.h \
worker-bandwidth.c worker-bandwidth.h ctl.h main-ctl.h \
str.c str.h gettime.h $(CCAN_SOURCES) $(HTTP_PARSER_SOURCES)
@@ -92,7 +97,7 @@ ocpasswd-args.c: $(srcdir)/ocpasswd-args.def
ocpasswd-args.h: ocpasswd-args.c
occtl_SOURCES = occtl.c occtl-pager.c occtl.h occtl-time.c occtl-cache.c \
occtl-nl.c ctl.h common.h common.c ctl.pb-c.c ctl.pb-c.h $(CCAN_SOURCES)
occtl-nl.c ctl.h ctl.pb-c.c ctl.pb-c.h $(CCAN_SOURCES) $(COMMON)
occtl_LDADD = ../gl/libgnu.a $(LIBREADLINE_LIBS) \
$(LIBNL3_LIBS) $(LIBPROTOBUF_C_LIBS) $(LIBTALLOC_LIBS)

View File

@@ -32,18 +32,10 @@ cmd_request_t cmd = _cmd;
static char tmp[32];
switch(cmd) {
case AUTH_INIT:
return "auth init";
case AUTH_REINIT:
return "auth reinit";
case AUTH_REP:
return "auth reply";
case AUTH_REQ:
return "auth request";
case AUTH_COOKIE_REP:
return "auth cookie reply";
case AUTH_COOKIE_REQ:
return "auth cookie request";
case AUTH_MSG:
return "auth msg";
case RESUME_STORE_REQ:
return "resume data store request";
case RESUME_DELETE_REQ:
@@ -62,6 +54,17 @@ static char tmp[32];
return "session info";
case CMD_CLI_STATS:
return "cli stats";
case SM_CMD_AUTH_INIT:
return "sm: auth init";
case SM_CMD_AUTH_CONT:
return "sm: auth cont";
case SM_CMD_AUTH_REP:
return "sm: auth rep";
case SM_CMD_DECRYPT:
return "sm: decrypt";
case SM_CMD_SIGN:
return "sm: sign";
default:
snprintf(tmp, sizeof(tmp), "unknown (%u)", _cmd);
return tmp;
@@ -295,8 +298,9 @@ int recv_socket_msg(void *pool, int fd, uint8_t cmd,
hdr.msg_control = control_un.control;
hdr.msg_controllen = sizeof(control_un.control);
/* FIXME: Add a timeout here */
do {
ret = recvmsg( fd, &hdr, 0);
ret = recvmsg(fd, &hdr, 0);
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
int e = errno;
@@ -306,13 +310,13 @@ int recv_socket_msg(void *pool, int fd, uint8_t cmd,
if (ret == 0) {
syslog(LOG_ERR, "%s:%u: recvmsg returned zero", __FILE__, __LINE__);
return ERR_WORKER_TERMINATED;
return ERR_PEER_TERMINATED;
}
if (rcmd != cmd) {
return ERR_BAD_COMMAND;
}
/* try to receive socket (if any) */
if (socketfd != NULL) {
if ( (cmptr = CMSG_FIRSTHDR(&hdr)) != NULL && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {

View File

@@ -35,19 +35,18 @@
#include <main.h>
#include <cookies.h>
int decrypt_cookie(main_server_st * s, const uint8_t* cookie, unsigned cookie_size,
int decrypt_cookie(gnutls_datum_t *key, const uint8_t* cookie, unsigned cookie_size,
struct stored_cookie_st* sc)
{
gnutls_datum_t iv = { (void*)cookie, COOKIE_IV_SIZE };
int ret;
uint8_t tag[COOKIE_MAC_SIZE];
gnutls_cipher_hd_t h;
gnutls_datum_t key = { (void*)s->cookie_key, sizeof(s->cookie_key) };
if (cookie_size != COOKIE_SIZE)
return -1;
ret = gnutls_cipher_init(&h, GNUTLS_CIPHER_AES_128_GCM, &key, &iv);
ret = gnutls_cipher_init(&h, GNUTLS_CIPHER_AES_128_GCM, key, &iv);
if (ret < 0)
return -1;
@@ -79,13 +78,12 @@ cleanup:
return ret;
}
int encrypt_cookie(main_server_st * s, const struct stored_cookie_st* sc,
int encrypt_cookie(gnutls_datum_t *key, const struct stored_cookie_st* sc,
uint8_t* cookie, unsigned cookie_size)
{
uint8_t _iv[COOKIE_IV_SIZE];
gnutls_cipher_hd_t h;
gnutls_datum_t iv = { _iv, sizeof(_iv) };
gnutls_datum_t key = { (void*)s->cookie_key, sizeof(s->cookie_key) };
int ret;
if (cookie_size != COOKIE_SIZE)
@@ -95,7 +93,7 @@ int ret;
if (ret < 0)
return -1;
ret = gnutls_cipher_init(&h, GNUTLS_CIPHER_AES_128_GCM, &key, &iv);
ret = gnutls_cipher_init(&h, GNUTLS_CIPHER_AES_128_GCM, key, &iv);
if (ret < 0)
return -1;
@@ -124,30 +122,3 @@ cleanup:
}
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->dtls_session_id, sizeof(proc->dtls_session_id));
if (ret < 0)
return -1;
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->dtls_session_id, sizeof(proc->dtls_session_id));
sc.expiration = time(0) + s->config->cookie_validity;
memcpy(sc.ipv4_seed, SA_IN_U8_P(&proc->ipv4->lip), 4);
ret = encrypt_cookie(s, &sc, proc->cookie, sizeof(proc->cookie));
if (ret < 0)
return -1;
return 0;
}

View File

@@ -27,7 +27,7 @@ struct __attribute__ ((__packed__)) stored_cookie_st {
char username[MAX_USERNAME_SIZE];
char groupname[MAX_GROUPNAME_SIZE];
char hostname[MAX_HOSTNAME_SIZE];
uint8_t session_id[GNUTLS_MAX_SESSION_ID];
uint8_t session_id[GNUTLS_MAX_SESSION_ID]; /* the DTLS one */
uint32_t expiration;
uint8_t ipv4_seed[4];
@@ -37,9 +37,9 @@ struct __attribute__ ((__packed__)) stored_cookie_st {
#define COOKIE_MAC_SIZE 12 /* 96-bits of AES-GCM */
#define COOKIE_SIZE (COOKIE_IV_SIZE + sizeof(struct stored_cookie_st) + COOKIE_MAC_SIZE)
int encrypt_cookie(struct main_server_st * s, const struct stored_cookie_st* sc,
uint8_t* cookie, unsigned cookie_size);
int decrypt_cookie(struct main_server_st * s, const uint8_t* cookie, unsigned cookie_size,
int encrypt_cookie(gnutls_datum_t *key, const struct stored_cookie_st* sc,
uint8_t* cookie, unsigned cookie_size);
int decrypt_cookie(gnutls_datum_t *key, const uint8_t* cookie, unsigned cookie_size,
struct stored_cookie_st* sc);
#endif

View File

@@ -47,6 +47,26 @@ static void bignum_add (uint8_t * num, unsigned size, unsigned step)
}
}
void ip_from_seed(uint8_t *seed, unsigned seed_size,
void *ip, size_t ip_size)
{
uint8_t digest[20];
int ret;
if (ip_size > sizeof(digest)) {
syslog(LOG_ERR, "too large IP!");
abort();
}
ret = gnutls_hash_fast(GNUTLS_DIG_SHA1, seed, seed_size, digest);
if (ret < 0) {
syslog(LOG_ERR, "cannot hash: %s", strerror(ret));
abort();
}
memcpy(ip, digest, ip_size);
}
void ip_lease_deinit(struct ip_lease_db_st* db)
{
@@ -173,14 +193,11 @@ int get_ipv4_lease(main_server_st* s, struct proc_st* proc)
goto fail;
}
if (max_loops == MAX_IP_TRIES) {
if (proc->seeds_are_set) {
memcpy(SA_IN_U8_P(&rnd), proc->ipv4_seed, 4);
} else {
uint32_t t = hash_any(proc->username, strlen(proc->username), 0);
memcpy(SA_IN_U8_P(&rnd), &t, 4);
}
} else
gnutls_rnd(GNUTLS_RND_NONCE, SA_IN_U8_P(&rnd), sizeof(struct in_addr));
memcpy(SA_IN_U8_P(&rnd), proc->ipv4_seed, 4);
} else {
ip_from_seed(SA_IN_U8_P(&rnd), sizeof(struct in_addr),
SA_IN_U8_P(&rnd), sizeof(struct in_addr));
}
max_loops--;
if (SA_IN_U8_P(&rnd)[3] == 255 || SA_IN_U8_P(&rnd)[3] == 254) /* avoid broadcast */
@@ -311,14 +328,11 @@ int get_ipv6_lease(main_server_st* s, struct proc_st* proc)
if (max_loops == MAX_IP_TRIES) {
memset(SA_IN6_U8_P(&rnd), 0, sizeof(struct in6_addr));
if (proc->seeds_are_set) {
memcpy(SA_IN6_U8_P(&rnd)+sizeof(struct in6_addr)-5, proc->ipv4_seed, 4);
} else {
uint32_t t = hash_any(proc->username, strlen(proc->username), 0);
memcpy(SA_IN6_U8_P(&rnd)+sizeof(struct in6_addr)-5, &t, 4);
}
} else
gnutls_rnd(GNUTLS_RND_NONCE, SA_IN6_U8_P(&rnd), sizeof(struct in6_addr));
memcpy(SA_IN6_U8_P(&rnd)+sizeof(struct in6_addr)-5, proc->ipv4_seed, 4);
} else {
ip_from_seed(SA_IN6_U8_P(&rnd), sizeof(struct in6_addr),
SA_IN6_U8_P(&rnd), sizeof(struct in6_addr));
}
max_loops--;
/* Mask the random number with the netmask */

View File

@@ -1,54 +1,16 @@
/*
* == Auth with username/password ==
* == Auth in main process (cookie auth only) ==
*
* main worker
* <------ AUTH_INIT (username)
* AUTH_REP(MSG) ------>
* <------ AUTH_REQ (password)
* .
* .
* .
* AUTH_REP(ΟΚ) ------>
*
*
* == Auth with cookie ==
*
* main worker
* <------ AUTH_COOKIE_REQ
* AUTH_REP(OK) ------>
*
*
* == Auth with legacy client ==
*
* main worker
* <------ AUTH_INIT (username)
* AUTH_REP(MSG) ------>
*
* (worker terminates as client disconnects)
*
* <------ AUTH_REINIT (password, sid)
* AUTH_REP(ΟΚ,newsid) ------>
* main worker
* <------ AUTH_COOKIE_REQ
* AUTH_REP(OK/FAILED) ------>
*
*/
/* AUTH_INIT */
message auth_init_msg
{
required bool user_present = 1;
required bool tls_auth_ok = 2 [default = false];
required string user_name = 3;
optional string cert_user_name = 4;
optional string cert_group_name = 5;
optional string hostname = 6;
}
/* 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 sid = 3;
enum AUTH_REP {
OK = 1;
MSG = 2;
FAILED = 3;
}
/* AUTH_COOKIE_REQ */
@@ -60,20 +22,9 @@ message auth_cookie_request_msg
optional string cert_group_name = 4;
}
/* AUTH_REQ */
message auth_request_msg
{
required string password = 1;
}
/* AUTH_REP */
message auth_reply_msg
{
enum AUTH_REP {
OK = 1;
MSG = 2;
FAILED = 3;
}
required AUTH_REP reply = 1;
optional bytes cookie = 2;
optional bytes session_id = 3;
@@ -98,12 +49,6 @@ message auth_reply_msg
repeated string dns = 22;
repeated string nbns = 23;
optional bool no_udp = 24 [default = false];
/* The sid is used when this message is a reply
* to an AUTH_REINIT, to let the worker know of
* any change in the sid.
*/
optional bytes sid = 25;
}
/* RESUME_FETCH_REQ + RESUME_DELETE_REQ */
@@ -155,3 +100,64 @@ message session_info_msg
required string dtls_ciphersuite = 2;
required string user_agent = 3;
}
/* Messages to and from the security module */
/*
* == Auth with username/password ==
*
* main worker
* <------ AUTH_INIT (username)
* AUTH_REP(MSG,SID) ------>
* <------ AUTH_CONT (SID,password)
* .
* .
* .
* AUTH_REP(OK,COOKIE)------>
*
*
* The authentication is now identical for openconnect and
* legacy cisco anyconnect clients. That is because the
* authentication method identifies the user using the SID.
*
*/
/* SEC_AUTH_INIT */
message sec_auth_init_msg
{
required bool user_present = 1;
required bool tls_auth_ok = 2 [default = false];
required string user_name = 3;
optional string cert_user_name = 4;
optional string cert_group_name = 5;
optional string hostname = 6;
required string ip = 7;
}
/* SEC_AUTH_CONT */
message sec_auth_cont_msg
{
required bool tls_auth_ok = 1 [default = false];
required string password = 2;
required bytes sid = 3;
required string ip = 4;
}
/* SEC_AUTH_REP */
message sec_auth_reply_msg
{
required AUTH_REP reply = 1;
optional bytes cookie = 2;
optional string user_name = 3;
optional string msg = 4;
optional bytes dtls_session_id = 5;
optional bytes sid = 6;
}
/* SEC_SIGN/DECRYPT */
message sec_op_msg
{
optional uint32 key_idx = 1;
required bytes data = 2;
}

View File

@@ -47,23 +47,8 @@
#include <common.h>
#include <pam.h>
static const struct auth_mod_st *module = NULL;
void main_auth_init(main_server_st *s)
{
#ifdef HAVE_PAM
if ((s->config->auth_types & pam_auth_funcs.type) == pam_auth_funcs.type)
module = &pam_auth_funcs;
else
#endif
if ((s->config->auth_types & plain_auth_funcs.type) == plain_auth_funcs.type) {
module = &plain_auth_funcs;
s->auth_extra = s->config->plain_passwd;
}
}
int send_auth_reply(main_server_st* s, struct proc_st* proc,
AuthReplyMsg__AUTHREP r, unsigned need_sid)
int send_cookie_auth_reply(main_server_st* s, struct proc_st* proc,
AUTHREP r)
{
AuthReplyMsg msg = AUTH_REPLY_MSG__INIT;
unsigned i;
@@ -74,14 +59,14 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc,
proc->config.routes_size = MAX_ROUTES;
}
if (r == AUTH_REPLY_MSG__AUTH__REP__OK && proc->tun_lease.name[0] != 0) {
if (r == AUTH__REP__OK && proc->tun_lease.name[0] != 0) {
char ipv6[MAX_IP_STR];
char ipv4[MAX_IP_STR];
char ipv6_local[MAX_IP_STR];
char ipv4_local[MAX_IP_STR];
/* fill message */
msg.reply = AUTH_REPLY_MSG__AUTH__REP__OK;
msg.reply = AUTH__REP__OK;
msg.has_cookie = 1;
msg.cookie.data = proc->cookie;
msg.cookie.len = COOKIE_SIZE;
@@ -148,20 +133,14 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc,
msg.routes = proc->config.routes;
}
if (need_sid) {
msg.has_sid = 1;
msg.sid.data = proc->sid;
msg.sid.len = sizeof(proc->sid);
}
ret = send_socket_msg_to_worker(s, proc, AUTH_REP, proc->tun_lease.fd,
ret = send_socket_msg_to_worker(s, proc, AUTH_COOKIE_REP, proc->tun_lease.fd,
&msg,
(pack_size_func)auth_reply_msg__get_packed_size,
(pack_func)auth_reply_msg__pack);
} else {
msg.reply = AUTH_REPLY_MSG__AUTH__REP__FAILED;
msg.reply = AUTH__REP__FAILED;
ret = send_msg_to_worker(s, proc, AUTH_REP,
ret = send_msg_to_worker(s, proc, AUTH_COOKIE_REP,
&msg,
(pack_size_func)auth_reply_msg__get_packed_size,
(pack_func)auth_reply_msg__pack);
@@ -176,82 +155,24 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc,
return 0;
}
int send_auth_reply_msg(main_server_st* s, struct proc_st* proc, unsigned need_sid)
{
AuthReplyMsg msg = AUTH_REPLY_MSG__INIT;
char tmp[MAX_MSG_SIZE] = "";
int ret;
if (proc->auth_ctx == NULL)
return -1;
ret = module->auth_msg(proc->auth_ctx, tmp, sizeof(tmp));
if (ret < 0)
return ret;
msg.msg = tmp;
msg.reply = AUTH_REPLY_MSG__AUTH__REP__MSG;
if (need_sid) {
msg.has_sid = 1;
msg.sid.data = proc->sid;
msg.sid.len = sizeof(proc->sid);
}
ret = send_msg_to_worker(s, proc, AUTH_REP, &msg,
(pack_size_func)auth_reply_msg__get_packed_size,
(pack_func)auth_reply_msg__pack);
if (ret < 0) {
mslog(s, proc, LOG_ERR, "send_msg error");
}
return ret;
}
static int check_user_group_status(main_server_st *s, struct proc_st* proc,
int tls_auth_ok, const char* cert_user, const char* cert_group)
{
if (s->config->auth_types & AUTH_TYPE_CERTIFICATE) {
if (tls_auth_ok == 0 && s->config->cisco_client_compat == 0) {
mslog(s, proc, LOG_INFO, "user '%s' presented no certificate", proc->username);
return -1;
}
if (tls_auth_ok != 0) {
if (proc->username[0] == 0) {
snprintf(proc->username, sizeof(proc->username), "%s", cert_user);
snprintf(proc->groupname, sizeof(proc->groupname), "%s", cert_group);
} else {
if (strcmp(proc->username, cert_user) != 0) {
mslog(s, proc, LOG_INFO, "user '%s' presented a certificate from user '%s'", proc->username, cert_user);
return -1;
}
if (s->config->cert_group_oid != NULL && strcmp(proc->groupname, cert_group) != 0) {
mslog(s, proc, LOG_INFO, "user '%s' presented a certificate from group '%s' but he is member of '%s'", proc->username, cert_group, proc->groupname);
return -1;
}
}
}
}
return 0;
}
int handle_auth_cookie_req(main_server_st* s, struct proc_st* proc,
const AuthCookieRequestMsg * req)
{
int ret;
struct stored_cookie_st sc;
time_t now = time(0);
gnutls_datum_t key = {s->cookie_key, sizeof(s->cookie_key)};
if (req->cookie.len == 0 || req->cookie.len != sizeof(proc->cookie))
if (req->cookie.len == 0 || req->cookie.len != sizeof(proc->cookie)) {
mslog(s, proc, LOG_INFO, "error in cookie size");
return -1;
}
ret = decrypt_cookie(s, req->cookie.data, req->cookie.len, &sc);
if (ret < 0)
ret = decrypt_cookie(&key, req->cookie.data, req->cookie.len, &sc);
if (ret < 0) {
mslog(s, proc, LOG_INFO, "error decrypting cookie");
return -1;
}
if (sc.expiration < now)
return -1;
@@ -268,136 +189,10 @@ time_t now = time(0);
proc->hostname[sizeof(proc->hostname)-1] = 0;
memcpy(proc->ipv4_seed, sc.ipv4_seed, sizeof(proc->ipv4_seed));
proc->seeds_are_set = 1;
ret = check_user_group_status(s, proc, req->tls_auth_ok, req->cert_user_name, req->cert_group_name);
if (ret < 0)
return ret;
return 0;
}
int handle_auth_init(main_server_st *s, struct proc_st* proc,
const AuthInitMsg * req)
{
int ret = -1;
char ipbuf[128];
const char* ip;
ip = human_addr((void*)&proc->remote_addr, proc->remote_addr_len,
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);
return -1;
}
if (req->hostname != NULL) {
snprintf(proc->hostname, sizeof(proc->hostname), "%s", req->hostname);
}
if (s->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
/* req->username is non-null at this point */
ret = module->auth_init(&proc->auth_ctx, proc, req->user_name, ip, s->auth_extra);
if (ret < 0)
return ret;
ret = module->auth_group(proc->auth_ctx, proc->groupname, sizeof(proc->groupname));
if (ret != 0)
return -1;
proc->groupname[sizeof(proc->groupname)-1] = 0;
/* a module is allowed to change the name of the user */
ret = module->auth_user(proc->auth_ctx, proc->username, sizeof(proc->username));
if (ret != 0 && req->user_name != NULL) {
snprintf(proc->username, MAX_USERNAME_SIZE, "%s", req->user_name);
}
}
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->sid.len != SID_SIZE) {
mslog(s, proc, LOG_DEBUG, "auth reinit from '%s' with no SID present", ip);
return -1;
}
if (req->password == NULL) {
mslog(s, proc, LOG_DEBUG, "auth reinit from '%s' with no password present", ip);
return -1;
}
/* search all procs for a matching SID */
list_for_each(&s->proc_list.head, ctmp, list) {
if (ctmp->status == PS_AUTH_ZOMBIE) {
if (memcmp(req->sid.data, ctmp->sid, SID_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->status = PS_AUTH_DEAD;
*_proc = proc = ctmp;
found = 1;
break;
}
}
}
if (found == 0) {
mslog_hex(s, proc, LOG_DEBUG, "auth reinit received but does not match any session with SID", req->sid.data, req->sid.len, 1);
return -1;
}
if (proc->auth_ctx == NULL) {
mslog(s, proc, LOG_ERR, "auth req but with no context!");
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)
{
if (proc->auth_ctx == NULL) {
mslog(s, proc, LOG_ERR, "auth req but with no context!");
return -1;
}
mslog(s, proc, LOG_DEBUG, "auth req for user '%s'", proc->username);
if (req->password == NULL)
return -1;
return module->auth_pass(proc->auth_ctx, req->password, strlen(req->password));
}
/* Checks for multiple users.
*
* It returns a negative error code if more than the maximum allowed
@@ -433,11 +228,3 @@ unsigned int entries = 1; /* that one */
return 0;
}
void proc_auth_deinit(main_server_st* s, struct proc_st* proc)
{
mslog(s, proc, LOG_DEBUG, "auth deinit for user '%s'", proc->username);
if (proc->auth_ctx != NULL) {
module->auth_deinit(proc->auth_ctx);
proc->auth_ctx = NULL;
}
}

View File

@@ -502,10 +502,6 @@ static int append_user_info(main_server_st * s, DBusMessageIter * subs,
strtmp = "connected";
else if (ctmp->status == PS_AUTH_INIT)
strtmp = "auth";
else if (ctmp->status == PS_AUTH_ZOMBIE)
strtmp = "zombie";
else if (ctmp->status == PS_AUTH_DEAD)
strtmp = "dead";
else if (ctmp->status == PS_AUTH_INACTIVE)
strtmp = "pre-auth";
else if (ctmp->status == PS_AUTH_FAILED)

View File

@@ -317,10 +317,6 @@ static int append_user_info(method_ctx *ctx,
strtmp = "connected";
else if (ctmp->status == PS_AUTH_INIT)
strtmp = "auth";
else if (ctmp->status == PS_AUTH_ZOMBIE)
strtmp = "zombie";
else if (ctmp->status == PS_AUTH_DEAD)
strtmp = "dead";
else if (ctmp->status == PS_AUTH_INACTIVE)
strtmp = "pre-auth";
else if (ctmp->status == PS_AUTH_FAILED)

View File

@@ -92,14 +92,14 @@ int set_tun_mtu(main_server_st * s, struct proc_st *proc, unsigned mtu)
return ret;
}
int handle_script_exit(main_server_st * s, struct proc_st *proc, int code, unsigned need_sid)
int handle_script_exit(main_server_st *s, struct proc_st *proc, int code)
{
int ret;
if (code == 0) {
proc->status = PS_AUTH_COMPLETED;
ret = send_auth_reply(s, proc, AUTH_REPLY_MSG__AUTH__REP__OK, need_sid);
ret = send_cookie_auth_reply(s, proc, AUTH__REP__OK);
if (ret < 0) {
mslog(s, proc, LOG_ERR,
"could not send auth reply cmd.");
@@ -113,7 +113,7 @@ int handle_script_exit(main_server_st * s, struct proc_st *proc, int code, unsig
"failed authentication attempt for user '%s'",
proc->username);
ret =
send_auth_reply(s, proc, AUTH_REPLY_MSG__AUTH__REP__FAILED, need_sid);
send_cookie_auth_reply(s, proc, AUTH__REP__FAILED);
if (ret < 0) {
mslog(s, proc, LOG_ERR,
"could not send reply auth cmd.");
@@ -125,7 +125,8 @@ int handle_script_exit(main_server_st * s, struct proc_st *proc, int code, unsig
fail:
/* we close the lease tun fd both on success and failure.
* The parent doesn't need to keep the tunfd.
* The parent doesn't need to keep the tunfd, and if it does,
* it causes issues to client.
*/
if (proc->tun_lease.name[0] != 0) {
if (proc->tun_lease.fd >= 0)
@@ -241,28 +242,12 @@ void remove_proc(main_server_st * s, struct proc_st *proc, unsigned k)
remove_iroutes(s, proc);
del_additional_config(&proc->config);
if (proc->auth_ctx != NULL)
proc_auth_deinit(s, proc);
if (proc->ipv4 || proc->ipv6)
remove_ip_leases(s, proc);
talloc_free(proc);
}
void proc_to_zombie(main_server_st * s, struct proc_st *proc)
{
proc->status = PS_AUTH_ZOMBIE;
mslog_hex(s, proc, LOG_INFO, "client disconnected, became zombie", proc->sid, sizeof(proc->sid), 1);
/* 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)
{
@@ -270,7 +255,6 @@ static int accept_user(main_server_st * s, struct proc_st *proc, unsigned cmd)
const char *group;
mslog(s, proc, LOG_DEBUG, "accepting user '%s'", proc->username);
proc_auth_deinit(s, proc);
/* check for multiple connections */
ret = check_multiple_users(s, proc);
@@ -298,16 +282,7 @@ static int accept_user(main_server_st * s, struct proc_st *proc, unsigned cmd)
else
group = proc->groupname;
if (cmd == AUTH_REQ || cmd == AUTH_INIT || cmd == AUTH_REINIT) {
/* generate cookie */
ret = generate_cookie(s, proc);
if (ret < 0) {
return ret;
}
mslog(s, proc, LOG_INFO,
"user '%s' of group '%s' authenticated", proc->username,
group);
} else if (cmd == AUTH_COOKIE_REQ) {
if (cmd == AUTH_COOKIE_REQ) {
mslog(s, proc, LOG_INFO,
"user '%s' of group '%s' re-authenticated (using cookie)",
proc->username, group);
@@ -334,31 +309,12 @@ static int accept_user(main_server_st * s, struct proc_st *proc, unsigned cmd)
* @cmd: the command received
* @result: the auth result
*/
static int handle_auth_res(main_server_st * s, struct proc_st *proc,
static int handle_cookie_auth_res(main_server_st *s, struct proc_st *proc,
unsigned cmd, int result)
{
int ret;
unsigned need_sid = 0;
unsigned can_cont = 1;
/* we use seeds only in AUTH_REINIT */
if (cmd == AUTH_REINIT)
need_sid = 1;
/* no point to allow ERR_AUTH_CONTINUE in cookie auth */
if (cmd == AUTH_COOKIE_REQ)
can_cont = 0;
if (can_cont != 0 && result == ERR_AUTH_CONTINUE) {
ret = send_auth_reply_msg(s, proc, need_sid);
if (ret < 0) {
proc->status = PS_AUTH_FAILED;
mslog(s, proc, LOG_ERR,
"could not send reply auth cmd.");
return ret;
}
return 0; /* wait for another command */
} else if (result == 0) {
if (result == 0) {
ret = accept_user(s, proc, cmd);
if (ret < 0) {
proc->status = PS_AUTH_FAILED;
@@ -367,8 +323,6 @@ static int handle_auth_res(main_server_st * s, struct proc_st *proc,
proc->status = PS_AUTH_COMPLETED;
} else if (result < 0) {
proc->status = PS_AUTH_FAILED;
add_to_ip_ban_list(s, &proc->remote_addr,
proc->remote_addr_len);
ret = result;
} else {
proc->status = PS_AUTH_FAILED;
@@ -377,11 +331,14 @@ static int handle_auth_res(main_server_st * s, struct proc_st *proc,
}
finished:
if (ret == ERR_WAIT_FOR_SCRIPT)
if (ret == ERR_WAIT_FOR_SCRIPT) {
/* we will wait for script termination to send our reply.
* The notification of peer will be done in handle_script_exit().
*/
ret = 0;
else {
} else {
/* no script was called. Handle it as a successful script call. */
ret = handle_script_exit(s, proc, ret, need_sid);
ret = handle_script_exit(s, proc, ret);
if (ret < 0)
proc->status = PS_AUTH_FAILED;
}
@@ -394,10 +351,7 @@ int handle_commands(main_server_st * s, struct proc_st *proc)
struct iovec iov[3];
uint8_t cmd;
struct msghdr hdr;
AuthInitMsg *auth_init;
AuthReinitMsg *auth_reinit;
AuthCookieRequestMsg *auth_cookie_req;
AuthRequestMsg *auth_req;
uint16_t length;
uint8_t *raw;
int ret, raw_len, e;
@@ -523,7 +477,6 @@ int handle_commands(main_server_st * s, struct proc_st *proc)
tmsg->user_agent);
session_info_msg__free_unpacked(tmsg, &pa);
}
break;
@@ -622,104 +575,6 @@ int handle_commands(main_server_st * s, struct proc_st *proc)
break;
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(&pa, 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, &pa);
proc->status = PS_AUTH_INIT;
ret = handle_auth_res(s, proc, cmd, ret);
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(&pa, 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, &pa);
proc->status = PS_AUTH_INIT;
ret = handle_auth_res(s, proc, cmd, ret);
if (ret < 0) {
goto cleanup;
}
/* handle_auth_reinit() has succeeded so the current proc
* is in dead state and unused. Terminate it.
*/
ret = ERR_WORKER_TERMINATED;
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(&pa, 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, &pa);
proc->status = PS_AUTH_INIT;
ret = handle_auth_res(s, proc, cmd, ret);
if (ret < 0) {
goto cleanup;
}
break;
case AUTH_COOKIE_REQ:
if (proc->status != PS_AUTH_INACTIVE) {
mslog(s, proc, LOG_ERR,
@@ -740,7 +595,7 @@ int handle_commands(main_server_st * s, struct proc_st *proc)
auth_cookie_request_msg__free_unpacked(auth_cookie_req, &pa);
ret = handle_auth_res(s, proc, cmd, ret);
ret = handle_cookie_auth_res(s, proc, cmd, ret);
if (ret < 0) {
goto cleanup;
}
@@ -760,93 +615,6 @@ int handle_commands(main_server_st * s, struct proc_st *proc)
return ret;
}
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 = NULL, *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) {
/* invalid entry. Clean it up */
list_del(&btmp->list);
talloc_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) {
return -1;
}
}
}
return 0;
}
void expire_banned(main_server_st * s)
{
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) {
/* invalid entry. Clean it up */
list_del(&btmp->list);
talloc_free(btmp);
}
}
return;
}
void add_to_ip_ban_list(main_server_st * s, struct sockaddr_storage *addr,
socklen_t addr_len)
{
struct banned_st *btmp;
if (s->config->min_reauth_time == 0)
return;
btmp = talloc(s, struct banned_st);
if (btmp == NULL)
return;
btmp->failed_time = time(0);
memcpy(&btmp->addr, addr, addr_len);
btmp->addr_len = addr_len;
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;
/* In CISCO compatibility mode we could have proc_st in
* mode INACTIVE or ZOMBIE that need to be cleaned up.
*/
list_for_each_safe(&s->proc_list.head, ctmp, cpos, list) {
if ((ctmp->status == PS_AUTH_ZOMBIE || ctmp->status == PS_AUTH_DEAD) &&
now - ctmp->conn_time > MAX_ZOMBIE_SECS) {
remove_proc(s, ctmp, 0);
}
}
return;
}
void run_sec_mod(main_server_st * s)
{
int e;
@@ -878,7 +646,7 @@ void run_sec_mod(main_server_st * s)
#endif
setproctitle(PACKAGE_NAME "-secmod");
sec_mod_server(s->config, p);
sec_mod_server(s, s->config, p, s->cookie_key, sizeof(s->cookie_key));
exit(0);
} else if (pid > 0) { /* parent */
s->sec_mod_pid = pid;

View File

@@ -432,7 +432,7 @@ struct script_wait_st *stmp = NULL, *spos;
if (stmp->pid == pid) {
mslog(s, stmp->proc, LOG_DEBUG, "%s-script exit status: %u", stmp->up?"connect":"disconnect", estatus);
list_del(&stmp->list);
ret = handle_script_exit(s, stmp->proc, estatus, 0);
ret = handle_script_exit(s, stmp->proc, estatus);
if (ret < 0) {
remove_proc(s, stmp->proc, 1);
} else {
@@ -569,8 +569,6 @@ void clear_lists(main_server_st *s)
close(ctmp->fd);
if (ctmp->tun_lease.fd >= 0)
close(ctmp->tun_lease.fd);
if (ctmp->auth_ctx != NULL)
proc_auth_deinit(s, ctmp);
list_del(&ctmp->list);
safe_memset(ctmp, 0, sizeof(*ctmp));
talloc_free(ctmp);
@@ -748,7 +746,7 @@ fail:
}
#define MAINTAINANCE_TIME(s) (MIN((300 + MAX_ZOMBIE_SECS), ((s)->config->cookie_validity + 300)))
#define MAINTAINANCE_TIME(s) (MIN(300, ((s)->config->cookie_validity + 300)))
static void check_other_work(main_server_st *s)
{
@@ -795,10 +793,8 @@ unsigned total = 10;
/* Check if we need to expire any cookies */
if (need_maintenance != 0) {
need_maintenance = 0;
mslog(s, NULL, LOG_DEBUG, "Performing maintenance");
mslog(s, NULL, LOG_DEBUG, "performing maintenance");
expire_tls_sessions(s);
expire_zombies(s);
expire_banned(s);
alarm(MAINTAINANCE_TIME(s));
}
}
@@ -852,18 +848,6 @@ int main(int argc, char** argv)
exit(1);
}
worker_pool = talloc_named(main_pool, 0, "worker", NULL);
if (worker_pool == NULL) {
fprintf(stderr, "talloc init error\n");
exit(1);
}
ws = talloc_zero(worker_pool, struct worker_st);
if (ws == NULL) {
fprintf(stderr, "memory error\n");
exit(1);
}
s = talloc_zero(main_pool, main_server_st);
if (s == NULL) {
fprintf(stderr, "memory error\n");
@@ -897,6 +881,8 @@ int main(int argc, char** argv)
/* Initialize GnuTLS */
tls_global_init(&creds);
/* this is the key used to sign and verify cookies. It is used
* by sec-mod (for signing) and main (for verification). */
ret = gnutls_rnd(GNUTLS_RND_RANDOM, s->cookie_key, sizeof(s->cookie_key));
if (ret < 0) {
fprintf(stderr, "Error in cookie key generation\n");
@@ -917,8 +903,6 @@ int main(int argc, char** argv)
exit(1);
}
main_auth_init(s);
/* Listen to network ports */
ret = listen_ports(s, s->config, &s->listen_list);
if (ret < 0) {
@@ -967,6 +951,19 @@ int main(int argc, char** argv)
/* Initialize certificates */
tls_load_certs(s, &creds);
/* initialize memory for worker process */
worker_pool = talloc_named(main_pool, 0, "worker", NULL);
if (worker_pool == NULL) {
fprintf(stderr, "talloc init error\n");
exit(1);
}
ws = talloc_zero(worker_pool, struct worker_st);
if (ws == NULL) {
fprintf(stderr, "memory error\n");
exit(1);
}
sigprocmask(SIG_BLOCK, &blockset, NULL);
alarm(MAINTAINANCE_TIME(s));
@@ -1036,15 +1033,6 @@ int main(int argc, char** argv)
break;
}
/* Check if the client is on the banned list */
ret = check_if_banned(s, &ws->remote_addr, ws->remote_addr_len);
if (ret < 0) {
/* banned */
close(fd);
mslog(s, NULL, LOG_INFO, "dropping client connection due to a previous failed authentication attempt");
break;
}
if (s->config->max_clients > 0 && s->active_clients >= s->config->max_clients) {
close(fd);
mslog(s, NULL, LOG_INFO, "Reached maximum client limit (active: %u)", s->active_clients);
@@ -1073,9 +1061,17 @@ int main(int argc, char** argv)
close(cmd_fd[0]);
clear_lists(s);
/* clear the cookie key */
safe_memset(s->cookie_key, 0, sizeof(s->cookie_key));
setproctitle(PACKAGE_NAME"-worker");
kill_on_parent_kill(SIGTERM);
/* write sec-mod's address */
ws->secmod_addr.sun_family = AF_UNIX;
snprintf(ws->secmod_addr.sun_path, sizeof(ws->secmod_addr.sun_path), "%s", s->socket_file);
ws->secmod_addr_len = SUN_LEN(&ws->secmod_addr);
ws->config = s->config;
ws->cmd_fd = cmd_fd[1];
ws->tun_fd = -1;
@@ -1135,10 +1131,7 @@ fork_failed:
list_for_each_safe(&s->proc_list.head, ctmp, cpos, list) {
if (ctmp->fd >= 0 && FD_ISSET(ctmp->fd, &rd_set)) {
ret = handle_commands(s, ctmp);
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) {
if (ret < 0) {
remove_proc(s, ctmp, (ret!=ERR_WORKER_TERMINATED)?1:0);
}
}

View File

@@ -68,8 +68,6 @@ enum {
PS_AUTH_INACTIVE, /* no comm with worker */
PS_AUTH_FAILED, /* no tried authenticated but failed */
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_DEAD, /* it was created but subsequently the client revived a zombie proc. - only present when cisco-client-compat is set */
PS_AUTH_COMPLETED, /* successful authentication */
};
@@ -97,7 +95,7 @@ struct proc_st {
* who re-uses it when it performs authentication in multiple
* sessions.
*/
uint8_t sid[SID_SIZE];
uint8_t sid[SID_SIZE];//XXX
/* The DTLS session ID associated with the TLS session
* it is either generated or restored from a cookie.
@@ -121,10 +119,8 @@ struct proc_st {
* and are considered when generating an IP address. That is used to
* generate the same address as previously allocated.
*/
uint8_t seeds_are_set; /* non zero if the following two elements are set */
uint8_t ipv4_seed[4];
void * auth_ctx; /* the context of authentication */
unsigned status; /* PS_AUTH_ */
unsigned auth_reqs; /* the number of requests received */
@@ -201,7 +197,6 @@ 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);
@@ -234,28 +229,14 @@ void mslog_hex(const main_server_st * s, const struct proc_st* proc,
int open_tun(main_server_st* s, struct proc_st* proc);
int set_tun_mtu(main_server_st* s, struct proc_st * proc, unsigned mtu);
int send_auth_reply_msg(main_server_st* s, struct proc_st* proc, unsigned need_sid);
int send_auth_reply(main_server_st* s, struct proc_st* proc,
AuthReplyMsg__AUTHREP r, unsigned need_sid);
int send_cookie_auth_reply(main_server_st* s, struct proc_st* proc,
AUTHREP r);
int handle_auth_cookie_req(main_server_st* s, struct proc_st* proc,
const AuthCookieRequestMsg * req);
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);
int check_multiple_users(main_server_st *s, struct proc_st* proc);
void add_to_ip_ban_list(main_server_st* s, struct sockaddr_storage *addr, socklen_t addr_len);
void expire_banned(main_server_st* s);
int check_if_banned(main_server_st* s, struct sockaddr_storage *addr, socklen_t addr_len);
int handle_script_exit(main_server_st *s, struct proc_st* proc, int code, unsigned need_sid);
int handle_script_exit(main_server_st *s, struct proc_st* proc, int code);
void run_sec_mod(main_server_st * s);

397
src/sec-mod-auth.c Normal file
View File

@@ -0,0 +1,397 @@
/*
* Copyright (C) 2013, 2014 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <signal.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include <tlslib.h>
#include <script-list.h>
#include <ip-lease.h>
#include "str.h"
#include <vpn.h>
#include <cookies.h>
#include <tun.h>
#include <main.h>
#include <ccan/list/list.h>
#include <main-auth.h>
#include <plain.h>
#include <common.h>
#include <pam.h>
#include <sec-mod.h>
#include <vpn.h>
static const struct auth_mod_st *module = NULL;
void sec_auth_init(struct cfg_st *config)
{
#ifdef HAVE_PAM
if ((config->auth_types & pam_auth_funcs.type) == pam_auth_funcs.type)
module = &pam_auth_funcs;
else
#endif
if ((config->auth_types & plain_auth_funcs.type) ==
plain_auth_funcs.type) {
module = &plain_auth_funcs;
}
}
static int generate_cookie(sec_mod_st * sec, client_entry_st * entry)
{
int ret;
struct stored_cookie_st sc;
uint32_t t;
ret =
gnutls_rnd(GNUTLS_RND_NONCE, sc.session_id, sizeof(sc.session_id));
if (ret < 0)
return -1;
/* Fixme: possibly we should allow for completely random seeds */
t = hash_any(entry->username, strlen(entry->username), 0);
memcpy(sc.ipv4_seed, &t, 4);
memcpy(sc.username, entry->username, sizeof(entry->username));
memcpy(sc.groupname, entry->groupname, sizeof(entry->groupname));
memcpy(sc.hostname, entry->hostname, sizeof(entry->hostname));
sc.expiration = time(0) + sec->config->cookie_validity;
ret =
encrypt_cookie(&sec->cookie_key, &sc, entry->cookie,
sizeof(entry->cookie));
if (ret < 0)
return -1;
return 0;
}
static
int send_sec_auth_reply(sec_mod_st * sec, client_entry_st * entry, AUTHREP r)
{
SecAuthReplyMsg msg = SEC_AUTH_REPLY_MSG__INIT;
int ret;
if (r == AUTH__REP__OK) {
/* fill message */
ret = generate_cookie(sec, entry);
if (ret < 0) {
seclog(LOG_INFO, "cannot generate cookie");
return ret;
}
msg.reply = AUTH__REP__OK;
msg.has_cookie = 1;
msg.cookie.data = entry->cookie;
msg.cookie.len = COOKIE_SIZE;
msg.user_name = entry->username;
msg.has_sid = 1;
msg.sid.data = entry->sid;
msg.sid.len = sizeof(entry->sid);
msg.has_dtls_session_id = 1;
msg.dtls_session_id.data = entry->dtls_session_id;
msg.dtls_session_id.len = sizeof(entry->dtls_session_id);
ret = send_msg(entry, sec->fd, SM_CMD_AUTH_REP,
&msg,
(pack_size_func)
sec_auth_reply_msg__get_packed_size,
(pack_func) sec_auth_reply_msg__pack);
} else {
msg.reply = AUTH__REP__FAILED;
ret = send_msg(entry, sec->fd, SM_CMD_AUTH_REP,
&msg,
(pack_size_func)
sec_auth_reply_msg__get_packed_size,
(pack_func) sec_auth_reply_msg__pack);
}
if (ret < 0) {
int e = errno;
seclog(LOG_ERR, "send_msg: %s", strerror(e));
return ret;
}
return 0;
}
static
int send_sec_auth_reply_msg(sec_mod_st * sec, client_entry_st * e)
{
SecAuthReplyMsg msg = SEC_AUTH_REPLY_MSG__INIT;
char tmp[MAX_MSG_SIZE] = "";
int ret;
if (e->auth_ctx == NULL)
return -1;
ret = module->auth_msg(e->auth_ctx, tmp, sizeof(tmp));
if (ret < 0)
return ret;
msg.msg = tmp;
msg.reply = AUTH__REP__MSG;
msg.has_sid = 1;
msg.sid.data = e->sid;
msg.sid.len = sizeof(e->sid);
ret = send_msg(e, sec->fd, SM_CMD_AUTH_REP, &msg,
(pack_size_func) sec_auth_reply_msg__get_packed_size,
(pack_func) sec_auth_reply_msg__pack);
if (ret < 0) {
seclog(LOG_ERR, "send_auth_reply_msg error");
}
return ret;
}
static int check_user_group_status(sec_mod_st * sec, client_entry_st * e,
int tls_auth_ok, const char *cert_user,
const char *cert_group)
{
if (sec->config->auth_types & AUTH_TYPE_CERTIFICATE) {
if (tls_auth_ok == 0 && sec->config->cisco_client_compat == 0) {
seclog(LOG_INFO, "user '%s' presented no certificate",
e->username);
return -1;
}
if (tls_auth_ok != 0) {
if (e->username[0] == 0) {
snprintf(e->username, sizeof(e->username), "%s",
cert_user);
snprintf(e->groupname, sizeof(e->groupname),
"%s", cert_group);
} else {
if (strcmp(e->username, cert_user) != 0) {
seclog(LOG_INFO,
"user '%s' presented a certificate from user '%s'",
e->username, cert_user);
return -1;
}
if (sec->config->cert_group_oid != NULL
&& strcmp(e->groupname, cert_group) != 0) {
seclog(LOG_INFO,
"user '%s' presented a certificate from group '%s' but he is member of '%s'",
e->username, cert_group,
e->groupname);
return -1;
}
}
}
}
return 0;
}
/* Performs the required steps based on the result from the
* authentication function (e.g. handle_auth_init).
*
* @cmd: the command received
* @result: the auth result
*/
static
int handle_sec_auth_res(sec_mod_st * sec, client_entry_st * e, int result)
{
int ret;
if (result == ERR_AUTH_CONTINUE) {
ret = send_sec_auth_reply_msg(sec, e);
if (ret < 0) {
e->status = PS_AUTH_FAILED;
seclog(LOG_ERR, "could not send reply auth cmd.");
return ret;
}
return 0; /* wait for another command */
} else if (result == 0) {
e->status = PS_AUTH_COMPLETED;
ret = send_sec_auth_reply(sec, e, AUTH__REP__OK);
if (ret < 0) {
e->status = PS_AUTH_FAILED;
seclog(LOG_ERR, "could not send reply auth cmd.");
return ret;
}
del_client_entry(sec->client_db, e);
} else {
e->status = PS_AUTH_FAILED;
add_ip_to_ban_list(sec->ban_db, e->ip, time(0) + sec->config->min_reauth_time);
ret = send_sec_auth_reply(sec, e, AUTH__REP__FAILED);
if (ret < 0) {
seclog(LOG_ERR, "could not send reply auth cmd.");
return ret;
}
if (result < 0) {
ret = result;
} else {
seclog(LOG_ERR, "unexpected auth result: %d\n", result);
ret = ERR_BAD_COMMAND;
}
}
return ret;
}
int handle_sec_auth_cont(sec_mod_st * sec, const SecAuthContMsg * req)
{
client_entry_st *e;
int ret;
if (check_if_banned(sec->ban_db, req->ip) != 0) {
seclog(LOG_INFO,
"IP '%s' is banned", req->ip);
return -1;
}
if (req->sid.len != SID_SIZE) {
seclog(LOG_ERR, "auth cont but with illegal sid size (%d)!",
(int)req->sid.len);
return -1;
}
e = find_client_entry(sec->client_db, req->sid.data);
if (e == NULL) {
seclog(LOG_ERR, "auth cont but with non-existing sid!");
return -1;
}
seclog(LOG_DEBUG, "auth cont for user '%s'", e->username);
if (req->password == NULL) {
seclog(LOG_ERR, "no password given in auth cont for user '%s'",
e->username);
return -1;
}
ret =
module->auth_pass(e->auth_ctx, req->password,
strlen(req->password));
if (ret < 0) {
seclog(LOG_DEBUG,
"error in password given in auth cont for user '%s'",
e->username);
}
return handle_sec_auth_res(sec, e, ret);
}
int handle_sec_auth_init(sec_mod_st * sec, const SecAuthInitMsg * req)
{
int ret = -1;
client_entry_st *e;
if (check_if_banned(sec->ban_db, req->ip) != 0) {
seclog(LOG_INFO,
"IP '%s' is banned", req->ip);
return -1;
}
if ((req->user_name == NULL || req->user_name[0] == 0)
&& (sec->config->auth_types & AUTH_TYPE_USERNAME_PASS)) {
seclog(LOG_DEBUG,
"auth init from '%s' with no username present", req->ip);
return -1;
}
e = new_client_entry(sec->client_db, req->ip);
if (e == NULL) {
seclog(LOG_ERR, "cannot initialize memory");
return -1;
}
if (req->hostname != NULL) {
snprintf(e->hostname, sizeof(e->hostname), "%s", req->hostname);
}
e->status = PS_AUTH_INIT;
if (sec->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
/* req->username is non-null at this point */
ret =
module->auth_init(&e->auth_ctx, e, req->user_name, req->ip,
sec->config->plain_passwd);
if (ret < 0)
return ret;
ret =
module->auth_group(e->auth_ctx, e->groupname,
sizeof(e->groupname));
if (ret != 0)
return -1;
e->groupname[sizeof(e->groupname) - 1] = 0;
/* a module is allowed to change the name of the user */
ret =
module->auth_user(e->auth_ctx, e->username,
sizeof(e->username));
if (ret != 0 && req->user_name != NULL) {
snprintf(e->username, MAX_USERNAME_SIZE, "%s",
req->user_name);
}
}
ret =
check_user_group_status(sec, e, req->tls_auth_ok,
req->cert_user_name, req->cert_group_name);
if (ret < 0) {
goto cleanup;
}
seclog(LOG_DEBUG, "auth init for user '%s' from '%s'", e->username, req->ip);
if (sec->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
ret = ERR_AUTH_CONTINUE;
goto cleanup;
}
ret = 0;
cleanup:
return handle_sec_auth_res(sec, e, ret);
}
void sec_auth_user_deinit(client_entry_st * e)
{
seclog(LOG_DEBUG, "auth deinit for user '%s'", e->username);
if (e->auth_ctx != NULL) {
module->auth_deinit(e->auth_ctx);
e->auth_ctx = NULL;
}
}

146
src/sec-mod-ban.c Normal file
View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2014 Red Hat
*
* This file is part of ocserv.
*
* ocserv 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.
*
* ocserv 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <system.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <common.h>
#include <syslog.h>
#include <vpn.h>
#include <tlslib.h>
#include <sec-mod.h>
#include <ccan/hash/hash.h>
#include <ccan/htable/htable.h>
typedef struct ban_entry_st {
char ip[MAX_IP_STR];
time_t expires; /* the time after the client is allowed to login */
} ban_entry_st;
static size_t rehash(const void *_e, void *unused)
{
ban_entry_st *e = (void*)_e;
return hash_any(e->ip, strlen(e->ip), 0);
}
void *sec_mod_ban_db_init(void *pool)
{
struct htable *db = talloc(pool, struct htable);
if (db == NULL)
return NULL;
htable_init(db, rehash, NULL);
return db;
}
void add_ip_to_ban_list(void *_db, const char *ip, time_t reenable_time)
{
struct htable *db = _db;
struct ban_entry_st *e;
if (db == NULL)
return;
e = talloc_zero(db, ban_entry_st);
if (e == NULL) {
return;
}
snprintf(e->ip, sizeof(e->ip), "%s", ip);
e->expires = reenable_time;
if (htable_add(db, rehash(e, NULL), e) == 0) {
seclog(LOG_INFO,
"could not add ban entry to hash table");
goto fail;
}
seclog(LOG_INFO,"added IP '%s' to ban list, will be removed at: %u", ip, (unsigned)reenable_time);
return;
fail:
talloc_free(e);
return;
}
/* The first argument is the entry from the hash, and
* the second is the entry from check_if_banned().
*/
static bool ban_entry_cmp(const void *_c1, void *_c2)
{
const struct ban_entry_st *c1 = _c1;
struct ban_entry_st *c2 = _c2;
if (strcmp(c1->ip, c2->ip) == 0 && c2->expires < c1->expires)
return 1;
return 0;
}
unsigned check_if_banned(void *_db, const char *ip)
{
struct htable *db = _db;
ban_entry_st t;
if (db == NULL || ip == NULL || ip[0] == 0)
return 0;
/* pass the current time somehow */
t.expires = time(0);
snprintf(t.ip, sizeof(t.ip), "%s", ip);
if (htable_get(db, rehash(&t, NULL), ban_entry_cmp, &t) != 0)
return 1;
return 0;
}
void cleanup_banned_entries(void *_db)
{
struct htable *db = _db;
ban_entry_st *t;
struct htable_iter iter;
time_t now = time(0);
if (db == NULL)
return;
t = htable_first(db, &iter);
while (t != NULL) {
if (now >= t->expires) {
htable_delval(db, &iter);
talloc_free(t);
}
t = htable_next(db, &iter);
}
}

148
src/sec-mod-db.c Normal file
View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2014 Red Hat
*
* This file is part of ocserv.
*
* ocserv 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.
*
* ocserv 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, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <system.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <common.h>
#include <syslog.h>
#include <vpn.h>
#include <tlslib.h>
#include <sec-mod.h>
#include <ccan/hash/hash.h>
#include <ccan/htable/htable.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
static size_t rehash(const void *_e, void *unused)
{
const client_entry_st *e = _e;
return hash_any(e->sid, sizeof(e->sid), 0);
}
void *sec_mod_client_db_init(void *pool)
{
struct htable *db = talloc(pool, struct htable);
if (db == NULL)
return NULL;
htable_init(db, rehash, NULL);
return db;
}
client_entry_st *new_client_entry(void *_db, const char *ip)
{
struct htable *db = _db;
client_entry_st *e;
int ret;
e = talloc_zero(db, client_entry_st);
if (e == NULL) {
return NULL;
}
snprintf(e->ip, sizeof(e->ip), "%s", ip);
ret = gnutls_rnd(GNUTLS_RND_RANDOM, e->sid, sizeof(e->sid));
if (ret < 0) {
seclog(LOG_ERR, "error generating SID");
goto fail;
}
e->time = time(0);
if (htable_add(db, rehash(e, NULL), e) == 0) {
seclog(LOG_ERR,
"could not add client entry to hash table");
goto fail;
}
return e;
fail:
talloc_free(e);
return NULL;
}
static bool client_entry_cmp(const void *_c1, void *_c2)
{
const struct client_entry_st *c1 = _c1;
struct client_entry_st *c2 = _c2;
if (memcmp(c1->sid, c2->sid, SID_SIZE) == 0)
return 1;
return 0;
}
client_entry_st *find_client_entry(void *_db, uint8_t sid[SID_SIZE])
{
struct htable *db = _db;
client_entry_st t;
memcpy(t.sid, sid, SID_SIZE);
return htable_get(db, rehash(&t, NULL), client_entry_cmp, &t);
}
static void clean_entry(client_entry_st * e)
{
sec_auth_user_deinit(e);
talloc_free(e);
}
void cleanup_client_entries(void *_db)
{
struct htable *db = _db;
client_entry_st *t;
struct htable_iter iter;
time_t now = time(0);
t = htable_first(db, &iter);
while (t != NULL) {
if (now - t->time > MAX_AUTH_SECS) {
htable_delval(db, &iter);
clean_entry(t);
}
t = htable_next(db, &iter);
}
}
void del_client_entry(void *_db, client_entry_st * e)
{
struct htable *db = _db;
htable_del(db, rehash(e, NULL), e);
clean_entry(e);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Nikos Mavrogiannopoulos
* Copyright (C) 2013, 2014 Nikos Mavrogiannopoulos
*
* This file is part of ocserv.
*
@@ -37,13 +37,18 @@
#include <common.h>
#include <syslog.h>
#include <vpn.h>
#include <sec-mod.h>
#include <tlslib.h>
#include <sys/uio.h>
#include <ipc.pb-c.h>
#include <gnutls/gnutls.h>
#include <gnutls/abstract.h>
#define MAX_WAIT_SECS 3
#define MAX_PIN_SIZE GNUTLS_PKCS11_MAX_PIN_LEN
#define MAINTAINANCE_TIME 300
static int need_maintainance = 0;
struct pin_st {
char pin[MAX_PIN_SIZE];
@@ -122,7 +127,7 @@ int load_pins(struct cfg_st *config, struct pin_st *s)
return -1;
}
ret = read(fd, s->srk_pin, sizeof(s->srk_pin)-1);
ret = read(fd, s->srk_pin, sizeof(s->srk_pin) - 1);
close(fd);
if (ret <= 1) {
syslog(LOG_ERR, "could not read from PIN file '%s'",
@@ -143,7 +148,7 @@ int load_pins(struct cfg_st *config, struct pin_st *s)
return -1;
}
ret = read(fd, s->pin, sizeof(s->pin)-1);
ret = read(fd, s->pin, sizeof(s->pin) - 1);
close(fd);
if (ret <= 1) {
syslog(LOG_ERR, "could not read from PIN file '%s'",
@@ -159,6 +164,140 @@ int load_pins(struct cfg_st *config, struct pin_st *s)
return 0;
}
static int handle_op(void *pool, sec_mod_st * sec, uint8_t type, uint8_t * rep,
size_t rep_size)
{
SecOpMsg msg = SEC_OP_MSG__INIT;
int ret;
msg.data.data = rep;
msg.data.len = rep_size;
ret = send_msg(pool, sec->fd, type, &msg,
(pack_size_func) sec_op_msg__get_packed_size,
(pack_func) sec_op_msg__pack);
if (ret < 0) {
seclog(LOG_WARNING, "sec-mod error in sending reply");
}
return 0;
}
static
int process_packet(void *pool, sec_mod_st * sec, cmd_request_t cmd,
uint8_t * buffer, size_t buffer_size)
{
unsigned i;
gnutls_datum_t data, out;
int ret;
SecOpMsg *op;
PROTOBUF_ALLOCATOR(pa, pool);
seclog(LOG_DEBUG, "cmd [size=%d] %s\n", (int)buffer_size,
cmd_request_to_str(cmd));
data.data = buffer;
data.size = buffer_size;
switch (cmd) {
case SM_CMD_SIGN:
case SM_CMD_DECRYPT:
op = sec_op_msg__unpack(&pa, data.size, data.data);
if (op == NULL) {
seclog(LOG_INFO, "error unpacking sec op\n");
return -1;
}
i = op->key_idx;
if (op->has_key_idx == 0 || i >= sec->key_size) {
seclog(LOG_INFO,
"received out-of-bounds key index (%d)", i);
return -1;
}
data.data = op->data.data;
data.size = op->data.len;
if (cmd == SM_CMD_DECRYPT) {
ret =
gnutls_privkey_decrypt_data(sec->key[i], 0, &data,
&out);
} else {
#if GNUTLS_VERSION_NUMBER >= 0x030200
ret =
gnutls_privkey_sign_hash(sec->key[i], 0,
GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA,
&data, &out);
#else
ret =
gnutls_privkey_sign_raw_data(sec->key[i], 0, &data,
&out);
#endif
}
sec_op_msg__free_unpacked(op, &pa);
if (ret < 0) {
seclog(LOG_INFO, "error in crypto operation: %s",
gnutls_strerror(ret));
return -1;
}
ret = handle_op(pool, sec, cmd, out.data, out.size);
gnutls_free(out.data);
return ret;
case SM_CMD_AUTH_INIT:{
SecAuthInitMsg *auth_init;
auth_init =
sec_auth_init_msg__unpack(&pa, data.size,
data.data);
if (auth_init == NULL) {
seclog(LOG_INFO, "error unpacking auth init\n");
return -1;
}
ret = handle_sec_auth_init(sec, auth_init);
sec_auth_init_msg__free_unpacked(auth_init, &pa);
return ret;
}
case SM_CMD_AUTH_CONT:{
SecAuthContMsg *auth_cont;
auth_cont =
sec_auth_cont_msg__unpack(&pa, data.size,
data.data);
if (auth_cont == NULL) {
seclog(LOG_INFO, "error unpacking auth cont\n");
return -1;
}
ret = handle_sec_auth_cont(sec, auth_cont);
sec_auth_cont_msg__free_unpacked(auth_cont, &pa);
return ret;
}
default:
seclog(LOG_WARNING, "unknown type 0x%.2x", cmd);
return -1;
}
return 0;
}
static void handle_alarm(int signo)
{
need_maintainance = 1;
}
static void check_other_work(sec_mod_st *sec)
{
if (need_maintainance) {
seclog(LOG_DEBUG, "performing maintenance");
cleanup_client_entries(sec->client_db);
cleanup_banned_entries(sec->ban_db);
alarm(MAINTAINANCE_TIME);
}
}
/* sec_mod_server:
* @config: server configuration
@@ -169,11 +308,6 @@ int load_pins(struct cfg_st *config, struct pin_st *s)
* and then accepts connections from the workers to it. Then
* it serves commands requested on the server's private key.
*
* The format of the command is:
* byte[0]: key index
* byte[1]: operation ('D': decrypt, 'S' sign)
* byte[2-total]: data
*
* When the operation is decrypt the provided data are
* decrypted and sent back to worker. The sign operation
* signs the provided data.
@@ -192,37 +326,59 @@ int load_pins(struct cfg_st *config, struct pin_st *s)
* clients fast without becoming a bottleneck due to private
* key operations.
*/
void sec_mod_server(struct cfg_st *config, const char *socket_file)
void sec_mod_server(void *pool, struct cfg_st *config, const char *socket_file,
uint8_t * cookie_key, unsigned cookie_key_size)
{
struct sockaddr_un sa;
socklen_t sa_len;
int cfd, ret, e;
unsigned i, buffer_size, type;
gnutls_privkey_t *key;
uint8_t *buffer;
unsigned key_size = config->key_size;
unsigned cmd, length;
unsigned i, buffer_size;
uint8_t *buffer, *tpool;
struct pin_st pins;
gnutls_datum_t data, out;
uint16_t length;
struct iovec iov[2];
int sd;
sec_mod_st *sec;
sec = talloc_zero(pool, sec_mod_st);
if (sec == NULL) {
seclog(LOG_ERR, "error in memory allocation");
exit(1);
}
sec->cookie_key.data = cookie_key;
sec->cookie_key.size = cookie_key_size;
sec->config = config;
ocsignal(SIGHUP, SIG_IGN);
ocsignal(SIGINT, SIG_DFL);
ocsignal(SIGTERM, SIG_DFL);
ocsignal(SIGALRM, handle_alarm);
alarm(MAINTAINANCE_TIME);
sec_auth_init(config);
#ifdef HAVE_PKCS11
ret = gnutls_pkcs11_reinit();
if (ret < 0) {
syslog(LOG_WARNING, "error in PKCS #11 reinitialization: %s",
seclog(LOG_WARNING, "error in PKCS #11 reinitialization: %s",
gnutls_strerror(ret));
}
#endif
sec->client_db = sec_mod_client_db_init(pool);
if (sec->client_db == NULL) {
seclog(LOG_ERR, "error in client db initialization");
exit(1);
}
if (config->min_reauth_time > 0)
sec->ban_db = sec_mod_ban_db_init(pool);
buffer_size = 8 * 1024;
buffer = malloc(buffer_size);
buffer = talloc_size(pool, buffer_size);
if (buffer == NULL) {
syslog(LOG_ERR, "error in memory allocation");
seclog(LOG_ERR, "error in memory allocation");
exit(1);
}
@@ -234,7 +390,7 @@ void sec_mod_server(struct cfg_st *config, const char *socket_file)
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd == -1) {
e = errno;
syslog(LOG_ERR, "could not create socket '%s': %s", socket_file,
seclog(LOG_ERR, "could not create socket '%s': %s", socket_file,
strerror(e));
exit(1);
}
@@ -243,7 +399,7 @@ void sec_mod_server(struct cfg_st *config, const char *socket_file)
ret = bind(sd, (struct sockaddr *)&sa, SUN_LEN(&sa));
if (ret == -1) {
e = errno;
syslog(LOG_ERR, "could not bind socket '%s': %s", socket_file,
seclog(LOG_ERR, "could not bind socket '%s': %s", socket_file,
strerror(e));
exit(1);
}
@@ -251,53 +407,55 @@ void sec_mod_server(struct cfg_st *config, const char *socket_file)
ret = chown(socket_file, config->uid, config->gid);
if (ret == -1) {
e = errno;
syslog(LOG_ERR, "could not chown socket '%s': %s", socket_file,
seclog(LOG_INFO, "could not chown socket '%s': %s", socket_file,
strerror(e));
}
ret = listen(sd, 1024);
if (ret == -1) {
e = errno;
syslog(LOG_ERR, "could not listen to socket '%s': %s",
seclog(LOG_ERR, "could not listen to socket '%s': %s",
socket_file, strerror(e));
exit(1);
}
ret = load_pins(config, &pins);
if (ret < 0) {
syslog(LOG_ERR, "error loading PIN files");
seclog(LOG_ERR, "error loading PIN files");
exit(1);
}
key = malloc(sizeof(*key) * config->key_size);
if (key == NULL) {
syslog(LOG_ERR, "error in memory allocation");
sec->key_size = config->key_size;
sec->key = talloc_size(sec, sizeof(*sec->key) * config->key_size);
if (sec->key == NULL) {
seclog(LOG_ERR, "error in memory allocation");
exit(1);
}
/* read private keys */
for (i = 0; i < key_size; i++) {
ret = gnutls_privkey_init(&key[i]);
for (i = 0; i < sec->key_size; i++) {
ret = gnutls_privkey_init(&sec->key[i]);
GNUTLS_FATAL_ERR(ret);
/* load the private key */
if (gnutls_url_is_supported(config->key[i]) != 0) {
gnutls_privkey_set_pin_function(key[i], pin_callback,
&pins);
gnutls_privkey_set_pin_function(sec->key[i],
pin_callback, &pins);
ret =
gnutls_privkey_import_url(key[i], config->key[i],
0);
gnutls_privkey_import_url(sec->key[i],
config->key[i], 0);
GNUTLS_FATAL_ERR(ret);
} else {
gnutls_datum_t data;
ret = gnutls_load_file(config->key[i], &data);
if (ret < 0) {
syslog(LOG_ERR, "error loading file '%s'",
seclog(LOG_ERR, "error loading file '%s'",
config->key[i]);
GNUTLS_FATAL_ERR(ret);
}
ret =
gnutls_privkey_import_x509_raw(key[i], &data,
gnutls_privkey_import_x509_raw(sec->key[i], &data,
GNUTLS_X509_FMT_PEM,
NULL, 0);
GNUTLS_FATAL_ERR(ret);
@@ -306,93 +464,66 @@ void sec_mod_server(struct cfg_st *config, const char *socket_file)
}
}
syslog(LOG_INFO, "sec-mod initialized (socket: %s)", socket_file);
seclog(LOG_INFO, "sec-mod initialized (socket: %s)", socket_file);
for (;;) {
check_other_work(sec);
sa_len = sizeof(sa);
cfd = accept(sd, (struct sockaddr *)&sa, &sa_len);
if (cfd == -1) {
e = errno;
syslog(LOG_ERR,
"sec-mod error accepting connection: %s",
strerror(e));
if (e != EINTR) {
seclog(LOG_DEBUG,
"sec-mod error accepting connection: %s",
strerror(e));
}
continue;
}
/* do not allow unauthorized processes to issue commands
*/
ret = check_upeer_id("sec-mod", cfd, config->uid, config->gid);
if (ret < 0) /* allow root connections */
ret = check_upeer_id("sec-mod", cfd, 0, 0);
if (ret < 0) {
syslog(LOG_ERR,
"sec-mod: rejected unauthorized connection");
seclog(LOG_INFO, "rejected unauthorized connection");
goto cont;
}
/* read request */
ret = recv(cfd, buffer, buffer_size, 0);
ret = force_read_timeout(cfd, buffer, 3, MAX_WAIT_SECS);
if (ret == 0)
goto cont;
else if (ret <= 2) {
else if (ret < 3) {
e = errno;
syslog(LOG_ERR, "error receiving sec-mod data: %s",
seclog(LOG_INFO, "error receiving msg head: %s",
strerror(e));
goto cont;
}
/* calculate */
i = buffer[0];
type = buffer[1];
cmd = buffer[0];
length = buffer[1] | buffer[2] << 8;
if (i >= key_size) {
syslog(LOG_ERR,
"sec-mod received out-of-bounds key index");
goto cont;
}
data.data = &buffer[2];
data.size = ret - 2;
if (type == 'S') {
#if GNUTLS_VERSION_NUMBER >= 0x030200
ret =
gnutls_privkey_sign_hash(key[i], 0,
GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA,
&data, &out);
#else
ret =
gnutls_privkey_sign_raw_data(key[i], 0, &data,
&out);
#endif
} else if (type == 'D') {
ret =
gnutls_privkey_decrypt_data(key[i], 0, &data, &out);
} else {
syslog(LOG_ERR, "unknown type 0x%.2x", type);
if (length > buffer_size - 4) {
seclog(LOG_INFO, "too big message");
goto cont;
}
/* read the body */
ret = force_read_timeout(cfd, buffer, length, MAX_WAIT_SECS);
if (ret < 0) {
syslog(LOG_ERR, "sec-mod error in crypto operation: %s",
gnutls_strerror(ret));
e = errno;
seclog(LOG_INFO, "error receiving msg body: %s",
strerror(e));
goto cont;
}
/* write reply */
length = out.size;
iov[0].iov_base = &length;
iov[0].iov_len = 2;
iov[1].iov_base = out.data;
iov[1].iov_len = out.size;
ret = writev(cfd, iov, 2);
if (ret == -1) {
e = errno;
syslog(LOG_ERR, "sec-mod error in writev: %s",
strerror(e));
tpool = talloc_new(sec);
sec->fd = cfd;
ret = process_packet(tpool, sec, cmd, buffer, ret);
if (ret < 0) {
seclog(LOG_INFO, "error processing data for '%s' command (%d)", cmd_request_to_str(cmd), ret);
}
talloc_free(tpool);
gnutls_free(out.data);
cont:
close(cfd);
}

View File

@@ -20,6 +20,66 @@
*/
#ifndef SEC_MOD_H
void sec_mod_server(struct cfg_st* config, const char* file);
#include <cookies.h>
typedef struct sec_mod_st {
gnutls_datum_t cookie_key; /* the key to generate cookies */
struct cfg_st *config;
gnutls_privkey_t *key;
unsigned key_size;
void *client_db;
void *ban_db;
int fd;
} sec_mod_st;
typedef struct client_entry_st {
/* A unique session identifier used to distinguish sessions
* prior to authentication. It is sent as cookie to the client
* who re-uses it when it performs authentication in multiple
* sessions.
*/
uint8_t sid[SID_SIZE];
void * auth_ctx; /* the context of authentication */
unsigned status; /* PS_AUTH_ */
char ip[MAX_IP_STR]; /* the user's IP */
char hostname[MAX_HOSTNAME_SIZE]; /* the requested hostname */
char username[MAX_USERNAME_SIZE]; /* the owner */
char groupname[MAX_GROUPNAME_SIZE]; /* the owner's group */
uint8_t cookie[COOKIE_SIZE]; /* the cookie associated with the session */
uint8_t dtls_session_id[GNUTLS_MAX_SESSION_ID];
time_t time;
} client_entry_st;
void *sec_mod_client_db_init(void *pool);
client_entry_st * new_client_entry(void *_db, const char *ip);
client_entry_st * find_client_entry(void *_db, uint8_t sid[SID_SIZE]);
void del_client_entry(void *_db, client_entry_st * e);
void cleanup_client_entries(void *_db);
#ifdef __GNUC__
# define seclog(prio, fmt, ...) \
syslog(prio, "sec-mod: "fmt, ##__VA_ARGS__)
#else
# define seclog syslog
#endif
void sec_auth_init(struct cfg_st *config);
void sec_auth_user_deinit(client_entry_st *e);
int handle_sec_auth_init(sec_mod_st *sec, const SecAuthInitMsg * req);
int handle_sec_auth_cont(sec_mod_st *sec, const SecAuthContMsg * req);
void sec_mod_server(void *pool, struct cfg_st *config, const char *socket_file,
uint8_t *cookie_key, unsigned cookie_key_size);
void cleanup_banned_entries(void *_db);
unsigned check_if_banned(void *_db, const char *ip);
void add_ip_to_ban_list(void *_db, const char *ip, time_t reenable_time);
void *sec_mod_ban_db_init(void *pool);
#endif

View File

@@ -78,7 +78,7 @@ int check_upeer_id(const char *mod, int cfd, int uid, int gid)
"%s received request from pid %u and uid %u",
mod, (unsigned)cr.pid, (unsigned)cr.uid);
if (cr.uid != uid || cr.gid != gid) {
if (cr.uid != 0 && (cr.uid != uid || cr.gid != gid)) {
syslog(LOG_DEBUG,
"%s received unauthorized request from pid %u and uid %u",
mod, (unsigned)cr.pid, (unsigned)cr.uid);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Nikos Mavrogiannopoulos
* Copyright (C) 2013, 2014 Nikos Mavrogiannopoulos
*
* This file is part of ocserv.
*
@@ -38,6 +38,7 @@
#include <vpn.h>
#include <main.h>
#include <worker.h>
#include <common.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <c-ctype.h>
@@ -394,10 +395,10 @@ int key_cb_common_func (gnutls_privkey_t key, void* userdata, const gnutls_datum
gnutls_datum_t * output, unsigned type)
{
struct key_cb_data* cdata = userdata;
int sd, ret, e;
uint8_t header[2];
struct iovec iov[2];
uint16_t length;
int sd = -1, ret, e;
SecOpMsg msg = SEC_OP_MSG__INIT;
SecOpMsg *reply = NULL;
PROTOBUF_ALLOCATOR(pa, userdata);
output->data = NULL;
@@ -416,50 +417,47 @@ int key_cb_common_func (gnutls_privkey_t key, void* userdata, const gnutls_datum
goto error;
}
header[0] = cdata->idx;
header[1] = type;
msg.has_key_idx = 1;
msg.key_idx = cdata->idx;
msg.data.data = raw_data->data;
msg.data.len = raw_data->size;
iov[0].iov_base = header;
iov[0].iov_len = 2;
iov[1].iov_base = raw_data->data;
iov[1].iov_len = raw_data->size;
ret = writev(sd, iov, 2);
if (ret == -1) {
e = errno;
syslog(LOG_ERR, "error writing to sec-mod: %s", strerror(e));
ret = send_msg(userdata, sd, type, &msg,
(pack_size_func)sec_op_msg__get_packed_size,
(pack_func)sec_op_msg__pack);
if (ret < 0) {
goto error;
}
ret = recv(sd, &length, 2, 0);
if (ret < 2) {
ret = recv_msg(userdata, sd, type, (void*)&reply, (unpack_func)sec_op_msg__unpack);
if (ret < 0) {
e = errno;
syslog(LOG_ERR, "error reading from sec-mod: %s", strerror(e));
syslog(LOG_ERR, "error receiving sec-mod reply: %s",
strerror(e));
goto error;
}
close(sd);
sd = -1;
output->size = length;
output->data = gnutls_malloc(output->size);
output->size = reply->data.len;
output->data = gnutls_malloc(reply->data.len);
if (output->data == NULL) {
syslog(LOG_ERR, "error allocating memory");
goto error;
}
ret = recv(sd, output->data, output->size, 0);
if (ret <= 0) {
e = errno;
syslog(LOG_ERR, "error reading from sec-mod: %s", strerror(e));
goto error;
}
memcpy(output->data, reply->data.data, reply->data.len);
output->size = ret;
close(sd);
if (reply != NULL)
sec_op_msg__free_unpacked(reply, &pa);
return 0;
error:
close(sd);
if (sd != -1)
close(sd);
gnutls_free(output->data);
if (reply != NULL)
sec_op_msg__free_unpacked(reply, &pa);
return GNUTLS_E_INTERNAL_ERROR;
}
@@ -468,13 +466,13 @@ static
int key_cb_sign_func (gnutls_privkey_t key, void* userdata, const gnutls_datum_t * raw_data,
gnutls_datum_t * signature)
{
return key_cb_common_func(key, userdata, raw_data, signature, 'S');
return key_cb_common_func(key, userdata, raw_data, signature, SM_CMD_SIGN);
}
static int key_cb_decrypt_func(gnutls_privkey_t key, void* userdata, const gnutls_datum_t * ciphertext,
gnutls_datum_t * plaintext)
{
return key_cb_common_func(key, userdata, ciphertext, plaintext, 'D');
return key_cb_common_func(key, userdata, ciphertext, plaintext, SM_CMD_DECRYPT);
}
static void key_cb_deinit_func(gnutls_privkey_t key, void* userdata)

View File

@@ -80,25 +80,23 @@ extern int syslog_open;
#define ERR_NO_IP -8
#define ERR_PARSING -9
#define ERR_EXEC -10
#define ERR_WORKER_TERMINATED -11
#define ERR_PEER_TERMINATED -11
#define ERR_CTL -12
#define ERR_WORKER_TERMINATED ERR_PEER_TERMINATED
#define LOG_HTTP_DEBUG 2048
#define LOG_TRANSFER_DEBUG 2049
#define MAX_AUTH_SECS 40
#define MAX_CIPHERSUITE_NAME 64
#define MAX_DTLS_CIPHERSUITE_NAME 24
#define MAX_MSG_SIZE 256
#define SID_SIZE 12
#define MAX_ZOMBIE_SECS 240
typedef enum {
AUTH_INIT = 1,
AUTH_REP = 2,
AUTH_REQ = 3,
AUTH_COOKIE_REP = 2,
AUTH_COOKIE_REQ = 4,
AUTH_MSG = 5,
RESUME_STORE_REQ = 6,
RESUME_DELETE_REQ = 7,
RESUME_FETCH_REQ = 8,
@@ -107,8 +105,13 @@ typedef enum {
CMD_TUN_MTU = 11,
CMD_TERMINATE = 12,
CMD_SESSION_INFO = 13,
AUTH_REINIT = 14,
CMD_CLI_STATS = 15,
SM_CMD_AUTH_INIT = 120,
SM_CMD_AUTH_CONT,
SM_CMD_AUTH_REP,
SM_CMD_DECRYPT,
SM_CMD_SIGN,
} cmd_request_t;
typedef struct

View File

@@ -46,7 +46,6 @@
#define VERSION_MSG "<version who=\"sg\">0.1(1)</version>\n"
#define SUCCESS_MSG_HEAD "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
"<config-auth client=\"vpn\" type=\"complete\">\n" \
VERSION_MSG \
@@ -56,22 +55,21 @@
#define SUCCESS_MSG_FOOT "</auth></config-auth>\n"
static const char login_msg_user[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<config-auth client=\"vpn\" type=\"auth-request\">\n"
VERSION_MSG \
VERSION_MSG
"<auth id=\"main\">\n"
"<message>Please enter your username</message>\n"
"<form method=\"post\" action=\"/auth\">\n"
"<input type=\"text\" name=\"username\" label=\"Username:\" />\n"
"</form></auth>\n"
"</config-auth>";
"</form></auth>\n" "</config-auth>";
static const char login_msg_no_user[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<config-auth client=\"vpn\" type=\"auth-request\">\n"
VERSION_MSG \
VERSION_MSG
"<auth id=\"main\">\n"
"<message>%s</message>\n"
"<message>%s</message>\n"
"<form method=\"post\" action=\"/auth\">\n"
"<input type=\"password\" name=\"password\" label=\"Password:\" />\n"
"</form></auth></config-auth>\n";
@@ -80,8 +78,7 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
{
int ret;
char login_msg[MAX_MSG_SIZE + sizeof(login_msg_user)];
char context[BASE64_LENGTH(SID_SIZE)+1];
struct http_req_st *req = &ws->req;
char context[BASE64_LENGTH(SID_SIZE) + 1];
unsigned int lsize;
tls_cork(ws->session);
@@ -93,12 +90,14 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
if (ret < 0)
return -1;
if (req->sid_cookie_set == 0 || memcmp(req->sid_cookie, ws->sid, SID_SIZE) != 0) {
base64_encode((char*)ws->sid, sizeof(ws->sid), (char*)context, sizeof(context));
if (ws->sid_set != 0) {
base64_encode((char *)ws->sid, sizeof(ws->sid), (char *)context,
sizeof(context));
ret =
tls_printf(ws->session, "Set-Cookie: webvpncontext=%s; Max-Age=%u; Secure\r\n",
context, (unsigned)MAX_ZOMBIE_SECS);
tls_printf(ws->session,
"Set-Cookie: webvpncontext=%s; Max-Age=%u; Secure\r\n",
context, (unsigned)MAX_AUTH_SECS);
if (ret < 0)
return -1;
@@ -118,7 +117,9 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
pmsg);
} else {
/* ask for username only */
lsize = snprintf(login_msg, sizeof(login_msg), "%s", login_msg_user);
lsize =
snprintf(login_msg, sizeof(login_msg), "%s",
login_msg_user);
}
ret =
@@ -211,7 +212,8 @@ int get_cert_names(worker_st * ws, const gnutls_datum_t * raw,
}
static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
/* auth reply from main process */
static int recv_cookie_auth_reply(worker_st * ws)
{
unsigned i;
int ret;
@@ -219,7 +221,7 @@ static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
AuthReplyMsg *msg = NULL;
PROTOBUF_ALLOCATOR(pa, ws);
ret = recv_socket_msg(ws, ws->cmd_fd, AUTH_REP, &socketfd,
ret = recv_socket_msg(ws, ws->cmd_fd, AUTH_COOKIE_REP, &socketfd,
(void *)&msg,
(unpack_func) auth_reply_msg__unpack);
if (ret < 0) {
@@ -231,21 +233,7 @@ static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
(unsigned)msg->reply);
switch (msg->reply) {
case AUTH_REPLY_MSG__AUTH__REP__MSG:
if (txt == NULL || msg->msg == NULL) {
oclog(ws, LOG_ERR, "received unexpected msg");
return ERR_AUTH_FAIL;
}
snprintf(txt, max_txt_size, "%s", msg->msg);
if (msg->has_sid && msg->sid.len == sizeof(ws->sid)) {
/* update our sid */
memcpy(ws->sid, msg->sid.data, sizeof(ws->sid));
}
ret = ERR_AUTH_CONTINUE;
goto cleanup;
case AUTH_REPLY_MSG__AUTH__REP__OK:
case AUTH__REP__OK:
if (socketfd != -1) {
ws->tun_fd = socketfd;
@@ -259,14 +247,9 @@ static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
snprintf(ws->username, sizeof(ws->username), "%s",
msg->user_name);
if (msg->has_sid && msg->sid.len == sizeof(ws->sid)) {
/* update our sid */
memcpy(ws->sid, msg->sid.data, sizeof(ws->sid));
}
if (msg->has_cookie == 0 ||
msg->cookie.len != sizeof(ws->cookie) ||
msg->session_id.len != sizeof(ws->session_id)) {
msg->cookie.len != sizeof(ws->cookie) ||
msg->session_id.len != sizeof(ws->session_id)) {
ret = ERR_AUTH_FAIL;
goto cleanup;
@@ -280,7 +263,8 @@ static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
if (strcmp(msg->ipv4, "0.0.0.0") == 0)
ws->vinfo.ipv4 = NULL;
else
ws->vinfo.ipv4 = talloc_strdup(ws, msg->ipv4);
ws->vinfo.ipv4 =
talloc_strdup(ws, msg->ipv4);
}
if (msg->ipv6 != NULL) {
@@ -288,7 +272,8 @@ static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
if (strcmp(msg->ipv6, "::") == 0)
ws->vinfo.ipv6 = NULL;
else
ws->vinfo.ipv6 = talloc_strdup(ws, msg->ipv6);
ws->vinfo.ipv6 =
talloc_strdup(ws, msg->ipv6);
}
if (msg->ipv4_local != NULL) {
@@ -296,7 +281,8 @@ static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
if (strcmp(msg->ipv4_local, "0.0.0.0") == 0)
ws->vinfo.ipv4_local = NULL;
else
ws->vinfo.ipv4_local = talloc_strdup(ws, msg->ipv4_local);
ws->vinfo.ipv4_local =
talloc_strdup(ws, msg->ipv4_local);
}
if (msg->ipv6_local != NULL) {
@@ -304,7 +290,8 @@ static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
if (strcmp(msg->ipv6_local, "::") == 0)
ws->vinfo.ipv6_local = NULL;
else
ws->vinfo.ipv6_local = talloc_strdup(ws, msg->ipv6_local);
ws->vinfo.ipv6_local =
talloc_strdup(ws, msg->ipv6_local);
}
/* Read any additional data */
@@ -338,7 +325,8 @@ static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
ws->routes_size = msg->n_routes;
for (i = 0; i < ws->routes_size; i++) {
ws->routes[i] = talloc_strdup(ws, msg->routes[i]);
ws->routes[i] =
talloc_strdup(ws, msg->routes[i]);
}
ws->dns_size = msg->n_dns;
@@ -358,9 +346,9 @@ static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
goto cleanup;
}
break;
case AUTH_REPLY_MSG__AUTH__REP__FAILED:
case AUTH__REP__FAILED:
default:
if (msg->reply != AUTH_REPLY_MSG__AUTH__REP__FAILED)
if (msg->reply != AUTH__REP__FAILED)
oclog(ws, LOG_ERR, "unexpected auth reply %u",
(unsigned)msg->reply);
ret = ERR_AUTH_FAIL;
@@ -374,6 +362,120 @@ static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
return ret;
}
/* returns the fd */
static int connect_to_secmod(worker_st * ws)
{
int sd, ret, e;
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd == -1) {
e = errno;
oclog(ws, LOG_ERR, "error opening unix socket (for sec-mod) %s",
strerror(e));
return -1;
}
ret =
connect(sd, (struct sockaddr *)&ws->secmod_addr,
ws->secmod_addr_len);
if (ret < 0) {
e = errno;
close(sd);
oclog(ws, LOG_ERR,
"error connecting to sec-mod socket '%s': %s",
ws->secmod_addr.sun_path, strerror(e));
return -1;
}
return sd;
}
static
int send_msg_to_secmod(worker_st * ws, int sd, uint8_t cmd,
const void *msg, pack_size_func get_size, pack_func pack)
{
oclog(ws, LOG_DEBUG, "sending message '%s' to secmod",
cmd_request_to_str(cmd));
return send_msg(ws, sd, cmd, msg, get_size, pack);
}
static int recv_auth_reply(worker_st * ws, int sd, char *txt,
size_t max_txt_size)
{
int ret;
SecAuthReplyMsg *msg = NULL;
PROTOBUF_ALLOCATOR(pa, ws);
ret = recv_msg(ws, sd, SM_CMD_AUTH_REP,
(void *)&msg, (unpack_func) sec_auth_reply_msg__unpack);
if (ret < 0) {
oclog(ws, LOG_ERR, "error receiving auth reply message");
return ret;
}
oclog(ws, LOG_DEBUG, "received auth reply message (value: %u)",
(unsigned)msg->reply);
switch (msg->reply) {
case AUTH__REP__MSG:
if (txt == NULL || msg->msg == NULL) {
oclog(ws, LOG_ERR, "received unexpected msg");
return ERR_AUTH_FAIL;
}
snprintf(txt, max_txt_size, "%s", msg->msg);
if (msg->has_sid && msg->sid.len == sizeof(ws->sid)) {
/* update our sid */
memcpy(ws->sid, msg->sid.data, sizeof(ws->sid));
ws->sid_set = 1;
}
ret = ERR_AUTH_CONTINUE;
goto cleanup;
case AUTH__REP__OK:
if (msg->user_name == NULL) {
ret = ERR_AUTH_FAIL;
goto cleanup;
}
snprintf(ws->username, sizeof(ws->username), "%s",
msg->user_name);
if (msg->has_sid && msg->sid.len == sizeof(ws->sid)) {
/* update our sid */
memcpy(ws->sid, msg->sid.data, sizeof(ws->sid));
ws->sid_set = 1;
}
if (msg->has_cookie == 0 ||
msg->cookie.len != sizeof(ws->cookie) ||
msg->dtls_session_id.len != sizeof(ws->session_id)) {
ret = ERR_AUTH_FAIL;
goto cleanup;
}
memcpy(ws->cookie, msg->cookie.data, msg->cookie.len);
ws->cookie_set = 1;
memcpy(ws->session_id, msg->dtls_session_id.data,
msg->dtls_session_id.len);
break;
case AUTH__REP__FAILED:
default:
if (msg->reply != AUTH__REP__FAILED)
oclog(ws, LOG_ERR, "unexpected auth reply %u",
(unsigned)msg->reply);
ret = ERR_AUTH_FAIL;
goto cleanup;
}
ret = 0;
cleanup:
if (msg != NULL)
sec_auth_reply_msg__free_unpacked(msg, &pa);
return ret;
}
/* grabs the username from the session certificate */
static
int get_cert_info(worker_st * ws, char *user, unsigned user_size,
@@ -435,8 +537,7 @@ int auth_cookie(worker_st * ws, void *cookie, size_t cookie_size)
msg.cookie.data = cookie;
msg.cookie.len = cookie_size;
ret = send_msg_to_main(ws, AUTH_COOKIE_REQ, &msg,
(pack_size_func)
ret = send_msg_to_main(ws, AUTH_COOKIE_REQ, &msg, (pack_size_func)
auth_cookie_request_msg__get_packed_size,
(pack_func) auth_cookie_request_msg__pack);
if (ret < 0) {
@@ -445,7 +546,7 @@ int auth_cookie(worker_st * ws, void *cookie, size_t cookie_size)
return ret;
}
ret = recv_auth_reply(ws, NULL, 0);
ret = recv_cookie_auth_reply(ws);
if (ret < 0) {
oclog(ws, LOG_INFO,
"error receiving cookie authentication reply");
@@ -458,11 +559,12 @@ int auth_cookie(worker_st * ws, void *cookie, size_t cookie_size)
int post_common_handler(worker_st * ws, unsigned http_ver)
{
int ret, size;
char str_cookie[BASE64_LENGTH(COOKIE_SIZE)+1];
char str_cookie[BASE64_LENGTH(COOKIE_SIZE) + 1];
size_t str_cookie_size = sizeof(str_cookie);
char msg[MAX_BANNER_SIZE + 32];
base64_encode((char*)ws->cookie, sizeof(ws->cookie), (char*)str_cookie, str_cookie_size);
base64_encode((char *)ws->cookie, sizeof(ws->cookie),
(char *)str_cookie, str_cookie_size);
/* reply */
tls_cork(ws->session);
@@ -501,7 +603,8 @@ int post_common_handler(worker_st * ws, unsigned http_ver)
return -1;
ret =
tls_printf(ws->session, "Set-Cookie: webvpn=%s; Max-Age=%u; Secure\r\n",
tls_printf(ws->session,
"Set-Cookie: webvpn=%s; Max-Age=%u; Secure\r\n",
str_cookie, (unsigned)ws->config->cookie_validity);
if (ret < 0)
return -1;
@@ -601,7 +704,8 @@ int read_user_pass(worker_st * ws, char *body, unsigned body_length,
}
*username =
unescape_html(ws, *username, strlen(*username), NULL);
unescape_html(ws, *username, strlen(*username),
NULL);
}
if (password != NULL) {
@@ -620,7 +724,8 @@ int read_user_pass(worker_st * ws, char *body, unsigned body_length,
}
*password =
unescape_html(ws, *password, strlen(*password), NULL);
unescape_html(ws, *password, strlen(*password),
NULL);
}
} else { /* non-xml version */
@@ -661,7 +766,8 @@ int read_user_pass(worker_st * ws, char *body, unsigned body_length,
}
*username =
unescape_url(ws, *username, strlen(*username), NULL);
unescape_url(ws, *username, strlen(*username),
NULL);
}
if (password != NULL) {
@@ -675,7 +781,8 @@ int read_user_pass(worker_st * ws, char *body, unsigned body_length,
}
*password =
unescape_url(ws, *password, strlen(*password), NULL);
unescape_url(ws, *password, strlen(*password),
NULL);
}
}
@@ -696,52 +803,72 @@ int read_user_pass(worker_st * ws, char *body, unsigned body_length,
int post_auth_handler(worker_st * ws, unsigned http_ver)
{
int ret;
int ret, sd = -1;
struct http_req_st *req = &ws->req;
const char *reason = "Authentication failed";
char *username = NULL;
char *password = NULL;
char tmp_user[MAX_USERNAME_SIZE];
char tmp_group[MAX_USERNAME_SIZE];
char ipbuf[128];
char msg[MAX_MSG_SIZE];
oclog(ws, LOG_HTTP_DEBUG, "POST body: '%.*s'", (int)req->body_length,
req->body);
req->body);
if (ws->auth_state == S_AUTH_INACTIVE) {
AuthInitMsg ireq = AUTH_INIT_MSG__INIT;
SecAuthInitMsg ireq = SEC_AUTH_INIT_MSG__INIT;
if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
ret =
read_user_pass(ws, req->body, req->body_length,
&username, NULL);
if (ret < 0) {
/* Try if we need to ReInit */
/* No username, see if we are continuing a previous session */
if (ws->config->cisco_client_compat != 0 &&
gnutls_session_is_resumed(ws->session) != 0) {
AuthReinitMsg rreq = AUTH_REINIT_MSG__INIT;
gnutls_session_is_resumed(ws->session) !=
0) {
SecAuthContMsg rreq =
SEC_AUTH_CONT_MSG__INIT;
/* could it be a client reconnecting and sending
* his password? */
ret =
read_user_pass(ws, req->body, req->body_length,
NULL, &password);
read_user_pass(ws, req->body,
req->body_length,
NULL, &password);
if (ret < 0) {
oclog(ws, LOG_INFO, "failed reading password as well");
oclog(ws, LOG_INFO,
"failed reading password as well");
goto ask_auth;
}
rreq.tls_auth_ok = ws->cert_auth_ok;
rreq.password = password;
rreq.ip =
human_addr2((void *)&ws->remote_addr, ws->remote_addr_len,
ipbuf, sizeof(ipbuf), 0);
if (req->sid_cookie_set != 0) {
rreq.sid.data = req->sid_cookie;
rreq.sid.len = sizeof(req->sid_cookie);
if (ws->sid_set != 0) {
rreq.sid.data = ws->sid;
rreq.sid.len = sizeof(ws->sid);
}
ret = send_msg_to_main(ws, AUTH_REINIT, &rreq,
(pack_size_func)auth_reinit_msg__get_packed_size,
(pack_func)auth_reinit_msg__pack);
sd = connect_to_secmod(ws);
if (sd == -1) {
oclog(ws, LOG_ERR,
"failed connecting to sec mod");
goto auth_fail;
}
ret =
send_msg_to_secmod(ws, sd,
SM_CMD_AUTH_CONT,
&rreq,
(pack_size_func)
sec_auth_cont_msg__get_packed_size,
(pack_func)
sec_auth_cont_msg__pack);
talloc_free(username);
if (ret < 0) {
@@ -758,9 +885,10 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
goto ask_auth;
}
snprintf(tmp_user, sizeof(tmp_user), "%s", username);
snprintf(ws->username, sizeof(ws->username), "%s",
username);
talloc_free(username);
ireq.user_name = tmp_user;
ireq.user_name = ws->username;
}
if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) {
@@ -784,22 +912,30 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
}
ireq.hostname = req->hostname;
ireq.ip =
human_addr2((void *)&ws->remote_addr, ws->remote_addr_len,
ipbuf, sizeof(ipbuf), 0);
ret = send_msg_to_main(ws, AUTH_INIT,
&ireq,
(pack_size_func)
auth_init_msg__get_packed_size,
(pack_func) auth_init_msg__pack);
sd = connect_to_secmod(ws);
if (sd == -1) {
oclog(ws, LOG_ERR, "failed connecting to sec mod");
goto auth_fail;
}
ret = send_msg_to_secmod(ws, sd, SM_CMD_AUTH_INIT,
&ireq, (pack_size_func)
sec_auth_init_msg__get_packed_size,
(pack_func) sec_auth_init_msg__pack);
if (ret < 0) {
oclog(ws, LOG_ERR,
"failed sending auth init message to main");
"failed sending auth init message to sec mod");
goto auth_fail;
}
ws->auth_state = S_AUTH_INIT;
} else if (ws->auth_state == S_AUTH_INIT
|| ws->auth_state == S_AUTH_REQ) {
AuthRequestMsg areq = AUTH_REQUEST_MSG__INIT;
SecAuthContMsg areq = SEC_AUTH_CONT_MSG__INIT;
if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
ret =
@@ -811,13 +947,24 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
}
areq.password = password;
if (ws->sid_set != 0) {
areq.sid.data = ws->sid;
areq.sid.len = sizeof(ws->sid);
}
ret = send_msg_to_main(ws, AUTH_REQ, &areq,
sd = connect_to_secmod(ws);
if (sd == -1) {
oclog(ws, LOG_ERR,
"failed connecting to sec mod");
goto auth_fail;
}
ret =
send_msg_to_secmod(ws, sd, SM_CMD_AUTH_CONT, &areq,
(pack_size_func)
auth_request_msg__get_packed_size,
sec_auth_cont_msg__get_packed_size,
(pack_func)
auth_request_msg__pack);
sec_auth_cont_msg__pack);
talloc_free(password);
if (ret < 0) {
@@ -836,7 +983,10 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
}
recv_reply:
ret = recv_auth_reply(ws, msg, sizeof(msg));
ret = recv_auth_reply(ws, sd, msg, sizeof(msg));
if (sd != -1)
close(sd);
if (ret == ERR_AUTH_CONTINUE) {
oclog(ws, LOG_DEBUG, "continuing authentication for '%s'",
ws->username);
@@ -849,15 +999,19 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
goto auth_fail;
}
oclog(ws, LOG_INFO, "user '%s' logged in", ws->username);
ws->auth_state = S_AUTH_COMPLETE;
oclog(ws, LOG_INFO, "user '%s' obtained cookie", ws->username);
ws->auth_state = S_AUTH_COOKIE;
return post_common_handler(ws, http_ver);
ask_auth:
if (sd != -1)
close(sd);
return get_auth_handler(ws, http_ver);
auth_fail:
if (sd != -1)
close(sd);
tls_printf(ws->session,
"HTTP/1.1 503 Service Unavailable\r\nX-Reason: %s\r\n\r\n",
reason);

View File

@@ -297,8 +297,8 @@ static void value_check(struct worker_st *ws, struct http_req_st *req)
i++) {
if (strcmp(token, ciphersuites[i].oc_name) == 0) {
if (req->selected_ciphersuite == NULL ||
req->selected_ciphersuite->
server_prio <
req->
selected_ciphersuite->server_prio <
ciphersuites[i].server_prio) {
req->selected_ciphersuite =
&ciphersuites[i];
@@ -341,17 +341,18 @@ static void value_check(struct worker_st *ws, struct http_req_st *req)
tmplen--;
}
nlen = sizeof(req->cookie);
nlen = sizeof(ws->cookie);
ret =
base64_decode((char *)p, tmplen,
(char *)req->cookie, &nlen);
(char *)ws->cookie, &nlen);
if (ret == 0 || nlen != COOKIE_SIZE) {
oclog(ws, LOG_DEBUG,
"could not decode cookie: %.*s",
tmplen, p);
req->cookie_set = 0;
ws->cookie_set = 0;
} else {
req->cookie_set = 1;
ws->auth_state = S_AUTH_COOKIE;
ws->cookie_set = 1;
}
} else if (strncmp(p, "webvpncontext=", 14) == 0) {
p += 14;
@@ -361,18 +362,17 @@ static void value_check(struct worker_st *ws, struct http_req_st *req)
tmplen--;
}
nlen = sizeof(req->sid_cookie);
nlen = sizeof(ws->sid);
ret =
base64_decode((char *)p, tmplen,
(char *)req->sid_cookie,
&nlen);
if (ret == 0 || nlen != sizeof(req->sid_cookie)) {
(char *)ws->sid, &nlen);
if (ret == 0 || nlen != sizeof(ws->sid)) {
oclog(ws, LOG_DEBUG,
"could not decode sid: %.*s",
tmplen, p);
req->sid_cookie_set = 0;
ws->sid_set = 0;
} else {
req->sid_cookie_set = 1;
ws->sid_set = 1;
oclog(ws, LOG_DEBUG,
"received sid: %.*s", tmplen, p);
}
@@ -541,8 +541,8 @@ static int setup_dtls_connection(struct worker_st *ws)
ret =
gnutls_priority_set_direct(session,
ws->req.selected_ciphersuite->
gnutls_name, NULL);
ws->req.
selected_ciphersuite->gnutls_name, NULL);
if (ret < 0) {
oclog(ws, LOG_ERR, "could not set TLS priority: %s",
gnutls_strerror(ret));
@@ -550,13 +550,14 @@ static int setup_dtls_connection(struct worker_st *ws)
}
ret = gnutls_session_set_premaster(session, GNUTLS_SERVER,
ws->req.selected_ciphersuite->
gnutls_version, GNUTLS_KX_RSA,
ws->req.selected_ciphersuite->
gnutls_cipher,
ws->req.selected_ciphersuite->
gnutls_mac, GNUTLS_COMP_NULL,
&master, &sid);
ws->req.
selected_ciphersuite->gnutls_version,
GNUTLS_KX_RSA,
ws->req.
selected_ciphersuite->gnutls_cipher,
ws->req.
selected_ciphersuite->gnutls_mac,
GNUTLS_COMP_NULL, &master, &sid);
if (ret < 0) {
oclog(ws, LOG_ERR, "could not set TLS premaster: %s",
gnutls_strerror(ret));
@@ -627,12 +628,14 @@ void exit_worker(worker_st * ws)
msg.bytes_out = ws->tun_bytes_out;
send_msg_to_main(ws, CMD_CLI_STATS, &msg,
(pack_size_func) cli_stats_msg__get_packed_size,
(pack_func) cli_stats_msg__pack);
(pack_size_func)
cli_stats_msg__get_packed_size,
(pack_func) cli_stats_msg__pack);
oclog(ws, LOG_DEBUG, "sending stats (in: %lu, out: %lu) to main",
(unsigned long)msg.bytes_in,
(unsigned long)msg.bytes_out);
oclog(ws, LOG_DEBUG,
"sending stats (in: %lu, out: %lu) to main",
(unsigned long)msg.bytes_in,
(unsigned long)msg.bytes_out);
}
closelog();
exit(1);
@@ -1325,28 +1328,26 @@ static int connect_handler(worker_st * ws)
ws->buffer_size = sizeof(ws->buffer);
if (ws->auth_state != S_AUTH_COMPLETE && req->cookie_set == 0) {
oclog(ws, LOG_INFO, "connect request without authentication");
/* we must be in S_AUTH_COOKIE state */
if (ws->auth_state != S_AUTH_COOKIE || ws->cookie_set == 0) {
oclog(ws, LOG_INFO, "no cookie found");
tls_puts(ws->session,
"HTTP/1.1 503 Service Unavailable\r\n\r\n");
tls_fatal_close(ws->session, GNUTLS_A_ACCESS_DENIED);
exit_worker(ws);
}
if (ws->auth_state != S_AUTH_COMPLETE) {
/* 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_worker(ws);
}
ws->auth_state= S_AUTH_COMPLETE;
/* we have authenticated against sec-mod, we need to complete
* our authentication by forwarding our cookie to main. */
ret = auth_cookie(ws, ws->cookie, sizeof(ws->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_worker(ws);
}
ws->auth_state = S_AUTH_COMPLETE;
if (strcmp(req->url, "/CSCOSSLC/tunnel") != 0) {
oclog(ws, LOG_INFO, "bad connect request: '%s'\n", req->url);
@@ -1575,7 +1576,8 @@ static int connect_handler(worker_st * ws)
method = REKEY_METHOD_NEW_TUNNEL;
ret = tls_printf(ws->session, "X-CSTP-Rekey-Method: %s\r\n",
(method == REKEY_METHOD_SSL) ? "ssl" : "new-tunnel");
(method ==
REKEY_METHOD_SSL) ? "ssl" : "new-tunnel");
SEND_ERR(ret);
} else {
ret = tls_puts(ws->session, "X-CSTP-Rekey-Method: none\r\n");
@@ -1712,10 +1714,10 @@ static int connect_handler(worker_st * ws)
/* crypto overhead for DTLS */
ws->crypto_overhead =
tls_get_overhead(ws->req.selected_ciphersuite->
gnutls_version,
ws->req.selected_ciphersuite->
gnutls_cipher,
tls_get_overhead(ws->req.
selected_ciphersuite->gnutls_version,
ws->req.
selected_ciphersuite->gnutls_cipher,
ws->req.selected_ciphersuite->gnutls_mac);
ws->crypto_overhead += CSTP_DTLS_OVERHEAD;
@@ -1730,7 +1732,8 @@ static int connect_handler(worker_st * ws)
MIN(ws->conn_mtu,
ws->vinfo.mtu - proto_overhead - ws->crypto_overhead);
ret = tls_printf(ws->session, "X-DTLS-MTU: %u\r\n", ws->conn_mtu);
ret =
tls_printf(ws->session, "X-DTLS-MTU: %u\r\n", ws->conn_mtu);
SEND_ERR(ret);
oclog(ws, LOG_DEBUG, "suggesting DTLS MTU %u", ws->conn_mtu);

View File

@@ -33,6 +33,8 @@
#include <common.h>
#include <str.h>
#include <worker-bandwidth.h>
#include <sys/un.h>
#include <sys/uio.h>
typedef enum {
UP_DISABLED,
@@ -77,6 +79,7 @@ enum {
S_AUTH_INACTIVE = 0,
S_AUTH_INIT,
S_AUTH_REQ,
S_AUTH_COOKIE,
S_AUTH_COMPLETE
};
@@ -106,11 +109,6 @@ struct http_req_st {
unsigned user_agent_type;;
unsigned int next_header;
unsigned char cookie[COOKIE_SIZE];
unsigned int cookie_set;
uint8_t sid_cookie[SID_SIZE];
unsigned int sid_cookie_set;
unsigned int is_mobile;
@@ -139,6 +137,7 @@ typedef struct worker_st {
/* inique session identifier */
uint8_t sid[SID_SIZE];
unsigned int sid_set;
int cmd_fd;
int conn_fd;
@@ -147,6 +146,9 @@ typedef struct worker_st {
struct cfg_st *config;
unsigned int auth_state; /* S_AUTH */
struct sockaddr_un secmod_addr; /* sec-mod unix address */
socklen_t secmod_addr_len;
struct sockaddr_storage remote_addr; /* peer's address */
socklen_t remote_addr_len;
int proto; /* AF_INET or AF_INET6 */
@@ -197,6 +199,8 @@ typedef struct worker_st {
char username[MAX_USERNAME_SIZE];
char hostname[MAX_HOSTNAME_SIZE];
uint8_t cookie[COOKIE_SIZE];
unsigned int cookie_set;
uint8_t master_secret[TLS_MASTER_SIZE];
uint8_t session_id[GNUTLS_MAX_SESSION_ID];
unsigned cert_auth_ok;