mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-03-11 07:17:02 +08:00
updated server.
This commit is contained in:
@@ -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])
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
492
src/main.c
492
src/main.c
@@ -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(<mp->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
130
src/vpn.c
@@ -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);
|
||||||
|
|||||||
69
src/vpn.h
69
src/vpn.h
@@ -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, ...);
|
||||||
|
|||||||
Reference in New Issue
Block a user