updated server.

This commit is contained in:
Nikos Mavrogiannopoulos
2013-01-28 19:01:12 +01:00
parent 2aa1bf3993
commit ba72042e03
7 changed files with 597 additions and 263 deletions

View File

@@ -3,7 +3,7 @@ AC_INIT([ocserv], [0.0.1], [nmav@gnutls.org])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([1.11.3 no-dist-gzip dist-xz -Wall -Werror -Wno-override]) AM_INIT_AUTOMAKE([1.11.0 no-dist-gzip dist-xz -Wall -Werror -Wno-override])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])

View File

@@ -3,7 +3,7 @@ AM_CPPFLAGS = -I$(srcdir)../gl/ -I$(builddir)../gl/ \
bin_PROGRAMS = ocserv bin_PROGRAMS = ocserv
ocserv_SOURCES = main.c vpn.c auth.c tlslib.c cookies.c http-parser/http_parser.c \ ocserv_SOURCES = main.c vpn.c http_auth.c tlslib.c cookies.c http-parser/http_parser.c \
vpn.h auth.h cookies.h tlslib.h http-parser/http_parser.h log.c vpn.h auth.h cookies.h tlslib.h http-parser/http_parser.h log.c
ocserv_LDADD = ../gl/libgnu.a ocserv_LDADD = ../gl/libgnu.a

View File

@@ -34,7 +34,7 @@
#include <limits.h> #include <limits.h>
#include <vpn.h> #include <vpn.h>
#include <auth.h> #include <http_auth.h>
#include <cookies.h> #include <cookies.h>
#include <tlslib.h> #include <tlslib.h>
@@ -103,6 +103,102 @@ fail:
} }
static int send_auth_req(int fd, struct cmd_auth_req_st* r)
{
struct iovec iov[2];
uint8_t cmd;
struct msghdr hdr;
int ret;
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
memset(&hdr, 0, sizeof(hdr));
cmd = AUTH_REQ;
iov[0].iov_base = &cmd;
iov[0].iov_len = 1;
iov[1].iov_base = r;
iov[1].iov_len = sizeof(*r);
hdr.msg_iov = iov;
hdr.msg_iovlen = 2;
return(sendmsg(fd, &hdr, 0));
}
static int recv_auth_reply(int cmdfd, int* tunfd)
{
struct iovec iov[2];
uint8_t cmd;
uint8_t reply;
struct msghdr hdr;
int ret;
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
iov[0].iov_base = &cmd;
iov[0].iov_len = 1;
iov[1].iov_base = &reply;
iov[1].iov_len = 1;
memset(&hdr, 0, sizeof(hdr));
hdr.msg_iov = iov;
hdr.msg_iovlen = 2;
hdr.msg_control = control_un.control;
hdr.msg_controllen = sizeof(control_un.control);
ret = recvmsg( cmdfd, &hdr, 0);
if (ret <= 0) {
return -1;
}
if (cmd != AUTH_REP)
return -1;
switch(reply) {
case REP_AUTH_OK:
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;
*tunfd = *((int *) CMSG_DATA(cmptr));
} else
return -1;
break;
default:
return -1;
}
return 0;
}
/* sends an authentication request to main thread and waits for
* a reply */
static int auth_user(int cmdfd, struct cmd_auth_req_st* areq, int* tunfd)
{
int ret;
ret = send_auth_req(cmdfd, areq);
if (ret < 0)
return ret;
return recv_auth_reply(cmdfd, tunfd);
}
int post_old_auth_handler(server_st *server) int post_old_auth_handler(server_st *server)
{ {
int ret; int ret;
@@ -110,12 +206,39 @@ struct req_data_st *req = server->parser->data;
const char* reason = "Authentication failed"; const char* reason = "Authentication failed";
unsigned char cookie[COOKIE_SIZE]; unsigned char cookie[COOKIE_SIZE];
char str_cookie[2*COOKIE_SIZE+1]; char str_cookie[2*COOKIE_SIZE+1];
char cert_username[MAX_USERNAME_SIZE];
char * username = NULL; char * username = NULL;
char * password = NULL; char * password = NULL;
char *p; char *p;
unsigned int i; unsigned int i;
struct stored_cookie_st sc; struct stored_cookie_st sc;
struct cmd_auth_req_st areq;
memset(&areq, 0, sizeof(areq));
if (server->config->auth_types & AUTH_TYPE_CERTIFICATE) {
const gnutls_datum_t * cert;
unsigned int ncerts;
/* this is superflous. Verification has already been performed
* during handshake. */
cert = gnutls_certificate_get_peers (server->session, &ncerts);
if (cert == NULL) {
reason = "No certificate found";
goto auth_fail;
}
areq.tls_auth_ok = 1;
if (server->config->cert_user_oid) { /* otherwise certificate username is ignored */
ret = get_cert_username(server, cert, areq.cert_user, sizeof(areq.cert_user));
if (ret < 0) {
oclog(server, LOG_ERR, "Cannot get username (%s) from certificate", server->config->cert_user_oid);
reason = "No username in certificate";
goto auth_fail;
}
username = areq.cert_user;
}
}
if (server->config->auth_types & AUTH_TYPE_USERNAME_PASS) { if (server->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
/* body should be "username=test&password=test" */ /* body should be "username=test&password=test" */
@@ -152,40 +275,14 @@ struct stored_cookie_st sc;
p++; p++;
} }
/* now verify username and passwords */ areq.user_pass_present = 1;
if (strcmp(username, "test") != 0 || strcmp(password, "test") != 0) snprintf(areq.user, sizeof(areq.user), "%s", username);
goto auth_fail; snprintf(areq.pass, sizeof(areq.pass), "%s", password);
} }
if (server->config->auth_types & AUTH_TYPE_CERTIFICATE) { ret = auth_user(server->cmdfd, &areq, &server->tunfd);
const gnutls_datum_t * cert; if (ret < 0)
unsigned int ncerts; goto auth_fail;
/* this is superflous. Verification has already been performed
* during handshake. */
cert = gnutls_certificate_get_peers (server->session, &ncerts);
if (cert == NULL) {
reason = "No certificate found";
goto auth_fail;
}
if (server->config->cert_user_oid) { /* otherwise certificate username is ignored */
ret = get_cert_username(server, cert, cert_username, sizeof(cert_username));
if (ret < 0) {
oclog(server, LOG_ERR, "Cannot get username (%s) from certificate", server->config->cert_user_oid);
reason = "No username in certificate";
goto auth_fail;
}
if (username) {
if (strcmp(username, cert_username) != 0)
oclog(server, LOG_NOTICE, "User '%s' presented the certificate of user '%s'", username, cert_username);
} else {
username = cert_username;
}
}
}
oclog(server, LOG_INFO, "User '%s' logged in\n", username); oclog(server, LOG_INFO, "User '%s' logged in\n", username);

View File

@@ -45,11 +45,19 @@
int syslog_open = 0; int syslog_open = 0;
static unsigned int need_to_expire_cookies = 0; static unsigned int need_to_expire_cookies = 0;
static int handle_commands(struct cfg_st *config, struct tun_st *tun, int fd);
struct listen_list_st { struct listen_list_st {
struct list_head list; struct list_head list;
int fd; int fd;
}; };
struct cmd_list_st {
struct list_head list;
int fd;
pid_t pid;
};
static void tls_log_func(int level, const char *str) static void tls_log_func(int level, const char *str)
{ {
syslog(LOG_DEBUG, "Debug[<%d>]: %s", level, str); syslog(LOG_DEBUG, "Debug[<%d>]: %s", level, str);
@@ -62,25 +70,26 @@ static void tls_audit_log_func(gnutls_session_t session, const char *str)
static struct cfg_st config = { static struct cfg_st config = {
.auth_types = AUTH_TYPE_USERNAME_PASS, .auth_types = AUTH_TYPE_USERNAME_PASS,
.workers = 1,
.name = NULL, .name = NULL,
.port = 3333, .port = 3333,
.cert = "./test.pem", .cert = "./test.pem",
.key = "./test.pem", .key = "./test.pem",
.cert_req = GNUTLS_CERT_IGNORE, .cert_req = GNUTLS_CERT_IGNORE,
.cert_user_oid = GNUTLS_OID_LDAP_UID /* or just GNUTLS_OID_X520_COMMON_NAME */, .cert_user_oid =
GNUTLS_OID_LDAP_UID /* or just GNUTLS_OID_X520_COMMON_NAME */ ,
.root_dir = "root/", .root_dir = "root/",
.cookie_validity = 3600, .cookie_validity = 3600,
.db_file = "/tmp/db", .db_file = "/tmp/db",
.uid = 65534, .uid = 65534,
.gid = 65534, .gid = 65534,
.ca = NULL, .ca = NULL,
.networks_size = 1, .network = {
.networks = {{ .name = "vpns",
.name = "vpns0", .ipv4_netmask = "255.255.255.0",
.ipv4_netmask = "255.255.255.0", .ipv4 = "192.168.55.1",
.ipv4 = "192.168.55.1", .ipv4_dns = "192.168.55.1",
.ipv4_dns = "192.168.55.1", }
}}
}; };
/* Returns 0 on success or negative value on error. /* Returns 0 on success or negative value on error.
@@ -187,7 +196,18 @@ listen_ports(struct listen_list_st *list, const char *node,
static void handle_children(int signo) static void handle_children(int signo)
{ {
while (waitpid(-1, NULL, WNOHANG) > 0); int status;
while (waitpid(-1, &status, WNOHANG) > 0) {
if (WEXITSTATUS(status) != 0 ||
(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV)) {
if (WIFSIGNALED(status))
syslog(LOG_ERR, "Child died with sigsegv\n");
else
syslog(LOG_DEBUG, "Child died with status %d\n", WEXITSTATUS(status));
} else
syslog(LOG_DEBUG, "Child died peacefully\n");
}
} }
static void handle_alarm(int signo) static void handle_alarm(int signo)
@@ -195,50 +215,56 @@ static void handle_alarm(int signo)
need_to_expire_cookies = 1; need_to_expire_cookies = 1;
} }
static int set_network_info(const struct vpn_st* vinfo) static int set_network_info(const struct vpn_st *vinfo)
{ {
struct ifreq ifr; struct ifreq ifr;
int fd, ret; int fd, ret;
fd = socket(AF_INET, SOCK_STREAM, 0); fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) if (fd == -1)
return -1; return -1;
/* set netmask */ /* set netmask */
if (vinfo->ipv4_netmask && vinfo->ipv4) { if (vinfo->ipv4_netmask && vinfo->ipv4) {
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_addr.sa_family = AF_INET;
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name); snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name);
ret = inet_pton(AF_INET, vinfo->ipv4_netmask, &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr); ret =
inet_pton(AF_INET, vinfo->ipv4_netmask,
&((struct sockaddr_in *) &ifr.ifr_addr)->
sin_addr);
if (ret != 1) { if (ret != 1) {
syslog(LOG_ERR, "%s: Error reading mask: %s\n", syslog(LOG_ERR, "%s: Error reading mask: %s\n",
vinfo->name, vinfo->ipv4_netmask); vinfo->name, vinfo->ipv4_netmask);
goto fail; goto fail;
} }
ret = ioctl(fd, SIOCSIFNETMASK, &ifr); ret = ioctl(fd, SIOCSIFNETMASK, &ifr);
if (ret != 0) { if (ret != 0) {
syslog(LOG_ERR, "%s: Error setting mask: %s\n", syslog(LOG_ERR, "%s: Error setting mask: %s\n",
vinfo->name, vinfo->ipv4_netmask); vinfo->name, vinfo->ipv4_netmask);
} }
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_addr.sa_family = AF_INET;
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name); snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name);
ret = inet_pton(AF_INET, vinfo->ipv4, &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr); ret =
inet_pton(AF_INET, vinfo->ipv4,
&((struct sockaddr_in *) &ifr.ifr_addr)->
sin_addr);
if (ret != 1) { if (ret != 1) {
syslog(LOG_ERR, "%s: Error reading IP: %s\n", syslog(LOG_ERR, "%s: Error reading IP: %s\n",
vinfo->name, vinfo->ipv4_netmask); vinfo->name, vinfo->ipv4_netmask);
goto fail; goto fail;
} }
ret = ioctl(fd, SIOCSIFADDR, &ifr); ret = ioctl(fd, SIOCSIFADDR, &ifr);
if (ret != 0) { if (ret != 0) {
syslog(LOG_ERR, "%s: Error setting IP: %s\n", syslog(LOG_ERR, "%s: Error setting IP: %s\n",
vinfo->name, vinfo->ipv4); vinfo->name, vinfo->ipv4);
} }
} }
@@ -246,52 +272,60 @@ int fd, ret;
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sa_family = AF_INET6; ifr.ifr_addr.sa_family = AF_INET6;
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name); snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name);
ret = inet_pton(AF_INET6, vinfo->ipv6_netmask, &((struct sockaddr_in6 *)&ifr.ifr_addr)->sin6_addr); ret =
inet_pton(AF_INET6, vinfo->ipv6_netmask,
&((struct sockaddr_in6 *) &ifr.ifr_addr)->
sin6_addr);
if (ret != 1) { if (ret != 1) {
syslog(LOG_ERR, "%s: Error reading mask: %s\n", syslog(LOG_ERR, "%s: Error reading mask: %s\n",
vinfo->name, vinfo->ipv6_netmask); vinfo->name, vinfo->ipv6_netmask);
goto fail; goto fail;
} }
ret = ioctl(fd, SIOCSIFNETMASK, &ifr); ret = ioctl(fd, SIOCSIFNETMASK, &ifr);
if (ret != 0) { if (ret != 0) {
syslog(LOG_ERR, "%s: Error setting mask: %s\n", syslog(LOG_ERR, "%s: Error setting mask: %s\n",
vinfo->name, vinfo->ipv6_netmask); vinfo->name, vinfo->ipv6_netmask);
} }
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sa_family = AF_INET6; ifr.ifr_addr.sa_family = AF_INET6;
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name); snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name);
ret = inet_pton(AF_INET6, vinfo->ipv6, &((struct sockaddr_in6 *)&ifr.ifr_addr)->sin6_addr); ret =
inet_pton(AF_INET6, vinfo->ipv6,
&((struct sockaddr_in6 *) &ifr.ifr_addr)->
sin6_addr);
if (ret != 1) { if (ret != 1) {
syslog(LOG_ERR, "%s: Error reading IP: %s\n", syslog(LOG_ERR, "%s: Error reading IP: %s\n",
vinfo->name, vinfo->ipv6_netmask); vinfo->name, vinfo->ipv6_netmask);
goto fail; goto fail;
} }
ret = ioctl(fd, SIOCSIFADDR, &ifr); ret = ioctl(fd, SIOCSIFADDR, &ifr);
if (ret != 0) { if (ret != 0) {
syslog(LOG_ERR, "%s: Error setting IP: %s\n", syslog(LOG_ERR, "%s: Error setting IP: %s\n",
vinfo->name, vinfo->ipv6); vinfo->name, vinfo->ipv6);
} }
} }
ret = 0; ret = 0;
fail: fail:
close(fd); close(fd);
return ret; return ret;
} }
static int open_tun(struct cfg_st *config) static int open_tun(struct cfg_st *config, struct tun_st* tun)
{ {
int tunfd, ret, e; int tunfd, ret, e;
struct ifreq ifr; struct ifreq ifr;
unsigned int i, t; unsigned int t;
/* XXX obtain random IPs + tun nr */
tunfd = open("/dev/net/tun", O_RDWR); tunfd = open("/dev/net/tun", O_RDWR);
if (tunfd < 0) { if (tunfd < 0) {
@@ -301,47 +335,48 @@ unsigned int i, t;
return -1; return -1;
} }
for (i=0;i<config->networks_size;i++) { memset(&ifr, 0, sizeof(ifr));
memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; snprintf(ifr.ifr_name, sizeof(ifr.ifr_name),
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), config->networks[i].name, 0); config->network.name, 0);
if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0) { if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0) {
e = errno; e = errno;
syslog(LOG_ERR, "TUNSETIFF: %s\n", strerror(e)); syslog(LOG_ERR, "TUNSETIFF: %s\n", strerror(e));
exit(1); goto fail;
} }
if (config->uid != -1) {
t = config->uid;
ret = ioctl(tunfd, TUNSETOWNER, t);
if (ret < 0) {
e = errno;
syslog(LOG_ERR, "TUNSETOWNER: %s\n", strerror(e));
exit(1);
}
}
if (config->gid != -1) { if (config->uid != -1) {
t = config->uid; t = config->uid;
ret = ioctl(tunfd, TUNSETGROUP, t); ret = ioctl(tunfd, TUNSETOWNER, t);
if (ret < 0) {
e = errno;
syslog(LOG_ERR, "TUNSETGROUP: %s\n", strerror(e));
exit(1);
}
}
/* set IP/mask */
ret = set_network_info(&config->networks[i]);
if (ret < 0) { if (ret < 0) {
exit(1); e = errno;
syslog(LOG_INFO, "TUNSETOWNER: %s\n",
strerror(e));
goto fail;
} }
}
if (config->gid != -1) {
t = config->uid;
ret = ioctl(tunfd, TUNSETGROUP, t);
if (ret < 0) {
e = errno;
syslog(LOG_ERR, "TUNSETGROUP: %s\n",
strerror(e));
goto fail;
}
}
/* set IP/mask */
ret = set_network_info(&config->network);
if (ret < 0) {
goto fail;
} }
return tunfd; return tunfd;
fail:
close(tunfd);
return -1;
} }
static int verify_certificate_cb(gnutls_session_t session) static int verify_certificate_cb(gnutls_session_t session)
@@ -381,40 +416,75 @@ static int verify_certificate_cb(gnutls_session_t session)
static void drop_privileges(struct cfg_st *config) static void drop_privileges(struct cfg_st *config)
{ {
int ret, e; int ret, e;
if (config->gid != -1) { if (config->gid != -1 && (getgid() == 0 || getegid() == 0)) {
ret = setgid(config->gid); ret = setgid(config->gid);
if (ret < 0) { if (ret < 0) {
e = errno; e = errno;
syslog(LOG_ERR, "Cannot set gid to %d: %s\n", (int)config->gid, strerror(e)); syslog(LOG_ERR, "Cannot set gid to %d: %s\n",
(int) config->gid, strerror(e));
exit(1); exit(1);
} }
} }
if (config->uid != -1) { if (config->uid != -1 && (getuid() == 0 || geteuid() == 0)) {
ret = setuid(config->uid); ret = setuid(config->uid);
if (ret < 0) { if (ret < 0) {
e = errno; e = errno;
syslog(LOG_ERR, "Cannot set uid to %d: %s\n", (int)config->uid, strerror(e)); syslog(LOG_ERR, "Cannot set uid to %d: %s\n",
(int) config->uid, strerror(e));
exit(1); exit(1);
} }
} }
} }
static void clear_listen_list(struct listen_list_st* llist)
{
struct list_head *cq;
struct list_head *pos;
struct listen_list_st *ltmp;
list_for_each_safe(pos, cq, &llist->list) {
ltmp = list_entry(pos, struct listen_list_st, list);
close(ltmp->fd);
list_del(&ltmp->list);
}
}
static void clear_cmd_list(struct cmd_list_st* clist)
{
struct list_head *cq;
struct list_head *pos;
struct cmd_list_st *ctmp;
list_for_each_safe(pos, cq, &clist->list) {
ctmp = list_entry(pos, struct cmd_list_st, list);
close(ctmp->fd);
list_del(&ctmp->list);
}
}
int main(void) int main(void)
{ {
int fd, pid, e; int fd, pid, e;
struct tls_st creds; struct tls_st creds;
struct listen_list_st llist; struct listen_list_st llist;
struct listen_list_st *tmp; struct cmd_list_st clist;
struct listen_list_st *ltmp;
struct cmd_list_st *ctmp;
struct list_head *cq;
struct list_head *pos; struct list_head *pos;
struct tun_st tun;
fd_set rd; fd_set rd;
int val, n = 0, ret, tunfd; int val, n = 0, ret;
struct timeval tv; struct timeval tv;
int cmd_fd[2];
INIT_LIST_HEAD(&clist.list);
/*signal(SIGINT, SIG_IGN); */ /*signal(SIGINT, SIG_IGN); */
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
@@ -424,18 +494,15 @@ int main(void)
/* XXX load configuration */ /* XXX load configuration */
/* Listen to network ports */
ret = listen_ports(&llist, config.name, config.port, SOCK_STREAM); ret = listen_ports(&llist, config.name, config.port, SOCK_STREAM);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "Cannot listen to specified ports\n");
exit(1); exit(1);
} }
tunfd = open_tun(&config);
if (tunfd < 0) {
exit(1);
}
drop_privileges(&config);
/* Initialize GnuTLS */
gnutls_global_set_log_function(tls_log_func); gnutls_global_set_log_function(tls_log_func);
gnutls_global_set_audit_log_function(tls_audit_log_func); gnutls_global_set_audit_log_function(tls_audit_log_func);
gnutls_global_set_log_level(0); gnutls_global_set_log_level(0);
@@ -452,6 +519,7 @@ int main(void)
GNUTLS_X509_FMT_PEM); GNUTLS_X509_FMT_PEM);
GNUTLS_FATAL_ERR(ret); GNUTLS_FATAL_ERR(ret);
if (config.ca != NULL) { if (config.ca != NULL) {
ret = ret =
gnutls_certificate_set_x509_trust_file(creds.xcred, gnutls_certificate_set_x509_trust_file(creds.xcred,
@@ -478,7 +546,8 @@ int main(void)
ret = gnutls_priority_init(&creds.cprio, config.priorities, NULL); ret = gnutls_priority_init(&creds.cprio, config.priorities, NULL);
GNUTLS_FATAL_ERR(ret); GNUTLS_FATAL_ERR(ret);
alarm(config.cookie_validity+300);
alarm(config.cookie_validity + 300);
openlog("ocserv", LOG_PID, LOG_LOCAL0); openlog("ocserv", LOG_PID, LOG_LOCAL0);
syslog_open = 1; syslog_open = 1;
@@ -486,39 +555,122 @@ int main(void)
FD_ZERO(&rd); FD_ZERO(&rd);
list_for_each(pos, &llist.list) { list_for_each(pos, &llist.list) {
tmp = list_entry(pos, struct listen_list_st, list); ltmp = list_entry(pos, struct listen_list_st, list);
#ifndef _WIN32
val = fcntl(tmp->fd, F_GETFL, 0); val = fcntl(ltmp->fd, F_GETFL, 0);
if ((val == -1) if ((val == -1)
|| (fcntl(tmp->fd, F_SETFL, val | O_NONBLOCK) < || (fcntl(ltmp->fd, F_SETFL, val | O_NONBLOCK) <
0)) { 0)) {
perror("fcntl()"); perror("fcntl()");
exit(1); exit(1);
} }
#endif
FD_SET(tmp->fd, &rd); FD_SET(ltmp->fd, &rd);
n = MAX(n, tmp->fd); n = MAX(n, ltmp->fd);
}
list_for_each(pos, &clist.list) {
ctmp = list_entry(pos, struct cmd_list_st, list);
FD_SET(ctmp->fd, &rd);
n = MAX(n, ctmp->fd);
} }
tv.tv_usec = 0; tv.tv_usec = 0;
tv.tv_sec = 10; tv.tv_sec = 10;
n = select(n + 1, &rd, NULL, NULL, &tv); ret = select(n + 1, &rd, NULL, NULL, &tv);
if (n == -1 && errno == EINTR) if (ret == -1 && errno == EINTR)
continue; continue;
if (n < 0) { if (ret < 0) {
syslog(LOG_ERR, "Error in select(): %s", strerror(errno)); syslog(LOG_ERR, "Error in select(): %s",
strerror(errno));
exit(1); exit(1);
} }
/* Check for new connections to accept */
list_for_each(pos, &llist.list) {
ltmp = list_entry(pos, struct listen_list_st, list);
if (FD_ISSET(ltmp->fd, &rd)) {
fd = accept(ltmp->fd, NULL, NULL);
if (fd < 0) {
syslog(LOG_ERR,
"Error in accept(): %s",
strerror(errno));
continue;
}
/* Create a command socket */
ret = socketpair(AF_UNIX, SOCK_STREAM, 0, cmd_fd);
if (ret < 0) {
syslog(LOG_ERR, "Error creating command socket");
exit(1);
}
pid = fork();
if (pid == 0) { /* child */
/* Drop privileges after this point */
drop_privileges(&config);
/* close any open descriptors before
* running the server
*/
close(cmd_fd[0]);
clear_listen_list(&llist);
clear_cmd_list(&clist);
vpn_server(&config, &creds,
cmd_fd[1], fd);
exit(0);
} else if (pid == -1) {
fork_failed:
close(cmd_fd[0]);
free(ctmp);
} else { /* parent */
ctmp = calloc(1, sizeof(struct cmd_list_st));
if (ctmp == NULL) {
kill(pid, SIGTERM);
goto fork_failed;
}
ctmp->pid = pid;
ctmp->fd = cmd_fd[0];
list_add(&(ctmp->list), &(clist.list));
}
close(cmd_fd[1]);
close(fd);
}
}
/* Check for any pending commands */
list_for_each_safe(pos, cq, &clist.list) {
ctmp = list_entry(pos, struct cmd_list_st, list);
if (FD_ISSET(ctmp->fd, &rd)) {
ret = handle_commands(&config, &tun, ctmp->fd);
if (ret < 0) {
close(ctmp->fd);
kill(ctmp->pid, SIGTERM);
list_del(&ctmp->list);
}
}
}
/* Check if we need to expire any cookies */
if (need_to_expire_cookies != 0) { if (need_to_expire_cookies != 0) {
need_to_expire_cookies = 0; need_to_expire_cookies = 0;
pid = fork(); pid = fork();
if (pid == 0) { /* child */ if (pid == 0) { /* child */
/* Drop privileges after this point */
drop_privileges(&config);
list_for_each(pos, &llist.list) { list_for_each(pos, &llist.list) {
tmp = list_entry(pos, struct listen_list_st, list); ltmp =
close(tmp->fd); list_entry(pos,
struct
listen_list_st,
list);
close(ltmp->fd);
} }
expire_cookies(&config); expire_cookies(&config);
@@ -526,30 +678,120 @@ int main(void)
} }
} }
list_for_each(pos, &llist.list) {
tmp = list_entry(pos, struct listen_list_st, list);
if (FD_ISSET(tmp->fd, &rd)) {
fd = accept(tmp->fd, NULL, NULL);
if (fd < 0) {
syslog(LOG_ERR, "Error in accept(): %s", strerror(errno));
continue;
}
pid = fork();
if (pid == 0) { /* child */
list_for_each(pos, &llist.list) {
tmp = list_entry(pos, struct listen_list_st, list);
close(tmp->fd);
}
vpn_server(&config, &creds,
tunfd, fd);
exit(0);
}
close(fd);
}
}
} }
return 0; return 0;
} }
static int send_auth_reply(int fd, cmd_auth_reply_t r, int sendfd)
{
struct iovec iov[1];
uint8_t cmd[2];
struct msghdr hdr;
union {
struct cmd_auth_req_st auth;
} cmd_data;
int ret;
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
memset(&hdr, 0, sizeof(hdr));
cmd[0] = AUTH_REP;
cmd[1] = r;
iov[0].iov_base = cmd;
iov[0].iov_len = 2;
hdr.msg_iov = iov;
hdr.msg_iovlen = 1;
if (r == REP_AUTH_OK) {
/* Send the tun fd */
hdr.msg_control = control_un.control;
hdr.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&hdr);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
*((int *) CMSG_DATA(cmptr)) = sendfd;
}
return(sendmsg(fd, &hdr, 0));
}
static int handle_auth_req(struct cfg_st *config, struct tun_st *tun,
struct cmd_auth_req_st * req, int *tunfd)
{
int ret;
if (strcmp(req->user, "test") == 0 && strcmp(req->pass, "test") == 0)
ret = 0;
else
ret = -1;
if (ret == 0) { /* open tun */
*tunfd = open_tun(config, tun);
if (*tunfd == -1)
ret = -1; /* sorry */
}
return ret;
}
static int handle_commands(struct cfg_st *config, struct tun_st *tun, int fd)
{
struct iovec iov[2];
uint8_t cmd;
struct msghdr hdr;
int tunfd;
union {
struct cmd_auth_req_st auth;
} cmd_data;
int ret;
iov[0].iov_base = &cmd;
iov[0].iov_len = 1;
iov[1].iov_base = &cmd_data;
iov[1].iov_len = sizeof(cmd_data);
memset(&hdr, 0, sizeof(hdr));
hdr.msg_iov = iov;
hdr.msg_iovlen = 2;
ret = recvmsg( fd, &hdr, 0);
if (ret == -1) {
syslog(LOG_ERR, "Cannot obtain data from command socket.");
return -1;
}
if (ret == 0) {
return -1;
}
switch(cmd) {
case AUTH_REQ:
ret = handle_auth_req(config, tun, &cmd_data.auth, &tunfd);
if (ret == 0) {
ret = send_auth_reply(fd, REP_AUTH_OK, tunfd);
close(tunfd);
} else
ret = send_auth_reply(fd, REP_AUTH_FAILED, -1);
if (ret < 0) {
syslog(LOG_ERR, "Could not send reply cmd.");
return -1;
}
break;
default:
syslog(LOG_ERR, "Unknown CMD 0x%x.", (unsigned)cmd);
return -1;
}
return 0;
}

130
src/vpn.c
View File

@@ -38,7 +38,7 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <vpn.h> #include <vpn.h>
#include <auth.h> #include <http_auth.h>
#include <cookies.h> #include <cookies.h>
#include <tlslib.h> #include <tlslib.h>
@@ -181,7 +181,7 @@ char* tmp = malloc(length+1);
} }
void vpn_server(struct cfg_st *config, struct tls_st *creds, int tunfd, int fd) void vpn_server(struct cfg_st *config, struct tls_st *creds, int cmdfd, int fd)
{ {
unsigned char buf[2048]; unsigned char buf[2048];
int ret; int ret;
@@ -193,10 +193,10 @@ void vpn_server(struct cfg_st *config, struct tls_st *creds, int tunfd, int fd)
server_st _server; server_st _server;
server_st *server; server_st *server;
url_handler_fn fn; url_handler_fn fn;
memset(&_server, 0, sizeof(_server)); memset(&_server, 0, sizeof(_server));
server = &_server; server = &_server;
server->remote_addr_len = sizeof(server->remote_addr); server->remote_addr_len = sizeof(server->remote_addr);
ret = getpeername (fd, (void*)&server->remote_addr, &server->remote_addr_len); ret = getpeername (fd, (void*)&server->remote_addr, &server->remote_addr_len);
if (ret < 0) if (ret < 0)
@@ -238,7 +238,8 @@ void vpn_server(struct cfg_st *config, struct tls_st *creds, int tunfd, int fd)
server->config = config; server->config = config;
server->session = session; server->session = session;
server->parser = &parser; server->parser = &parser;
server->tunfd = tunfd; server->cmdfd = cmdfd;
server->tunfd = -1;
restart: restart:
http_parser_init(&parser, HTTP_REQUEST); http_parser_init(&parser, HTTP_REQUEST);
@@ -307,8 +308,8 @@ finish:
tls_close(session); tls_close(session);
} }
static int get_network_mask(int fd, int family, const char* vname, static int get_remote_ip(int fd, int family,
struct vpn_st* vinfo, char** buffer, size_t* buffer_size) struct vpn_st* vinfo, char** buffer, size_t* buffer_size)
{ {
unsigned char *ptr; unsigned char *ptr;
const char* p; const char* p;
@@ -321,7 +322,8 @@ int ret;
ifr.ifr_addr.sa_family = family; ifr.ifr_addr.sa_family = family;
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name); snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name);
ret = ioctl(fd, SIOCGIFNETMASK, &ifr); /* local: SIOCGIFADDR */
ret = ioctl(fd, SIOCGIFDSTADDR, &ifr);
if (ret != 0) { if (ret != 0) {
goto fail; goto fail;
} }
@@ -343,39 +345,6 @@ int ret;
*buffer += ret; *buffer += ret;
*buffer_size -= ret; *buffer_size -= ret;
if (family == AF_INET) {
vinfo->ipv4_netmask = p;
} else {
vinfo->ipv6_netmask = p;
}
/* get IP */
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sa_family = family;
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name);
ret = ioctl(fd, SIOCGIFADDR, &ifr);
if (ret != 0) {
goto fail;
}
if (family == AF_INET) {
ptr = (void*)&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
} else if (family == AF_INET6) {
ptr = (void*)&((struct sockaddr_in6 *)&ifr.ifr_addr)->sin6_addr;
} else {
return -1;
}
p = inet_ntop(family, (void*)ptr, *buffer, *buffer_size);
if (p == NULL) {
goto fail;
}
ret = strlen(p)+1;
*buffer += ret;
*buffer_size -= ret;
if (family == AF_INET) { if (family == AF_INET) {
if (strcmp(p, "0.0.0.0")==0) if (strcmp(p, "0.0.0.0")==0)
p = NULL; p = NULL;
@@ -397,7 +366,7 @@ fail:
* *
* Returns 0 on success. * Returns 0 on success.
*/ */
static int get_rt_vpn_info(server_st * server, const char* vname, struct vpn_st* vinfo, static int get_rt_vpn_info(server_st * server, struct vpn_st* vinfo,
char* buffer, size_t buffer_size) char* buffer, size_t buffer_size)
{ {
unsigned int i; unsigned int i;
@@ -410,40 +379,38 @@ const char* p;
fd = socket(AF_INET, SOCK_STREAM, 0); fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) if (fd == -1)
return -1; return -1;
ret = get_remote_ip(fd, AF_INET6, vinfo, &buffer, &buffer_size);
if (ret < 0)
oclog(server, LOG_INFO, "Cannot obtain IPv6 IP for %s\n", vinfo->name);
for (i=0;i<server->config->networks_size;i++) { ret = get_remote_ip(fd, AF_INET, vinfo, &buffer, &buffer_size);
if (strcmp(vname, server->config->networks[i].name) == 0) { if (ret < 0)
vinfo->name = server->config->networks[i].name; oclog(server, LOG_INFO, "Cannot obtain IPv4 IP for %s\n", vinfo->name);
vinfo->ipv4_dns = server->config->networks[i].ipv4_dns;
vinfo->ipv6_dns = server->config->networks[i].ipv6_dns; if (vinfo->ipv4 == NULL && vinfo->ipv6 == NULL) {
vinfo->routes_size = server->config->networks[i].routes_size; ret = -1;
memcpy(vinfo->routes, server->config->networks[i].routes, sizeof(vinfo->routes)); goto fail;
}
ret = get_network_mask(fd, AF_INET, vname, vinfo, &buffer, &buffer_size); vinfo->name = server->config->network.name;
if (ret < 0) { vinfo->ipv4_dns = server->config->network.ipv4_dns;
oclog(server, LOG_INFO, "Cannot obtain IPv4 IP or mask for %s", vinfo->name); vinfo->ipv6_dns = server->config->network.ipv6_dns;
} vinfo->routes_size = server->config->network.routes_size;
memcpy(vinfo->routes, server->config->network.routes, sizeof(vinfo->routes));
ret = get_network_mask(fd, AF_INET6, vname, vinfo, &buffer, &buffer_size); vinfo->ipv4_netmask = server->config->network.ipv4_netmask;
if (ret < 0) { vinfo->ipv6_netmask = server->config->network.ipv6_netmask;
oclog(server, LOG_INFO, "Cannot obtain IPv6 IP or mask for %s", vinfo->name);
}
if (vinfo->ipv4_netmask == NULL && vinfo->ipv6_netmask == NULL) {
ret = -1;
goto fail;
}
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_addr.sa_family = AF_INET;
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name); snprintf(ifr.ifr_name, IFNAMSIZ, "%s", vinfo->name);
ret = ioctl(fd, SIOCGIFMTU, (caddr_t)&ifr); ret = ioctl(fd, SIOCGIFMTU, (caddr_t)&ifr);
if (ret < 0) { if (ret < 0) {
oclog(server, LOG_ERR, "Cannot obtain MTU for %s. Assuming 1500.", vinfo->name); oclog(server, LOG_ERR, "Cannot obtain MTU for %s. Assuming 1500.", vinfo->name);
vinfo->mtu = 1500; vinfo->mtu = 1500;
} else } else {
vinfo->mtu = ifr.ifr_mtu; vinfo->mtu = ifr.ifr_mtu;
}
} }
ret = 0; ret = 0;
@@ -489,7 +456,7 @@ unsigned int buffer_size;
exit(1); exit(1);
} }
if (server->config->networks_size == 0) { if (server->config->network.name == NULL) {
oclog(server, LOG_ERR, "No networks are configured. Rejecting client."); oclog(server, LOG_ERR, "No networks are configured. Rejecting client.");
tls_puts(server->session, "HTTP/1.1 503 Service Unavailable\r\n\r\n"); tls_puts(server->session, "HTTP/1.1 503 Service Unavailable\r\n\r\n");
return -1; return -1;
@@ -505,11 +472,12 @@ unsigned int buffer_size;
return -1; return -1;
} }
ret = get_rt_vpn_info(server, server->config->networks[0].name, ret = get_rt_vpn_info(server, &vinfo, buffer, buffer_size);
&vinfo, buffer, buffer_size);
if (ret < 0) { if (ret < 0) {
oclog(server, LOG_ERR, "Network interfaces are not configured. Rejecting client."); oclog(server, LOG_ERR, "Network interfaces are not configured. Rejecting client.");
tls_puts(server->session, "HTTP/1.1 503 Service Unavailable\r\n\r\n");
tls_puts(server->session, "HTTP/1.1 503 Service Unavailable\r\n");
tls_puts(server->session, "X-Reason: Server configuration error\r\n\r\n");
return -1; return -1;
} }
@@ -520,8 +488,7 @@ unsigned int buffer_size;
tls_puts(server->session, "X-CSTP-DPD: 60\r\n"); tls_puts(server->session, "X-CSTP-DPD: 60\r\n");
if (vinfo.ipv4_netmask && vinfo.ipv4) { if (vinfo.ipv4_netmask && vinfo.ipv4) {
tls_printf(server->session, "X-CSTP-Address: 172.31.255.%d\r\n", tls_printf(server->session, "X-CSTP-Address: %s\r\n", vinfo.ipv4);
100 + tun_nr);
tls_printf(server->session, "X-CSTP-Netmask: %s\r\n", vinfo.ipv4_netmask); tls_printf(server->session, "X-CSTP-Netmask: %s\r\n", vinfo.ipv4_netmask);
if (vinfo.ipv4_dns) if (vinfo.ipv4_dns)
tls_printf(server->session, "X-CSTP-DNS: %s\r\n", vinfo.ipv4_dns); tls_printf(server->session, "X-CSTP-DNS: %s\r\n", vinfo.ipv4_dns);
@@ -529,8 +496,7 @@ unsigned int buffer_size;
if (vinfo.ipv6_netmask && vinfo.ipv6) { if (vinfo.ipv6_netmask && vinfo.ipv6) {
tls_printf(server->session, "X-CSTP-Netmask: %s\r\n", vinfo.ipv6_netmask); tls_printf(server->session, "X-CSTP-Netmask: %s\r\n", vinfo.ipv6_netmask);
tls_printf(server->session, "X-CSTP-Address: 2001:770:15f::%x\r\n", tls_printf(server->session, "X-CSTP-Address: %s\r\n", vinfo.ipv6);
0x100 + tun_nr);
if (vinfo.ipv6_dns) if (vinfo.ipv6_dns)
tls_printf(server->session, "X-CSTP-DNS: %s\r\n", vinfo.ipv6_dns); tls_printf(server->session, "X-CSTP-DNS: %s\r\n", vinfo.ipv6_dns);
} }
@@ -552,8 +518,10 @@ unsigned int buffer_size;
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_SET(tls_fd, &rfds); FD_SET(tls_fd, &rfds);
FD_SET(server->cmdfd, &rfds);
FD_SET(server->tunfd, &rfds); FD_SET(server->tunfd, &rfds);
max = MAX(server->tunfd,tls_fd); max = MAX(server->cmdfd,tls_fd);
max = MAX(max,server->tunfd);
if (gnutls_record_check_pending(server->session) == 0) { if (gnutls_record_check_pending(server->session) == 0) {
ret = select(max + 1, &rfds, NULL, NULL, NULL); ret = select(max + 1, &rfds, NULL, NULL, NULL);

View File

@@ -27,39 +27,45 @@ extern int syslog_open;
#define MAX_NETWORKS 24 #define MAX_NETWORKS 24
#define MAX_ROUTES 64 #define MAX_ROUTES 64
struct tun_st {
const char *ign;
};
struct vpn_st { struct vpn_st {
const char* name; /* device name */ const char *name; /* device name */
const char* ipv4_netmask; const char *ipv4_netmask;
const char* ipv4; const char *ipv4;
const char* ipv6_netmask; const char *ipv6_netmask;
const char* ipv6; const char *ipv6;
const char* ipv4_dns; const char *ipv4_dns;
const char* ipv6_dns; const char *ipv6_dns;
unsigned int mtu; unsigned int mtu;
const char* routes[MAX_ROUTES]; const char *routes[MAX_ROUTES];
unsigned int routes_size; unsigned int routes_size;
}; };
struct cfg_st { struct cfg_st {
const char *name; const char *name;
unsigned workers;
unsigned int port; unsigned int port;
const char *cert; const char *cert;
const char *key; const char *key;
const char *ca; const char *ca;
const char *crl; const char *crl;
const char *cert_user_oid; /* The OID that will be used to extract the username */ const char *cert_user_oid; /* The OID that will be used to extract the username */
gnutls_certificate_request_t cert_req; gnutls_certificate_request_t cert_req;
const char *priorities; const char *priorities;
const char *root_dir; /* where the xml files are served from */ const char *root_dir; /* where the xml files are served from */
unsigned int auth_types; /* or'ed sequence of AUTH_TYPE */ unsigned int auth_types; /* or'ed sequence of AUTH_TYPE */
time_t cookie_validity; /* in seconds */ time_t cookie_validity; /* in seconds */
const char* db_file; const char *db_file;
uid_t uid; uid_t uid;
gid_t gid; gid_t gid;
struct vpn_st networks[MAX_NETWORKS]; /* the tun network */
unsigned int networks_size; struct vpn_st network;
}; };
struct tls_st { struct tls_st {
@@ -69,19 +75,21 @@ struct tls_st {
typedef struct server_st { typedef struct server_st {
gnutls_session_t session; gnutls_session_t session;
http_parser* parser; int cmdfd;
struct cfg_st *config;
int tunfd; int tunfd;
http_parser *parser;
struct cfg_st *config;
struct sockaddr_storage remote_addr; /* peer's address */ struct sockaddr_storage remote_addr; /* peer's address */
socklen_t remote_addr_len; socklen_t remote_addr_len;
} server_st; } server_st;
#define MAX_USERNAME_SIZE 64 #define MAX_USERNAME_SIZE 64
#define MAX_PASSWORD_SIZE 64
#define COOKIE_SIZE 32 #define COOKIE_SIZE 32
enum { enum {
HEADER_COOKIE=1, HEADER_COOKIE = 1,
}; };
struct req_data_st { struct req_data_st {
@@ -94,10 +102,29 @@ struct req_data_st {
unsigned int message_complete; unsigned int message_complete;
}; };
void vpn_server(struct cfg_st *config, struct tls_st *creds, int tunfd, int fd); typedef enum {
AUTH_REQ = 1,
AUTH_REP = 2,
} cmd_request_t;
typedef enum {
REP_AUTH_OK = 0,
REP_AUTH_FAILED = 1,
} cmd_auth_reply_t;
struct cmd_auth_req_st {
uint8_t user_pass_present;
char user[MAX_USERNAME_SIZE];
char pass[MAX_PASSWORD_SIZE];
uint8_t tls_auth_ok;
char cert_user[MAX_USERNAME_SIZE];
};
void vpn_server(struct cfg_st *config, struct tls_st *creds, int cmd_fd,
int fd);
const char *human_addr(const struct sockaddr *sa, socklen_t salen, const char *human_addr(const struct sockaddr *sa, socklen_t salen,
void *buf, size_t buflen); void *buf, size_t buflen);
int __attribute__ ((format(printf, 3, 4))) int __attribute__ ((format(printf, 3, 4)))
oclog(server_st * server, int priority, const char *fmt, ...); oclog(server_st * server, int priority, const char *fmt, ...);