Be user-friendly when opening a pfx/p12 file
Before this change the user had to provide a password callback, even if the PKCS#12 encoded file has no or an empty password. This changes the behavior, so we first try to open the file without a password and only then ask the user to provide one. This fixes #204. Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
This commit is contained in:
@@ -116,6 +116,8 @@ EXTRA_DIST = \
|
|||||||
src/tls_schannel.c \
|
src/tls_schannel.c \
|
||||||
tests/cert.pem \
|
tests/cert.pem \
|
||||||
tests/cert.pfx \
|
tests/cert.pfx \
|
||||||
|
tests/cert.emptypass.pfx \
|
||||||
|
tests/cert.nopass.pfx \
|
||||||
tests/key.pem \
|
tests/key.pem \
|
||||||
tests/key_encrypted.pem \
|
tests/key_encrypted.pem \
|
||||||
tests/res_query_dump.c
|
tests/res_query_dump.c
|
||||||
|
|||||||
@@ -93,10 +93,6 @@ static gnutls_x509_crt_t _tls_load_cert_p12(xmpp_conn_t *conn)
|
|||||||
gnutls_x509_privkey_t key;
|
gnutls_x509_privkey_t key;
|
||||||
unsigned int cert_num = 0, retries = 0;
|
unsigned int cert_num = 0, retries = 0;
|
||||||
int err = -1;
|
int err = -1;
|
||||||
if (!conn->password_callback) {
|
|
||||||
strophe_error(conn->ctx, "tls", "No password callback set");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (gnutls_pkcs12_init(&p12) < 0)
|
if (gnutls_pkcs12_init(&p12) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (gnutls_load_file(conn->tls_client_key, &data) < 0)
|
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)
|
if (gnutls_pkcs12_import(p12, &data, GNUTLS_X509_FMT_DER, 0) < 0)
|
||||||
goto error_out2;
|
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) {
|
while (retries++ < conn->password_retries) {
|
||||||
char pass[GNUTLS_PKCS11_MAX_PIN_LEN];
|
char pass[GNUTLS_PKCS11_MAX_PIN_LEN];
|
||||||
int passlen =
|
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?");
|
strophe_debug(conn->ctx, "tls", "wrong password?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
gnutls_pkcs12_deinit(p12);
|
gnutls_pkcs12_deinit(p12);
|
||||||
gnutls_free(data.data);
|
gnutls_free(data.data);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|||||||
@@ -948,6 +948,25 @@ static X509 *_tls_cert_read_x509(xmpp_conn_t *conn)
|
|||||||
return c;
|
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 *
|
static X509 *
|
||||||
_tls_cert_read_p12(xmpp_conn_t *conn, EVP_PKEY **pkey, STACK_OF(X509) * *ca)
|
_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;
|
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;
|
unsigned int retries = 0;
|
||||||
|
|
||||||
pem_password_cb *cb = PEM_def_callback;
|
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);
|
int passlen = cb(pass, PEM_BUFSIZE, 0, userdata);
|
||||||
if (passlen < 0 || passlen > PEM_BUFSIZE)
|
if (passlen < 0 || passlen > PEM_BUFSIZE)
|
||||||
goto error_out;
|
goto error_out;
|
||||||
|
int parse_ok = _tls_parse_p12(p12, pass, pkey, &cert, 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_);
|
|
||||||
if (parse_ok) {
|
if (parse_ok) {
|
||||||
goto success;
|
goto success;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
tests/cert.emptypass.pfx
Normal file
BIN
tests/cert.emptypass.pfx
Normal file
Binary file not shown.
BIN
tests/cert.nopass.pfx
Normal file
BIN
tests/cert.nopass.pfx
Normal file
Binary file not shown.
@@ -33,10 +33,15 @@ int main()
|
|||||||
xmpp_ctx_t *ctx;
|
xmpp_ctx_t *ctx;
|
||||||
xmpp_conn_t *conn;
|
xmpp_conn_t *conn;
|
||||||
xmpp_log_t *log;
|
xmpp_log_t *log;
|
||||||
char *client_cert[][2] = {
|
struct {
|
||||||
{"tests/cert.pem", "tests/key.pem"},
|
int needs_callback;
|
||||||
{"tests/cert.pem", "tests/key_encrypted.pem"},
|
char *pem, *key;
|
||||||
{NULL, "tests/cert.pfx"},
|
} 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";
|
char xmppaddr_num[] = "0";
|
||||||
@@ -49,9 +54,10 @@ int main()
|
|||||||
for (m = 0; m < sizeof(client_cert) / sizeof(client_cert[0]); ++m) {
|
for (m = 0; m < sizeof(client_cert) / sizeof(client_cert[0]); ++m) {
|
||||||
conn = xmpp_conn_new(ctx);
|
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);
|
xmppaddr_num[0] = '0' + xmpp_conn_cert_xmppaddr_num(conn);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user