diff --git a/Makefile.am b/Makefile.am index c57dd38..58c1d0f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -116,6 +116,8 @@ EXTRA_DIST = \ src/tls_schannel.c \ tests/cert.pem \ tests/cert.pfx \ + tests/cert.emptypass.pfx \ + tests/cert.nopass.pfx \ tests/key.pem \ tests/key_encrypted.pem \ tests/res_query_dump.c diff --git a/src/tls_gnutls.c b/src/tls_gnutls.c index ac3d027..5b14ca6 100644 --- a/src/tls_gnutls.c +++ b/src/tls_gnutls.c @@ -93,10 +93,6 @@ static gnutls_x509_crt_t _tls_load_cert_p12(xmpp_conn_t *conn) gnutls_x509_privkey_t key; unsigned int cert_num = 0, retries = 0; int err = -1; - if (!conn->password_callback) { - strophe_error(conn->ctx, "tls", "No password callback set"); - return NULL; - } if (gnutls_pkcs12_init(&p12) < 0) return NULL; if (gnutls_load_file(conn->tls_client_key, &data) < 0) @@ -104,6 +100,22 @@ static gnutls_x509_crt_t _tls_load_cert_p12(xmpp_conn_t *conn) if (gnutls_pkcs12_import(p12, &data, GNUTLS_X509_FMT_DER, 0) < 0) goto error_out2; + /* First try to open file with no pass */ + if ((err = gnutls_pkcs12_simple_parse(p12, NULL, &key, &cert, &cert_num, + NULL, NULL, NULL, 0)) == 0) { + goto done; + } + /* Now let's try to open file with an empty pass */ + if ((err = gnutls_pkcs12_simple_parse(p12, "", &key, &cert, &cert_num, NULL, + NULL, NULL, 0)) == 0) { + goto done; + } + + if (!conn->password_callback) { + strophe_error(conn->ctx, "tls", "No password callback set"); + goto error_out2; + } + /* ... and only now ask the user for a password */ while (retries++ < conn->password_retries) { char pass[GNUTLS_PKCS11_MAX_PIN_LEN]; int passlen = @@ -125,6 +137,7 @@ static gnutls_x509_crt_t _tls_load_cert_p12(xmpp_conn_t *conn) strophe_debug(conn->ctx, "tls", "wrong password?"); } +done: gnutls_pkcs12_deinit(p12); gnutls_free(data.data); if (err < 0) diff --git a/src/tls_openssl.c b/src/tls_openssl.c index 4a52947..097b666 100644 --- a/src/tls_openssl.c +++ b/src/tls_openssl.c @@ -948,6 +948,25 @@ static X509 *_tls_cert_read_x509(xmpp_conn_t *conn) return c; } +static int _tls_parse_p12(PKCS12 *p12, + const char *pass, + EVP_PKEY **pkey, + X509 **cert, + STACK_OF(X509) * *ca) +{ + /* For some reason `PKCS12_parse()` fails without a `EVP_PKEY` + * so if the user doesn't want it, use a local one and free it + * again directly after parsing. + */ + EVP_PKEY *pkey_; + if (!pkey) + pkey = &pkey_; + int parse_ok = PKCS12_parse(p12, pass, pkey, cert, ca); + if (pkey == &pkey_ && pkey_) + EVP_PKEY_free(pkey_); + return parse_ok; +} + static X509 * _tls_cert_read_p12(xmpp_conn_t *conn, EVP_PKEY **pkey, STACK_OF(X509) * *ca) { @@ -967,6 +986,12 @@ _tls_cert_read_p12(xmpp_conn_t *conn, EVP_PKEY **pkey, STACK_OF(X509) * *ca) goto error_out; } + /* First try to open file w/o a pass */ + if (_tls_parse_p12(p12, NULL, pkey, &cert, ca)) { + goto success; + } + cert = NULL; + unsigned int retries = 0; pem_password_cb *cb = PEM_def_callback; @@ -981,17 +1006,7 @@ _tls_cert_read_p12(xmpp_conn_t *conn, EVP_PKEY **pkey, STACK_OF(X509) * *ca) int passlen = cb(pass, PEM_BUFSIZE, 0, userdata); if (passlen < 0 || passlen > PEM_BUFSIZE) goto error_out; - - /* For some reason `PKCS12_parse()` fails without a `EVP_PKEY` - * so if the user doesn't want it, use a local one and free it - * again directly after parsing. - */ - EVP_PKEY *pkey_; - if (!pkey) - pkey = &pkey_; - int parse_ok = PKCS12_parse(p12, pass, pkey, &cert, ca); - if (pkey == &pkey_ && pkey_) - EVP_PKEY_free(pkey_); + int parse_ok = _tls_parse_p12(p12, pass, pkey, &cert, ca); if (parse_ok) { goto success; } diff --git a/tests/cert.emptypass.pfx b/tests/cert.emptypass.pfx new file mode 100644 index 0000000..b556eee Binary files /dev/null and b/tests/cert.emptypass.pfx differ diff --git a/tests/cert.nopass.pfx b/tests/cert.nopass.pfx new file mode 100644 index 0000000..358c65d Binary files /dev/null and b/tests/cert.nopass.pfx differ diff --git a/tests/test_xmppaddr.c b/tests/test_xmppaddr.c index 485004f..7d542b5 100644 --- a/tests/test_xmppaddr.c +++ b/tests/test_xmppaddr.c @@ -33,10 +33,15 @@ int main() xmpp_ctx_t *ctx; xmpp_conn_t *conn; xmpp_log_t *log; - char *client_cert[][2] = { - {"tests/cert.pem", "tests/key.pem"}, - {"tests/cert.pem", "tests/key_encrypted.pem"}, - {NULL, "tests/cert.pfx"}, + struct { + int needs_callback; + char *pem, *key; + } client_cert[] = { + {0, "tests/cert.pem", "tests/key.pem"}, + {1, "tests/cert.pem", "tests/key_encrypted.pem"}, + {0, NULL, "tests/cert.emptypass.pfx"}, + {0, NULL, "tests/cert.nopass.pfx"}, + {1, NULL, "tests/cert.pfx"}, }; char xmppaddr_num[] = "0"; @@ -49,9 +54,10 @@ int main() for (m = 0; m < sizeof(client_cert) / sizeof(client_cert[0]); ++m) { conn = xmpp_conn_new(ctx); - xmpp_conn_set_password_callback(conn, password_callback, NULL); + if (client_cert[m].needs_callback) + xmpp_conn_set_password_callback(conn, password_callback, NULL); - xmpp_conn_set_client_cert(conn, client_cert[m][0], client_cert[m][1]); + xmpp_conn_set_client_cert(conn, client_cert[m].pem, client_cert[m].key); xmppaddr_num[0] = '0' + xmpp_conn_cert_xmppaddr_num(conn);