From ef188512378863d70dde9b0f5f61c8f158995313 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Fri, 1 Mar 2013 21:52:57 +0100 Subject: [PATCH] Added option to allow sending a cookie without the corresponding certificate. This option is required for the cisco clients, that do not always use the client certificate. When this option is set to false it means that the cookie itself is sufficient for authentication. This is bad practice of smart cards are in use. --- doc/sample.config | 8 +++++++- src/config.c | 22 +++++++++++++++++++++- src/ocserv-args.c | 2 +- src/ocserv-args.def | 6 ++++++ src/ocserv-args.h | 2 +- src/tlslib.c | 18 +++++++++++++----- src/vpn.h | 1 + src/worker-auth.c | 12 +++++++++++- src/worker.h | 1 + 9 files changed, 62 insertions(+), 10 deletions(-) diff --git a/doc/sample.config b/doc/sample.config index 9ae0124a..01e546db 100644 --- a/doc/sample.config +++ b/doc/sample.config @@ -10,7 +10,13 @@ auth = "pam" # Client config xml. The variable $GROUP will be replaced by # the user's group name. This file must be accessible from inside # the worker's chroot. It is not used by the openconnect client. -user-profile = ../doc/profile.xml +#user-profile = /profile.xml + +# Unless set to false it is required for clients to present their +# certificate even if they are authenticating via a previously granted +# cookie. Legacy CISCO clients do not do that, and thus this option +# should be set for them. +#always-require-cert = false # Use listen-host to limit to specific IPs or to the IPs of a provided hostname. #listen-host = [IP|HOSTNAME] diff --git a/src/config.c b/src/config.c index 3e044e3e..d39692a7 100644 --- a/src/config.c +++ b/src/config.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -124,7 +125,7 @@ unsigned j; #endif } else if (strcasecmp(auth[j], "certificate") == 0) { config->auth_types |= AUTH_TYPE_CERTIFICATE; - config->cert_req = GNUTLS_CERT_REQUIRE; + config->cert_req = GNUTLS_CERT_REQUEST; } else { fprintf(stderr, "Unknown auth method: %s\n", auth[j]); exit(1); @@ -161,6 +162,7 @@ unsigned j; READ_STRING("pid-file", pid_file, 0); READ_STRING("banner", config->banner, 0); + READ_TF("always-require-cert", config->force_cert_auth, 0, 1); READ_TF("use-utmp", config->use_utmp, 0, 1); READ_TF("try-mtu-discovery", config->try_mtu, 0, 0); @@ -237,9 +239,27 @@ static void check_cfg( struct cfg_st *config) if (config->cert) { config->cert_hash = calc_sha1_hash(config->cert, 1); } + + if (config->force_cert_auth) + config->cert_req = GNUTLS_CERT_REQUIRE; if (config->xml_config_file) { config->xml_config_hash = calc_sha1_hash(config->xml_config_file, 0); + if (config->xml_config_hash == NULL && config->chroot_dir != NULL) { + char path[_POSIX_PATH_MAX]; + + snprintf(path, sizeof(path), "%s/%s", config->chroot_dir, config->xml_config_file); + config->xml_config_hash = calc_sha1_hash(path, 0); + + if (config->xml_config_hash == NULL) { + fprintf(stderr, "Cannot open file '%s'\n", path); + exit(1); + } + } + if (config->xml_config_hash == NULL) { + fprintf(stderr, "Cannot open file '%s'\n", config->xml_config_file); + exit(1); + } } if (config->keepalive == 0) diff --git a/src/ocserv-args.c b/src/ocserv-args.c index e6e6acd5..a9d60517 100644 --- a/src/ocserv-args.c +++ b/src/ocserv-args.c @@ -2,7 +2,7 @@ * * DO NOT EDIT THIS FILE (ocserv-args.c) * - * It has been AutoGen-ed March 1, 2013 at 07:46:59 PM by AutoGen 5.16 + * It has been AutoGen-ed March 1, 2013 at 09:44:33 PM by AutoGen 5.16 * From the definitions ocserv-args.def * and the template file options * diff --git a/src/ocserv-args.def b/src/ocserv-args.def index a89da03b..68e709f1 100644 --- a/src/ocserv-args.def +++ b/src/ocserv-args.def @@ -87,6 +87,12 @@ auth = "pam" # It is not used by the openconnect client. #user-profile = /path/to/file.xml +# Unless set to false it is required for clients to present their +# certificate even if they are authenticating via a previously granted +# cookie. Legacy CISCO clients do not do that, and thus this option +# should be set for them. +#always-require-cert = false + # Use listen-host to limit to specific IPs or to the IPs of a provided hostname. #listen-host = [IP|HOSTNAME] diff --git a/src/ocserv-args.h b/src/ocserv-args.h index b02af133..1b744c47 100644 --- a/src/ocserv-args.h +++ b/src/ocserv-args.h @@ -2,7 +2,7 @@ * * DO NOT EDIT THIS FILE (ocserv-args.h) * - * It has been AutoGen-ed March 1, 2013 at 07:46:59 PM by AutoGen 5.16 + * It has been AutoGen-ed March 1, 2013 at 09:44:33 PM by AutoGen 5.16 * From the definitions ocserv-args.def * and the template file options * diff --git a/src/tlslib.c b/src/tlslib.c index 899ccade..48f4f6af 100644 --- a/src/tlslib.c +++ b/src/tlslib.c @@ -196,13 +196,15 @@ static int verify_certificate_cb(gnutls_session_t session) if (session == ws->dtls_session) /* no certificate is verified in DTLS */ return 0; + ws->cert_auth_ok = 0; + /* This verification function uses the trusted CAs in the credentials * structure. So you must have installed one or more CA certificates. */ ret = gnutls_certificate_verify_peers2(session, &status); if (ret < 0) { oclog(ws, LOG_ERR, "error verifying client certificate"); - return GNUTLS_E_CERTIFICATE_ERROR; + goto fail; } if (status != 0) { @@ -214,7 +216,7 @@ static int verify_certificate_cb(gnutls_session_t session) gnutls_certificate_verification_status_print(status, type, &out, 0); if (ret < 0) - return GNUTLS_E_CERTIFICATE_ERROR; + goto fail; oclog(ws, LOG_INFO, "client certificate verification failed: %s", out.data); @@ -223,13 +225,20 @@ static int verify_certificate_cb(gnutls_session_t session) oclog(ws, LOG_INFO, "client certificate verification failed."); #endif - return GNUTLS_E_CERTIFICATE_ERROR; + goto fail; } else { + ws->cert_auth_ok = 1; oclog(ws, LOG_INFO, "client certificate verification succeeded"); } /* notify gnutls to continue handshake normally */ return 0; +fail: + if (ws->config->force_cert_auth != 0) + return GNUTLS_E_CERTIFICATE_ERROR; + else + return 0; + } int pin_callback (void *user, int attempt, const char *token_url, @@ -479,8 +488,7 @@ gnutls_x509_crt_t crt; ret = gnutls_load_file(file, &data); if (ret < 0) { - fprintf(stderr, "Cannot open file '%s': %s\n", file, gnutls_strerror(ret)); - exit(1); + return NULL; } if (cert != 0) { diff --git a/src/vpn.h b/src/vpn.h index 4fd17a64..da0cde23 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -83,6 +83,7 @@ struct cfg_st { unsigned max_same_clients; unsigned use_utmp; unsigned try_mtu; /* MTU discovery enabled */ + unsigned force_cert_auth; /* always require client certificate */ /* if gdbm is there */ char* cookie_db_name; diff --git a/src/worker-auth.c b/src/worker-auth.c index ea18add0..e343d30d 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -292,6 +292,11 @@ static int auth_user(worker_st *ws, struct cmd_auth_req_st* areq) int ret; if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) { + if (ws->cert_auth_ok == 0) { + oclog(ws, LOG_INFO, "no certificate provided for authentication"); + return -1; + } + ret = get_cert_info(ws, areq->cert_user, sizeof(areq->cert_user), areq->cert_group, sizeof(areq->cert_group)); if (ret < 0) @@ -322,7 +327,12 @@ struct cmd_auth_cookie_req_st areq; if (cookie_size != sizeof(areq.cookie)) return -1; - if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) { + if ((ws->config->auth_types & AUTH_TYPE_CERTIFICATE) && ws->config->force_cert_auth != 0) { + if (ws->cert_auth_ok == 0) { + oclog(ws, LOG_INFO, "no certificate provided for cookie authentication"); + return -1; + } + ret = get_cert_info(ws, areq.cert_user, sizeof(areq.cert_user), areq.cert_group, sizeof(areq.cert_group)); if (ret < 0) diff --git a/src/worker.h b/src/worker.h index f4b6de89..f849b72a 100644 --- a/src/worker.h +++ b/src/worker.h @@ -87,6 +87,7 @@ typedef struct worker_st { uint8_t master_secret[TLS_MASTER_SIZE]; uint8_t session_id[GNUTLS_MAX_SESSION_ID]; unsigned auth_ok; + unsigned cert_auth_ok; int tun_fd; struct http_req_st req;