/* +----------------------------------------------------------------------+ | Copyright (c) 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 | | available through the world-wide-web at the following url: | | https://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Jakub Zelenka | +----------------------------------------------------------------------+ */ #ifndef PHP_OPENSSL_BACKEND_H #define PHP_OPENSSL_BACKEND_H #ifdef HAVE_CONFIG_H #include #endif #include "php.h" #include "php_openssl.h" #include #include #include #include #include #include #include #include #include #include #include #include /* number conversion flags checks */ #define PHP_OPENSSL_CHECK_NUMBER_CONVERSION(_cond, _name, _arg_num) \ do { \ if (_cond) { \ zend_argument_value_error((_arg_num), #_name" is too long"); \ RETURN_THROWS(); \ } \ } while(0) #define PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NULL_RETURN(_cond, _name) \ do { \ if (_cond) { \ zend_value_error(#_name" is too long"); \ return NULL; \ } \ } while(0) /* check if size_t can be safely casted to int */ #define PHP_OPENSSL_CHECK_SIZE_T_TO_INT(_var, _name, _arg_num) \ PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_SIZE_T_INT_OVFL(_var), _name, _arg_num) #define PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(_var, _name) \ PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NULL_RETURN(ZEND_SIZE_T_INT_OVFL(_var), _name) /* check if size_t can be safely casted to unsigned int */ #define PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(_var, _name, _arg_num) \ PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_SIZE_T_UINT_OVFL(_var), _name, _arg_num) /* check if long can be safely casted to int */ #define PHP_OPENSSL_CHECK_LONG_TO_INT(_var, _name, _arg_num) \ PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_LONG_EXCEEDS_INT(_var), _name, _arg_num) #define PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(_var, _name) \ PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NULL_RETURN(ZEND_LONG_EXCEEDS_INT(_var), _name) /* FIXME: Use the openssl constants instead of * enum. It is now impossible to match real values * against php constants. Also sorry to break the * enum principles here, BC... */ enum php_openssl_key_type { OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, OPENSSL_KEYTYPE_EC, OPENSSL_KEYTYPE_X25519, OPENSSL_KEYTYPE_ED25519, OPENSSL_KEYTYPE_X448, OPENSSL_KEYTYPE_ED448, OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA, }; /* Cipher constants, do not forget to update php_openssl_cipher_names in * openssl_backend_v3.c if new constant added. */ enum php_openssl_cipher_type { PHP_OPENSSL_CIPHER_RC2_40, PHP_OPENSSL_CIPHER_RC2_128, PHP_OPENSSL_CIPHER_RC2_64, PHP_OPENSSL_CIPHER_DES, PHP_OPENSSL_CIPHER_3DES, PHP_OPENSSL_CIPHER_AES_128_CBC, PHP_OPENSSL_CIPHER_AES_192_CBC, PHP_OPENSSL_CIPHER_AES_256_CBC, PHP_OPENSSL_CIPHER_DEFAULT = PHP_OPENSSL_CIPHER_AES_128_CBC }; /* Add some encoding rules. This is normally handled through filters * in the OpenSSL code, but we will do that part as if we were one * of the OpenSSL binaries along the lines of -outform {DER|CMS|PEM} */ enum php_openssl_encoding { ENCODING_DER, ENCODING_SMIME, ENCODING_PEM, }; #define MIN_KEY_LENGTH 384 /* Constants used in ext/phar/util.c, keep in sync and do not forget to update * php_openssl_digest_names in openssl_backend_v3.c if new constant added. */ #define OPENSSL_ALGO_SHA1 1 #define OPENSSL_ALGO_MD5 2 #ifndef OPENSSL_NO_MD4 #define OPENSSL_ALGO_MD4 3 #endif #ifndef OPENSSL_NO_MD2 #define OPENSSL_ALGO_MD2 4 #endif /* Number 5 was used for OPENSSL_ALGO_DSS1 which is no longer available */ #define OPENSSL_ALGO_SHA224 6 #define OPENSSL_ALGO_SHA256 7 #define OPENSSL_ALGO_SHA384 8 #define OPENSSL_ALGO_SHA512 9 #ifndef OPENSSL_NO_RMD160 #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 /* the OPENSSL_EC_EXPLICIT_CURVE value was added * in OpenSSL 1.1.0; previous versions should * use 0 instead. */ #ifndef OPENSSL_EC_EXPLICIT_CURVE #define OPENSSL_EC_EXPLICIT_CURVE 0x000 #endif #endif struct php_x509_request { CONF *global_config; /* Global SSL config */ CONF *req_config; /* SSL config for this request */ const EVP_MD * md_alg; const EVP_MD * digest; char * section_name, * config_filename, * digest_name, * extensions_section, * request_extensions_section; int priv_key_bits; int priv_key_type; int priv_key_encrypt; #ifdef HAVE_EVP_PKEY_EC int curve_name; #endif EVP_PKEY * priv_key; const EVP_CIPHER * priv_key_encrypt_cipher; }; void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname); void php_openssl_add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str); time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr); int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, CONF *config); char *php_openssl_conf_get_string(CONF *conf, const char *group, const char *name); long php_openssl_conf_get_number(CONF *conf, const char *group, const char *name); int php_openssl_add_oid_section(struct php_x509_request * req); int php_openssl_spki_cleanup(const char *src, char *dest); X509 *php_openssl_x509_from_param( zend_object *cert_obj, zend_string *cert_str, uint32_t arg_num); X509 *php_openssl_x509_from_zval( zval *val, bool *free_cert, uint32_t arg_num, bool is_from_array, const char *option_name); X509_REQ *php_openssl_csr_from_param( zend_object *csr_obj, zend_string *csr_str, uint32_t arg_num); EVP_PKEY *php_openssl_pkey_from_zval( zval *val, int public_key, char *passphrase, size_t passphrase_len, uint32_t arg_num); X509_STORE * php_openssl_setup_verify(zval * calist, uint32_t arg_num); STACK_OF(X509) * php_openssl_load_all_certs_from_file( char *cert_file, size_t cert_file_len, uint32_t arg_num); EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req); zend_string *php_openssl_pkey_derive(EVP_PKEY *key, EVP_PKEY *peer_key, size_t key_size); #define PHP_SSL_REQ_INIT(req) memset(req, 0, sizeof(*req)) #define PHP_SSL_REQ_DISPOSE(req) php_openssl_dispose_config(req) #define PHP_SSL_REQ_PARSE(req, zval) php_openssl_parse_config(req, zval) #define PHP_SSL_CONFIG_SYNTAX_CHECK(var) if (req->var && php_openssl_config_check_syntax(#var, \ req->config_filename, req->var, req->req_config) == FAILURE) return FAILURE #define SET_OPTIONAL_STRING_ARG(key, varname, defval) \ do { \ if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_STRING) { \ varname = Z_STRVAL_P(item); \ } else { \ varname = defval; \ if (varname == NULL) { \ php_openssl_store_errors(); \ } \ } \ } while(0) #define SET_OPTIONAL_LONG_ARG(key, varname, defval) \ if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_LONG) \ varname = (int)Z_LVAL_P(item); \ else \ varname = defval const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo); int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args); void php_openssl_dispose_config(struct php_x509_request * req); zend_result php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded); zend_result php_openssl_write_rand_file(const char * file, int egdsocket, int seeded); const EVP_MD *php_openssl_get_evp_md_by_name(const char *name); const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo); void php_openssl_release_evp_md(const EVP_MD *md); const EVP_CIPHER * php_openssl_get_evp_cipher_by_name(const char *name); const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo); void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher); void php_openssl_backend_init(void); void php_openssl_backend_init_common(void); void php_openssl_backend_gshutdown(void); void php_openssl_backend_shutdown(void); #if PHP_OPENSSL_API_VERSION >= 0x30000 void php_openssl_backend_init_libctx(OSSL_LIB_CTX **plibctx, char **ppropq); void php_openssl_backend_destroy_libctx(OSSL_LIB_CTX *libctx, char *propq); #endif const char *php_openssl_get_conf_filename(void); void php_openssl_set_cert_locations(zval *return_value); X509 *php_openssl_x509_from_str( zend_string *cert_str, uint32_t arg_num, bool is_from_array, const char *option_name); X509 *php_openssl_x509_from_param( zend_object *cert_obj, zend_string *cert_str, uint32_t arg_num); X509 *php_openssl_x509_from_zval( zval *val, bool *free_cert, uint32_t arg_num, bool is_from_array, const char *option_name); zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool raw); int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension); STACK_OF(X509) *php_openssl_load_all_certs_from_file( char *cert_file, size_t cert_file_len, uint32_t arg_num); int php_openssl_check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose); X509_STORE *php_openssl_setup_verify(zval *calist, uint32_t arg_num); void php_openssl_sk_X509_free(STACK_OF(X509) * sk); STACK_OF(X509) *php_openssl_array_to_X509_sk(zval * zcerts, uint32_t arg_num, const char *option_name); zend_result php_openssl_csr_add_subj_entry(zval *item, X509_NAME *subj, int nid); zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs); X509_REQ *php_openssl_csr_from_str(zend_string *csr_str, uint32_t arg_num); X509_REQ *php_openssl_csr_from_param( zend_object *csr_obj, zend_string *csr_str, uint32_t arg_num); #if !defined (LIBRESSL_VERSION_NUMBER) #define PHP_OPENSSL_ASN1_INTEGER_set ASN1_INTEGER_set_int64 #else #define PHP_OPENSSL_ASN1_INTEGER_set ASN1_INTEGER_set #endif EVP_PKEY *php_openssl_extract_public_key(EVP_PKEY *priv_key); struct php_openssl_pem_password { char *key; int len; }; EVP_PKEY *php_openssl_pkey_from_zval( zval *val, int public_key, char *passphrase, size_t passphrase_len, uint32_t arg_num); EVP_PKEY *php_openssl_generate_private_key(struct php_x509_request * req); void php_openssl_add_bn_to_array(zval *ary, const BIGNUM *bn, const char *name); void php_openssl_add_bn_to_array(zval *ary, const BIGNUM *bn, const char *name); #define OPENSSL_PKEY_GET_BN(_type, _name) php_openssl_add_bn_to_array(&_type, _name, #_name) #define OPENSSL_PKEY_SET_BN(_data, _name) do { \ zval *bn; \ if ((bn = zend_hash_str_find(Z_ARRVAL_P(_data), #_name, sizeof(#_name)-1)) != NULL && \ Z_TYPE_P(bn) == IS_STRING) { \ _name = BN_bin2bn( \ (unsigned char*)Z_STRVAL_P(bn), \ (int)Z_STRLEN_P(bn), NULL); \ } else { \ _name = NULL; \ } \ } while (0); EVP_PKEY_CTX *php_openssl_pkey_new_from_name(const char *name, int id); EVP_PKEY_CTX *php_openssl_pkey_new_from_pkey(EVP_PKEY *pkey); EVP_PKEY *php_openssl_pkey_init_rsa(zval *data); EVP_PKEY *php_openssl_pkey_init_dsa(zval *data, bool *is_private); BIGNUM *php_openssl_dh_pub_from_priv(BIGNUM *priv_key, BIGNUM *g, BIGNUM *p); EVP_PKEY *php_openssl_pkey_init_dh(zval *data, bool *is_private); EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private); #if PHP_OPENSSL_API_VERSION >= 0x30000 void php_openssl_pkey_object_curve_25519_448(zval *return_value, const char *name, zval *data); #endif zend_long php_openssl_pkey_get_details(zval *return_value, EVP_PKEY *pkey); zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pub_len); BIO *php_openssl_bio_new_file( const char *filename, size_t filename_len, uint32_t arg_num, const char *mode); void php_openssl_add_method_or_alias(const OBJ_NAME *name, void *arg); void php_openssl_add_method(const OBJ_NAME *name, void *arg); void php_openssl_get_md_methods(zval *return_value, bool aliases); void php_openssl_get_cipher_methods(zval *return_value, bool aliases); /* Cipher mode info */ struct php_openssl_cipher_mode { bool is_aead; bool is_single_run_aead; bool set_tag_length_always; bool set_tag_length_when_encrypting; int aead_get_tag_flag; int aead_set_tag_flag; int aead_ivlen_flag; }; static inline void php_openssl_set_aead_flags(struct php_openssl_cipher_mode *mode) { mode->is_aead = true; mode->aead_get_tag_flag = EVP_CTRL_AEAD_GET_TAG; mode->aead_set_tag_flag = EVP_CTRL_AEAD_SET_TAG; mode->aead_ivlen_flag = EVP_CTRL_AEAD_SET_IVLEN; } void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type); zend_result php_openssl_validate_iv(const char **piv, size_t *piv_len, size_t iv_required_len, bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode); zend_result php_openssl_cipher_init(const EVP_CIPHER *cipher_type, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode, const char **ppassword, size_t *ppassword_len, bool *free_password, const char **piv, size_t *piv_len, bool *free_iv, const char *tag, int tag_len, zend_long options, int enc); zend_result php_openssl_cipher_update(const EVP_CIPHER *cipher_type, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode, zend_string **poutbuf, int *poutlen, const char *data, size_t data_len, const char *aad, size_t aad_len, int enc); const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *method); #endif