mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 08:46:58 +08:00
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:
6
TODO
6
TODO
@@ -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).
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
30
src/common.c
30
src/common.c
@@ -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))) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
132
src/ipc.proto
132
src/ipc.proto
@@ -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;
|
||||
}
|
||||
|
||||
243
src/main-auth.c
243
src/main-auth.c
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
264
src/main-misc.c
264
src/main-misc.c
@@ -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;
|
||||
|
||||
63
src/main.c
63
src/main.c
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
27
src/main.h
27
src/main.h
@@ -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
397
src/sec-mod-auth.c
Normal 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
146
src/sec-mod-ban.c
Normal 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
148
src/sec-mod-db.c
Normal 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);
|
||||
}
|
||||
315
src/sec-mod.c
315
src/sec-mod.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
64
src/tlslib.c
64
src/tlslib.c
@@ -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)
|
||||
|
||||
19
src/vpn.h
19
src/vpn.h
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
14
src/worker.h
14
src/worker.h
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user