--- embedaddon/php/ext/openssl/openssl.c 2012/05/29 12:34:36 1.1.1.2 +++ embedaddon/php/ext/openssl/openssl.c 2014/06/15 20:03:52 1.1.1.5 @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2012 The PHP Group | + | Copyright (c) 1997-2014 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: openssl.c,v 1.1.1.2 2012/05/29 12:34:36 misho Exp $ */ +/* $Id: openssl.c,v 1.1.1.5 2014/06/15 20:03:52 misho Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -69,9 +69,19 @@ #define OPENSSL_ALGO_MD2 4 #endif #define OPENSSL_ALGO_DSS1 5 - +#if OPENSSL_VERSION_NUMBER >= 0x0090708fL +#define OPENSSL_ALGO_SHA224 6 +#define OPENSSL_ALGO_SHA256 7 +#define OPENSSL_ALGO_SHA384 8 +#define OPENSSL_ALGO_SHA512 9 +#define OPENSSL_ALGO_RMD160 10 +#endif #define DEBUG_SMIME 0 +#if !defined(OPENSSL_NO_EC) && defined(EVP_PKEY_EC) +#define HAVE_EVP_PKEY_EC 1 +#endif + /* FIXME: Use the openssl constants instead of * enum. It is now impossible to match real values * against php constants. Also sorry to break the @@ -82,7 +92,7 @@ enum php_openssl_key_type { OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA, -#ifdef EVP_PKEY_EC +#ifdef HAVE_EVP_PKEY_EC OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1 #endif }; @@ -555,6 +565,7 @@ static EVP_PKEY * php_openssl_generate_private_key(str static void add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname TSRMLS_DC) /* {{{ */ { + zval **data; zval *subitem, *subentries; int i, j = -1, last = -1, obj_cnt = 0; char *sname; @@ -572,7 +583,7 @@ static void add_assoc_name_entry(zval * val, char * ke for (i = 0; i < X509_NAME_entry_count(name); i++) { unsigned char *to_add; - int to_add_len; + int to_add_len = 0; ne = X509_NAME_get_entry(name, i); @@ -586,39 +597,27 @@ static void add_assoc_name_entry(zval * val, char * ke sname = (char *) OBJ_nid2ln(nid); } - MAKE_STD_ZVAL(subentries); - array_init(subentries); + str = X509_NAME_ENTRY_get_data(ne); + if (ASN1_STRING_type(str) != V_ASN1_UTF8STRING) { + to_add_len = ASN1_STRING_to_UTF8(&to_add, str); + } else { + to_add = ASN1_STRING_data(str); + to_add_len = ASN1_STRING_length(str); + } - last = -1; - for (;;) { - j = X509_NAME_get_index_by_OBJ(name, obj, last); - if (j < 0) { - if (last != -1) break; - } else { - obj_cnt++; - ne = X509_NAME_get_entry(name, j); - str = X509_NAME_ENTRY_get_data(ne); - if (ASN1_STRING_type(str) != V_ASN1_UTF8STRING) { - to_add_len = ASN1_STRING_to_UTF8(&to_add, str); - if (to_add_len != -1) { - add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1); - } - } else { - to_add = ASN1_STRING_data(str); - to_add_len = ASN1_STRING_length(str); + if (to_add_len != -1) { + if (zend_hash_find(Z_ARRVAL_P(subitem), sname, strlen(sname)+1, (void**)&data) == SUCCESS) { + if (Z_TYPE_PP(data) == IS_ARRAY) { + subentries = *data; add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1); + } else if (Z_TYPE_PP(data) == IS_STRING) { + MAKE_STD_ZVAL(subentries); + array_init(subentries); + add_next_index_stringl(subentries, Z_STRVAL_PP(data), Z_STRLEN_PP(data), 1); + add_next_index_stringl(subentries, (char *)to_add, to_add_len, 1); + zend_hash_update(Z_ARRVAL_P(subitem), sname, strlen(sname)+1, &subentries, sizeof(zval*), NULL); } - } - last = j; - } - i = last; - - if (obj_cnt > 1) { - add_assoc_zval_ex(subitem, sname, strlen(sname) + 1, subentries); - } else { - zval_dtor(subentries); - FREE_ZVAL(subentries); - if (obj_cnt && str && to_add_len > -1) { + } else { add_assoc_stringl(subitem, sname, (char *)to_add, to_add_len, 1); } } @@ -650,18 +649,28 @@ static time_t asn1_time_to_time_t(ASN1_UTCTIME * times char * thestr; long gmadjust = 0; - if (timestr->length < 13) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "extension author too lazy to parse %s correctly", timestr->data); + if (ASN1_STRING_type(timestr) != V_ASN1_UTCTIME) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "illegal ASN1 data type for timestamp"); return (time_t)-1; } - strbuf = estrdup((char *)timestr->data); + if (ASN1_STRING_length(timestr) != strlen(ASN1_STRING_data(timestr))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "illegal length in timestamp"); + return (time_t)-1; + } + if (ASN1_STRING_length(timestr) < 13) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to parse time string %s correctly", timestr->data); + return (time_t)-1; + } + + strbuf = estrdup((char *)ASN1_STRING_data(timestr)); + memset(&thetime, 0, sizeof(thetime)); /* we work backwards so that we can use atoi more easily */ - thestr = strbuf + timestr->length - 3; + thestr = strbuf + ASN1_STRING_length(timestr) - 3; thetime.tm_sec = atoi(thestr); *thestr = '\0'; @@ -849,7 +858,7 @@ static int php_openssl_parse_config(struct php_x509_re req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name); } if (req->md_alg == NULL) { - req->md_alg = req->digest = EVP_md5(); + req->md_alg = req->digest = EVP_sha1(); } PHP_SSL_CONFIG_SYNTAX_CHECK(extensions_section); @@ -954,6 +963,23 @@ static EVP_MD * php_openssl_get_evp_md_from_algo(long case OPENSSL_ALGO_DSS1: mdtype = (EVP_MD *) EVP_dss1(); break; +#if OPENSSL_VERSION_NUMBER >= 0x0090708fL + case OPENSSL_ALGO_SHA224: + mdtype = (EVP_MD *) EVP_sha224(); + break; + case OPENSSL_ALGO_SHA256: + mdtype = (EVP_MD *) EVP_sha256(); + break; + case OPENSSL_ALGO_SHA384: + mdtype = (EVP_MD *) EVP_sha384(); + break; + case OPENSSL_ALGO_SHA512: + mdtype = (EVP_MD *) EVP_sha512(); + break; + case OPENSSL_ALGO_RMD160: + mdtype = (EVP_MD *) EVP_ripemd160(); + break; +#endif default: return NULL; break; @@ -1048,6 +1074,13 @@ PHP_MINIT_FUNCTION(openssl) REGISTER_LONG_CONSTANT("OPENSSL_ALGO_MD2", OPENSSL_ALGO_MD2, CONST_CS|CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("OPENSSL_ALGO_DSS1", OPENSSL_ALGO_DSS1, CONST_CS|CONST_PERSISTENT); +#if OPENSSL_VERSION_NUMBER >= 0x0090708fL + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA224", OPENSSL_ALGO_SHA224, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA256", OPENSSL_ALGO_SHA256, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA384", OPENSSL_ALGO_SHA384, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_SHA512", OPENSSL_ALGO_SHA512, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_ALGO_RMD160", OPENSSL_ALGO_RMD160, CONST_CS|CONST_PERSISTENT); +#endif /* flags for S/MIME */ REGISTER_LONG_CONSTANT("PKCS7_DETACHED", PKCS7_DETACHED, CONST_CS|CONST_PERSISTENT); @@ -1087,7 +1120,7 @@ PHP_MINIT_FUNCTION(openssl) REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DSA", OPENSSL_KEYTYPE_DSA, CONST_CS|CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DH", OPENSSL_KEYTYPE_DH, CONST_CS|CONST_PERSISTENT); -#ifdef EVP_PKEY_EC +#ifdef HAVE_EVP_PKEY_EC REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_EC", OPENSSL_KEYTYPE_EC, CONST_CS|CONST_PERSISTENT); #endif @@ -1368,6 +1401,74 @@ PHP_FUNCTION(openssl_x509_check_private_key) } /* }}} */ +/* Special handling of subjectAltName, see CVE-2013-4073 + * Christian Heimes + */ + +static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension) +{ + GENERAL_NAMES *names; + const X509V3_EXT_METHOD *method = NULL; + long i, length, num; + const unsigned char *p; + + method = X509V3_EXT_get(extension); + if (method == NULL) { + return -1; + } + + p = extension->value->data; + length = extension->value->length; + if (method->it) { + names = (GENERAL_NAMES*)(ASN1_item_d2i(NULL, &p, length, + ASN1_ITEM_ptr(method->it))); + } else { + names = (GENERAL_NAMES*)(method->d2i(NULL, &p, length)); + } + if (names == NULL) { + return -1; + } + + num = sk_GENERAL_NAME_num(names); + for (i = 0; i < num; i++) { + GENERAL_NAME *name; + ASN1_STRING *as; + name = sk_GENERAL_NAME_value(names, i); + switch (name->type) { + case GEN_EMAIL: + BIO_puts(bio, "email:"); + as = name->d.rfc822Name; + BIO_write(bio, ASN1_STRING_data(as), + ASN1_STRING_length(as)); + break; + case GEN_DNS: + BIO_puts(bio, "DNS:"); + as = name->d.dNSName; + BIO_write(bio, ASN1_STRING_data(as), + ASN1_STRING_length(as)); + break; + case GEN_URI: + BIO_puts(bio, "URI:"); + as = name->d.uniformResourceIdentifier; + BIO_write(bio, ASN1_STRING_data(as), + ASN1_STRING_length(as)); + break; + default: + /* use builtin print for GEN_OTHERNAME, GEN_X400, + * GEN_EDIPARTY, GEN_DIRNAME, GEN_IPADD and GEN_RID + */ + GENERAL_NAME_print(bio, name); + } + /* trailing ', ' except for last element */ + if (i < (num - 1)) { + BIO_puts(bio, ", "); + } + } + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + + return 0; +} + /* {{{ proto array openssl_x509_parse(mixed x509 [, bool shortnames=true]) Returns an array of the fields/values of the CERT */ PHP_FUNCTION(openssl_x509_parse) @@ -1464,15 +1565,30 @@ PHP_FUNCTION(openssl_x509_parse) for (i = 0; i < X509_get_ext_count(cert); i++) { + int nid; extension = X509_get_ext(cert, i); - if (OBJ_obj2nid(X509_EXTENSION_get_object(extension)) != NID_undef) { + nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension)); + if (nid != NID_undef) { extname = (char *)OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(extension))); } else { OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension), 1); extname = buf; } bio_out = BIO_new(BIO_s_mem()); - if (X509V3_EXT_print(bio_out, extension, 0, 0)) { + if (nid == NID_subject_alt_name) { + if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) { + BIO_get_mem_ptr(bio_out, &bio_buf); + add_assoc_stringl(subitem, extname, bio_buf->data, bio_buf->length, 1); + } else { + zval_dtor(return_value); + if (certresource == -1 && cert) { + X509_free(cert); + } + BIO_free(bio_out); + RETURN_FALSE; + } + } + else if (X509V3_EXT_print(bio_out, extension, 0, 0)) { BIO_get_mem_ptr(bio_out, &bio_buf); add_assoc_stringl(subitem, extname, bio_buf->data, bio_buf->length, 1); } else { @@ -2902,6 +3018,15 @@ static int php_openssl_is_private_key(EVP_PKEY* pkey T } break; #endif +#ifdef HAVE_EVP_PKEY_EC + case EVP_PKEY_EC: + assert(pkey->pkey.ec != NULL); + + if ( NULL == EC_KEY_get0_private_key(pkey->pkey.ec)) { + return 0; + } + break; +#endif default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); break; @@ -3174,6 +3299,7 @@ PHP_FUNCTION(openssl_pkey_get_public) if (pkey == NULL) { RETURN_FALSE; } + zend_list_addref(Z_LVAL_P(return_value)); } /* }}} */ @@ -3210,6 +3336,7 @@ PHP_FUNCTION(openssl_pkey_get_private) if (pkey == NULL) { RETURN_FALSE; } + zend_list_addref(Z_LVAL_P(return_value)); } /* }}} */ @@ -3300,7 +3427,7 @@ PHP_FUNCTION(openssl_pkey_get_details) } break; -#ifdef EVP_PKEY_EC +#ifdef HAVE_EVP_PKEY_EC case EVP_PKEY_EC: ktype = OPENSSL_KEYTYPE_EC; break; @@ -4194,6 +4321,7 @@ PHP_FUNCTION(openssl_seal) if (!EVP_EncryptInit(&ctx,cipher,NULL,NULL)) { RETVAL_FALSE; + EVP_CIPHER_CTX_cleanup(&ctx); goto clean_exit; } @@ -4204,10 +4332,12 @@ PHP_FUNCTION(openssl_seal) #endif /* allocate one byte extra to make room for \0 */ buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(&ctx)); + EVP_CIPHER_CTX_cleanup(&ctx); if (!EVP_SealInit(&ctx, cipher, eks, eksl, NULL, pkeys, nkeys) || !EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { RETVAL_FALSE; efree(buf); + EVP_CIPHER_CTX_cleanup(&ctx); goto clean_exit; } @@ -4240,6 +4370,7 @@ PHP_FUNCTION(openssl_seal) efree(buf); } RETVAL_LONG(len1 + len2); + EVP_CIPHER_CTX_cleanup(&ctx); clean_exit: for (i=0; i= 10001001 ) */ + X509 *cert = NULL; + EVP_PKEY *key = NULL; + SSL *tmpssl = SSL_new(ctx); + cert = SSL_get_certificate(tmpssl); - if (cert) { - key = X509_get_pubkey(cert); - EVP_PKEY_copy_parameters(key, SSL_get_privatekey(tmpssl)); - EVP_PKEY_free(key); - } - SSL_free(tmpssl); - + if (cert) { + key = X509_get_pubkey(cert); + EVP_PKEY_copy_parameters(key, SSL_get_privatekey(tmpssl)); + EVP_PKEY_free(key); + } + SSL_free(tmpssl); + } while (0); +#endif if (!SSL_CTX_check_private_key(ctx)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Private key does not match certificate!"); } @@ -4884,7 +5014,7 @@ PHP_FUNCTION(openssl_cipher_iv_length) /* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key) - Computes shared sicret for public value of remote DH key and local DH key */ + Computes shared secret for public value of remote DH key and local DH key */ PHP_FUNCTION(openssl_dh_compute_key) { zval *key;