Merged the hostap_2.6 updates, and the Leap of Faith work, from the hostap_update...
[mech_eap.git] / libeap / src / crypto / tls_internal.c
index 64124d8..c7cb5de 100644 (file)
@@ -1,15 +1,9 @@
 /*
  * TLS interface functions and an internal TLS implementation
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
  *
- * 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.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  *
  * This file interface functions for hostapd/wpa_supplicant to use the
  * integrated TLSv1 implementation.
@@ -29,11 +23,17 @@ struct tls_global {
        int server;
        struct tlsv1_credentials *server_cred;
        int check_crl;
+
+       void (*event_cb)(void *ctx, enum tls_event ev,
+                        union tls_event_data *data);
+       void *cb_ctx;
+       int cert_in_cb;
 };
 
 struct tls_connection {
        struct tlsv1_client *client;
        struct tlsv1_server *server;
+       struct tls_global *global;
 };
 
 
@@ -56,6 +56,11 @@ void * tls_init(const struct tls_config *conf)
        global = os_zalloc(sizeof(*global));
        if (global == NULL)
                return NULL;
+       if (conf) {
+               global->event_cb = conf->event_cb;
+               global->cb_ctx = conf->cb_ctx;
+               global->cert_in_cb = conf->cert_in_cb;
+       }
 
        return global;
 }
@@ -69,10 +74,12 @@ void tls_deinit(void *ssl_ctx)
                tlsv1_client_global_deinit();
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
-               tlsv1_cred_free(global->server_cred);
                tlsv1_server_global_deinit();
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
        }
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       tlsv1_cred_free(global->server_cred);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
        os_free(global);
 }
 
@@ -91,6 +98,7 @@ struct tls_connection * tls_connection_init(void *tls_ctx)
        conn = os_zalloc(sizeof(*conn));
        if (conn == NULL)
                return NULL;
+       conn->global = global;
 
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
        if (!global->server) {
@@ -99,6 +107,8 @@ struct tls_connection * tls_connection_init(void *tls_ctx)
                        os_free(conn);
                        return NULL;
                }
+               tlsv1_client_set_cb(conn->client, global->event_cb,
+                                   global->cb_ctx, global->cert_in_cb);
        }
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
@@ -115,6 +125,28 @@ struct tls_connection * tls_connection_init(void *tls_ctx)
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags)
+{
+       if (conn->server)
+               tlsv1_server_set_test_flags(conn->server, flags);
+}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void tls_connection_set_log_cb(struct tls_connection *conn,
+                              void (*log_cb)(void *ctx, const char *msg),
+                              void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               tlsv1_server_set_log_cb(conn->server, log_cb, ctx);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
 void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
 {
        if (conn == NULL)
@@ -168,10 +200,46 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
        if (conn->client == NULL)
                return -1;
 
+       if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
+               wpa_printf(MSG_INFO,
+                          "TLS: tls_ext_cert_check=1 not supported");
+               return -1;
+       }
+
        cred = tlsv1_cred_alloc();
        if (cred == NULL)
                return -1;
 
+       if (params->subject_match) {
+               wpa_printf(MSG_INFO, "TLS: subject_match not supported");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (params->altsubject_match) {
+               wpa_printf(MSG_INFO, "TLS: altsubject_match not supported");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (params->suffix_match) {
+               wpa_printf(MSG_INFO, "TLS: suffix_match not supported");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (params->domain_match) {
+               wpa_printf(MSG_INFO, "TLS: domain_match not supported");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (params->openssl_ciphers) {
+               wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
        if (tlsv1_set_ca_cert(cred, params->ca_cert,
                              params->ca_cert_blob, params->ca_cert_blob_len,
                              params->ca_path)) {
@@ -211,6 +279,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                return -1;
        }
 
+       tlsv1_client_set_flags(conn->client, params->flags);
+
        return 0;
 #else /* CONFIG_TLS_INTERNAL_CLIENT */
        return -1;
@@ -261,6 +331,13 @@ int tls_global_set_params(void *tls_ctx,
                return -1;
        }
 
+       if (params->ocsp_stapling_response)
+               cred->ocsp_stapling_response =
+                       os_strdup(params->ocsp_stapling_response);
+       if (params->ocsp_stapling_response_multi)
+               cred->ocsp_stapling_response_multi =
+                       os_strdup(params->ocsp_stapling_response_multi);
+
        return 0;
 #else /* CONFIG_TLS_INTERNAL_SERVER */
        return -1;
@@ -277,7 +354,8 @@ int tls_global_set_verify(void *tls_ctx, int check_crl)
 
 
 int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-                             int verify_peer)
+                             int verify_peer, unsigned int flags,
+                             const u8 *session_ctx, size_t session_ctx_len)
 {
 #ifdef CONFIG_TLS_INTERNAL_SERVER
        if (conn->server)
@@ -287,47 +365,87 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
 }
 
 
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-                         int tls_ia)
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+                             struct tls_random *data)
 {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_get_random(conn->client, data);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_get_random(conn->server, data);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
        return -1;
 }
 
 
-int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
-                           struct tls_keys *keys)
+static int tls_get_keyblock_size(struct tls_connection *conn)
 {
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
        if (conn->client)
-               return tlsv1_client_get_keys(conn->client, keys);
+               return tlsv1_client_get_keyblock_size(conn->client);
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
        if (conn->server)
-               return tlsv1_server_get_keys(conn->server, keys);
+               return tlsv1_server_get_keyblock_size(conn->server);
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
        return -1;
 }
 
 
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-                      const char *label, int server_random_first,
-                      u8 *out, size_t out_len)
+static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+                             const char *label, int server_random_first,
+                             int skip_keyblock, u8 *out, size_t out_len)
 {
+       int ret = -1, skip = 0;
+       u8 *tmp_out = NULL;
+       u8 *_out = out;
+
+       if (skip_keyblock) {
+               skip = tls_get_keyblock_size(conn);
+               if (skip < 0)
+                       return -1;
+               tmp_out = os_malloc(skip + out_len);
+               if (!tmp_out)
+                       return -1;
+               _out = tmp_out;
+       }
+
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
        if (conn->client) {
-               return tlsv1_client_prf(conn->client, label,
-                                       server_random_first,
-                                       out, out_len);
+               ret = tlsv1_client_prf(conn->client, label,
+                                      server_random_first,
+                                      _out, skip + out_len);
        }
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
        if (conn->server) {
-               return tlsv1_server_prf(conn->server, label,
-                                       server_random_first,
-                                       out, out_len);
+               ret = tlsv1_server_prf(conn->server, label,
+                                      server_random_first,
+                                      _out, skip + out_len);
        }
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
-       return -1;
+       if (ret == 0 && skip_keyblock)
+               os_memcpy(out, _out + skip, out_len);
+       bin_clear_free(tmp_out, skip);
+
+       return ret;
+}
+
+
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+                             const char *label, u8 *out, size_t out_len)
+{
+       return tls_connection_prf(tls_ctx, conn, label, 0, 0, out, out_len);
+}
+
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+                                   u8 *out, size_t out_len)
+{
+       return tls_connection_prf(tls_ctx, conn, "key expansion", 1, 1, out,
+                                 out_len);
 }
 
 
@@ -336,6 +454,17 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
                                         const struct wpabuf *in_data,
                                         struct wpabuf **appl_data)
 {
+       return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
+                                        NULL);
+}
+
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+                                         struct tls_connection *conn,
+                                         const struct wpabuf *in_data,
+                                         struct wpabuf **appl_data,
+                                         int *need_more_data)
+{
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
        u8 *res, *ad;
        size_t res_len, ad_len;
@@ -348,7 +477,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
        res = tlsv1_client_handshake(conn->client,
                                     in_data ? wpabuf_head(in_data) : NULL,
                                     in_data ? wpabuf_len(in_data) : 0,
-                                    &res_len, &ad, &ad_len);
+                                    &res_len, &ad, &ad_len, need_more_data);
        if (res == NULL)
                return NULL;
        out = wpabuf_alloc_ext_data(res, res_len);
@@ -459,23 +588,23 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
                                       struct tls_connection *conn,
                                       const struct wpabuf *in_data)
 {
+       return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
+}
+
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+                                       struct tls_connection *conn,
+                                       const struct wpabuf *in_data,
+                                       int *need_more_data)
+{
+       if (need_more_data)
+               *need_more_data = 0;
+
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
        if (conn->client) {
-               struct wpabuf *buf;
-               int res;
-               buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
-               if (buf == NULL)
-                       return NULL;
-               res = tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
-                                          wpabuf_len(in_data),
-                                          wpabuf_mhead(buf),
-                                          wpabuf_size(buf));
-               if (res < 0) {
-                       wpabuf_free(buf);
-                       return NULL;
-               }
-               wpabuf_put(buf, res);
-               return buf;
+               return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
+                                           wpabuf_len(in_data),
+                                           need_more_data);
        }
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
@@ -530,6 +659,19 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
 }
 
 
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+                   char *buf, size_t buflen)
+{
+       if (conn == NULL)
+               return -1;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_get_version(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+       return -1;
+}
+
+
 int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
                   char *buf, size_t buflen)
 {
@@ -587,65 +729,51 @@ int tls_connection_get_write_alerts(void *tls_ctx,
 }
 
 
-int tls_connection_get_keyblock_size(void *tls_ctx,
-                                    struct tls_connection *conn)
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        tls_session_ticket_cb cb,
+                                        void *ctx)
 {
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
-       if (conn->client)
-               return tlsv1_client_get_keyblock_size(conn->client);
+       if (conn->client) {
+               tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
+               return 0;
+       }
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
-       if (conn->server)
-               return tlsv1_server_get_keyblock_size(conn->server);
+       if (conn->server) {
+               tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
+               return 0;
+       }
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
        return -1;
 }
 
 
-unsigned int tls_capabilities(void *tls_ctx)
+int tls_get_library_version(char *buf, size_t buf_len)
 {
-       return 0;
+       return os_snprintf(buf, buf_len, "internal");
 }
 
 
-struct wpabuf * tls_connection_ia_send_phase_finished(
-       void *tls_ctx, struct tls_connection *conn, int final)
+void tls_connection_set_success_data(struct tls_connection *conn,
+                                    struct wpabuf *data)
 {
-       return NULL;
 }
 
 
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-                                          struct tls_connection *conn)
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
 {
-       return -1;
 }
 
 
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-                                          struct tls_connection *conn,
-                                          const u8 *key, size_t key_len)
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
 {
-       return -1;
+       return NULL;
 }
 
 
-int tls_connection_set_session_ticket_cb(void *tls_ctx,
-                                        struct tls_connection *conn,
-                                        tls_session_ticket_cb cb,
-                                        void *ctx)
+void tls_connection_remove_session(struct tls_connection *conn)
 {
-#ifdef CONFIG_TLS_INTERNAL_CLIENT
-       if (conn->client) {
-               tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
-               return 0;
-       }
-#endif /* CONFIG_TLS_INTERNAL_CLIENT */
-#ifdef CONFIG_TLS_INTERNAL_SERVER
-       if (conn->server) {
-               tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
-               return 0;
-       }
-#endif /* CONFIG_TLS_INTERNAL_SERVER */
-       return -1;
 }