Skip to content

Commit

Permalink
Add smartcard support with openssl-engine (#464)
Browse files Browse the repository at this point in the history
  • Loading branch information
rmuehl authored and mrbaseman committed Sep 19, 2019
1 parent 1c166d6 commit 320f95f
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 20 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,32 @@ Examples
```


---------
Smartcard
---------

Smartcard support needs `openssl pkcs engine` and `opensc` to be installed.

To make use of your smartcard put at least `pkcs11:` to the user-cert config or commandline.
Takes full or partially PKCS#11 token URI. Also username and password must not be empty, but
doesn't get used. So you can type in anything.

```
user-cert = pkcs11:
user-cert = pkcs11:token=someuser
user-cert = pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=012345678;token=someuser
username = none
password = none
```

In most cases `user-cert = pkcs11:` will do it, but if needed you can get the token-URI
with `p11tool --list-token-urls`.

Multiple readers are not supported.

Tested with Yubikey, but other PIV enabled smartcards may work too.


----------
Installing
----------
Expand Down
13 changes: 13 additions & 0 deletions doc/openfortivpn.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ openfortivpn \- Client for PPP+SSL VPN tunnel services
[\fB\-\-half\-internet\-routes=<bool>\fR]
[\fB\-\-ca\-file=\fI<file>\fR]
[\fB\-\-user\-cert=\fI<file>\fR]
[\fB\-\-user-cert=\fIpkcs11:\fR]
[\fB\-\-user\-key=\fI<file>\fR]
[\fB\-\-use\-syslog\fR]
[\fB\-\-trusted\-cert=\fI<digest>\fR]
Expand Down Expand Up @@ -107,6 +108,18 @@ verify the gateway certificate.
Use specified PEM-encoded certificate if the server requires authentication
with a certificate.
.TP
\fB\-\-user-cert=\fIpkcs11:\fR
Use at least the string pkcs11: for using a smartcard. Takes also
full or partially PKCS11-URI (p11tool --list-token-urls)

--user-cert = pkcs11:

--user-cert = pkcs11:token=someuser

--user-cert = pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=012345678;token=someuser

\fBRequires openssl pkcs engine!
.TP
\fB\-\-user\-key=\fI<file>\fR
Use specified PEM-encoded key if the server requires authentication with
a certificate.
Expand Down
7 changes: 6 additions & 1 deletion src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ const struct vpn_config invalid_cfg = {
.cipher_list = NULL,
.min_tls = -1,
.seclevel_1 = -1,
.cert_whitelist = NULL
.cert_whitelist = NULL,
.use_engine = -1
};

/*
Expand Down Expand Up @@ -350,6 +351,8 @@ int load_config(struct vpn_config *cfg, const char *filename)
} else if (strcmp(key, "user-cert") == 0) {
free(cfg->user_cert);
cfg->user_cert = strdup(val);
if (strncmp(strdup(val), "pkcs11:", 7) == 0)
cfg->use_engine = 1;
} else if (strcmp(key, "user-key") == 0) {
free(cfg->user_key);
cfg->user_key = strdup(val);
Expand Down Expand Up @@ -491,6 +494,8 @@ void merge_config(struct vpn_config *dst, struct vpn_config *src)
}
if (src->user_cert) {
free(dst->user_cert);
if (strncmp(src->user_cert, "pkcs11:", 7) == 0)
dst->use_engine = 1;
dst->user_cert = src->user_cert;
}
if (src->user_key) {
Expand Down
1 change: 1 addition & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ struct vpn_config {
int seclevel_1;
char *cipher_list;
struct x509_digest *cert_whitelist;
int use_engine;
};

int add_trusted_cert(struct vpn_config *cfg, const char *digest);
Expand Down
17 changes: 12 additions & 5 deletions src/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -570,12 +570,19 @@ int auth_log_in(struct tunnel *tunnel)

tunnel->cookie[0] = '\0';

snprintf(data, sizeof(data), "username=%s&credential=%s&realm=%s&ajax=1"
"&redir=%%2Fremote%%2Findex&just_logged_in=1",
username, password, realm);
if (tunnel->config->use_engine) {
snprintf(data, sizeof(data), "cert=&nup=1");
ret = http_request(tunnel, "GET", "/remote/login",
data, &res, &response_size);
} else {
snprintf(data, sizeof(data), "username=%s&credential=%s&realm=%s&ajax=1"
"&redir=%%2Fremote%%2Findex&just_logged_in=1",
username, password, realm);

ret = http_request(tunnel, "POST", "/remote/logincheck",
data, &res, &response_size);
}

ret = http_request(
tunnel, "POST", "/remote/logincheck", data, &res, &response_size);
if (ret != 1)
goto end;

Expand Down
1 change: 1 addition & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ PPPD_USAGE \
" certificate.\n" \
" --user-cert=<file> Use specified PEM-encoded certificate if the server\n" \
" requires authentication with a certificate.\n" \
" --user-cert=pkcs11: Use smartcard. Takes also partial or full PKCS11-URI.\n" \
" --user-key=<file> Use specified PEM-encoded key if the server requires\n" \
" authentication with a certificate.\n" \
" --use-syslog Log to syslog instead of terminal.\n" \
Expand Down
91 changes: 77 additions & 14 deletions src/tunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <sys/ioctl.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
#include <openssl/engine.h>
#if HAVE_PTY_H
#include <pty.h>
#elif HAVE_UTIL_H
Expand Down Expand Up @@ -754,32 +755,94 @@ int ssl_connect(struct tunnel *tunnel)
}
}

if (tunnel->config->user_cert) {
if (!SSL_CTX_use_certificate_file(
tunnel->ssl_context, tunnel->config->user_cert,
SSL_FILETYPE_PEM)) {
log_error("SSL_CTX_use_certificate_file: %s\n",
/* Use engine for PIV if user-cert config starts with pkcs11 URI: */
if (tunnel->config->use_engine > 0) {

ENGINE *e;
ENGINE_load_builtin_engines();
e = ENGINE_by_id("pkcs11");
if (!e) {
log_error("Could not load pkcs11 Engine: %s\n",
ERR_error_string(ERR_peek_last_error(), NULL));
return 1;
}
}
if (!ENGINE_init(e)) {
log_error("Could not init pkcs11 Engine: %s\n",
ERR_error_string(ERR_peek_last_error(), NULL));
ENGINE_free(e);
return 1;
}
if (!ENGINE_set_default_RSA(e))
abort();

ENGINE_finish(e);
ENGINE_free(e);

if (tunnel->config->user_key) {
if (!SSL_CTX_use_PrivateKey_file(
tunnel->ssl_context, tunnel->config->user_key,
SSL_FILETYPE_PEM)) {
log_error("SSL_CTX_use_PrivateKey_file: %s\n",
struct token parms;
parms.uri = tunnel->config->user_cert;
parms.cert = NULL;

if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 1)) {
log_error("PKCS11 ENGINE_ctrl_cmd: %s\n",
ERR_error_string(ERR_peek_last_error(), NULL));
return 1;
}

if (!SSL_CTX_use_certificate(tunnel->ssl_context, parms.cert)) {
log_error("PKCS11 SSL_CTX_use_certificate: %s\n",
ERR_error_string(ERR_peek_last_error(), NULL));
return 1;
}

EVP_PKEY *privkey = ENGINE_load_private_key(
e, parms.uri, UI_OpenSSL(), NULL);
if (!privkey) {
log_error("PKCS11 ENGINE_load_private_key: %s\n",
ERR_error_string(ERR_peek_last_error(), NULL));
return 1;
}

if (!SSL_CTX_use_PrivateKey(tunnel->ssl_context, privkey)) {
log_error("PKCS11 SSL_CTX_use_PrivateKey_file: %s\n",
ERR_error_string(ERR_peek_last_error(), NULL));
return 1;
}
}

if (tunnel->config->user_cert && tunnel->config->user_key) {
if (!SSL_CTX_check_private_key(tunnel->ssl_context)) {
log_error("SSL_CTX_check_private_key: %s\n",
log_error("PKCS11 SSL_CTX_check_private_key: %s\n",
ERR_error_string(ERR_peek_last_error(), NULL));
return 1;
}

} else { /* end PKCS11-engine */

if (tunnel->config->user_cert) {
if (!SSL_CTX_use_certificate_file(
tunnel->ssl_context, tunnel->config->user_cert,
SSL_FILETYPE_PEM)) {
log_error("SSL_CTX_use_certificate_file: %s\n",
ERR_error_string(ERR_peek_last_error(), NULL));
return 1;
}
}

if (tunnel->config->user_key) {
if (!SSL_CTX_use_PrivateKey_file(
tunnel->ssl_context, tunnel->config->user_key,
SSL_FILETYPE_PEM)) {
log_error("SSL_CTX_use_PrivateKey_file: %s\n",
ERR_error_string(ERR_peek_last_error(), NULL));
return 1;
}
}

if (tunnel->config->user_cert && tunnel->config->user_key) {
if (!SSL_CTX_check_private_key(tunnel->ssl_context)) {
log_error("SSL_CTX_check_private_key: %s\n",
ERR_error_string(ERR_peek_last_error(), NULL));
return 1;
}
}
}

if (!tunnel->config->insecure_ssl) {
Expand Down
6 changes: 6 additions & 0 deletions src/tunnel.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#endif

#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <sys/types.h>

#ifdef __clang__
Expand Down Expand Up @@ -78,6 +79,11 @@ struct tunnel {
int (*on_ppp_if_down)(struct tunnel *);
};

struct token {
const char *uri;
X509 *cert;
};

int ppp_interface_is_up(struct tunnel *tunnel);

int ssl_connect(struct tunnel *tunnel);
Expand Down

0 comments on commit 320f95f

Please sign in to comment.