Skip to content

OpenSSL extension has many inconsistencies in how errors are handled/reported/propagated #21643

@ndossche

Description

@ndossche

Description

The OpenSSL extension has a number of error-handling inconsistencies that I'm unsure how to handle. We should come to a consensus on how to move forward.
Inconsistencies include:

  • Failure reason is not properly communicated to the caller of the function, or can't be distinguished at the call site.
  • Some functions emit warnings, some don't.
  • Some error-handling paths call php_openssl_store_errors(), some don't.
  • Some error-handling paths call php_openssl_store_errors() but still return a success.

This means that partial success isn't obviously reported to the user in some cases, so theoretically they'd always have to check the openssl_error_string() function even if the function returned success, which isn't great.

Some examples

Partial failure example (error stored but loop not aborted, may be intentional but this isn't communicated):

} else {
php_openssl_store_errors();
}


The following two calls may fail because of an inexistent file or some I/O failure

if (is_file) {
in = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
} else {
in = BIO_new_mem_buf(ZSTR_VAL(val_str), (int)ZSTR_LEN(val_str));
}

but at least one of the callsites can't distinguish between different kinds of failure:

key = php_openssl_pkey_from_zval(zkey, 0, "", 0, 2);
if (key) {
RETVAL_BOOL(X509_check_private_key(cert, key));
EVP_PKEY_free(key);
}


Very similarly to the previous one, different kinds of I/O or validation failures are not distinguished here:

X509 *php_openssl_x509_from_str(


Example of silently swallowing errors without returning a different return value or without emitting a warning; even though this same function emits warnings for other cases:

if (nfiles == 0) {
file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (file_lookup == NULL || !X509_LOOKUP_load_file(file_lookup, NULL, X509_FILETYPE_DEFAULT)) {
php_openssl_store_errors();
}
}
if (ndirs == 0) {
dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
if (dir_lookup == NULL || !X509_LOOKUP_add_dir(dir_lookup, NULL, X509_FILETYPE_DEFAULT)) {
php_openssl_store_errors();
}
}


There's a bunch of calls to php_openssl_conf_get_string that silently ignore errors: because e.g. the key may be optional but note that a configuration error can also result in a silent failure.

For example here:

str = php_openssl_conf_get_string(req->req_config, NULL, "oid_section");
if (str == NULL) {
return SUCCESS;
}

but also in various spots in

int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args)

etc...


Usage of E_ERROR for some reason, which sometimes could be appropriate but sometimes isn't.
Examples:

csc = X509_STORE_CTX_new();
if (csc == NULL) {
php_openssl_store_errors();
php_error_docref(NULL, E_ERROR, "Memory allocation failure");
return 0;
}

here we're dealing with allocation failure so it may be appropriate.

However, here it seems inappropriate:

} else if (!X509_digest(peer, mdtype, md, &n)) {
php_openssl_store_errors();
php_error_docref(NULL, E_ERROR, "Could not generate signature");
return NULL;
}


These are just some examples, but shows a much broader problem with the error-handling design of the extension.

PHP Version

Report was made on master, but applies to all versions really.

Operating System

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions