aboutsummaryrefslogtreecommitdiff
path: root/ssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'ssl.c')
-rw-r--r--ssl.c322
1 files changed, 279 insertions, 43 deletions
diff --git a/ssl.c b/ssl.c
index 8644ae4..2fa091a 100644
--- a/ssl.c
+++ b/ssl.c
@@ -5,7 +5,11 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * 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
@@ -285,6 +289,10 @@ pem_password_callback (char *buf, int size, int rwflag, void *u)
static bool auth_user_pass_enabled; /* GLOBAL */
static struct user_pass auth_user_pass; /* GLOBAL */
+#ifdef ENABLE_CLIENT_CR
+static char *auth_challenge; /* GLOBAL */
+#endif
+
void
auth_user_pass_setup (const char *auth_file)
{
@@ -293,6 +301,8 @@ auth_user_pass_setup (const char *auth_file)
{
#if AUTO_USERID
get_user_pass_auto_userid (&auth_user_pass, auth_file);
+#elif defined(ENABLE_CLIENT_CR)
+ get_user_pass_cr (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE, auth_challenge);
#else
get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE);
#endif
@@ -320,8 +330,29 @@ ssl_purge_auth (void)
#endif
purge_user_pass (&passbuf, true);
purge_user_pass (&auth_user_pass, true);
+#ifdef ENABLE_CLIENT_CR
+ ssl_purge_auth_challenge();
+#endif
}
+#ifdef ENABLE_CLIENT_CR
+
+void
+ssl_purge_auth_challenge (void)
+{
+ free (auth_challenge);
+ auth_challenge = NULL;
+}
+
+void
+ssl_put_auth_challenge (const char *cr_str)
+{
+ ssl_purge_auth_challenge();
+ auth_challenge = string_alloc(cr_str, NULL);
+}
+
+#endif
+
/*
* OpenSSL callback to get a temporary RSA key, mostly
* used for export ciphers.
@@ -709,7 +740,7 @@ get_peer_cert(X509_STORE_CTX *ctx, const char *tmp_dir, struct gc_arena *gc)
}
/* create tmp file to store peer cert */
- peercert_filename = create_temp_filename (tmp_dir, "pcf", gc);
+ peercert_filename = create_temp_file (tmp_dir, "pcf", gc);
/* write peer-cert in tmp-file */
peercert_file = fopen(peercert_filename, "w+");
@@ -826,6 +857,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);
@@ -834,9 +875,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 */
@@ -931,7 +993,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
/* run --tls-verify script */
if (opt->verify_command)
{
- const char *tmp_file;
+ const char *tmp_file = NULL;
struct gc_arena gc;
int ret;
@@ -1003,10 +1065,10 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
goto end;
}
- n = sk_num(X509_CRL_get_REVOKED(crl));
+ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
for (i = 0; i < n; i++) {
- revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i);
+ revoked = (X509_REVOKED *)sk_X509_REVOKED_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;
@@ -1028,13 +1090,13 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", ctx->error_depth, subject);
session->verified = true;
- free (subject);
+ OPENSSL_free (subject);
argv_reset (&argv);
return 1; /* Accept connection */
err:
ERR_clear_error ();
- free (subject);
+ OPENSSL_free (subject);
argv_reset (&argv);
return 0; /* Reject connection */
}
@@ -1156,10 +1218,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);
}
@@ -1576,23 +1639,41 @@ init_ssl (const struct options *options)
if (options->pkcs12_file)
{
- /* Use PKCS #12 file for key, cert and CA certs */
+ /* Use PKCS #12 file for key, cert and CA certs */
FILE *fp;
EVP_PKEY *pkey;
X509 *cert;
STACK_OF(X509) *ca = NULL;
- PKCS12 *p12;
+ PKCS12 *p12=NULL;
int i;
char password[256];
- /* Load the PKCS #12 file */
- if (!(fp = fopen(options->pkcs12_file, "rb")))
- msg (M_SSLERR, "Error opening file %s", options->pkcs12_file);
- p12 = d2i_PKCS12_fp(fp, NULL);
- fclose (fp);
- if (!p12) msg (M_SSLERR, "Error reading PKCS#12 file %s", options->pkcs12_file);
-
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->pkcs12_file, INLINE_FILE_TAG) && options->pkcs12_file_inline)
+ {
+ BIO *b64 = BIO_new (BIO_f_base64());
+ BIO *bio = BIO_new_mem_buf ((void *)options->pkcs12_file_inline, (int)strlen(options->pkcs12_file_inline));
+ ASSERT(b64 && bio);
+ BIO_push (b64, bio);
+ p12 = d2i_PKCS12_bio(b64, NULL);
+ if (!p12)
+ msg (M_SSLERR, "Error reading inline PKCS#12 file");
+ BIO_free (b64);
+ BIO_free (bio);
+ }
+ else
+#endif
+ {
+ /* Load the PKCS #12 file */
+ if (!(fp = fopen(options->pkcs12_file, "rb")))
+ msg (M_SSLERR, "Error opening file %s", options->pkcs12_file);
+ p12 = d2i_PKCS12_fp(fp, NULL);
+ fclose (fp);
+ if (!p12)
+ msg (M_SSLERR, "Error reading PKCS#12 file %s", options->pkcs12_file);
+ }
+
/* Parse the PKCS #12 file */
if (!PKCS12_parse(p12, "", &pkey, &cert, &ca))
{
@@ -1601,8 +1682,12 @@ init_ssl (const struct options *options)
ca = NULL;
if (!PKCS12_parse(p12, password, &pkey, &cert, &ca))
{
+#ifdef ENABLE_MANAGEMENT
+ if (management && (ERR_GET_REASON (ERR_peek_error()) == PKCS12_R_MAC_VERIFY_FAILURE))
+ management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL);
+#endif
PKCS12_free(p12);
- msg (M_WARN|M_SSL, "Error parsing PKCS#12 file %s", options->pkcs12_file);
+ msg (M_INFO, "OpenSSL ERROR code: %d", (ERR_GET_REASON (ERR_peek_error()))); // fixme
goto err;
}
}
@@ -1624,7 +1709,7 @@ init_ssl (const struct options *options)
/* Set Certificate Verification chain */
if (!options->ca_file)
{
- if (ca && sk_num(ca))
+ if (ca && sk_X509_num(ca))
{
for (i = 0; i < sk_X509_num(ca); i++)
{
@@ -1701,7 +1786,7 @@ init_ssl (const struct options *options)
{
#ifdef ENABLE_MANAGEMENT
if (management && (ERR_GET_REASON (ERR_peek_error()) == EVP_R_BAD_DECRYPT))
- management_auth_failure (management, UP_TYPE_PRIVATE_KEY);
+ management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL);
#endif
msg (M_WARN|M_SSL, "Cannot load private key file %s", options->priv_key_file);
goto err;
@@ -2329,6 +2414,7 @@ key_state_free (struct key_state *ks, bool clear)
free_buf (&ks->plaintext_read_buf);
free_buf (&ks->plaintext_write_buf);
free_buf (&ks->ack_write_buf);
+ buffer_list_free(ks->paybuf);
if (ks->send_reliable)
{
@@ -2621,6 +2707,8 @@ tls_multi_free (struct tls_multi *multi, bool clear)
#ifdef MANAGEMENT_DEF_AUTH
man_def_auth_set_client_reason(multi, NULL);
+
+ free (multi->peer_info);
#endif
if (multi->locked_cn)
@@ -3125,6 +3213,17 @@ key_source2_read (struct key_source2 *k2,
return 1;
}
+static void
+flush_payload_buffer (struct tls_multi *multi, struct key_state *ks)
+{
+ struct buffer *b;
+ while ((b = buffer_list_peek (ks->paybuf)))
+ {
+ key_state_write_plaintext_const (multi, ks, b->data, b->len);
+ buffer_list_pop (ks->paybuf);
+ }
+}
+
/*
* Macros for key_state_soft_reset & tls_process
*/
@@ -3169,6 +3268,14 @@ write_string (struct buffer *buf, const char *str, const int maxlen)
}
static bool
+write_empty_string (struct buffer *buf)
+{
+ if (!buf_write_u16 (buf, 0))
+ return false;
+ return true;
+}
+
+static bool
read_string (struct buffer *buf, char *str, const unsigned int capacity)
{
const int len = buf_read_u16 (buf);
@@ -3180,6 +3287,33 @@ read_string (struct buffer *buf, char *str, const unsigned int capacity)
return true;
}
+static char *
+read_string_alloc (struct buffer *buf)
+{
+ const int len = buf_read_u16 (buf);
+ char *str;
+
+ if (len < 1)
+ return NULL;
+ str = (char *) malloc(len);
+ check_malloc_return(str);
+ if (!buf_read (buf, str, len))
+ {
+ free (str);
+ return NULL;
+ }
+ str[len-1] = '\0';
+ return str;
+}
+
+void
+read_string_discard (struct buffer *buf)
+{
+ char *data = read_string_alloc(buf);
+ if (data)
+ free (data);
+}
+
/*
* Authenticate a client using username/password.
* Runs on server.
@@ -3206,17 +3340,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);
@@ -3245,7 +3384,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);
@@ -3385,6 +3524,73 @@ key_method_1_write (struct buffer *buf, struct tls_session *session)
}
static bool
+push_peer_info(struct buffer *buf, struct tls_session *session)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+
+#ifdef ENABLE_PUSH_PEER_INFO
+ if (session->opt->push_peer_info) /* write peer info */
+ {
+ struct env_set *es = session->opt->es;
+ struct env_item *e;
+ struct buffer out = alloc_buf_gc (512*3, &gc);
+
+ /* push version */
+ buf_printf (&out, "IV_VER=%s\n", PACKAGE_VERSION);
+
+ /* push platform */
+#if defined(TARGET_LINUX)
+ buf_printf (&out, "IV_PLAT=linux\n");
+#elif defined(TARGET_SOLARIS)
+ buf_printf (&out, "IV_PLAT=solaris\n");
+#elif defined(TARGET_OPENBSD)
+ buf_printf (&out, "IV_PLAT=openbsd\n");
+#elif defined(TARGET_DARWIN)
+ buf_printf (&out, "IV_PLAT=mac\n");
+#elif defined(TARGET_NETBSD)
+ buf_printf (&out, "IV_PLAT=netbsd\n");
+#elif defined(TARGET_FREEBSD)
+ buf_printf (&out, "IV_PLAT=freebsd\n");
+#elif defined(WIN32)
+ buf_printf (&out, "IV_PLAT=win\n");
+#endif
+
+ /* push mac addr */
+ {
+ bool get_default_gateway_mac_addr (unsigned char *macaddr);
+ uint8_t macaddr[6];
+ get_default_gateway_mac_addr (macaddr);
+ buf_printf (&out, "IV_HWADDR=%s\n", format_hex_ex (macaddr, 6, 0, 1, ":", &gc));
+ }
+
+ /* push env vars that begin with UV_ */
+ for (e=es->list; e != NULL; e=e->next)
+ {
+ if (e->string)
+ {
+ if (!strncmp(e->string, "UV_", 3) && buf_safe(&out, strlen(e->string)+1))
+ buf_printf (&out, "%s\n", e->string);
+ }
+ }
+
+ if (!write_string(buf, BSTR(&out), -1))
+ goto error;
+ }
+ else
+#endif
+ {
+ if (!write_empty_string (buf)) /* no peer info */
+ goto error;
+ }
+ ret = true;
+
+ error:
+ gc_free (&gc);
+ return ret;
+}
+
+static bool
key_method_2_write (struct buffer *buf, struct tls_session *session)
{
ASSERT (session->opt->key_method == 2);
@@ -3418,6 +3624,16 @@ key_method_2_write (struct buffer *buf, struct tls_session *session)
goto error;
purge_user_pass (&auth_user_pass, false);
}
+ else
+ {
+ if (!write_empty_string (buf)) /* no username */
+ goto error;
+ if (!write_empty_string (buf)) /* no password */
+ goto error;
+ }
+
+ if (!push_peer_info (buf, session))
+ goto error;
/*
* generate tunnel keys if server
@@ -3564,11 +3780,13 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS;
bool s2 = true;
char *raw_username;
+ bool username_status, password_status;
/* get username/password from plaintext buffer */
ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc);
- if (!read_string (buf, up->username, USER_PASS_LEN)
- || !read_string (buf, up->password, USER_PASS_LEN))
+ username_status = read_string (buf, up->username, USER_PASS_LEN);
+ password_status = read_string (buf, up->password, USER_PASS_LEN);
+ if (!username_status || !password_status)
{
CLEAR (*up);
if (!(session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL))
@@ -3589,6 +3807,10 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
/* call plugin(s) and/or script */
#ifdef MANAGEMENT_DEF_AUTH
+ /* get peer info from control channel */
+ free (multi->peer_info);
+ multi->peer_info = read_string_alloc (buf);
+
if (man_def_auth == KMDA_DEF)
man_def_auth = verify_user_pass_management (session, up, raw_username);
#endif
@@ -3759,9 +3981,12 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
static int
auth_deferred_expire_window (const struct tls_options *o)
{
- const int hw = o->handshake_window;
+ int ret = o->handshake_window;
const int r2 = o->renegotiate_seconds / 2;
- return min_int (hw, r2);
+
+ if (o->renegotiate_seconds && r2 < ret)
+ ret = r2;
+ return ret;
}
/*
@@ -3801,7 +4026,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);
@@ -3910,6 +4136,9 @@ tls_process (struct tls_multi *multi,
/* Set outgoing address for data channel packets */
link_socket_set_outgoing_addr (NULL, to_link_socket_info, &ks->remote_addr, session->common_name, session->opt->es);
+ /* Flush any payload packets that were buffered before our state transitioned to S_ACTIVE */
+ flush_payload_buffer (multi, ks);
+
#ifdef MEASURE_TLS_HANDSHAKE_STATS
show_tls_performance_stats();
#endif
@@ -5007,6 +5236,13 @@ tls_send_payload (struct tls_multi *multi,
if (key_state_write_plaintext_const (multi, ks, data, size) == 1)
ret = true;
}
+ else
+ {
+ if (!ks->paybuf)
+ ks->paybuf = buffer_list_new (0);
+ buffer_list_push_data (ks->paybuf, data, (size_t)size);
+ ret = true;
+ }
ERR_clear_error ();