mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 16:57:00 +08:00
Advanced auth implemented
This commit is contained in:
16
configure.ac
16
configure.ac
@@ -112,6 +112,22 @@ if [ test "$anyconnect_enabled" = "yes" ];then
|
||||
AC_DEFINE([ANYCONNECT_CLIENT_COMPAT], [], [Enable Anyconnect compatibility])
|
||||
fi
|
||||
|
||||
pcl_enabled=no
|
||||
LIBS="$oldlibs -lpcl"
|
||||
AC_MSG_CHECKING([for pcl library])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([
|
||||
#include <pcl.h>],[
|
||||
co_create(0, 0, 0, 0);])],
|
||||
[AC_MSG_RESULT(yes)
|
||||
AC_SUBST([PCL_LIBS], [-lpcl])
|
||||
AC_SUBST([PCL_CFLAGS], [])
|
||||
pcl_enabled=yes],
|
||||
[AC_MSG_RESULT(no)
|
||||
AC_MSG_ERROR([[
|
||||
***
|
||||
*** libpcl (portable co-routines) was not found.
|
||||
*** ]])])
|
||||
LIBS="$oldlibs"
|
||||
|
||||
enable_local_libopts=yes
|
||||
NEED_LIBOPTS_DIR=true
|
||||
|
||||
@@ -16,7 +16,7 @@ ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \
|
||||
http-parser/http_parser.c ipc.h cookies.c worker-tun.c main-misc.c \
|
||||
vpn.h cookies.h tlslib.h http-parser/http_parser.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 \
|
||||
worker-extras.c main-auth.h \
|
||||
main-user.c cookies-gdbm.c cookies-hash.c worker-misc.c setproctitle.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 \
|
||||
@@ -26,7 +26,7 @@ ocserv_SOURCES += ocserv-args.def ocserv-args.c ocserv-args.h
|
||||
|
||||
ocserv_LDADD = ../gl/libgnu.a ../libopts/libopts.a
|
||||
ocserv_LDADD += $(LIBGNUTLS_LIBS) $(GDBM_LIBS) $(PAM_LIBS) $(LIBUTIL) \
|
||||
$(LIBSECCOMP) $(LIBWRAP) $(LIBCRYPT)
|
||||
$(LIBSECCOMP) $(LIBWRAP) $(LIBCRYPT) $(PCL_LIBS)
|
||||
|
||||
ocserv-args.c ocserv-args.h: $(srcdir)/ocserv-args.def
|
||||
@AUTOGEN@ $<
|
||||
|
||||
22
src/ipc.h
22
src/ipc.h
@@ -10,10 +10,14 @@
|
||||
#include <vpn.h>
|
||||
#include <tlslib.h>
|
||||
|
||||
#define MAX_MSG_SIZE 256
|
||||
|
||||
typedef enum {
|
||||
AUTH_REQ = 1,
|
||||
AUTH_COOKIE_REQ,
|
||||
AUTH_INIT=1,
|
||||
AUTH_REP,
|
||||
AUTH_REQ,
|
||||
AUTH_COOKIE_REQ,
|
||||
AUTH_MSG,
|
||||
RESUME_STORE_REQ,
|
||||
RESUME_DELETE_REQ,
|
||||
RESUME_FETCH_REQ,
|
||||
@@ -24,8 +28,9 @@ typedef enum {
|
||||
} cmd_request_t;
|
||||
|
||||
typedef enum {
|
||||
REP_AUTH_OK = 0,
|
||||
REP_AUTH_FAILED = 1,
|
||||
REP_AUTH_OK = 1,
|
||||
REP_AUTH_MSG = 2,
|
||||
REP_AUTH_FAILED = 3,
|
||||
} cmd_auth_reply_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -43,9 +48,13 @@ struct __attribute__ ((__packed__)) cmd_auth_cookie_req_st {
|
||||
|
||||
/* AUTH_REQ */
|
||||
struct __attribute__ ((__packed__)) cmd_auth_req_st {
|
||||
uint8_t user_pass_present;
|
||||
char user[MAX_USERNAME_SIZE];
|
||||
uint8_t pass_present;
|
||||
char pass[MAX_PASSWORD_SIZE];
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) cmd_auth_init_st {
|
||||
uint8_t user_present;
|
||||
char user[MAX_USERNAME_SIZE];
|
||||
uint8_t tls_auth_ok;
|
||||
char cert_user[MAX_USERNAME_SIZE];
|
||||
char cert_group[MAX_GROUPNAME_SIZE];
|
||||
@@ -59,6 +68,7 @@ struct __attribute__ ((__packed__)) cmd_auth_reply_st {
|
||||
uint8_t session_id[GNUTLS_MAX_SESSION_ID];
|
||||
char vname[IFNAMSIZ]; /* interface name */
|
||||
char user[MAX_USERNAME_SIZE];
|
||||
char msg[MAX_MSG_SIZE]; /* in case of REP_AUTH_CONTINUE */
|
||||
};
|
||||
|
||||
/* RESUME_FETCH_REQ + RESUME_DELETE_REQ */
|
||||
|
||||
148
src/main-auth.c
148
src/main-auth.c
@@ -41,8 +41,24 @@
|
||||
#include <tun.h>
|
||||
#include <main.h>
|
||||
#include <ccan/list/list.h>
|
||||
#include "pam.h"
|
||||
#include "plain.h"
|
||||
#include <main-auth.h>
|
||||
#include <plain.h>
|
||||
#include <pam.h>
|
||||
|
||||
static const struct auth_mod_st *module;
|
||||
|
||||
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,
|
||||
cmd_auth_reply_t r)
|
||||
@@ -100,6 +116,41 @@ int send_auth_reply(main_server_st* s, struct proc_st* proc,
|
||||
return(sendmsg(proc->fd, &hdr, 0));
|
||||
}
|
||||
|
||||
int send_auth_reply_msg(main_server_st* s, struct proc_st* proc)
|
||||
{
|
||||
struct iovec iov[2];
|
||||
uint8_t cmd[1];
|
||||
struct msghdr hdr;
|
||||
struct cmd_auth_reply_st resp;
|
||||
int ret;
|
||||
|
||||
if (proc->auth_ctx == NULL)
|
||||
return -1;
|
||||
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
ret = module->auth_msg(proc->auth_ctx, resp.msg, sizeof(resp.msg));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
|
||||
hdr.msg_iov = iov;
|
||||
|
||||
cmd[0] = AUTH_REP;
|
||||
|
||||
resp.reply = REP_AUTH_MSG;
|
||||
|
||||
iov[0].iov_base = cmd;
|
||||
iov[0].iov_len = 1;
|
||||
hdr.msg_iovlen++;
|
||||
|
||||
iov[1].iov_base = &resp;
|
||||
iov[1].iov_len = sizeof(resp);
|
||||
hdr.msg_iovlen++;
|
||||
|
||||
return(sendmsg(proc->fd, &hdr, 0));
|
||||
}
|
||||
|
||||
int handle_auth_cookie_req(main_server_st* s, struct proc_st* proc,
|
||||
const struct cmd_auth_cookie_req_st * req)
|
||||
{
|
||||
@@ -145,12 +196,6 @@ time_t now = time(0);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
ret = open_tun(s, &proc->lease);
|
||||
if (ret < 0) {
|
||||
ret = -1; /* sorry */
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* ok auth ok. Renew the cookie. */
|
||||
sc->expiration = time(0) + s->config->cookie_validity;
|
||||
@@ -201,36 +246,38 @@ struct stored_cookie_st *sc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_auth_req(main_server_st *s, struct proc_st* proc,
|
||||
const struct cmd_auth_req_st * req)
|
||||
int handle_auth_init(main_server_st *s, struct proc_st* proc,
|
||||
const struct cmd_auth_init_st * req)
|
||||
{
|
||||
int ret = -1;
|
||||
char ipbuf[128];
|
||||
const char* ip;
|
||||
unsigned username_set = 0;
|
||||
|
||||
ip = human_addr((void*)&proc->remote_addr, proc->remote_addr_len,
|
||||
ipbuf, sizeof(ipbuf));
|
||||
ipbuf, sizeof(ipbuf));
|
||||
|
||||
if (req->user_pass_present != 0) {
|
||||
#ifdef HAVE_PAM
|
||||
if ((s->config->auth_types & AUTH_TYPE_PAM) == AUTH_TYPE_PAM) {
|
||||
ret = pam_auth_user(req->user, req->pass, proc->groupname, sizeof(proc->groupname), ip);
|
||||
if (ret != 0)
|
||||
ret = -1;
|
||||
if (req->user_present == 0 && s->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
mslog(s, proc, LOG_DEBUG, "auth init from '%s' with no username present", ip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(proc->username, req->user, MAX_USERNAME_SIZE);
|
||||
username_set = 1;
|
||||
}
|
||||
#endif
|
||||
if ((s->config->auth_types & AUTH_TYPE_PLAIN) == AUTH_TYPE_PLAIN) {
|
||||
ret = plain_auth_user(s->config->plain_passwd, req->user, req->pass, proc->groupname, sizeof(proc->groupname));
|
||||
if (ret != 0)
|
||||
ret = -1;
|
||||
if (req->hostname[0] != 0) {
|
||||
memcpy(proc->hostname, req->hostname, MAX_HOSTNAME_SIZE);
|
||||
proc->hostname[sizeof(proc->hostname)-1] = 0;
|
||||
}
|
||||
|
||||
memcpy(proc->username, req->user, MAX_USERNAME_SIZE);
|
||||
username_set = 1;
|
||||
}
|
||||
if (req->user_present != 0 && s->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
ret = module->auth_init(&proc->auth_ctx, req->user, 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;
|
||||
|
||||
memcpy(proc->username, req->user, MAX_USERNAME_SIZE);
|
||||
proc->username[sizeof(proc->username)-1] = 0;
|
||||
}
|
||||
|
||||
if (s->config->auth_types & AUTH_TYPE_CERTIFICATE) {
|
||||
@@ -238,35 +285,42 @@ unsigned username_set = 0;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (username_set == 0) {
|
||||
if (proc->username[0] == 0) {
|
||||
memcpy(proc->username, req->cert_user, sizeof(proc->username));
|
||||
memcpy(proc->groupname, req->cert_group, sizeof(proc->groupname));
|
||||
proc->username[sizeof(proc->username)-1] = 0;
|
||||
proc->groupname[sizeof(proc->groupname)-1] = 0;
|
||||
} else {
|
||||
if (strcmp(proc->username, req->cert_user) != 0) {
|
||||
mslog(s, proc, LOG_INFO, "user '%s' presented a certificate from user '%s'", proc->username, req->cert_user);
|
||||
ret = -1;
|
||||
return -1;
|
||||
}
|
||||
if (strcmp(proc->groupname, req->cert_group) != 0) {
|
||||
mslog(s, proc, LOG_INFO, "user '%s' presented a certificate from group '%s' but he is member of '%s'", proc->username, req->cert_group, proc->groupname);
|
||||
ret = -1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) { /* open tun */
|
||||
if (req->hostname[0] != 0)
|
||||
memcpy(proc->hostname, req->hostname, MAX_HOSTNAME_SIZE);
|
||||
mslog(s, proc, LOG_DEBUG, "auth init for user '%s' from '%s'", proc->username, ip);
|
||||
|
||||
proc->username[sizeof(proc->username)-1] = 0;
|
||||
proc->groupname[sizeof(proc->groupname)-1] = 0;
|
||||
proc->hostname[sizeof(proc->hostname)-1] = 0;
|
||||
|
||||
ret = open_tun(s, &proc->lease);
|
||||
if (ret < 0)
|
||||
ret = -1; /* sorry */
|
||||
if (s->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
return ERR_AUTH_CONTINUE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_auth_req(main_server_st *s, struct proc_st* proc,
|
||||
const struct cmd_auth_req_st * 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);
|
||||
|
||||
return module->auth_pass(proc->auth_ctx, req->pass);
|
||||
}
|
||||
|
||||
int check_multiple_users(main_server_st *s, struct proc_st* proc)
|
||||
@@ -292,3 +346,11 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
18
src/main-auth.h
Normal file
18
src/main-auth.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef AUTH_H
|
||||
# define AUTH_H
|
||||
|
||||
#include <main.h>
|
||||
|
||||
struct auth_mod_st {
|
||||
unsigned int type;
|
||||
int (*auth_init)(void** ctx, const char* username, const char* ip, void* additional);
|
||||
int (*auth_msg)(void* ctx, char* msg, size_t msg_size);
|
||||
int (*auth_pass)(void* ctx, const char* pass);
|
||||
int (*auth_group)(void* ctx, char *groupname, int groupname_size);
|
||||
void (*auth_deinit)(void* ctx);
|
||||
};
|
||||
|
||||
void main_auth_init(main_server_st *s);
|
||||
void proc_auth_deinit(main_server_st* s, struct proc_st* proc);
|
||||
|
||||
#endif
|
||||
180
src/main-misc.c
180
src/main-misc.c
@@ -147,6 +147,54 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int accept_user(main_server_st *s, struct proc_st* proc, unsigned cmd)
|
||||
{
|
||||
int ret;
|
||||
const char* group;
|
||||
|
||||
mslog(s, proc, LOG_DEBUG, "accepting user '%s'", proc->username);
|
||||
proc_auth_deinit(s, proc);
|
||||
|
||||
ret = open_tun(s, &proc->lease);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check for multiple connections */
|
||||
ret = check_multiple_users(s, proc);
|
||||
if (ret < 0) {
|
||||
mslog(s, proc, LOG_INFO, "user '%s' tried to connect more than %u times", proc->username, s->config->max_same_clients);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (proc->groupname[0] == 0)
|
||||
group = "[unknown]";
|
||||
else
|
||||
group = proc->groupname;
|
||||
|
||||
if (cmd == AUTH_REQ || cmd == AUTH_INIT) {
|
||||
/* generate and store cookie */
|
||||
ret = generate_and_store_vals(s, proc);
|
||||
if (ret < 0) {
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
mslog(s, proc, LOG_INFO, "user '%s' of group '%s' authenticated", proc->username, group);
|
||||
} else {
|
||||
mslog(s, proc, LOG_INFO, "user '%s' of group '%s' re-authenticated (using cookie)", proc->username, group);
|
||||
}
|
||||
|
||||
/* do scripts and utmp */
|
||||
ret = user_connected(s, proc);
|
||||
if (ret == ERR_WAIT_FOR_SCRIPT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
mslog(s, proc, LOG_INFO, "user '%s' disconnected due to script", proc->username);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_commands(main_server_st *s, struct proc_st* proc)
|
||||
{
|
||||
@@ -159,9 +207,9 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
|
||||
struct cmd_resume_store_req_st sresume;
|
||||
struct cmd_resume_fetch_req_st fresume;
|
||||
struct cmd_tun_mtu_st tmtu;
|
||||
struct cmd_auth_init_st auth_init;
|
||||
} cmd_data;
|
||||
int ret, cmd_data_len, e;
|
||||
const char* group;
|
||||
|
||||
memset(&cmd_data, 0, sizeof(cmd_data));
|
||||
|
||||
@@ -187,7 +235,12 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
|
||||
}
|
||||
|
||||
cmd_data_len = ret - 1;
|
||||
|
||||
|
||||
if (proc->auth_status == PS_AUTH_INIT && cmd != AUTH_REQ) {
|
||||
mslog(s, proc, LOG_ERR, "received message %u when expecting auth req.", (unsigned)cmd);
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
|
||||
switch(cmd) {
|
||||
case CMD_TUN_MTU:
|
||||
if (cmd_data_len != sizeof(cmd_data.tmtu)) {
|
||||
@@ -242,64 +295,83 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
|
||||
|
||||
break;
|
||||
|
||||
case AUTH_REQ:
|
||||
case AUTH_COOKIE_REQ:
|
||||
if (cmd == AUTH_REQ) {
|
||||
if (cmd_data_len != sizeof(cmd_data.auth)) {
|
||||
mslog(s, proc, LOG_ERR, "error in received message (%u) length.", (unsigned)cmd);
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
|
||||
ret = handle_auth_req(s, proc, &cmd_data.auth);
|
||||
} else {
|
||||
if (cmd_data_len != sizeof(cmd_data.cauth)) {
|
||||
mslog(s, proc, LOG_ERR, "error in received message (%u) length.", (unsigned)cmd);
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
|
||||
ret = handle_auth_cookie_req(s, proc, &cmd_data.cauth);
|
||||
case AUTH_INIT:
|
||||
if (cmd_data_len != sizeof(cmd_data.auth_init)) {
|
||||
mslog(s, proc, LOG_ERR, "error in received message (%u) length.", (unsigned)cmd);
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
/* check for multiple connections */
|
||||
ret = check_multiple_users(s, proc);
|
||||
if (proc->auth_status != PS_AUTH_INACTIVE) {
|
||||
mslog(s, proc, LOG_ERR, "received authentication init when complete.");
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
|
||||
ret = handle_auth_init(s, proc, &cmd_data.auth_init);
|
||||
if (ret == ERR_AUTH_CONTINUE) {
|
||||
proc->auth_status = PS_AUTH_INIT;
|
||||
|
||||
ret = send_auth_reply_msg(s, proc);
|
||||
if (ret < 0) {
|
||||
mslog(s, proc, LOG_INFO, "user '%s' tried to connect more than %u times", proc->username, s->config->max_same_clients);
|
||||
mslog(s, proc, LOG_ERR, "could not send reply auth cmd.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if (proc->groupname[0] == 0)
|
||||
group = "[unknown]";
|
||||
else
|
||||
group = proc->groupname;
|
||||
|
||||
if (cmd == AUTH_REQ) {
|
||||
/* generate and store cookie */
|
||||
ret = generate_and_store_vals(s, proc);
|
||||
if (ret < 0) {
|
||||
ret = ERR_BAD_COMMAND;
|
||||
goto cleanup;
|
||||
}
|
||||
mslog(s, proc, LOG_INFO, "user '%s' of group '%s' authenticated", proc->username, group);
|
||||
} else {
|
||||
mslog(s, proc, LOG_INFO, "user '%s' of group '%s' re-authenticated (using cookie)", proc->username, group);
|
||||
}
|
||||
}
|
||||
|
||||
/* do scripts and utmp */
|
||||
if (ret == 0) {
|
||||
ret = user_connected(s, proc);
|
||||
if (ret == ERR_WAIT_FOR_SCRIPT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
mslog(s, proc, LOG_INFO, "user '%s' disconnected due to script", proc->username);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break; /* wait for another command */
|
||||
} else if (ret < 0) {
|
||||
add_to_ip_ban_list(s, &proc->remote_addr, proc->remote_addr_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AUTH_REQ:
|
||||
if (proc->auth_status != PS_AUTH_INIT) {
|
||||
mslog(s, proc, LOG_ERR, "received authentication request when not initialized.");
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
|
||||
if (cmd_data_len != sizeof(cmd_data.auth)) {
|
||||
mslog(s, proc, LOG_ERR, "error in received message (%u) length.", (unsigned)cmd);
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
|
||||
ret = handle_auth_req(s, proc, &cmd_data.auth);
|
||||
if (ret == ERR_AUTH_CONTINUE) {
|
||||
ret = send_auth_reply_msg(s, proc);
|
||||
if (ret < 0) {
|
||||
mslog(s, proc, LOG_ERR, "could not send reply auth cmd.");
|
||||
return ret;
|
||||
}
|
||||
break; /* wait for another command */
|
||||
} else if (ret < 0) {
|
||||
add_to_ip_ban_list(s, &proc->remote_addr, proc->remote_addr_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = accept_user(s, proc, cmd);
|
||||
if (ret < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
proc->auth_status = PS_AUTH_COMPLETED;
|
||||
goto cleanup;
|
||||
|
||||
case AUTH_COOKIE_REQ:
|
||||
if (cmd_data_len != sizeof(cmd_data.cauth)) {
|
||||
mslog(s, proc, LOG_ERR, "error in received message (%u) length.", (unsigned)cmd);
|
||||
return ERR_BAD_COMMAND;
|
||||
}
|
||||
|
||||
ret = handle_auth_cookie_req(s, proc, &cmd_data.cauth);
|
||||
if (ret < 0) {
|
||||
add_to_ip_ban_list(s, &proc->remote_addr, proc->remote_addr_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = accept_user(s, proc, cmd);
|
||||
if (ret < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
proc->auth_status = PS_AUTH_COMPLETED;
|
||||
|
||||
cleanup:
|
||||
/* no script was called. Handle it as a successful script call. */
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#endif
|
||||
|
||||
#include <main.h>
|
||||
#include <main-auth.h>
|
||||
#include <worker.h>
|
||||
#include <cookies.h>
|
||||
#include <tun.h>
|
||||
@@ -299,6 +300,9 @@ static void remove_proc(main_server_st* s, struct proc_st *proc, unsigned k)
|
||||
close(proc->fd);
|
||||
proc->fd = -1;
|
||||
proc->pid = -1;
|
||||
|
||||
if (proc->auth_ctx != NULL)
|
||||
proc_auth_deinit(s, proc);
|
||||
|
||||
if (proc->lease)
|
||||
proc->lease->in_use = 0;
|
||||
@@ -669,6 +673,8 @@ int main(int argc, char** argv)
|
||||
s.config = &config;
|
||||
s.tun = &tun;
|
||||
|
||||
main_auth_init(&s);
|
||||
|
||||
ret = cookie_db_init(&s);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not initialize cookie database.\n");
|
||||
@@ -861,6 +867,7 @@ fork_failed:
|
||||
if (FD_ISSET(ctmp->fd, &rd)) {
|
||||
ret = handle_commands(&s, ctmp);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "error in command deleting\n");
|
||||
remove_from_script_list(&s, ctmp);
|
||||
remove_proc(&s, ctmp, (ret==ERR_BAD_COMMAND)?1:0);
|
||||
}
|
||||
|
||||
20
src/main.h
20
src/main.h
@@ -9,10 +9,6 @@
|
||||
#include <tlslib.h>
|
||||
#include "ipc.h"
|
||||
|
||||
#define ERR_WAIT_FOR_SCRIPT -5
|
||||
#define ERR_BAD_COMMAND -2
|
||||
#define ERR_MEM -6
|
||||
|
||||
int cmd_parser (int argc, char **argv, struct cfg_st* config);
|
||||
void reload_cfg_file(struct cfg_st* config);
|
||||
void write_pid_file(void);
|
||||
@@ -45,6 +41,12 @@ struct script_wait_st {
|
||||
struct proc_st* proc;
|
||||
};
|
||||
|
||||
enum {
|
||||
PS_AUTH_INACTIVE,
|
||||
PS_AUTH_INIT,
|
||||
PS_AUTH_COMPLETED,
|
||||
};
|
||||
|
||||
struct proc_st {
|
||||
struct list_node list;
|
||||
int fd;
|
||||
@@ -68,6 +70,9 @@ struct proc_st {
|
||||
char groupname[MAX_GROUPNAME_SIZE]; /* the owner's group */
|
||||
char hostname[MAX_HOSTNAME_SIZE]; /* the requested hostname */
|
||||
uint8_t cookie[COOKIE_SIZE]; /* the cookie associated with the session */
|
||||
|
||||
void * auth_ctx; /* the context of authentication */
|
||||
unsigned auth_status; /* PS_AUTH_ */
|
||||
};
|
||||
|
||||
struct proc_list_st {
|
||||
@@ -108,6 +113,8 @@ typedef struct main_server_st {
|
||||
pid_t sec_mod_pid;
|
||||
|
||||
unsigned active_clients;
|
||||
|
||||
void * auth_extra;
|
||||
} main_server_st;
|
||||
|
||||
void clear_lists(main_server_st *s);
|
||||
@@ -144,13 +151,16 @@ void mslog_hex(const main_server_st * s, const struct proc_st* proc,
|
||||
int open_tun(main_server_st* s, struct lease_st** l);
|
||||
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);
|
||||
int send_auth_reply(main_server_st* s, struct proc_st* proc,
|
||||
cmd_auth_reply_t r);
|
||||
int handle_auth_cookie_req(main_server_st* s, struct proc_st* proc,
|
||||
const struct cmd_auth_cookie_req_st * req);
|
||||
int generate_and_store_vals(main_server_st *s, struct proc_st* proc);
|
||||
int handle_auth_init(main_server_st *s, struct proc_st* proc,
|
||||
const struct cmd_auth_init_st * req);
|
||||
int handle_auth_req(main_server_st *s, struct proc_st* proc,
|
||||
const struct cmd_auth_req_st * req);
|
||||
const struct cmd_auth_req_st * req);
|
||||
|
||||
int check_multiple_users(main_server_st *s, struct proc_st* proc);
|
||||
|
||||
|
||||
252
src/pam.c
252
src/pam.c
@@ -21,6 +21,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <vpn.h>
|
||||
#include <main-auth.h>
|
||||
|
||||
#ifdef HAVE_PAM
|
||||
|
||||
@@ -28,89 +30,245 @@
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <pcl.h>
|
||||
|
||||
#define APP_NAME PACKAGE
|
||||
|
||||
#define MAX_REPLIES 2
|
||||
|
||||
struct local_st {
|
||||
const char* password;
|
||||
const char* username;
|
||||
enum {
|
||||
PAM_S_INIT,
|
||||
PAM_S_WAIT_FOR_PASS,
|
||||
PAM_S_COMPLETE,
|
||||
};
|
||||
|
||||
int dummy_conv(int msg_size, const struct pam_message **msg,
|
||||
struct pam_ctx_st {
|
||||
char password[MAX_PASSWORD_SIZE];
|
||||
char username[MAX_USERNAME_SIZE];
|
||||
pam_handle_t * ph;
|
||||
struct pam_conv dc;
|
||||
coroutine_t cr;
|
||||
int cr_ret;
|
||||
const char* cr_msg;
|
||||
unsigned sent_msg;
|
||||
struct pam_response *replies; /* for safety */
|
||||
unsigned state; /* PAM_S_ */
|
||||
};
|
||||
|
||||
static int dummy_conv(int msg_size, const struct pam_message **msg,
|
||||
struct pam_response **resp, void *uptr)
|
||||
{
|
||||
struct local_st * l = uptr;
|
||||
struct pam_ctx_st * pctx = uptr;
|
||||
unsigned i;
|
||||
struct pam_response *replies;
|
||||
|
||||
if (msg_size == 0)
|
||||
return PAM_SUCCESS;
|
||||
|
||||
replies = calloc(1, msg_size*sizeof(*replies));
|
||||
if (replies == NULL)
|
||||
pctx->replies = calloc(1, msg_size*sizeof(*pctx->replies));
|
||||
if (pctx->replies == NULL)
|
||||
return PAM_BUF_ERR;
|
||||
|
||||
for (i=0;i<msg_size;i++) {
|
||||
/*syslog(LOG_DEBUG, "PAM message: %s\n", msg[i]->msg);*/
|
||||
/*if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)*/
|
||||
replies[i].resp = strdup(l->password);
|
||||
switch (msg[i]->msg_style) {
|
||||
case PAM_ERROR_MSG:
|
||||
case PAM_TEXT_INFO:
|
||||
fprintf(stderr, "msg: %s\n", msg[i]->msg);
|
||||
if (pctx->sent_msg == 0) {
|
||||
pctx->state = PAM_S_WAIT_FOR_PASS;
|
||||
pctx->cr_msg = msg[i]->msg;
|
||||
pctx->cr_ret = PAM_SUCCESS;
|
||||
|
||||
pctx->sent_msg = 1;
|
||||
co_resume();
|
||||
pctx->state = PAM_S_INIT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
switch (msg[i]->msg_style) {
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
if (pctx->sent_msg == 0) {
|
||||
/* no message, just asking for password */
|
||||
pctx->state = PAM_S_WAIT_FOR_PASS;
|
||||
pctx->cr_msg = NULL;
|
||||
pctx->cr_ret = PAM_SUCCESS;
|
||||
pctx->sent_msg = 1;
|
||||
co_resume();
|
||||
pctx->state = PAM_S_INIT;
|
||||
}
|
||||
|
||||
pctx->replies[i].resp = strdup(pctx->password);
|
||||
pctx->sent_msg = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*resp = replies;
|
||||
*resp = pctx->replies;
|
||||
pctx->replies = NULL;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
static void co_auth_user(void* data)
|
||||
{
|
||||
struct pam_ctx_st * pctx = data;
|
||||
int pret;
|
||||
|
||||
pctx->state = PAM_S_INIT;
|
||||
|
||||
pret = pam_authenticate(pctx->ph, 0);
|
||||
if (pret != PAM_SUCCESS) {
|
||||
pctx->cr_ret = pret;
|
||||
return;
|
||||
}
|
||||
|
||||
pret = pam_acct_mgmt(pctx->ph, PAM_SILENT);
|
||||
if (pret != PAM_SUCCESS) {
|
||||
pctx->cr_ret = pret;
|
||||
return;
|
||||
}
|
||||
|
||||
pctx->state = PAM_S_COMPLETE;
|
||||
|
||||
pctx->cr_ret = PAM_SUCCESS;
|
||||
|
||||
co_resume();
|
||||
}
|
||||
|
||||
static int pam_auth_init(void** ctx, const char* user, const char* ip, void* additional)
|
||||
{
|
||||
int pret;
|
||||
struct pam_ctx_st * pctx;
|
||||
|
||||
if (user == NULL)
|
||||
return -1;
|
||||
|
||||
pctx = calloc(1, sizeof(*pctx));
|
||||
if (pctx == NULL)
|
||||
return -1;
|
||||
|
||||
pctx->cr = co_create(co_auth_user, pctx, NULL, 128*1024);
|
||||
if (pctx->cr == NULL)
|
||||
goto fail;
|
||||
|
||||
pctx->dc.conv = dummy_conv;
|
||||
pctx->dc.appdata_ptr = pctx;
|
||||
snprintf(pctx->username, sizeof(pctx->username), "%s", user);
|
||||
|
||||
pret = pam_start(APP_NAME, user, &pctx->dc, &pctx->ph);
|
||||
if (pret != PAM_SUCCESS) {
|
||||
syslog(LOG_AUTH, "Error in PAM authentication initialization: %s", pam_strerror(pctx->ph, pret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ip != NULL)
|
||||
pam_set_item(pctx->ph, PAM_RHOST, ip);
|
||||
|
||||
*ctx = pctx;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
free(pctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pam_auth_msg(void* ctx, char* msg, size_t msg_size)
|
||||
{
|
||||
struct pam_ctx_st * pctx = ctx;
|
||||
|
||||
if (pctx->state != PAM_S_INIT && pctx->state != PAM_S_WAIT_FOR_PASS) {
|
||||
syslog(LOG_AUTH, "PAM conversation in wrong state (%d)", pctx->state);
|
||||
return ERR_AUTH_FAIL;
|
||||
}
|
||||
|
||||
if (pctx->state == PAM_S_INIT) {
|
||||
/* get the prompt */
|
||||
pctx->cr_ret = PAM_CONV_ERR;
|
||||
co_call(pctx->cr);
|
||||
}
|
||||
|
||||
if (pctx->cr_ret != PAM_SUCCESS) {
|
||||
syslog(LOG_AUTH, "Error in PAM authentication: %s", pam_strerror(pctx->ph, pctx->cr_ret));
|
||||
return ERR_AUTH_FAIL;
|
||||
}
|
||||
|
||||
if (msg != NULL) {
|
||||
if (pctx->cr_msg == NULL)
|
||||
snprintf(msg, msg_size, "Please enter your password");
|
||||
else
|
||||
snprintf(msg, msg_size, "%s", pctx->cr_msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 0 if the user is successfully authenticated
|
||||
*/
|
||||
int pam_auth_user(const char* user, const char* pass, char *groupname, int groupname_size, const char* ip)
|
||||
static int pam_auth_pass(void* ctx, const char* pass)
|
||||
{
|
||||
pam_handle_t * ph;
|
||||
int ret, pret;
|
||||
struct local_st local;
|
||||
const struct pam_conv dc = { dummy_conv, &local };
|
||||
struct passwd * pwd;
|
||||
struct pam_ctx_st * pctx = ctx;
|
||||
|
||||
local.username = user;
|
||||
local.password = pass;
|
||||
|
||||
pret = pam_start(APP_NAME, user, &dc, &ph);
|
||||
if (pret != PAM_SUCCESS) {
|
||||
syslog(LOG_AUTH, "Error in PAM authentication initialization: %s", pam_strerror(ph, pret));
|
||||
if (pass == NULL)
|
||||
return -1;
|
||||
|
||||
if (pctx->state != PAM_S_WAIT_FOR_PASS) {
|
||||
syslog(LOG_AUTH, "PAM conversation in wrong state (%d/expecting %d)", pctx->state, PAM_S_WAIT_FOR_PASS);
|
||||
return ERR_AUTH_FAIL;
|
||||
}
|
||||
|
||||
snprintf(pctx->password, sizeof(pctx->password), "%s", pass);
|
||||
|
||||
pctx->cr_ret = PAM_CONV_ERR;
|
||||
co_call(pctx->cr);
|
||||
|
||||
if (pctx->cr_ret != PAM_SUCCESS) {
|
||||
syslog(LOG_AUTH, "Error in PAM authentication: %s", pam_strerror(pctx->ph, pctx->cr_ret));
|
||||
return ERR_AUTH_FAIL;
|
||||
}
|
||||
|
||||
if (ip != NULL)
|
||||
pam_set_item(ph, PAM_RHOST, ip);
|
||||
|
||||
pret = pam_authenticate(ph, PAM_SILENT);
|
||||
if (pret != PAM_SUCCESS) {
|
||||
syslog(LOG_AUTH, "Error in PAM authentication: %s", pam_strerror(ph, pret));
|
||||
ret = -1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pret = pam_acct_mgmt(ph, PAM_SILENT);
|
||||
if (pret != PAM_SUCCESS) {
|
||||
syslog(LOG_AUTH, "Error in PAM account management: %s", pam_strerror(ph, pret));
|
||||
ret = -1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pctx->state != PAM_S_COMPLETE)
|
||||
return ERR_AUTH_CONTINUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 0 if the user is successfully authenticated
|
||||
*/
|
||||
static int pam_auth_group(void* ctx, char *groupname, int groupname_size)
|
||||
{
|
||||
struct passwd * pwd;
|
||||
struct pam_ctx_st * pctx = ctx;
|
||||
|
||||
groupname[0] = 0;
|
||||
pwd = getpwnam(user);
|
||||
pwd = getpwnam(pctx->username);
|
||||
if (pwd != NULL) {
|
||||
struct group* grp = getgrgid(pwd->pw_gid);
|
||||
if (grp != NULL)
|
||||
snprintf(groupname, groupname_size, "%s", grp->gr_name);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
pam_end(ph, pret);
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pam_auth_deinit(void* ctx)
|
||||
{
|
||||
struct pam_ctx_st * pctx = ctx;
|
||||
|
||||
pam_end(pctx->ph, pctx->cr_ret);
|
||||
free(pctx->replies);
|
||||
co_delete(pctx->cr);
|
||||
free(pctx);
|
||||
}
|
||||
|
||||
const struct auth_mod_st pam_auth_funcs = {
|
||||
.type = AUTH_TYPE_PAM | AUTH_TYPE_USERNAME_PASS,
|
||||
.auth_init = pam_auth_init,
|
||||
.auth_deinit = pam_auth_deinit,
|
||||
.auth_msg = pam_auth_msg,
|
||||
.auth_pass = pam_auth_pass,
|
||||
.auth_group = pam_auth_group
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef PAM_H
|
||||
#define PAM_H
|
||||
|
||||
int pam_auth_user(const char* user, const char* pass, char *groupname, int groupname_size, const char* ip);
|
||||
#include <main-auth.h>
|
||||
|
||||
extern const struct auth_mod_st pam_auth_funcs;
|
||||
|
||||
#endif
|
||||
|
||||
69
src/plain.c
69
src/plain.c
@@ -22,12 +22,47 @@
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <vpn.h>
|
||||
#include <plain.h>
|
||||
|
||||
struct plain_ctx_st {
|
||||
char username[MAX_USERNAME_SIZE];
|
||||
char groupname[MAX_GROUPNAME_SIZE];
|
||||
const char* passwd;
|
||||
};
|
||||
|
||||
static int plain_auth_init(void** ctx, const char* username, const char* ip, void* additional)
|
||||
{
|
||||
struct plain_ctx_st* pctx;
|
||||
|
||||
pctx = malloc(sizeof(*pctx));
|
||||
if (pctx == NULL)
|
||||
return ERR_AUTH_FAIL;
|
||||
|
||||
snprintf(pctx->username, sizeof(pctx->username), "%s", username);
|
||||
pctx->passwd = additional;
|
||||
|
||||
*ctx = pctx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plain_auth_group(void* ctx, char *groupname, int groupname_size)
|
||||
{
|
||||
struct plain_ctx_st* pctx = ctx;
|
||||
|
||||
snprintf(groupname, groupname_size, "%s", pctx->groupname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Returns 0 if the user is successfully authenticated, and sets the appropriate group name.
|
||||
*/
|
||||
int plain_auth_user(const char* passwd, const char* user, const char* pass, char *groupname, int groupname_size)
|
||||
static int plain_auth_pass(void* ctx, const char* pass)
|
||||
{
|
||||
struct plain_ctx_st* pctx = ctx;
|
||||
unsigned groupname_size;
|
||||
FILE* fp;
|
||||
char * line = NULL;
|
||||
size_t len;
|
||||
@@ -35,9 +70,9 @@ ssize_t ll;
|
||||
char* p;
|
||||
int ret;
|
||||
|
||||
fp = fopen(passwd, "r");
|
||||
fp = fopen(pctx->passwd, "r");
|
||||
if (fp == NULL) {
|
||||
syslog(LOG_AUTH, "error in plain authentication; cannot open: %s", passwd);
|
||||
syslog(LOG_AUTH, "error in plain authentication; cannot open: %s", pctx->passwd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -52,12 +87,13 @@ int ret;
|
||||
|
||||
p = strtok(line, ":");
|
||||
|
||||
if (p != NULL && strcmp(user, p) == 0) {
|
||||
if (p != NULL && strcmp(pctx->username, p) == 0) {
|
||||
p = strtok(NULL, ":");
|
||||
if (p != NULL) {
|
||||
groupname_size = snprintf(groupname, groupname_size, "%s", p);
|
||||
groupname_size = sizeof(pctx->groupname);
|
||||
groupname_size = snprintf(pctx->groupname, groupname_size, "%s", p);
|
||||
if (groupname_size == 1) /* values like '*' or 'x' indicate empty group */
|
||||
groupname[0] = 0;
|
||||
pctx->groupname[0] = 0;
|
||||
|
||||
p = strtok(NULL, ":");
|
||||
if (p != NULL && strcmp(crypt(pass, p), p) == 0) {
|
||||
@@ -69,10 +105,29 @@ int ret;
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
syslog(LOG_AUTH, "error in plain authentication; error in user '%s'", user);
|
||||
syslog(LOG_AUTH, "error in plain authentication; error in user '%s'", pctx->username);
|
||||
exit:
|
||||
fclose(fp);
|
||||
free(line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int plain_auth_msg(void* ctx, char* msg, size_t msg_size)
|
||||
{
|
||||
snprintf(msg, msg_size, "%s", "Please enter your password");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void plain_auth_deinit(void* ctx)
|
||||
{
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
const struct auth_mod_st plain_auth_funcs = {
|
||||
.type = AUTH_TYPE_PLAIN | AUTH_TYPE_USERNAME_PASS,
|
||||
.auth_init = plain_auth_init,
|
||||
.auth_deinit = plain_auth_deinit,
|
||||
.auth_msg = plain_auth_msg,
|
||||
.auth_pass = plain_auth_pass,
|
||||
.auth_group = plain_auth_group
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef PLAIN_H
|
||||
#define PLAIN_H
|
||||
|
||||
int plain_auth_user(const char* passwd, const char* user, const char* pass, char *groupname, int groupname_size);
|
||||
#include <main-auth.h>
|
||||
|
||||
extern const struct auth_mod_st plain_auth_funcs;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -41,13 +41,19 @@ extern int syslog_open;
|
||||
#define AUTH_TYPE_CERTIFICATE (1<<2)
|
||||
#define AUTH_TYPE_PLAIN (1<<3 | AUTH_TYPE_USERNAME_PASS)
|
||||
|
||||
#define ERR_SUCCESS 0
|
||||
#define ERR_BAD_COMMAND -2
|
||||
#define ERR_AUTH_FAIL -3
|
||||
#define ERR_AUTH_CONTINUE -4
|
||||
#define ERR_WAIT_FOR_SCRIPT -5
|
||||
#define ERR_MEM -6
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct htable ht;
|
||||
unsigned int entries;
|
||||
} hash_db_st;
|
||||
|
||||
|
||||
struct vpn_st {
|
||||
char *name; /* device name */
|
||||
char *ipv4_netmask;
|
||||
|
||||
@@ -46,17 +46,25 @@
|
||||
|
||||
#define SUCCESS_MSG_FOOT "</auth>\n"
|
||||
|
||||
const char login_msg[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
static const char login_msg_user[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<auth id=\"main\">\n"
|
||||
"<message>Please enter your username and password.</message>\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";
|
||||
|
||||
static const char login_msg_no_user[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<auth id=\"main\">\n"
|
||||
"<message>%s</message>\n"
|
||||
"<form method=\"post\" action=\"/auth\">\n"
|
||||
"<input type=\"password\" name=\"password\" label=\"Password:\" />\n"
|
||||
"</form></auth>\n";
|
||||
|
||||
int get_auth_handler(worker_st *ws, unsigned http_ver)
|
||||
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)];
|
||||
unsigned int lsize;
|
||||
|
||||
tls_cork(ws->session);
|
||||
ret = tls_printf(ws->session, "HTTP/1.%u 200 OK\r\n", http_ver);
|
||||
@@ -70,8 +78,18 @@ int ret;
|
||||
ret = tls_puts(ws->session, "Content-Type: text/xml\r\n");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
if (ws->auth_state == S_AUTH_REQ) {
|
||||
/* only ask password */
|
||||
if (pmsg == NULL)
|
||||
pmsg = "Please enter password";
|
||||
lsize = snprintf(login_msg, sizeof(login_msg), login_msg_no_user, pmsg);
|
||||
} else {
|
||||
/* ask for username only */
|
||||
lsize = snprintf(login_msg, sizeof(login_msg), login_msg_user);
|
||||
}
|
||||
|
||||
ret = tls_printf(ws->session, "Content-Length: %u\r\n", (unsigned int)sizeof(login_msg)-1);
|
||||
ret = tls_printf(ws->session, "Content-Length: %u\r\n", (unsigned int)lsize);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
@@ -83,7 +101,7 @@ int ret;
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
ret = tls_send(ws->session, login_msg, sizeof(login_msg)-1);
|
||||
ret = tls_send(ws->session, login_msg, lsize);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
@@ -94,6 +112,11 @@ int ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_auth_handler(worker_st *ws, unsigned http_ver)
|
||||
{
|
||||
return get_auth_handler2(ws, http_ver, NULL);
|
||||
}
|
||||
|
||||
static
|
||||
int get_cert_names(worker_st *ws, const gnutls_datum_t* raw,
|
||||
char* username, size_t username_size,
|
||||
@@ -166,6 +189,28 @@ static int send_auth_req(int fd, const struct cmd_auth_req_st* r)
|
||||
return(sendmsg(fd, &hdr, 0));
|
||||
}
|
||||
|
||||
static int send_auth_init(int fd, const struct cmd_auth_init_st* r)
|
||||
{
|
||||
struct iovec iov[2];
|
||||
uint8_t cmd;
|
||||
struct msghdr hdr;
|
||||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
|
||||
cmd = AUTH_INIT;
|
||||
|
||||
iov[0].iov_base = &cmd;
|
||||
iov[0].iov_len = 1;
|
||||
|
||||
iov[1].iov_base = (void*)r;
|
||||
iov[1].iov_len = sizeof(*r);
|
||||
|
||||
hdr.msg_iov = iov;
|
||||
hdr.msg_iovlen = 2;
|
||||
|
||||
return(sendmsg(fd, &hdr, 0));
|
||||
}
|
||||
|
||||
static int send_auth_cookie_req(int fd, const struct cmd_auth_cookie_req_st* r)
|
||||
{
|
||||
struct iovec iov[2];
|
||||
@@ -188,11 +233,10 @@ static int send_auth_cookie_req(int fd, const struct cmd_auth_cookie_req_st* r)
|
||||
return(sendmsg(fd, &hdr, 0));
|
||||
}
|
||||
|
||||
static int recv_auth_reply(worker_st *ws)
|
||||
static int recv_auth_reply(worker_st *ws, struct cmd_auth_reply_st *resp)
|
||||
{
|
||||
struct iovec iov[2];
|
||||
uint8_t cmd = 0;
|
||||
struct cmd_auth_reply_st resp;
|
||||
struct msghdr hdr;
|
||||
int ret, cmdlen;
|
||||
union {
|
||||
@@ -204,8 +248,8 @@ static int recv_auth_reply(worker_st *ws)
|
||||
iov[0].iov_base = &cmd;
|
||||
iov[0].iov_len = 1;
|
||||
|
||||
iov[1].iov_base = &resp;
|
||||
iov[1].iov_len = sizeof(resp);
|
||||
iov[1].iov_base = resp;
|
||||
iov[1].iov_len = sizeof(*resp);
|
||||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
hdr.msg_iov = iov;
|
||||
@@ -213,44 +257,46 @@ static int recv_auth_reply(worker_st *ws)
|
||||
|
||||
hdr.msg_control = control_un.control;
|
||||
hdr.msg_controllen = sizeof(control_un.control);
|
||||
|
||||
|
||||
ret = recvmsg( ws->cmd_fd, &hdr, 0);
|
||||
|
||||
cmdlen = ret;
|
||||
|
||||
if (cmdlen < 2) {
|
||||
oclog(ws, LOG_ERR, "Received incorrect data (%d, expected %d) from main", cmdlen, (int)2);
|
||||
return -1;
|
||||
return ERR_AUTH_FAIL;
|
||||
}
|
||||
if (cmd != AUTH_REP)
|
||||
return -1;
|
||||
return ERR_AUTH_FAIL;
|
||||
|
||||
cmdlen--;
|
||||
|
||||
switch(resp.reply) {
|
||||
switch(resp->reply) {
|
||||
case REP_AUTH_MSG:
|
||||
return ERR_AUTH_CONTINUE;
|
||||
case REP_AUTH_OK:
|
||||
if (cmdlen < sizeof(resp)) {
|
||||
oclog(ws, LOG_ERR, "Received incorrect data (%d, expected %d) from main", ret, (int)sizeof(resp)+1);
|
||||
return -1;
|
||||
if (cmdlen < sizeof(*resp)) {
|
||||
oclog(ws, LOG_ERR, "Received incorrect data (%d, expected %d) from main", ret, (int)sizeof(*resp)+1);
|
||||
return ERR_AUTH_FAIL;
|
||||
}
|
||||
|
||||
if ( (cmptr = CMSG_FIRSTHDR(&hdr)) != NULL && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
|
||||
if (cmptr->cmsg_level != SOL_SOCKET)
|
||||
return -1;
|
||||
if (cmptr->cmsg_type != SCM_RIGHTS)
|
||||
return -1;
|
||||
return ERR_AUTH_FAIL;
|
||||
|
||||
memcpy(&ws->tun_fd, CMSG_DATA(cmptr), sizeof(int));
|
||||
memcpy(ws->tun_name, resp.vname, sizeof(ws->tun_name));
|
||||
memcpy(ws->username, resp.user, sizeof(ws->username));
|
||||
memcpy(ws->cookie, resp.cookie, sizeof(ws->cookie));
|
||||
memcpy(ws->session_id, resp.session_id, sizeof(ws->session_id));
|
||||
memcpy(ws->tun_name, resp->vname, sizeof(ws->tun_name));
|
||||
memcpy(ws->username, resp->user, sizeof(ws->username));
|
||||
memcpy(ws->cookie, resp->cookie, sizeof(ws->cookie));
|
||||
memcpy(ws->session_id, resp->session_id, sizeof(ws->session_id));
|
||||
ws->auth_ok = 1;
|
||||
} else
|
||||
return -1;
|
||||
return ERR_AUTH_FAIL;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
return ERR_AUTH_FAIL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -283,33 +329,21 @@ int ret;
|
||||
}
|
||||
|
||||
/* sends an authentication request to main thread and waits for
|
||||
* a reply.
|
||||
* Returns 0 on success.
|
||||
* a reply.
|
||||
* Returns 0 on success, AUTH_ERR_CONTINUE on partial success (must
|
||||
* be called again in that case) and a negative error code on other errors.
|
||||
*/
|
||||
static int auth_user(worker_st *ws, struct cmd_auth_req_st* areq)
|
||||
static int auth_user_pass(worker_st *ws, struct cmd_auth_req_st* areq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) {
|
||||
if (ws->cert_auth_ok == 0) {
|
||||
oclog(ws, LOG_INFO, "no certificate provided for authentication");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = get_cert_info(ws, areq->cert_user, sizeof(areq->cert_user),
|
||||
areq->cert_group, sizeof(areq->cert_group));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
areq->tls_auth_ok = 1;
|
||||
}
|
||||
|
||||
oclog(ws, LOG_DEBUG, "sending authentication request");
|
||||
oclog(ws, LOG_DEBUG, "sending auth request");
|
||||
|
||||
ret = send_auth_req(ws->cmd_fd, areq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return recv_auth_reply(ws);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sends a cookie authentication request to main thread and waits for
|
||||
@@ -320,6 +354,7 @@ int auth_cookie(worker_st *ws, void* cookie, size_t cookie_size)
|
||||
{
|
||||
int ret;
|
||||
struct cmd_auth_cookie_req_st areq;
|
||||
struct cmd_auth_reply_st resp;
|
||||
|
||||
memset(&areq, 0, sizeof(areq));
|
||||
|
||||
@@ -347,7 +382,7 @@ struct cmd_auth_cookie_req_st areq;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return recv_auth_reply(ws);
|
||||
return recv_auth_reply(ws, &resp);
|
||||
}
|
||||
|
||||
int post_common_handler(worker_st *ws, unsigned http_ver)
|
||||
@@ -437,39 +472,34 @@ char msg[MAX_BANNER_SIZE+32];
|
||||
#define XMLUSER_END "</username>"
|
||||
#define XMLPASS_END "</password>"
|
||||
|
||||
int post_auth_handler(worker_st *ws, unsigned http_ver)
|
||||
static
|
||||
int read_user_pass(worker_st *ws, char* body, unsigned body_length, char** username, char** password)
|
||||
{
|
||||
int ret;
|
||||
struct http_req_st *req = &ws->req;
|
||||
const char* reason = "Authentication failed";
|
||||
char * username = NULL;
|
||||
char * password = NULL;
|
||||
char *p;
|
||||
struct cmd_auth_req_st areq;
|
||||
|
||||
memset(&areq, 0, sizeof(areq));
|
||||
|
||||
if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
if (memmem(req->body, req->body_length, "<?xml", 5) != 0) {
|
||||
oclog(ws, LOG_DEBUG, "POST body: '%.*s'", req->body_length, req->body);
|
||||
char *p;
|
||||
|
||||
if (memmem(body, body_length, "<?xml", 5) != 0) {
|
||||
oclog(ws, LOG_DEBUG, "POST body: '%.*s'", body_length, body);
|
||||
|
||||
if (username != NULL) {
|
||||
/* body should contain <username>test</username><password>test</password> */
|
||||
username = memmem(req->body, req->body_length, XMLUSER, sizeof(XMLUSER)-1);
|
||||
if (username == NULL) {
|
||||
reason = "No username";
|
||||
goto ask_auth;
|
||||
*username = memmem(body, body_length, XMLUSER, sizeof(XMLUSER)-1);
|
||||
if (*username == NULL) {
|
||||
return -1;
|
||||
}
|
||||
username += sizeof(XMLUSER)-1;
|
||||
*username += sizeof(XMLUSER)-1;
|
||||
}
|
||||
|
||||
password = memmem(req->body, req->body_length, XMLPASS, sizeof(XMLPASS)-1);
|
||||
if (password == NULL) {
|
||||
reason = "No password";
|
||||
goto auth_fail;
|
||||
}
|
||||
password += sizeof(XMLPASS)-1;
|
||||
|
||||
/* modify body */
|
||||
p = username;
|
||||
if (password != NULL) {
|
||||
*password = memmem(body, body_length, XMLPASS, sizeof(XMLPASS)-1);
|
||||
if (*password == NULL) {
|
||||
return -1;
|
||||
}
|
||||
*password += sizeof(XMLPASS)-1;
|
||||
}
|
||||
|
||||
/* modify body */
|
||||
if (username != NULL) {
|
||||
p = *username;
|
||||
while(*p != 0) {
|
||||
if (*p == '<' && (strncmp(p, XMLUSER_END, sizeof(XMLUSER_END)-1) == 0)) {
|
||||
*p = 0;
|
||||
@@ -477,72 +507,141 @@ struct cmd_auth_req_st areq;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
p = password;
|
||||
while(*p != 0) {
|
||||
if (*p == '<' && (strncmp(p, XMLPASS_END, sizeof(XMLPASS_END)-1) == 0)) {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
areq.user_pass_present = 1;
|
||||
snprintf(areq.user, sizeof(areq.user), "%s", username);
|
||||
snprintf(areq.pass, sizeof(areq.pass), "%s", password);
|
||||
} else { /* non-xml version */
|
||||
/* body should be "username=test&password=test" */
|
||||
username = memmem(req->body, req->body_length, "username=", sizeof("username=")-1);
|
||||
if (username == NULL) {
|
||||
reason = "No username";
|
||||
goto auth_fail;
|
||||
}
|
||||
username += sizeof("username=")-1;
|
||||
|
||||
password = memmem(req->body, req->body_length, "password=", sizeof("password=")-1);
|
||||
if (password == NULL) {
|
||||
reason = "No password";
|
||||
goto auth_fail;
|
||||
}
|
||||
password += sizeof("password=")-1;
|
||||
|
||||
/* modify body */
|
||||
p = username;
|
||||
while(*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
p = password;
|
||||
while(*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
areq.user_pass_present = 1;
|
||||
snprintf(areq.user, sizeof(areq.user), "%s", username);
|
||||
snprintf(areq.pass, sizeof(areq.pass), "%s", password);
|
||||
}
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
p = *password;
|
||||
while(*p != 0) {
|
||||
if (*p == '<' && (strncmp(p, XMLPASS_END, sizeof(XMLPASS_END)-1) == 0)) {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (req->hostname[0] != 0) {
|
||||
memcpy(areq.hostname, req->hostname, sizeof(areq.hostname));
|
||||
} else { /* non-xml version */
|
||||
/* body should be "username=test&password=test" */
|
||||
if (username != NULL) {
|
||||
*username = memmem(body, body_length, "username=", sizeof("username=")-1);
|
||||
if (*username == NULL) {
|
||||
return -1;
|
||||
}
|
||||
*username += sizeof("username=")-1;
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
*password = memmem(body, body_length, "password=", sizeof("password=")-1);
|
||||
if (*password == NULL) {
|
||||
return -1;
|
||||
}
|
||||
*password += sizeof("password=")-1;
|
||||
}
|
||||
|
||||
/* modify body */
|
||||
if (username != NULL) {
|
||||
p = *username;
|
||||
while(*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
p = *password;
|
||||
while(*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int post_auth_handler(worker_st *ws, unsigned http_ver)
|
||||
{
|
||||
int ret;
|
||||
struct http_req_st *req = &ws->req;
|
||||
const char* reason = "Authentication failed";
|
||||
char * username = NULL;
|
||||
char * password = NULL;
|
||||
struct cmd_auth_reply_st resp;
|
||||
|
||||
if (ws->auth_state == S_AUTH_INACTIVE) {
|
||||
struct cmd_auth_init_st areq;
|
||||
|
||||
memset(&areq, 0, sizeof(areq));
|
||||
|
||||
if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
ret = read_user_pass(ws, req->body, req->body_length, &username, NULL);
|
||||
if (ret < 0)
|
||||
goto ask_auth;
|
||||
|
||||
snprintf(areq.user, sizeof(areq.user), "%s", username);
|
||||
areq.user_present = 1;
|
||||
}
|
||||
|
||||
if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) {
|
||||
if (ws->cert_auth_ok == 0) {
|
||||
oclog(ws, LOG_INFO, "no certificate provided for authentication");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = get_cert_info(ws, areq.cert_user, sizeof(areq.cert_user),
|
||||
areq.cert_group, sizeof(areq.cert_group));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
areq.tls_auth_ok = 1;
|
||||
}
|
||||
|
||||
if (req->hostname[0] != 0) {
|
||||
memcpy(areq.hostname, req->hostname, sizeof(areq.hostname));
|
||||
}
|
||||
|
||||
ret = send_auth_init(ws->cmd_fd, &areq);
|
||||
if (ret < 0)
|
||||
goto auth_fail;
|
||||
|
||||
ws->auth_state = S_AUTH_INIT;
|
||||
} else {
|
||||
struct cmd_auth_req_st areq;
|
||||
|
||||
if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
memset(&areq, 0, sizeof(areq));
|
||||
|
||||
ret = read_user_pass(ws, req->body, req->body_length, NULL, &password);
|
||||
if (ret < 0)
|
||||
goto ask_auth;
|
||||
|
||||
snprintf(areq.pass, sizeof(areq.pass), "%s", password);
|
||||
|
||||
ret = auth_user_pass(ws, &areq);
|
||||
if (ret < 0)
|
||||
goto auth_fail;
|
||||
|
||||
ws->auth_state = S_AUTH_REQ;
|
||||
} else
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
ret = auth_user(ws, &areq);
|
||||
if (ret < 0) {
|
||||
ret = recv_auth_reply(ws, &resp);
|
||||
if (ret == ERR_AUTH_CONTINUE) {
|
||||
ws->auth_state = S_AUTH_REQ;
|
||||
return get_auth_handler2(ws, http_ver, resp.msg);
|
||||
} else if (ret < 0)
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
oclog(ws, LOG_INFO, "User '%s' logged in", ws->username);
|
||||
ws->auth_state = S_AUTH_COMPLETE;
|
||||
|
||||
return post_common_handler(ws, http_ver);;
|
||||
return post_common_handler(ws, http_ver);
|
||||
|
||||
ask_auth:
|
||||
return get_auth_handler(ws, http_ver);
|
||||
|
||||
@@ -752,6 +752,13 @@ time_t udp_recv_time = 0, now;
|
||||
unsigned mtu_overhead = 0;
|
||||
socklen_t sl;
|
||||
|
||||
if (ws->auth_state != S_AUTH_COMPLETE) {
|
||||
oclog(ws, LOG_INFO, "authentication was not completed");
|
||||
tls_puts(ws->session, "HTTP/1.1 503 Service Unavailable\r\n\r\n");
|
||||
tls_close(ws->session);
|
||||
exit_worker(ws);
|
||||
}
|
||||
|
||||
ws->buffer_size = 16*1024;
|
||||
ws->buffer = malloc(ws->buffer_size);
|
||||
if (ws->buffer == NULL) {
|
||||
|
||||
12
src/worker.h
12
src/worker.h
@@ -37,6 +37,13 @@ enum {
|
||||
HTTP_HEADER_VALUE_RECV
|
||||
};
|
||||
|
||||
enum {
|
||||
S_AUTH_INACTIVE = 0,
|
||||
S_AUTH_INIT,
|
||||
S_AUTH_REQ,
|
||||
S_AUTH_COMPLETE
|
||||
};
|
||||
|
||||
struct http_req_st {
|
||||
char url[256];
|
||||
|
||||
@@ -52,12 +59,13 @@ struct http_req_st {
|
||||
unsigned int master_secret_set;
|
||||
|
||||
char *body;
|
||||
unsigned int body_length;
|
||||
|
||||
char *gnutls_ciphersuite; /* static string */
|
||||
char *selected_ciphersuite; /* static string */
|
||||
int gnutls_cipher;
|
||||
int gnutls_mac;
|
||||
|
||||
unsigned int body_length;
|
||||
unsigned int headers_complete;
|
||||
unsigned int message_complete;
|
||||
unsigned dtls_mtu;
|
||||
@@ -76,6 +84,7 @@ typedef struct worker_st {
|
||||
|
||||
http_parser *parser;
|
||||
struct cfg_st *config;
|
||||
unsigned int auth_state; /* S_AUTH */
|
||||
|
||||
struct sockaddr_storage remote_addr; /* peer's address */
|
||||
socklen_t remote_addr_len;
|
||||
@@ -116,6 +125,7 @@ typedef struct worker_st {
|
||||
void vpn_server(struct worker_st* ws);
|
||||
|
||||
int auth_cookie(worker_st *ws, void* cookie, size_t cookie_size);
|
||||
int auth_user_deinit(worker_st *ws);
|
||||
|
||||
int get_auth_handler(worker_st *server, unsigned http_ver);
|
||||
int post_auth_handler(worker_st *server, unsigned http_ver);
|
||||
|
||||
Reference in New Issue
Block a user