/** * @file openssl/tls.c TLS backend using OpenSSL * * Copyright (C) 2010 Creytiv.com */ #include #define OPENSSL_NO_KRB5 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tls.h" #define DEBUG_MODULE "tls" #define DEBUG_LEVEL 5 #include /* NOTE: shadow struct defined in tls_*.c */ struct tls_conn { SSL *ssl; }; static void destructor(void *data) { struct tls *tls = data; if (tls->ctx) SSL_CTX_free(tls->ctx); if (tls->cert) X509_free(tls->cert); mem_deref(tls->pass); } /*The password code is not thread safe*/ static int password_cb(char *buf, int size, int rwflag, void *userdata) { struct tls *tls = userdata; (void)rwflag; DEBUG_NOTICE("password callback\n"); if (size < (int)strlen(tls->pass)+1) return 0; strncpy(buf, tls->pass, size); return (int)strlen(tls->pass); } /** * Allocate a new TLS context * * @param tlsp Pointer to allocated TLS context * @param method TLS method * @param keyfile Optional private key file * @param pwd Optional password * * @return 0 if success, otherwise errorcode */ int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, const char *pwd) { struct tls *tls; int r, err; if (!tlsp) return EINVAL; tls = mem_zalloc(sizeof(*tls), destructor); if (!tls) return ENOMEM; switch (method) { case TLS_METHOD_SSLV23: tls->ctx = SSL_CTX_new(SSLv23_method()); break; #ifdef USE_OPENSSL_DTLS case TLS_METHOD_DTLSV1: tls->ctx = SSL_CTX_new(DTLSv1_method()); break; #endif default: DEBUG_WARNING("tls method %d not supported\n", method); err = ENOSYS; goto out; } if (!tls->ctx) { ERR_clear_error(); err = ENOMEM; goto out; } #if (OPENSSL_VERSION_NUMBER < 0x00905100L) SSL_CTX_set_verify_depth(tls->ctx, 1); #endif if (method == TLS_METHOD_DTLSV1) { SSL_CTX_set_read_ahead(tls->ctx, 1); } /* Load our keys and certificates */ if (keyfile) { if (pwd) { err = str_dup(&tls->pass, pwd); if (err) goto out; SSL_CTX_set_default_passwd_cb(tls->ctx, password_cb); SSL_CTX_set_default_passwd_cb_userdata(tls->ctx, tls); } r = SSL_CTX_use_certificate_chain_file(tls->ctx, keyfile); if (r <= 0) { DEBUG_WARNING("Can't read certificate file: %s (%d)\n", keyfile, r); ERR_clear_error(); err = EINVAL; goto out; } r = SSL_CTX_use_PrivateKey_file(tls->ctx, keyfile, SSL_FILETYPE_PEM); if (r <= 0) { DEBUG_WARNING("Can't read key file: %s (%d)\n", keyfile, r); ERR_clear_error(); err = EINVAL; goto out; } } err = 0; out: if (err) mem_deref(tls); else *tlsp = tls; return err; } /** * Set default locations for trusted CA certificates * * @param tls TLS Context * @param capath Path to CA certificates * * @return 0 if success, otherwise errorcode */ int tls_add_ca(struct tls *tls, const char *capath) { if (!tls || !capath) return EINVAL; /* Load the CAs we trust */ if (!(SSL_CTX_load_verify_locations(tls->ctx, capath, 0))) { DEBUG_WARNING("Can't read CA list: %s\n", capath); ERR_clear_error(); return EINVAL; } return 0; } /** * Generate and set selfsigned certificate on TLS context * * @param tls TLS Context * @param cn Common Name * * @return 0 if success, otherwise errorcode */ int tls_set_selfsigned(struct tls *tls, const char *cn) { X509_NAME *subj = NULL; EVP_PKEY *key = NULL; X509 *cert = NULL; RSA *rsa = NULL; int r, err = ENOMEM; if (!tls || !cn) return EINVAL; rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL); if (!rsa) goto out; key = EVP_PKEY_new(); if (!key) goto out; if (!EVP_PKEY_set1_RSA(key, rsa)) goto out; cert = X509_new(); if (!cert) goto out; if (!X509_set_version(cert, 2)) goto out; if (!ASN1_INTEGER_set(X509_get_serialNumber(cert), rand_u32())) goto out; subj = X509_NAME_new(); if (!subj) goto out; if (!X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC, (unsigned char *)cn, (int)strlen(cn), -1, 0)) goto out; if (!X509_set_issuer_name(cert, subj) || !X509_set_subject_name(cert, subj)) goto out; if (!X509_gmtime_adj(X509_get_notBefore(cert), -3600*24*365) || !X509_gmtime_adj(X509_get_notAfter(cert), 3600*24*365*10)) goto out; if (!X509_set_pubkey(cert, key)) goto out; if (!X509_sign(cert, key, EVP_sha1())) goto out; r = SSL_CTX_use_certificate(tls->ctx, cert); if (r != 1) goto out; r = SSL_CTX_use_PrivateKey(tls->ctx, key); if (r != 1) goto out; if (tls->cert) X509_free(tls->cert); tls->cert = cert; cert = NULL; err = 0; out: if (subj) X509_NAME_free(subj); if (cert) X509_free(cert); if (key) EVP_PKEY_free(key); if (rsa) RSA_free(rsa); if (err) ERR_clear_error(); return err; } static int verify_handler(int ok, X509_STORE_CTX *ctx) { (void)ok; (void)ctx; return 1; /* We trust the certificate from peer */ } /** * Set TLS server context to request certificate from client * * @param tls TLS Context */ void tls_set_verify_client(struct tls *tls) { if (!tls) return; SSL_CTX_set_verify_depth(tls->ctx, 0); SSL_CTX_set_verify(tls->ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_handler); } /** * Set SRTP suites on TLS context * * @param tls TLS Context * @param suites Secure-RTP Profiles * * @return 0 if success, otherwise errorcode */ int tls_set_srtp(struct tls *tls, const char *suites) { #ifdef USE_OPENSSL_SRTP if (!tls || !suites) return EINVAL; if (0 != SSL_CTX_set_tlsext_use_srtp(tls->ctx, suites)) { ERR_clear_error(); return ENOSYS; } return 0; #else (void)tls; (void)suites; return ENOSYS; #endif } static int cert_fingerprint(X509 *cert, enum tls_fingerprint type, uint8_t *md, size_t size) { unsigned int len = (unsigned int)size; int n; switch (type) { case TLS_FINGERPRINT_SHA1: if (size < 20) return EOVERFLOW; n = X509_digest(cert, EVP_sha1(), md, &len); break; case TLS_FINGERPRINT_SHA256: if (size < 32) return EOVERFLOW; n = X509_digest(cert, EVP_sha256(), md, &len); break; default: return ENOSYS; } if (n != 1) { ERR_clear_error(); return ENOENT; } return 0; } /** * Get fingerprint of local certificate * * @param tls TLS Context * @param type Digest type * @param md Buffer for fingerprint digest * @param size Buffer size * * @return 0 if success, otherwise errorcode */ int tls_fingerprint(const struct tls *tls, enum tls_fingerprint type, uint8_t *md, size_t size) { if (!tls || !tls->cert || !md) return EINVAL; return cert_fingerprint(tls->cert, type, md, size); } /** * Get fingerprint of peer certificate of a TLS connection * * @param tc TLS Connection * @param type Digest type * @param md Buffer for fingerprint digest * @param size Buffer size * * @return 0 if success, otherwise errorcode */ int tls_peer_fingerprint(const struct tls_conn *tc, enum tls_fingerprint type, uint8_t *md, size_t size) { X509 *cert; int err; if (!tc || !md) return EINVAL; cert = SSL_get_peer_certificate(tc->ssl); if (!cert) return ENOENT; err = cert_fingerprint(cert, type, md, size); X509_free(cert); return err; } /** * Get common name of peer certificate of a TLS connection * * @param tc TLS Connection * @param cn Returned common name * @param size Size of common name * * @return 0 if success, otherwise errorcode */ int tls_peer_common_name(const struct tls_conn *tc, char *cn, size_t size) { X509 *cert; int n; if (!tc || !cn || !size) return EINVAL; cert = SSL_get_peer_certificate(tc->ssl); if (!cert) return ENOENT; n = X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, cn, (int)size); X509_free(cert); if (n < 0) { ERR_clear_error(); return ENOENT; } return 0; } /** * Verify peer certificate of a TLS connection * * @param tc TLS Connection * * @return 0 if verified, otherwise errorcode */ int tls_peer_verify(const struct tls_conn *tc) { if (!tc) return EINVAL; if (SSL_get_verify_result(tc->ssl) != X509_V_OK) return EAUTH; return 0; } /** * Get SRTP suite and keying material of a TLS connection * * @param tc TLS Connection * @param suite Returned SRTP suite * @param cli_key Client key * @param cli_key_size Client key size * @param srv_key Server key * @param srv_key_size Server key size * * @return 0 if success, otherwise errorcode */ int tls_srtp_keyinfo(const struct tls_conn *tc, enum srtp_suite *suite, uint8_t *cli_key, size_t cli_key_size, uint8_t *srv_key, size_t srv_key_size) { #ifdef USE_OPENSSL_SRTP static const char *label = "EXTRACTOR-dtls_srtp"; size_t key_size, salt_size, size; SRTP_PROTECTION_PROFILE *sel; uint8_t keymat[256], *p; if (!tc || !suite || !cli_key || !srv_key) return EINVAL; sel = SSL_get_selected_srtp_profile(tc->ssl); if (!sel) return ENOENT; switch (sel->id) { case SRTP_AES128_CM_SHA1_80: *suite = SRTP_AES_CM_128_HMAC_SHA1_80; key_size = 16; salt_size = 14; break; case SRTP_AES128_CM_SHA1_32: *suite = SRTP_AES_CM_128_HMAC_SHA1_32; key_size = 16; salt_size = 14; break; default: return ENOSYS; } size = key_size + salt_size; if (cli_key_size < size || srv_key_size < size) return EOVERFLOW; if (sizeof(keymat) < 2*size) return EOVERFLOW; if (1 != SSL_export_keying_material(tc->ssl, keymat, 2*size, label, strlen(label), NULL, 0, 0)) { ERR_clear_error(); return ENOENT; } p = keymat; memcpy(cli_key, p, key_size); p += key_size; memcpy(srv_key, p, key_size); p += key_size; memcpy(cli_key + key_size, p, salt_size); p += salt_size; memcpy(srv_key + key_size, p, salt_size); return 0; #else (void)tc; (void)suite; (void)cli_key; (void)cli_key_size; (void)srv_key; (void)srv_key_size; return ENOSYS; #endif }