Use PAM account management and added support for user groups.

This commit is contained in:
Nikos Mavrogiannopoulos
2013-02-12 18:53:25 +01:00
parent 121b2491aa
commit e8f6332f36
14 changed files with 493 additions and 113 deletions

299
doc/ocserv.1 Normal file
View File

@@ -0,0 +1,299 @@
.TH ocserv 1 "12 Feb 2013" "" "User Commands"
.\"
.\" DO NOT EDIT THIS FILE (ocserv-args.man)
.\"
.\" It has been AutoGen-ed February 12, 2013 at 04:34:58 PM by AutoGen 5.16
.\" From the definitions ../src/ocserv-args.def.tmp
.\" and the template file agman-cmd.tpl
.\"
.SH NAME
ocserv \- OpenConnect server
.SH SYNOPSIS
.B ocserv
.\" Mixture of short (flag) options and long options
.RB [ \-\fIflag\fP " [\fIvalue\fP]]... [" \-\-\fIopt\-name\fP " [[=| ]\fIvalue\fP]]..."
.PP
All arguments must be options.
.PP
.SH "DESCRIPTION"
This program is openconnect VPN server (ocserv), a server compatible with the
openconnect VPN client. It is believed to be compatible with the protocol
used by CISCO's AnyConnect SSL VPN.
.SH "OPTIONS"
.TP
.BR \-f ", " -\-foreground
Do not fork into background.
.sp
.TP
.BR \-\-tls\-debug
Enable verbose TLS debugging information.
.sp
.TP
.BR \-d ", " -\-debug
Enable verbose network debugging information.
.sp
.TP
.BR \-c " \fIfile\fP, " \-\-config "=" \fIfile\fP
Configuration file for the server.
.sp
.TP
.BR \-h , " \-\-help"
Display usage information and exit.
.TP
.BR \-! , " \-\-more-help"
Pass the extended usage information through a pager.
.SH AUTHENTICATION
.br
\fBPassword authentication\fP
.br
If your system supports Pluggable Authentication Modules (PAM), then
ocserv will take advantage of it to password authenticate its users.
It can be used in conjunction with certificate authentication.
.sp
.br
\fBCertificate authentication\fP
.br
In order to support certificate authentication you will need in addition to
the server certificate and key for TLS, a certificate authority (CA) to sign
certificates for the clients. That authority should also provide a CRL to
allow the server to reject the revoked clients (see \fBca\-cert, crl\fP).
.sp
Each client will then hold a key and certificate that identifies him.
The user ID of the client must be embedded in the certificate's Distinguished
Name (DN), e.g. in the Common Name, or UID fields. For the server to
read the name, the \fBcert\-user\-oid\fP configuration option must be set.
.sp
The following examples demonstrate how to use certtool from GnuTLS to
generate such CA.
.sp
.br
\fBGenerating the CA\fP
.br
.br
.in +4
.nf
$ certtool \-\-generate\-privkey \-\-outfile ca\-key.pem
$ cat << _EOF_ >ca.tmpl
cn = "VPN CA"
organization = "Big Corp"
serial = 1
expiration_days = 9999
ca
signing_key
cert_signing_key
crl_signing_key
_EOF_
$ certtool \-\-generate\-self\-signed \-\-load\-privkey ca\-key.pem \
-\-template ca.tmpl \-\-outfile ca\-cert.pem
.in -4
.fi
.sp
.br
\fBGenerating the client certificates\fP
.br
.br
.in +4
.nf
$ certtool \-\-generate\-privkey \-\-outfile user\-key.pem
$ cat << _EOF_ >user.tmpl
cn = "user"
serial = 1824
email = "user@example.com"
expiration_days = 9999
signing_key
tls_www_client
_EOF_
$ certtool \-\-generate\-certificate \-\-load\-privkey user\-key.pem \
-\-load\-ca\-certificate ca\-cert.pem \-\-load\-ca\-privkey ca\-key.pem \
-\-template user.tmpl \-\-outfile user\-cert.pem
.sp
.in -4
.fi
.sp
.br
\fBRevoking a client certificate\fP
.br
To revoke the previous client certificate use:
.br
.in +4
.nf
$ cat user\-cert.pem >>revoked.pem
$ certtool \-\-generate\-crl \-\-load\-ca\-privkey ca\-key.pem \
-\-load\-ca\-certificate ca.pem \-\-load\-certificate revoked.pem \
-\-outfile crl.pem
.in -4
.fi
After that you may want to notify ocserv of the new CRL by using
the HUP signal.
.sp
.SH "IMPLEMENTATION NOTES"
Note that while this server utilizes privilege separation for password
authentication, this does not occur for TLS and client certificate authentication.
This was done to take advantage of multi\-core systems by distributing the
expensive TLS calculations to the workers.
.SH FILES
.br
\fBocserv's configuration file format\fP
.br
By default, if no other file is specified, ocserv looks for its configuration file at \fI/etc/ocserv.conf\fP.
An example configuration file follows.
.sp
.br
.in +4
.nf
.sp
# User authentication method. Could be set multiple times and in that case
# all should succeed.
# Options: certificate, pam.
#auth = "certificate"
auth = "pam"
.sp
# Use listen\-host to limit to specific IPs or to the IPs of a provided hostname.
#listen\-host = [IP|HOSTNAME]
.sp
# Limit the number of clients. Unset or set to zero for unlimited.
#max\-clients = 1024
max\-clients = 16
.sp
# Limit the number of identical clients (i.e., users connecting multiple times)
# Unset or set to zero for unlimited.
max\-same\-clients = 1
.sp
# TCP and UDP port number
tcp\-port = 3333
udp\-port = 3333
.sp
# Keepalive in seconds
keepalive = 3600
.sp
# Dead peer detection in seconds
dpd = 60
.sp
# The key and the certificates of the server
# The key may be a file, or any URL supported by GnuTLS (i.e., tpmkey or pkcs11)
server\-cert = /path/to/cert.pem
server\-key = /path/to/key.pem
.sp
# The Certificate Authority that will be used
# to verify clients if certificate authentication
# is set.
#ca\-cert = /path/to/ca.pem
.sp
# The object identifier that will be used to read the user ID in the client certificate.
# The object identifier should be part of the certificate's DN
# Useful OIDs are:
# CN = 2.5.4.3, UID = 0.9.2342.19200300.100.1.1
#cert\-user\-oid = 0.9.2342.19200300.100.1.1
.sp
# The object identifier that will be used to read the user group in the client
# certificate. The object identifier should be part of the certificate's DN
# Useful OIDs are:
# OU (organizational unit) = 2.5.4.11
#cert\-group\-oid = 2.5.4.11
.sp
# A revocation list of ca\-cert is set
#crl = /path/to/crl.pem
.sp
# GnuTLS priority string
tls\-priorities = "PERFORMANCE:%SERVER_PRECEDENCE"
.sp
# The default server directory
#chroot\-dir = /path/to/chroot
.sp
# The time (in seconds) that a client is allowed to stay connected prior
# to authentication
auth\-timeout = 40
.sp
# Cookie validity time (in seconds)
# Once a client is authenticated he's provided a cookie with
# which he can reconnect. This option sets the maximum lifetime
# of that cookie.
cookie\-validity = 14400
.sp
# A cookie database. If not set cookies are stored in memory and
# server restarts won't preserve them.
#cookie\-db = /var/tmp/cookies.db
.sp
# Script to call when a client connects and obtains an IP
# Parameters: username groupname hostname device IP\-REAL IP\-LOCAL IP\-REMOTE
# hostname is the hostname selected by the client
# IP\-REAL is the remote IP of the client,
# IP\-LOCAL is the local IP in the P\-t\-P connection and IP\-REMOTE
# is the VPN client IP.
connect\-script = /bin/echo
disconnect\-script = /bin/echo
.sp
# UTMP
use\-utmp = true
.sp
# PID file
pid\-file = /var/run/ocserv.pid
.sp
run\-as\-user = nobody
run\-as\-group = nogroup
.sp
# Network settings
.sp
device = vpns
.sp
ipv4\-network = 192.168.1.0
ipv4\-netmask = 255.255.255.0
# Use the keywork local to advertize the local P\-t\-P address as DNS server
# ipv4\-dns = 192.168.2.1
ipv4\-dns = local
.sp
#ipv6\-address =
#ipv6\-mask =
#ipv6\-dns =
.sp
# Leave empty to assign the default MTU of the device
# mtu =
.sp
route = 192.168.1.0/255.255.255.0
route = 192.168.5.0/255.255.255.0
.sp
.in -4
.fi
.sp
.SH "EXIT STATUS"
One of the following exit values will be returned:
.TP
.BR 0 " (EXIT_SUCCESS)"
Successful program execution.
.TP
.BR 1 " (EXIT_FAILURE)"
The operation failed or the command syntax was not valid.
.SH COMPATIBILITY
.br
\fBFeatures of the server\fP
.br
.in +4
.ti -4
\fB*\fP
Supports both TCP and UDP VPN tunnels using TLS and DTLS.
.ti -4
\fB*\fP
Support for the server key being stored in TPM, a hardware security module (HSM), or smart card.
.ti -4
\fB*\fP
Authentication using PAM (username\-password) or certificates
.ti -4
\fB*\fP
Privilege separation between the main process which performs TUN allocation and authentication, with the worker processes which handles messages from the client.
.ti -4
\fB*\fP
Registers VPN leases to UTMP and WTMP files.
.ti -4
\fB*\fP
Persistent storage of cookies, to allow a seamless server restart.
.in -4
.SH "AUTHORS"
Nikos Mavrogiannopoulos
.SH "COPYRIGHT"
Copyright (C) 2013 Nikos Mavrogiannopoulos all rights reserved.
This program is released under the terms of the GNU General Public License, version 2.
.SH "BUGS"
Please send bug reports to: nmav@gnutls.org
.SH "NOTES"
This manual page was \fIAutoGen\fP-erated from the \fBocserv\fP
option definitions.

View File

@@ -146,6 +146,7 @@ unsigned j;
READ_STRING("ca-cert", config->ca, 0);
READ_STRING("crl", config->crl, 0);
READ_STRING("cert-user-oid", config->cert_user_oid, 0);
READ_STRING("cert-group-oid", config->cert_group_oid, 0);
READ_STRING("connect-script", config->connect_script, 0);
READ_STRING("disconnect-script", config->disconnect_script, 0);
@@ -274,6 +275,7 @@ unsigned i;
DEL(config->ca);
DEL(config->crl);
DEL(config->cert_user_oid);
DEL(config->cert_group_oid);
DEL(config->priorities);
DEL(config->chroot_dir);
DEL(config->cookie_db_name);

View File

@@ -7,7 +7,8 @@
struct stored_cookie_st {
uint8_t cookie[COOKIE_SIZE];
char username[MAX_USERNAME_SIZE];
char hostname[MAX_USERNAME_SIZE];
char groupname[MAX_GROUPNAME_SIZE];
char hostname[MAX_HOSTNAME_SIZE];
uint8_t session_id[GNUTLS_MAX_SESSION_ID];
time_t expiration;
};

View File

@@ -38,6 +38,7 @@ struct __attribute__ ((__packed__)) cmd_auth_cookie_req_st {
uint8_t cookie[COOKIE_SIZE];
uint8_t tls_auth_ok;
char cert_user[MAX_USERNAME_SIZE];
char cert_group[MAX_GROUPNAME_SIZE];
};
/* AUTH_REQ */
@@ -47,6 +48,7 @@ struct __attribute__ ((__packed__)) cmd_auth_req_st {
char pass[MAX_PASSWORD_SIZE];
uint8_t tls_auth_ok;
char cert_user[MAX_USERNAME_SIZE];
char cert_group[MAX_GROUPNAME_SIZE];
char hostname[MAX_HOSTNAME_SIZE];
};

View File

@@ -112,15 +112,27 @@ struct stored_cookie_st sc;
memcpy(proc->cookie, req->cookie, sizeof(proc->cookie));
memcpy(proc->username, sc.username, sizeof(proc->username));
memcpy(proc->groupname, sc.groupname, sizeof(proc->groupname));
memcpy(proc->hostname, sc.hostname, sizeof(proc->hostname));
memcpy(proc->session_id, sc.session_id, sizeof(proc->session_id));
proc->session_id_size = sizeof(proc->session_id);
if (req->tls_auth_ok != 0) {
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);
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);
return -1;
}
}
ret = open_tun(s, lease);
if (ret < 0)
ret = -1; /* sorry */
return -1; /* sorry */
return ret;
return 0;
}
int generate_and_store_vals(main_server_st *s, struct proc_st* proc)
@@ -144,6 +156,7 @@ struct stored_cookie_st *sc;
memcpy(sc->cookie, proc->cookie, sizeof(proc->cookie));
memcpy(sc->username, proc->username, sizeof(sc->username));
memcpy(sc->groupname, proc->groupname, sizeof(sc->groupname));
memcpy(sc->hostname, proc->hostname, sizeof(sc->hostname));
memcpy(sc->session_id, proc->session_id, sizeof(sc->session_id));
@@ -165,7 +178,7 @@ unsigned username_set = 0;
#ifdef HAVE_PAM
if (req->user_pass_present != 0 && s->config->auth_types & AUTH_TYPE_PAM) {
ret = pam_auth_user(req->user, req->pass);
ret = pam_auth_user(req->user, req->pass, proc->groupname, sizeof(proc->groupname));
if (ret != 0)
ret = -1;
@@ -179,11 +192,16 @@ unsigned username_set = 0;
ret = 0;
}
if (username_set == 0)
memcpy(proc->username, req->cert_user, MAX_USERNAME_SIZE);
else {
if (username_set == 0) {
memcpy(proc->username, req->cert_user, sizeof(proc->username));
memcpy(proc->groupname, req->cert_group, sizeof(proc->groupname));
} 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);
mslog(s, proc, LOG_INFO, "user '%s' presented a certificate from user '%s'", proc->username, req->cert_user);
ret = -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;
}
}

View File

@@ -112,7 +112,6 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
{
struct iovec iov[2];
char buf[128];
int e;
uint8_t cmd;
struct msghdr hdr;
struct lease_st *lease;
@@ -123,8 +122,8 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
struct cmd_resume_fetch_req_st fresume;
struct cmd_tun_mtu_st tmtu;
} cmd_data;
int ret, cmd_data_len;
const char* peer_ip;
int ret, cmd_data_len, e;
const char* peer_ip, *group;
peer_ip = human_addr((void*)&proc->remote_addr, proc->remote_addr_len, buf, sizeof(buf));
@@ -143,7 +142,7 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
ret = recvmsg( proc->fd, &hdr, 0);
if (ret == -1) {
e = errno;
mslog(s, proc, LOG_ERR, "Cannot obtain data from command socket (pid: %d, peer: %s): %s", proc->pid, peer_ip, strerror(e));
mslog(s, proc, LOG_ERR, "cannot obtain data from command socket (pid: %d, peer: %s): %s", proc->pid, peer_ip, strerror(e));
return -1;
}
@@ -156,7 +155,7 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
switch(cmd) {
case CMD_TUN_MTU:
if (cmd_data_len != sizeof(cmd_data.tmtu)) {
mslog(s, proc, LOG_ERR, "Error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
return -2;
}
@@ -165,24 +164,24 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
case RESUME_STORE_REQ:
if (cmd_data_len <= sizeof(cmd_data.sresume)-MAX_SESSION_DATA_SIZE) {
mslog(s, proc, LOG_ERR, "Error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
return -2;
}
ret = handle_resume_store_req(s, proc, &cmd_data.sresume);
if (ret < 0) {
mslog(s, proc, LOG_DEBUG, "Could not store resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_DEBUG, "could not store resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
}
break;
case RESUME_DELETE_REQ:
if (cmd_data_len != sizeof(cmd_data.fresume)) {
mslog(s, proc, LOG_ERR, "Error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
return -2;
}
ret = handle_resume_delete_req(s, proc, &cmd_data.fresume);
if (ret < 0) {
mslog(s, proc, LOG_DEBUG, "Could not delete resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_DEBUG, "could not delete resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
}
break;
@@ -190,19 +189,19 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
struct cmd_resume_fetch_reply_st reply;
if (cmd_data_len != sizeof(cmd_data.fresume)) {
mslog(s, proc, LOG_ERR, "Error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
return -2;
}
ret = handle_resume_fetch_req(s, proc, &cmd_data.fresume, &reply);
if (ret < 0) {
mslog(s, proc, LOG_DEBUG, "Could not fetch resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_DEBUG, "could not fetch resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
ret = send_resume_fetch_reply(s, proc, REP_RESUME_FAILED, NULL);
} else
ret = send_resume_fetch_reply(s, proc, REP_RESUME_OK, &reply);
}
if (ret < 0) {
mslog(s, proc, LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
return -2;
}
@@ -214,14 +213,14 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
if (cmd == AUTH_REQ) {
if (cmd_data_len != sizeof(cmd_data.auth)) {
mslog(s, proc, LOG_ERR, "Error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
return -2;
}
ret = handle_auth_req(s, proc, &cmd_data.auth, &lease);
} else {
if (cmd_data_len != sizeof(cmd_data.cauth)) {
mslog(s, proc, LOG_ERR, "Error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "error in received message (%u) length (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
return -2;
}
@@ -232,19 +231,24 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
/* 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);
mslog(s, proc, LOG_INFO, "user '%s' tried to connect more than %u times", proc->username, s->config->max_same_clients);
}
/* do scripts and utmp */
if (ret == 0) {
ret = user_connected(s, proc, lease);
if (ret < 0) {
mslog(s, proc, LOG_INFO, "User '%s' disconnected due to script", proc->username);
mslog(s, proc, LOG_INFO, "user '%s' disconnected due to script", proc->username);
}
}
}
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);
@@ -252,14 +256,14 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
ret = -2;
goto lease_cleanup;
}
mslog(s, proc, LOG_INFO, "User '%s' authenticated", proc->username);
mslog(s, proc, LOG_INFO, "user '%s' of group '%s' authenticated", proc->username, group);
} else {
mslog(s, proc, LOG_INFO, "User '%s' re-authenticated (using cookie)", proc->username);
mslog(s, proc, LOG_INFO, "user '%s' of group '%s' re-authenticated (using cookie)", proc->username, group);
}
ret = send_auth_reply(s, proc, REP_AUTH_OK, lease);
if (ret < 0) {
mslog(s, proc, LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
ret = -2;
goto lease_cleanup;
}
@@ -268,10 +272,10 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
proc->lease->in_use = 1;
ret = 0;
} else {
mslog(s, proc, LOG_INFO, "Failed authentication attempt for user '%s'", proc->username);
mslog(s, proc, LOG_INFO, "failed authentication attempt for user '%s'", proc->username);
ret = send_auth_reply( s, proc, REP_AUTH_FAILED, NULL);
if (ret < 0) {
mslog(s, proc, LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
ret = -2;
goto lease_cleanup;
}
@@ -288,7 +292,7 @@ lease_cleanup:
break;
default:
mslog(s, proc, LOG_ERR, "Unknown CMD 0x%x (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "unknown CMD 0x%x (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
return -2;
}

View File

@@ -48,6 +48,7 @@ struct proc_st {
/* The following are set by the worker process (or by a stored cookie) */
char username[MAX_USERNAME_SIZE]; /* the owner */
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 */
};

View File

@@ -2,7 +2,7 @@
*
* DO NOT EDIT THIS FILE (ocserv-args.c)
*
* It has been AutoGen-ed February 12, 2013 at 01:34:57 PM by AutoGen 5.16
* It has been AutoGen-ed February 12, 2013 at 04:34:56 PM by AutoGen 5.16
* From the definitions ocserv-args.def
* and the template file options
*
@@ -65,7 +65,7 @@ extern FILE * option_usage_fp;
/*
* ocserv option static const strings
*/
static char const ocserv_opt_strs[1505] =
static char const ocserv_opt_strs[1501] =
/* 0 */ "ocserv\n"
"Copyright (C) 2013 Nikos Mavrogiannopoulos, all rights reserved.\n"
"This is free software. It is licensed for use, modification and\n"
@@ -80,75 +80,75 @@ static char const ocserv_opt_strs[1505] =
"details.\n\n"
"You should have received a copy of the GNU General Public License, version\n"
"2, along with this program. If not, see <http://www.gnu.org/licenses/>.\n\0"
/* 803 */ "Do not fork into background.\0"
/* 832 */ "FOREGROUND\0"
/* 843 */ "foreground\0"
/* 854 */ "Enable verbose TLS debugging information.\0"
/* 896 */ "TLS_DEBUG\0"
/* 906 */ "tls-debug\0"
/* 916 */ "Enable verbose network debugging information.\0"
/* 962 */ "DEBUG\0"
/* 968 */ "debug\0"
/* 974 */ "Configuration file for the server.\0"
/* 1009 */ "CONFIG\0"
/* 1016 */ "config\0"
/* 1023 */ "Display extended usage information and exit\0"
/* 1067 */ "help\0"
/* 1072 */ "Extended usage information passed thru pager\0"
/* 1117 */ "more-help\0"
/* 1127 */ "OCSERV\0"
/* 1134 */ "ocserv - OpenConnect server\n"
/* 803 */ "Do not fork into background\0"
/* 831 */ "FOREGROUND\0"
/* 842 */ "foreground\0"
/* 853 */ "Enable verbose TLS debugging information\0"
/* 894 */ "TLS_DEBUG\0"
/* 904 */ "tls-debug\0"
/* 914 */ "Enable verbose network debugging information\0"
/* 959 */ "DEBUG\0"
/* 965 */ "debug\0"
/* 971 */ "Configuration file for the server\0"
/* 1005 */ "CONFIG\0"
/* 1012 */ "config\0"
/* 1019 */ "Display extended usage information and exit\0"
/* 1063 */ "help\0"
/* 1068 */ "Extended usage information passed thru pager\0"
/* 1113 */ "more-help\0"
/* 1123 */ "OCSERV\0"
/* 1130 */ "ocserv - OpenConnect server\n"
"USAGE: %s [ -<flag> [<val>] | --<name>[{=| }<val>] ]...\n\0"
/* 1220 */ "nmav@gnutls.org\0"
/* 1236 */ "\n\n\0"
/* 1239 */ "\n"
/* 1216 */ "nmav@gnutls.org\0"
/* 1232 */ "\n\n\0"
/* 1235 */ "\n"
"This program is openconnect VPN server (ocserv), a server compatible with\n"
"the openconnect VPN client. It is believed to be compatible with the\n"
"protocol used by CISCO's AnyConnect SSL VPN.\n\0"
/* 1430 */ "Usage: ocserv [options] -c [config]\n"
/* 1426 */ "Usage: ocserv [options] -c [config]\n"
"ocserv --help for usage instructions.\n";
/*
* foreground option description:
*/
#define FOREGROUND_DESC (ocserv_opt_strs+803)
#define FOREGROUND_NAME (ocserv_opt_strs+832)
#define FOREGROUND_name (ocserv_opt_strs+843)
#define FOREGROUND_NAME (ocserv_opt_strs+831)
#define FOREGROUND_name (ocserv_opt_strs+842)
#define FOREGROUND_FLAGS (OPTST_DISABLED)
/*
* tls-debug option description:
*/
#define TLS_DEBUG_DESC (ocserv_opt_strs+854)
#define TLS_DEBUG_NAME (ocserv_opt_strs+896)
#define TLS_DEBUG_name (ocserv_opt_strs+906)
#define TLS_DEBUG_DESC (ocserv_opt_strs+853)
#define TLS_DEBUG_NAME (ocserv_opt_strs+894)
#define TLS_DEBUG_name (ocserv_opt_strs+904)
#define TLS_DEBUG_FLAGS (OPTST_DISABLED)
/*
* debug option description:
*/
#define DEBUG_DESC (ocserv_opt_strs+916)
#define DEBUG_NAME (ocserv_opt_strs+962)
#define DEBUG_name (ocserv_opt_strs+968)
#define DEBUG_DESC (ocserv_opt_strs+914)
#define DEBUG_NAME (ocserv_opt_strs+959)
#define DEBUG_name (ocserv_opt_strs+965)
#define DEBUG_FLAGS (OPTST_DISABLED)
/*
* config option description:
*/
#define CONFIG_DESC (ocserv_opt_strs+974)
#define CONFIG_NAME (ocserv_opt_strs+1009)
#define CONFIG_name (ocserv_opt_strs+1016)
#define CONFIG_DESC (ocserv_opt_strs+971)
#define CONFIG_NAME (ocserv_opt_strs+1005)
#define CONFIG_name (ocserv_opt_strs+1012)
#define CONFIG_FLAGS (OPTST_DISABLED \
| OPTST_SET_ARGTYPE(OPARG_TYPE_FILE))
/*
* Help/More_Help option descriptions:
*/
#define HELP_DESC (ocserv_opt_strs+1023)
#define HELP_name (ocserv_opt_strs+1067)
#define HELP_DESC (ocserv_opt_strs+1019)
#define HELP_name (ocserv_opt_strs+1063)
#ifdef HAVE_WORKING_FORK
#define MORE_HELP_DESC (ocserv_opt_strs+1072)
#define MORE_HELP_name (ocserv_opt_strs+1117)
#define MORE_HELP_DESC (ocserv_opt_strs+1068)
#define MORE_HELP_name (ocserv_opt_strs+1113)
#define MORE_HELP_FLAGS (OPTST_IMM | OPTST_NO_INIT)
#else
#define MORE_HELP_DESC NULL
@@ -251,13 +251,13 @@ static tOptDesc optDesc[OPTION_CT] = {
*
* Define the ocserv Option Environment
*/
#define zPROGNAME (ocserv_opt_strs+1127)
#define zUsageTitle (ocserv_opt_strs+1134)
#define zPROGNAME (ocserv_opt_strs+1123)
#define zUsageTitle (ocserv_opt_strs+1130)
#define zRcName NULL
#define apzHomeList NULL
#define zBugsAddr (ocserv_opt_strs+1220)
#define zExplain (ocserv_opt_strs+1236)
#define zDetail (ocserv_opt_strs+1239)
#define zBugsAddr (ocserv_opt_strs+1216)
#define zExplain (ocserv_opt_strs+1232)
#define zDetail (ocserv_opt_strs+1235)
#define zFullVersion (NULL)
/* extracted from optcode.tlib near line 350 */
@@ -272,7 +272,7 @@ static tOptDesc optDesc[OPTION_CT] = {
#define ocserv_full_usage (NULL)
#define ocserv_short_usage (ocserv_opt_strs+1430)
#define ocserv_short_usage (ocserv_opt_strs+1426)
#endif /* not defined __doxygen__ */

View File

@@ -10,11 +10,11 @@ long-opts;
no-misuse-usage;
short-usage = "Usage: ocserv [options] -c [config]\nocserv --help for usage instructions.\n";
explain = "";
detail = "This program is openconnect VPN server (ocserv), a server compatible with the
openconnect VPN client. It is believed to be compatible with the protocol
used by CISCO's AnyConnect SSL VPN.
";
detail = "This program is openconnect VPN server (ocserv), a server compatible with the
openconnect VPN client. It is believed to be compatible with the protocol
used by CISCO's AnyConnect SSL VPN.";
copyright = {
@@ -28,20 +28,20 @@ copyright = {
flag = {
name = foreground;
value = f;
descrip = "Do not fork into background.";
descrip = "Do not fork into background";
doc = "";
};
flag = {
name = tls-debug;
descrip = "Enable verbose TLS debugging information.";
descrip = "Enable verbose TLS debugging information";
doc = "";
};
flag = {
name = debug;
value = d;
descrip = "Enable verbose network debugging information.";
descrip = "Enable verbose network debugging information";
doc = "";
};
@@ -50,34 +50,19 @@ flag = {
value = c;
arg-type = file;
file-exists = yes;
descrip = "Configuration file for the server.";
descrip = "Configuration file for the server";
doc = "";
};
help-value = h;
doc-section = {
ds-type = 'FEATURES';
ds-format = 'texi';
ds-text = <<-_EOT_
@subheading Features of the server
@itemize
@item Supports both TCP and UDP VPN tunnels using TLS and DTLS.
@item Authentication using PAM (username-password) or certificates
@item Privilege separation between the main process which performs TUN allocation and authentication, with the worker processes which handles messages from the client.
@item Registers VPN leases to UTMP and WTMP files.
@item Persistent storage of cookies, to allow a seamless server restart.
@end itemize
_EOT_;
};
doc-section = {
ds-type = 'FILES';
ds-format = 'texi';
ds-text = <<-_EOT_
@subheading ocserv's configuration file format
By default, if no other file is specified, ocserv looks for its configuration file at @file{/etc/ocserv.conf}.
An example configuration file follows.
@example
@@ -125,6 +110,12 @@ server-key = /path/to/key.pem
# CN = 2.5.4.3, UID = 0.9.2342.19200300.100.1.1
#cert-user-oid = 0.9.2342.19200300.100.1.1
# The object identifier that will be used to read the user group in the client
# certificate. The object identifier should be part of the certificate's DN
# Useful OIDs are:
# OU (organizational unit) = 2.5.4.11
#cert-group-oid = 2.5.4.11
# A revocation list of ca-cert is set
#crl = /path/to/crl.pem
@@ -149,7 +140,7 @@ cookie-validity = 14400
#cookie-db = /var/tmp/cookies.db
# Script to call when a client connects and obtains an IP
# Parameters: username hostname device IP-REAL IP-LOCAL IP-REMOTE
# Parameters: username groupname hostname device IP-REAL IP-LOCAL IP-REMOTE
# hostname is the hostname selected by the client
# IP-REAL is the remote IP of the client,
# IP-LOCAL is the local IP in the P-t-P connection and IP-REMOTE
@@ -201,7 +192,6 @@ ocserv will take advantage of it to password authenticate its users.
It can be used in conjunction with certificate authentication.
@subheading Certificate authentication
In order to support certificate authentication you will need in addition to
the server certificate and key for TLS, a certificate authority (CA) to sign
certificates for the clients. That authority should also provide a CRL to
@@ -262,3 +252,30 @@ the HUP signal.
_EOT_;
};
doc-section = {
ds-type = 'IMPLEMENTATION NOTES';
ds-format = 'texi';
ds-text = <<-_EOT_
Note that while this server utilizes privilege separation for password
authentication, this does not occur for TLS and client certificate authentication.
This was done to take advantage of multi-core systems by distributing the
expensive TLS calculations to the workers.
_EOT_;
};
doc-section = {
ds-type = 'COMPATIBILITY';
ds-format = 'texi';
ds-text = <<-_EOT_
@subheading Features of the server
@itemize
@item Supports both TCP and UDP VPN tunnels using TLS and DTLS.
@item Support for the server key being stored in TPM, a hardware security module (HSM), or smart card.
@item Authentication using PAM (username-password) or certificates
@item Privilege separation between the main process which performs TUN allocation and authentication, with the worker processes which handles messages from the client.
@item Registers VPN leases to UTMP and WTMP files.
@item Persistent storage of cookies, to allow a seamless server restart.
@end itemize
_EOT_;
};

View File

@@ -2,7 +2,7 @@
*
* DO NOT EDIT THIS FILE (ocserv-args.h)
*
* It has been AutoGen-ed February 12, 2013 at 01:34:57 PM by AutoGen 5.16
* It has been AutoGen-ed February 12, 2013 at 04:34:56 PM by AutoGen 5.16
* From the definitions ocserv-args.def
* and the template file options
*

View File

@@ -6,6 +6,9 @@
#ifdef HAVE_PAM
#include <security/pam_appl.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#define APP_NAME PACKAGE
@@ -44,12 +47,13 @@ struct pam_response *replies;
/* Returns 0 if the user is successfully authenticated
*/
int pam_auth_user(const char* user, const char* pass)
int pam_auth_user(const char* user, const char* pass, char *groupname, int groupname_size)
{
pam_handle_t * ph;
int ret, pret;
struct local_st local;
const struct pam_conv dc = { dummy_conv, &local };
struct passwd * pwd;
local.username = user;
local.password = pass;
@@ -66,6 +70,21 @@ const struct pam_conv dc = { dummy_conv, &local };
ret = -1;
goto fail;
}
pret = pam_acct_mgmt(ph, PAM_SILENT|PAM_DISALLOW_NULL_AUTHTOK);
if (pret != PAM_SUCCESS) {
syslog(LOG_AUTH, "Error in PAM account management: %s", pam_strerror(ph, pret));
ret = -1;
goto fail;
}
groupname[0] = 0;
pwd = getpwnam(user);
if (pwd != NULL) {
struct group* grp = getgrgid(pwd->pw_gid);
if (grp != NULL)
snprintf(groupname, groupname_size, "%s", grp->gr_name);
}
ret = 0;
fail:

View File

@@ -1,6 +1,6 @@
#ifndef PAM_H
#define PAM_H
int pam_auth_user(const char* user, const char* pass);
int pam_auth_user(const char* user, const char* pass, char *groupname, int groupname_size);
#endif

View File

@@ -60,6 +60,7 @@ struct cfg_st {
char *ca;
char *crl;
char *cert_user_oid; /* The OID that will be used to extract the username */
char *cert_group_oid; /* The OID that will be used to extract the groupname */
unsigned int auth_types; /* or'ed sequence of AUTH_TYPE */
gnutls_certificate_request_t cert_req;
char *priorities;
@@ -98,6 +99,7 @@ struct main_server_st;
#define MAX_PASSWORD_SIZE 64
#define TLS_MASTER_SIZE 48
#define MAX_HOSTNAME_SIZE MAX_USERNAME_SIZE
#define MAX_GROUPNAME_SIZE MAX_USERNAME_SIZE
#define COOKIE_SIZE 32
#define MAX_SESSION_DATA_SIZE (4*1024)

View File

@@ -95,8 +95,9 @@ int ret;
}
static
int get_cert_username(worker_st *ws, const gnutls_datum_t* raw,
char* username, size_t username_size)
int get_cert_names(worker_st *ws, const gnutls_datum_t* raw,
char* username, size_t username_size,
char* groupname, size_t groupname_size)
{
gnutls_x509_crt_t crt;
int ret;
@@ -120,9 +121,20 @@ int ret;
ret = gnutls_x509_crt_get_dn (crt, username, &username_size);
}
if (ret < 0) {
oclog(ws, LOG_ERR, "certificate error in DN: %s", gnutls_strerror(ret));
oclog(ws, LOG_ERR, "cannot obtain user from certificate DN: %s", gnutls_strerror(ret));
goto fail;
}
if (ws->config->cert_group_oid) {
ret = gnutls_x509_crt_get_dn_by_oid (crt, ws->config->cert_group_oid,
0, 0, groupname, &groupname_size);
if (ret < 0) {
oclog(ws, LOG_ERR, "cannot obtain group from certificate DN: %s", gnutls_strerror(ret));
goto fail;
}
} else {
groupname[0] = 0;
}
ret = 0;
@@ -246,7 +258,8 @@ static int recv_auth_reply(worker_st *ws)
/* grabs the username from the session certificate */
static
int get_cert_info(worker_st *ws, char* user, unsigned user_size)
int get_cert_info(worker_st *ws, char* user, unsigned user_size,
char* group, unsigned group_size)
{
const gnutls_datum_t * cert;
unsigned int ncerts;
@@ -260,7 +273,7 @@ int ret;
return -1;
}
ret = get_cert_username(ws, cert, user, user_size);
ret = get_cert_names(ws, cert, user, user_size, group, group_size);
if (ret < 0) {
oclog(ws, LOG_ERR, "Cannot get username (%s) from certificate", ws->config->cert_user_oid);
return -1;
@@ -278,7 +291,8 @@ static int auth_user(worker_st *ws, struct cmd_auth_req_st* areq)
int ret;
if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) {
ret = get_cert_info(ws, areq->cert_user, sizeof(areq->cert_user));
ret = get_cert_info(ws, areq->cert_user, sizeof(areq->cert_user),
areq->cert_group, sizeof(areq->cert_group));
if (ret < 0)
return -1;
@@ -308,7 +322,8 @@ struct cmd_auth_cookie_req_st areq;
return -1;
if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) {
ret = get_cert_info(ws, areq.cert_user, sizeof(areq.cert_user));
ret = get_cert_info(ws, areq.cert_user, sizeof(areq.cert_user),
areq.cert_group, sizeof(areq.cert_group));
if (ret < 0)
return -1;
@@ -321,7 +336,7 @@ struct cmd_auth_cookie_req_st areq;
ret = send_auth_cookie_req(ws->cmd_fd, &areq);
if (ret < 0)
return ret;
return recv_auth_reply(ws);
}