aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--buffer.c9
-rw-r--r--buffer.h2
-rw-r--r--openvpn.812
-rw-r--r--options.c4
-rw-r--r--push.c2
-rw-r--r--ssl.c127
-rw-r--r--ssl.h1
7 files changed, 96 insertions, 61 deletions
diff --git a/buffer.c b/buffer.c
index 2952767..5499d99 100644
--- a/buffer.c
+++ b/buffer.c
@@ -218,20 +218,21 @@ buf_printf (struct buffer *buf, const char *format, ...)
/*
* This is necessary due to certain buggy implementations of snprintf,
* that don't guarantee null termination for size > 0.
+ * Return false on overflow.
*/
-int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+bool openvpn_snprintf(char *str, size_t size, const char *format, ...)
{
va_list arglist;
- int ret = 0;
+ int len = -1;
if (size > 0)
{
va_start (arglist, format);
- ret = vsnprintf (str, size, format, arglist);
+ len = vsnprintf (str, size, format, arglist);
va_end (arglist);
str[size - 1] = 0;
}
- return ret;
+ return (len >= 0 && len < size);
}
/*
diff --git a/buffer.h b/buffer.h
index 0f22cda..58273e2 100644
--- a/buffer.h
+++ b/buffer.h
@@ -280,7 +280,7 @@ bool buf_printf (struct buffer *buf, const char *format, ...)
/*
* Like snprintf but guarantees null termination for size > 0
*/
-int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+bool openvpn_snprintf(char *str, size_t size, const char *format, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 3, 4)))
#endif
diff --git a/openvpn.8 b/openvpn.8
index 037ba7e..1953b16 100644
--- a/openvpn.8
+++ b/openvpn.8
@@ -4457,7 +4457,7 @@ or
.B --tls-verify.
.\"*********************************************************
.TP
-.B --crl-verify crl
+.B --crl-verify crl ['dir']
Check peer certificate against the file
.B crl
in PEM format.
@@ -4473,6 +4473,16 @@ overall integrity of the PKI.
The only time when it would be necessary to rebuild the entire PKI from scratch would be
if the root certificate key itself was compromised.
+
+If the optional
+.B dir
+flag is specified, enable a different mode where
+.B crl
+is a directory containing files named as revoked serial numbers
+(the files may be empty, the contents are never read). If a client
+requests a connection, where the client certificate serial number
+(decimal string) is the name of a file present in the directory,
+it will be rejected.
.\"*********************************************************
.SS SSL Library information:
.\"*********************************************************
diff --git a/options.c b/options.c
index 32ea07f..fd3fec6 100644
--- a/options.c
+++ b/options.c
@@ -534,7 +534,7 @@ static const char usage_message[] =
" see --secret option for more info.\n"
"--askpass [file]: Get PEM password from controlling tty before we daemonize.\n"
"--auth-nocache : Don't cache --askpass or --auth-user-pass passwords.\n"
- "--crl-verify crl: Check peer certificate against a CRL.\n"
+ "--crl-verify crl ['dir']: Check peer certificate against a CRL.\n"
"--tls-verify cmd: Execute shell command cmd to verify the X509 name of a\n"
" pending TLS connection that has otherwise passed all other\n"
" tests of certification. cmd should return 0 to allow\n"
@@ -5836,6 +5836,8 @@ add_option (struct options *options,
else if (streq (p[0], "crl-verify") && p[1])
{
VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (p[2] && streq(p[2], "dir"))
+ options->ssl_flags |= SSLF_CRL_VERIFY_DIR;
options->crl_file = p[1];
}
else if (streq (p[0], "tls-verify") && p[1])
diff --git a/push.c b/push.c
index 339478a..5b870ce 100644
--- a/push.c
+++ b/push.c
@@ -42,7 +42,7 @@
void
receive_auth_failed (struct context *c, const struct buffer *buffer)
{
- msg (M_VERB0, "AUTH: Received AUTH_FAILED control message");
+ msg (M_VERB0, "AUTH: Received control message: %s", BSTR(buffer));
connection_list_set_no_advance(&c->options);
if (c->options.pull)
{
diff --git a/ssl.c b/ssl.c
index 572d8e2..095b6a6 100644
--- a/ssl.c
+++ b/ssl.c
@@ -836,6 +836,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
const struct tls_options *opt;
const int max_depth = MAX_CERT_DEPTH;
struct argv argv = argv_new ();
+ char *serial = NULL;
/* get the tls_session pointer */
ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
@@ -929,14 +930,12 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
{
ASN1_INTEGER *asn1_i;
BIGNUM *bignum;
- char *dec;
asn1_i = X509_get_serialNumber(ctx->current_cert);
bignum = ASN1_INTEGER_to_BN(asn1_i, NULL);
- dec = BN_bn2dec(bignum);
+ serial = BN_bn2dec(bignum);
openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", ctx->error_depth);
- setenv_str (opt->es, envname, dec);
+ setenv_str (opt->es, envname, serial);
BN_free(bignum);
- OPENSSL_free(dec);
}
/* export current untrusted IP */
@@ -1060,67 +1059,89 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
/* check peer cert against CRL */
if (opt->crl_file)
{
- X509_CRL *crl=NULL;
- X509_REVOKED *revoked;
- BIO *in=NULL;
- int n,i,retval = 0;
+ if (opt->ssl_flags & SSLF_CRL_VERIFY_DIR)
+ {
+ char fn[256];
+ int fd;
+ if (!openvpn_snprintf(fn, sizeof(fn), "%s%c%s", opt->crl_file, OS_SPECIFIC_DIRSEP, serial))
+ {
+ msg (D_HANDSHAKE, "VERIFY CRL: filename overflow");
+ goto err;
+ }
+ fd = open (fn, O_RDONLY);
+ if (fd >= 0)
+ {
+ msg (D_HANDSHAKE, "VERIFY CRL: certificate serial number %s is revoked", serial);
+ close(fd);
+ goto err;
+ }
+ }
+ else
+ {
+ X509_CRL *crl=NULL;
+ X509_REVOKED *revoked;
+ BIO *in=NULL;
+ int n,i,retval = 0;
- in=BIO_new(BIO_s_file());
+ in=BIO_new(BIO_s_file());
- if (in == NULL) {
- msg (M_ERR, "CRL: BIO err");
- goto end;
- }
- if (BIO_read_filename(in, opt->crl_file) <= 0) {
- msg (M_ERR, "CRL: cannot read: %s", opt->crl_file);
- goto end;
- }
- crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
- if (crl == NULL) {
- msg (M_ERR, "CRL: cannot read CRL from file %s", opt->crl_file);
- goto end;
- }
+ if (in == NULL) {
+ msg (M_ERR, "CRL: BIO err");
+ goto end;
+ }
+ if (BIO_read_filename(in, opt->crl_file) <= 0) {
+ msg (M_ERR, "CRL: cannot read: %s", opt->crl_file);
+ goto end;
+ }
+ crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
+ if (crl == NULL) {
+ msg (M_ERR, "CRL: cannot read CRL from file %s", opt->crl_file);
+ goto end;
+ }
- if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(ctx->current_cert)) != 0) {
- msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of certificate %s", opt->crl_file, subject);
- retval = 1;
- goto end;
- }
+ if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(ctx->current_cert)) != 0) {
+ msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of certificate %s", opt->crl_file, subject);
+ retval = 1;
+ goto end;
+ }
- n = sk_num(X509_CRL_get_REVOKED(crl));
+ n = sk_num(X509_CRL_get_REVOKED(crl));
- for (i = 0; i < n; i++) {
- revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i);
- if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(ctx->current_cert)) == 0) {
- msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject);
- goto end;
- }
- }
+ for (i = 0; i < n; i++) {
+ revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i);
+ if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(ctx->current_cert)) == 0) {
+ msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject);
+ goto end;
+ }
+ }
- retval = 1;
- msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject);
+ retval = 1;
+ msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject);
- end:
+ end:
- BIO_free(in);
- if (crl)
- X509_CRL_free (crl);
- if (!retval)
- goto err;
+ BIO_free(in);
+ if (crl)
+ X509_CRL_free (crl);
+ if (!retval)
+ goto err;
+ }
}
msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", ctx->error_depth, subject);
-
session->verified = true;
+
+ done:
OPENSSL_free (subject);
+ if (serial)
+ OPENSSL_free(serial);
argv_reset (&argv);
- return 1; /* Accept connection */
+ return (session->verified == true) ? 1 : 0;
err:
ERR_clear_error ();
- OPENSSL_free (subject);
- argv_reset (&argv);
- return 0; /* Reject connection */
+ session->verified = false;
+ goto done;
}
void
@@ -1954,7 +1975,7 @@ init_ssl (const struct options *options)
{
if (!X509_STORE_add_cert(ctx->cert_store,sk_X509_value(ca, i)))
msg (M_SSLERR, "Cannot add certificate to certificate chain (X509_STORE_add_cert)");
- if (!SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i)))
+ if (options->tls_server && !SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i)))
msg (M_SSLERR, "Cannot add certificate to client CA list (SSL_CTX_add_client_CA)");
}
}
@@ -2066,11 +2087,11 @@ init_ssl (const struct options *options)
#endif
{
/* Load CA file for verifying peer supplied certificate */
- status = SSL_CTX_load_verify_locations (ctx, options->ca_file, options->ca_path);
+ status = SSL_CTX_load_verify_locations (ctx, options->ca_file, NULL);
}
if (!status)
- msg (M_SSLERR, "Cannot load CA certificate file %s path %s (SSL_CTX_load_verify_locations)", options->ca_file, options->ca_path);
+ msg (M_SSLERR, "Cannot load CA certificate file %s path %s (SSL_CTX_load_verify_locations)", np(options->ca_file), np(options->ca_path));
/* Set a store for certs (CA & CRL) with a lookup on the "capath" hash directory */
if (options->ca_path) {
@@ -2094,7 +2115,7 @@ init_ssl (const struct options *options)
}
/* Load names of CAs from file and use it as a client CA list */
- if (options->ca_file) {
+ if (options->ca_file && options->tls_server) {
STACK_OF(X509_NAME) *cert_names = NULL;
#if ENABLE_INLINE_FILES
if (!strcmp (options->ca_file, INLINE_FILE_TAG) && options->ca_file_inline)
@@ -2108,7 +2129,7 @@ init_ssl (const struct options *options)
}
if (!cert_names)
msg (M_SSLERR, "Cannot load CA certificate file %s (SSL_load_client_CA_file)", options->ca_file);
- SSL_CTX_set_client_CA_list (ctx, cert_names);
+ SSL_CTX_set_client_CA_list (ctx, cert_names);
}
}
diff --git a/ssl.h b/ssl.h
index eca3922..fe494b1 100644
--- a/ssl.h
+++ b/ssl.h
@@ -504,6 +504,7 @@ struct tls_options
# define SSLF_AUTH_USER_PASS_OPTIONAL (1<<2)
# define SSLF_NO_NAME_REMAPPING (1<<3)
# define SSLF_OPT_VERIFY (1<<4)
+# define SSLF_CRL_VERIFY_DIR (1<<5)
unsigned int ssl_flags;
#ifdef MANAGEMENT_DEF_AUTH