diff options
Diffstat (limited to 'ssl.c')
-rw-r--r-- | ssl.c | 192 |
1 files changed, 146 insertions, 46 deletions
@@ -7,6 +7,10 @@ * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> * + * Additions for eurephia plugin done by: + * David Sommerseth <dazo@users.sourceforge.net> Copyright (C) 2008-2009 + * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. @@ -687,6 +691,51 @@ string_mod_sslname (char *str, const unsigned int restrictive_flags, const unsig string_mod (str, restrictive_flags, 0, '_'); } +/* Get peer cert and store it in pem format in a temporary file + * in tmp_dir + */ + +const char * +get_peer_cert(X509_STORE_CTX *ctx, const char *tmp_dir, struct gc_arena *gc) +{ + X509 *peercert; + FILE *peercert_file; + const char *peercert_filename=""; + + if(!tmp_dir) + return NULL; + + /* get peer cert */ + peercert = X509_STORE_CTX_get_current_cert(ctx); + if(!peercert) + { + msg (M_ERR, "Unable to get peer certificate from current context"); + return NULL; + } + + /* create tmp file to store peer cert */ + peercert_filename = create_temp_file (tmp_dir, "pcf", gc); + + /* write peer-cert in tmp-file */ + peercert_file = fopen(peercert_filename, "w+"); + if(!peercert_file) + { + msg (M_ERR, "Failed to open temporary file : %s", peercert_filename); + return NULL; + } + if(PEM_write_X509(peercert_file,peercert)<0) + { + msg (M_ERR, "Failed to write peer certificate in PEM format"); + fclose(peercert_file); + return NULL; + } + + fclose(peercert_file); + return peercert_filename; +} + +char * x509_username_field; /* GLOBAL */ + /* * Our verify callback function -- check * that an incoming peer certificate is good. @@ -697,7 +746,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) { char *subject = NULL; char envname[64]; - char common_name[TLS_CN_LEN]; + char common_name[TLS_USERNAME_LEN]; SSL *ssl; struct tls_session *session; const struct tls_options *opt; @@ -729,18 +778,20 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) string_mod_sslname (subject, X509_NAME_CHAR_CLASS, opt->ssl_flags); string_replace_leading (subject, '-', '_'); - /* extract the common name */ - if (!extract_x509_field_ssl (X509_get_subject_name (ctx->current_cert), "CN", common_name, TLS_CN_LEN)) + /* extract the username (default is CN) */ + if (!extract_x509_field_ssl (X509_get_subject_name (ctx->current_cert), x509_username_field, common_name, sizeof(common_name))) { if (!ctx->error_depth) - { - msg (D_TLS_ERRORS, "VERIFY ERROR: could not extract Common Name from X509 subject string ('%s') -- note that the Common Name length is limited to %d characters", - subject, - TLS_CN_LEN); - goto err; - } + { + msg (D_TLS_ERRORS, "VERIFY ERROR: could not extract %s from X509 subject string ('%s') -- note that the username length is limited to %d characters", + x509_username_field, + subject, + TLS_USERNAME_LEN); + goto err; + } } + string_mod_sslname (common_name, COMMON_NAME_CHAR_CLASS, opt->ssl_flags); cert_hash_remember (session, ctx->error_depth, ctx->current_cert->sha1_hash); @@ -780,6 +831,16 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) openvpn_snprintf (envname, sizeof(envname), "tls_id_%d", ctx->error_depth); setenv_str (opt->es, envname, subject); +#ifdef ENABLE_EUREPHIA + /* export X509 cert SHA1 fingerprint */ + { + struct gc_arena gc = gc_new (); + openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", ctx->error_depth); + setenv_str (opt->es, envname, + format_hex_ex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, 1, ":", &gc)); + gc_free(&gc); + } +#endif #if 0 /* export common name string as environmental variable */ openvpn_snprintf (envname, sizeof(envname), "tls_common_name_%d", ctx->error_depth); @@ -788,9 +849,30 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) /* export serial number as environmental variable */ { - const int serial = (int) ASN1_INTEGER_get (X509_get_serialNumber (ctx->current_cert)); - openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", ctx->error_depth); - setenv_int (opt->es, envname, serial); + BIO *bio = NULL; + char serial[100]; + int n1, n2; + + CLEAR (serial); + if ((bio = BIO_new (BIO_s_mem ())) == NULL) + { + msg (M_WARN, "CALLBACK: Cannot create BIO (for tls_serial_%d)", ctx->error_depth); + } + else + { + /* "prints" the serial number onto the BIO and read it back */ + if ( ! ( ( (n1 = i2a_ASN1_INTEGER(bio, X509_get_serialNumber (ctx->current_cert))) >= 0 ) && + ( (n2 = BIO_read (bio, serial, sizeof (serial)-1)) >= 0 ) && + ( n1 == n2 ) ) ) + { + msg (M_WARN, "CALLBACK: Error reading/writing BIO (for tls_serial_%d)", ctx->error_depth); + CLEAR (serial); /* empty string */ + } + + openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", ctx->error_depth); + setenv_str (opt->es, envname, serial); + BIO_free(bio); + } } /* export current untrusted IP */ @@ -885,32 +967,48 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) /* run --tls-verify script */ if (opt->verify_command) { + const char *tmp_file; + struct gc_arena gc; int ret; setenv_str (opt->es, "script_type", "tls-verify"); + if (opt->verify_export_cert) + { + gc = gc_new(); + if (tmp_file=get_peer_cert(ctx, opt->verify_export_cert,&gc)) + { + setenv_str(opt->es, "peer_cert", tmp_file); + } + } + argv_printf (&argv, "%sc %d %s", opt->verify_command, ctx->error_depth, subject); argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command"); - ret = openvpn_execve (&argv, opt->es, S_SCRIPT); + ret = openvpn_run_script (&argv, opt->es, 0, "--tls-verify script"); - if (system_ok (ret)) + if (opt->verify_export_cert) + { + if (tmp_file) + delete_file(tmp_file); + gc_free(&gc); + } + + if (ret) { msg (D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s", ctx->error_depth, subject); } else { - if (!system_executed (ret)) - argv_msg_prefix (M_ERR, &argv, "Verify command failed to execute"); msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s", ctx->error_depth, subject); goto err; /* Reject connection */ } } - + /* check peer cert against CRL */ if (opt->crl_file) { @@ -1094,10 +1192,11 @@ key_state_gen_auth_control_file (struct key_state *ks, const struct tls_options const char *acf; key_state_rm_auth_control_file (ks); - acf = create_temp_filename (opt->tmp_dir, "acf", &gc); - ks->auth_control_file = string_alloc (acf, NULL); - setenv_str (opt->es, "auth_control_file", ks->auth_control_file); - + acf = create_temp_file (opt->tmp_dir, "acf", &gc); + if( acf ) { + ks->auth_control_file = string_alloc (acf, NULL); + setenv_str (opt->es, "auth_control_file", ks->auth_control_file); + } /* FIXME: Should have better error handling? */ gc_free (&gc); } @@ -1727,7 +1826,8 @@ init_ssl (const struct options *options) } else #endif - SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + x509_username_field = (char *) options->x509_username_field; + SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); /* Connection information callback */ @@ -3180,7 +3280,6 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up struct gc_arena gc = gc_new (); struct argv argv = argv_new (); const char *tmp_file = ""; - int retval; bool ret = false; /* Is username defined? */ @@ -3193,17 +3292,22 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up { struct status_output *so; - tmp_file = create_temp_filename (session->opt->tmp_dir, "up", &gc); - so = status_open (tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE); - status_printf (so, "%s", up->username); - status_printf (so, "%s", up->password); - if (!status_close (so)) - { - msg (D_TLS_ERRORS, "TLS Auth Error: could not write username/password to file: %s", - tmp_file); - goto done; - } - } + tmp_file = create_temp_file (session->opt->tmp_dir, "up", &gc); + if( tmp_file ) { + so = status_open (tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE); + status_printf (so, "%s", up->username); + status_printf (so, "%s", up->password); + if (!status_close (so)) + { + msg (D_TLS_ERRORS, "TLS Auth Error: could not write username/password to file: %s", + tmp_file); + goto done; + } + } else { + msg (D_TLS_ERRORS, "TLS Auth Error: could not create write " + "username/password to temp file"); + } + } else { setenv_str (session->opt->es, "username", up->username); @@ -3218,16 +3322,11 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up /* format command line */ argv_printf (&argv, "%sc %s", session->opt->auth_user_pass_verify_script, tmp_file); - + /* call command */ - retval = openvpn_execve (&argv, session->opt->es, S_SCRIPT); + ret = openvpn_run_script (&argv, session->opt->es, 0, + "--auth-user-pass-verify"); - /* test return status of command */ - if (system_ok (retval)) - ret = true; - else if (!system_executed (retval)) - argv_msg_prefix (D_TLS_ERRORS, &argv, "TLS Auth Error: user-pass-verify script failed to execute"); - if (!session->opt->auth_user_pass_verify_script_via_file) setenv_del (session->opt->es, "password"); } @@ -3237,7 +3336,7 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up } done: - if (strlen (tmp_file) > 0) + if (tmp_file && strlen (tmp_file) > 0) delete_file (tmp_file); argv_reset (&argv); @@ -3673,9 +3772,9 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi s2 = verify_user_pass_script (session, up); /* check sizing of username if it will become our common name */ - if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen (up->username) >= TLS_CN_LEN) + if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen (up->username) >= TLS_USERNAME_LEN) { - msg (D_TLS_ERRORS, "TLS Auth Error: --username-as-common name specified and username is longer than the maximum permitted Common Name length of %d characters", TLS_CN_LEN); + msg (D_TLS_ERRORS, "TLS Auth Error: --username-as-common name specified and username is longer than the maximum permitted Common Name length of %d characters", TLS_USERNAME_LEN); s1 = OPENVPN_PLUGIN_FUNC_ERROR; } @@ -3879,7 +3978,8 @@ tls_process (struct tls_multi *multi, && ks->n_packets >= session->opt->renegotiate_packets) || (packet_id_close_to_wrapping (&ks->packet_id.send)))) { - msg (D_TLS_DEBUG_LOW, "TLS: soft reset sec=%d bytes=%d/%d pkts=%d/%d", + msg (D_TLS_DEBUG_LOW, + "TLS: soft reset sec=%d bytes=" counter_format "/%d pkts=" counter_format "/%d", (int)(ks->established + session->opt->renegotiate_seconds - now), ks->n_bytes, session->opt->renegotiate_bytes, ks->n_packets, session->opt->renegotiate_packets); |