diff --git a/buffer.c b/buffer.c index 29527677..5499d99f 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 0f22cda4..58273e29 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 037ba7e1..1953b161 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 32ea07f1..fd3fec60 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 339478ad..5b870ce7 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 572d8e25..095b6a62 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; - - 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 (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)); - - 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; + 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; - retval = 1; - msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject); + in=BIO_new(BIO_s_file()); - 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; + } - BIO_free(in); - if (crl) - X509_CRL_free (crl); - if (!retval) - goto err; + 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)); + + 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); + + end: + + 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 eca3922f..fe494b10 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