pkcs11.c
ce98fd24
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
564a2109
  *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
ce98fd24
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  as published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program (see the file COPYING included with this
  *  distribution); if not, write to the Free Software Foundation, Inc.,
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include "syshead.h"
be38c051
 
 #if defined(ENABLE_PKCS11)
 
718526e0
 #include <pkcs11-helper-1.0/pkcs11h-certificate.h>
 #include <pkcs11-helper-1.0/pkcs11h-openssl.h>
 #include "basic.h"
 #include "error.h"
 #include "manage.h"
1bda73a7
 #include "base64.h"
ce98fd24
 #include "pkcs11.h"
 
 static
718526e0
 time_t
 __mytime (void) {
 	return openvpn_time (NULL);
 }
 
 #if !defined(_WIN32)
 static
 int
 __mygettimeofday (struct timeval *tv) {
 	return gettimeofday (tv, NULL);
 }
 #endif
 
 static
 void
 __mysleep (const unsigned long usec) {
 #if defined(_WIN32)
 	Sleep (usec/1000);
 #else
 	usleep (usec);
 #endif
 }
 
 
 static pkcs11h_engine_system_t s_pkcs11h_sys_engine = {
 	malloc,
 	free,
 	__mytime,
 	__mysleep,
 #if defined(_WIN32)
 	NULL
 #else
 	__mygettimeofday
 #endif
 };
 
 static
18597b93
 unsigned
 _pkcs11_msg_pkcs112openvpn (
718526e0
 	const unsigned flags
18597b93
 ) {
 	unsigned openvpn_flags;
 
 	switch (flags) {
 		case PKCS11H_LOG_DEBUG2:
 			openvpn_flags = D_PKCS11_DEBUG;
 		break;
 		case PKCS11H_LOG_DEBUG1:
 			openvpn_flags = D_SHOW_PKCS11;
 		break;
 		case PKCS11H_LOG_INFO:
 			openvpn_flags = M_INFO;
 		break;
 		case PKCS11H_LOG_WARN:
 			openvpn_flags = M_WARN;
 		break;
 		case PKCS11H_LOG_ERROR:
 			openvpn_flags = M_FATAL;
 		break;
 		default:
 			openvpn_flags = M_FATAL;
 		break;
 	}
 
 #if defined(ENABLE_PKCS11_FORCE_DEBUG)
 	openvpn_flags=M_INFO;
 #endif
 
 	return openvpn_flags;
 }
 
 static
 unsigned
 _pkcs11_msg_openvpn2pkcs11 (
718526e0
 	const unsigned flags
18597b93
 ) {
 	unsigned pkcs11_flags;
 
 	if ((flags & D_PKCS11_DEBUG) != 0) {
 		pkcs11_flags = PKCS11H_LOG_DEBUG2;
 	}
 	else if ((flags & D_SHOW_PKCS11) != 0) {
 		pkcs11_flags = PKCS11H_LOG_DEBUG1;
 	}
 	else if ((flags & M_INFO) != 0) {
 		pkcs11_flags = PKCS11H_LOG_INFO;
 	}
 	else if ((flags & M_WARN) != 0) {
 		pkcs11_flags = PKCS11H_LOG_WARN;
 	}
 	else if ((flags & M_FATAL) != 0) {
 		pkcs11_flags = PKCS11H_LOG_ERROR;
 	}
 	else {
 		pkcs11_flags = PKCS11H_LOG_ERROR;
 	}
 
 #if defined(ENABLE_PKCS11_FORCE_DEBUG)
 	pkcs11_flags = PKCS11H_LOG_DEBUG2;
 #endif
 
 	return pkcs11_flags;
 }
 
 static
ce98fd24
 void
18597b93
 _pkcs11_openvpn_log (
718526e0
 	void * const global_data,
 	unsigned flags,
 	const char * const szFormat,
 	va_list args
18597b93
 ) {
 	char Buffer[10*1024];
b110c9c4
 
 	(void)global_data;
18597b93
 	
 	vsnprintf (Buffer, sizeof (Buffer), szFormat, args);
 	Buffer[sizeof (Buffer)-1] = 0;
 
 	msg (_pkcs11_msg_pkcs112openvpn (flags), "%s", Buffer);
 }
 
 static
718526e0
 PKCS11H_BOOL
18597b93
 _pkcs11_openvpn_token_prompt (
718526e0
 	void * const global_data,
 	void * const user_data,
 	const pkcs11h_token_id_t token,
 	const unsigned retry
6835555e
 ) {
1bda73a7
 	struct user_pass token_resp;
6835555e
 
718526e0
 	(void)global_data;
 	(void)user_data;
14a4962a
 	(void)retry;
718526e0
 
18597b93
 	ASSERT (token!=NULL);
6835555e
 
33c8c4d4
 	CLEAR (token_resp);
 	token_resp.defined = false;
 	token_resp.nocache = true;
18597b93
 	openvpn_snprintf (
 		token_resp.username,
 		sizeof (token_resp.username),
 		"Please insert %s token",
 		token->label
 	);
6835555e
 
1d89886e
 	if (
 		!get_user_pass (
 			&token_resp,
 			NULL,
 			"token-insertion-request",
 			GET_USER_PASS_MANAGEMENT|GET_USER_PASS_NEED_OK|GET_USER_PASS_NOFATAL
 		)
 	) {
 		return false;
 	}
 	else {
 		return strcmp (token_resp.password, "ok") == 0;
 	}
6835555e
 }
 
 static
65433d74
 PKCS11H_BOOL
984cf003
 _pkcs11_openvpn_pin_prompt (
718526e0
 	void * const global_data,
 	void * const user_data,
 	const pkcs11h_token_id_t token,
 	const unsigned retry,
 	char * const pin,
 	const size_t pin_max
6835555e
 ) {
1bda73a7
 	struct user_pass token_pass;
718526e0
 	char prompt[1024];
6835555e
 
718526e0
 	(void)global_data;
 	(void)user_data;
14a4962a
 	(void)retry;
718526e0
 
18597b93
 	ASSERT (token!=NULL);
6835555e
 
718526e0
 	openvpn_snprintf (prompt, sizeof (prompt), "%s token", token->label);
6835555e
 
 	token_pass.defined = false;
 	token_pass.nocache = true;
718526e0
 	
1d89886e
 	if (
 		!get_user_pass (
 			&token_pass,
 			NULL,
718526e0
 			prompt,
1d89886e
 			GET_USER_PASS_MANAGEMENT|GET_USER_PASS_PASSWORD_ONLY|GET_USER_PASS_NOFATAL
 		)
 	) {
6835555e
 		return false;
 	}
 	else {
718526e0
 		strncpynt (pin, token_pass.password, pin_max);
1d89886e
 		purge_user_pass (&token_pass, true);
 
718526e0
 		if (strlen (pin) == 0) {
1d89886e
 			return false;
 		}
 		else {
 			return true;
 		}
6835555e
 	}
 }
 
33c8c4d4
 bool
984cf003
 pkcs11_initialize (
718526e0
 	const bool protected_auth,
 	const int nPINCachePeriod
6835555e
 ) {
718526e0
 	CK_RV rv = CKR_FUNCTION_FAILED;
6835555e
 
18597b93
 	dmsg (
 		D_PKCS11_DEBUG,
984cf003
 		"PKCS#11: pkcs11_initialize - entered"
6835555e
 	);
 
718526e0
 	if ((rv = pkcs11h_engine_setSystem (&s_pkcs11h_sys_engine)) != CKR_OK) {
 		msg (M_FATAL, "PKCS#11: Cannot initialize system engine %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	if ((rv = pkcs11h_initialize ()) != CKR_OK) {
18597b93
 		msg (M_FATAL, "PKCS#11: Cannot initialize %ld-'%s'", rv, pkcs11h_getMessage (rv));
718526e0
 		goto cleanup;
6835555e
 	}
984cf003
 
718526e0
 	if ((rv = pkcs11h_setLogHook (_pkcs11_openvpn_log, NULL)) != CKR_OK) {
18597b93
 		msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
718526e0
 		goto cleanup;
18597b93
 	}
 
718526e0
 	pkcs11h_setLogLevel (_pkcs11_msg_openvpn2pkcs11 (get_debug_level ()));
 
 	if ((rv = pkcs11h_setForkMode (TRUE)) != CKR_OK) {
 		msg (M_FATAL, "PKCS#11: Cannot set fork mode %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
18597b93
 	}
 
718526e0
 	if ((rv = pkcs11h_setTokenPromptHook (_pkcs11_openvpn_token_prompt, NULL)) != CKR_OK) {
18597b93
 		msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
718526e0
 		goto cleanup;
6835555e
 	}
984cf003
 
718526e0
 	if ((rv = pkcs11h_setPINPromptHook (_pkcs11_openvpn_pin_prompt, NULL)) != CKR_OK) {
18597b93
 		msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
718526e0
 		goto cleanup;
18597b93
 	}
 
718526e0
 	if ((rv = pkcs11h_setProtectedAuthentication (protected_auth)) != CKR_OK) {
18597b93
 		msg (M_FATAL, "PKCS#11: Cannot set protected authentication mode %ld-'%s'", rv, pkcs11h_getMessage (rv));
718526e0
 		goto cleanup;
6835555e
 	}
 
718526e0
 	if ((rv = pkcs11h_setPINCachePeriod (nPINCachePeriod)) != CKR_OK) {
 		msg (M_FATAL, "PKCS#11: Cannot set Pcache period %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
6835555e
 	}
 
718526e0
 	rv = CKR_OK;
 
 cleanup:
18597b93
 	dmsg (
 		D_PKCS11_DEBUG,
33c8c4d4
 		"PKCS#11: pkcs11_initialize - return %ld-'%s'",
 		rv,
 		pkcs11h_getMessage (rv)
6835555e
 	);
33c8c4d4
 
 	return rv == CKR_OK;
6835555e
 }
 
 void
984cf003
 pkcs11_terminate () {
18597b93
 	dmsg (
 		D_PKCS11_DEBUG,
984cf003
 		"PKCS#11: pkcs11_terminate - entered"
6835555e
 	);
 
984cf003
 	pkcs11h_terminate ();
6835555e
 
18597b93
 	dmsg (
 		D_PKCS11_DEBUG,
984cf003
 		"PKCS#11: pkcs11_terminate - return"
6835555e
 	);
 }
 
 void
984cf003
 pkcs11_forkFixup () {
 	pkcs11h_forkFixup ();
6835555e
 }
 
33c8c4d4
 bool
984cf003
 pkcs11_addProvider (
718526e0
 	const char * const provider,
 	const bool protected_auth,
 	const unsigned private_mode,
 	const bool cert_private
6835555e
 ) {
33c8c4d4
 	CK_RV rv = CKR_OK;
6835555e
 
18597b93
 	ASSERT (provider!=NULL);
 
 	dmsg (
 		D_PKCS11_DEBUG,
718526e0
 		"PKCS#11: pkcs11_addProvider - entered - provider='%s', private_mode=%08x",
6835555e
 		provider,
718526e0
 		private_mode
6835555e
 	);
 
18597b93
 	msg (
 		M_INFO,
6835555e
 		"PKCS#11: Adding PKCS#11 provider '%s'",
 		provider
 	);
 
33c8c4d4
 	if (
18597b93
 		(rv = pkcs11h_addProvider (
 			provider,
 			provider,
718526e0
 			protected_auth,
 			private_mode,
18597b93
 			PKCS11H_SLOTEVENT_METHOD_AUTO,
 			0,
718526e0
 			cert_private
18597b93
 		)) != CKR_OK
33c8c4d4
 	) {
18597b93
 		msg (M_WARN, "PKCS#11: Cannot initialize provider '%s' %ld-'%s'", provider, rv, pkcs11h_getMessage (rv));
6835555e
 	}
 
18597b93
 	dmsg (
 		D_PKCS11_DEBUG,
33c8c4d4
 		"PKCS#11: pkcs11_addProvider - return rv=%ld-'%s'",
 		rv,
 		pkcs11h_getMessage (rv)
6835555e
 	);
33c8c4d4
 
 	return rv == CKR_OK;
6835555e
 }
 
ce98fd24
 int
718526e0
 pkcs11_logout() {
 	return pkcs11h_logout () == CKR_OK;
 }
 
 int
1bda73a7
 pkcs11_management_id_count () {
 	pkcs11h_certificate_id_list_t id_list = NULL;
 	pkcs11h_certificate_id_list_t t = NULL;
 	CK_RV rv = CKR_OK;
 	int count = 0;
 
 	dmsg (
 		D_PKCS11_DEBUG,
 		"PKCS#11: pkcs11_management_id_count - entered"
 	);
 
 	if (
 		(rv = pkcs11h_certificate_enumCertificateIds (
 			PKCS11H_ENUM_METHOD_CACHE_EXIST,
 			NULL,
 			PKCS11H_PROMPT_MASK_ALLOW_ALL,
 			NULL,
 			&id_list
 		)) != CKR_OK
 	) {
 		msg (M_WARN, "PKCS#11: Cannot get certificate list %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	for (count = 0, t = id_list; t != NULL; t = t->next) {
 		count++;
 	}
 
 cleanup:
 
 	if (id_list != NULL) {
 		pkcs11h_certificate_freeCertificateIdList (id_list);
 		id_list = NULL;
 	}
 
 	dmsg (
 		D_PKCS11_DEBUG,
 		"PKCS#11: pkcs11_management_id_count - return count=%d",
 		count
 	);
 
 	return count;
 }
 
 bool
 pkcs11_management_id_get (
 	const int index,
 	char ** id,
 	char **base64
 ) {
 	pkcs11h_certificate_id_list_t id_list = NULL;
 	pkcs11h_certificate_id_list_t entry = NULL;
c373382c
 #if 0 /* certificate_id seems to be unused -- JY */
1bda73a7
 	pkcs11h_certificate_id_t certificate_id = NULL;
c373382c
 #endif
1bda73a7
 	pkcs11h_certificate_t certificate = NULL;
 	CK_RV rv = CKR_OK;
b110c9c4
 	unsigned char *certificate_blob = NULL;
1bda73a7
 	size_t certificate_blob_size = 0;
 	size_t max;
 	char *internal_id = NULL;
 	char *internal_base64 = NULL;
 	int count = 0;
 	bool success = false;
 
 	ASSERT (id!=NULL);
 	ASSERT (base64!=NULL);
 
 	dmsg (
 		D_PKCS11_DEBUG,
 		"PKCS#11: pkcs11_management_id_get - entered index=%d",
 		index
 	);
 
 	*id = NULL;
 	*base64 = NULL;
 
 	if (
 		(rv = pkcs11h_certificate_enumCertificateIds (
 			PKCS11H_ENUM_METHOD_CACHE_EXIST,
 			NULL,
 			PKCS11H_PROMPT_MASK_ALLOW_ALL,
 			NULL,
 			&id_list
 		)) != CKR_OK
 	) {
 		msg (M_WARN, "PKCS#11: Cannot get certificate list %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	entry = id_list;
 	count = 0;
 	while (entry != NULL && count != index) {
 		count++;
 		entry = entry->next;
 	}
 
 	if (entry == NULL) {
 		dmsg (
 			D_PKCS11_DEBUG,
 			"PKCS#11: pkcs11_management_id_get - no certificate at index=%d",
 			index
 		);
 		goto cleanup;
 	}
 
 	if (
 		(rv = pkcs11h_certificate_serializeCertificateId (
 			NULL,
 			&max,
 			entry->certificate_id
 		)) != CKR_OK
 	) {
 		msg (M_WARN, "PKCS#11: Cannot serialize certificate id %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	if ((internal_id = (char *)malloc (max)) == NULL) {
 		msg (M_FATAL, "PKCS#11: Cannot allocate memory");
 		goto cleanup;
 	}
 
 	if (
 		(rv = pkcs11h_certificate_serializeCertificateId (
 			internal_id,
 			&max,
 			entry->certificate_id
 		)) != CKR_OK
 	) {
 		msg (M_WARN, "PKCS#11: Cannot serialize certificate id %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	if (
 		(rv = pkcs11h_certificate_create (
 			entry->certificate_id,
 			NULL,
 			PKCS11H_PROMPT_MASK_ALLOW_ALL,
 			PKCS11H_PIN_CACHE_INFINITE,
 			&certificate
 		)) != CKR_OK
 	) {
 		msg (M_WARN, "PKCS#11: Cannot get certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	if (
 		(rv = pkcs11h_certificate_getCertificateBlob (
 			certificate,
 			NULL,
 			&certificate_blob_size
 		)) != CKR_OK
 	) {
 		msg (M_WARN, "PKCS#11: Cannot get certificate blob %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
b110c9c4
 	if ((certificate_blob = (unsigned char *)malloc (certificate_blob_size)) == NULL) {
1bda73a7
 		msg (M_FATAL, "PKCS#11: Cannot allocate memory");
 		goto cleanup;
 	}
 
 	if (
 		(rv = pkcs11h_certificate_getCertificateBlob (
 			certificate,
 			certificate_blob,
 			&certificate_blob_size
 		)) != CKR_OK
 	) {
 		msg (M_WARN, "PKCS#11: Cannot get certificate blob %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	if (base64_encode (certificate_blob, certificate_blob_size, &internal_base64) == -1) {
 		msg (M_WARN, "PKCS#11: Cannot encode certificate");
 		goto cleanup;
 	}
 
 	*id = internal_id;
 	internal_id = NULL;
 	*base64 = internal_base64;
 	internal_base64 = NULL;
 	success = true;
 	
 cleanup:
 
 	if (id_list != NULL) {
 		pkcs11h_certificate_freeCertificateIdList (id_list);
 		id_list = NULL;
 	}
 
 	if (internal_id != NULL) {
 		free (internal_id);
 		internal_id = NULL;
 	}
 
 	if (internal_base64 != NULL) {
 		free (internal_base64);
 		internal_base64 = NULL;
 	}
 
 	if (certificate_blob != NULL) {
 		free (certificate_blob);
 		certificate_blob = NULL;
 	}
 
 	dmsg (
 		D_PKCS11_DEBUG,
 		"PKCS#11: pkcs11_management_id_get - return success=%d, id='%s'",
 		success ? 1 : 0,
 		*id
 	);
 
 	return success;
 }
 
 int
ce98fd24
 SSL_CTX_use_pkcs11 (
718526e0
 	SSL_CTX * const ssl_ctx,
1bda73a7
 	bool pkcs11_id_management,
718526e0
 	const char * const pkcs11_id
ce98fd24
 ) {
 	X509 *x509 = NULL;
 	RSA *rsa = NULL;
18597b93
 	pkcs11h_certificate_id_t certificate_id = NULL;
 	pkcs11h_certificate_t certificate = NULL;
 	pkcs11h_openssl_session_t openssl_session = NULL;
ce98fd24
 	CK_RV rv = CKR_OK;
 
718526e0
 	bool ok = false;
ce98fd24
 
18597b93
 	ASSERT (ssl_ctx!=NULL);
1bda73a7
 	ASSERT (pkcs11_id_management || pkcs11_id!=NULL);
18597b93
 
 	dmsg (
 		D_PKCS11_DEBUG,
1bda73a7
 		"PKCS#11: SSL_CTX_use_pkcs11 - entered - ssl_ctx=%p, pkcs11_id_management=%d, pkcs11_id='%s'",
6835555e
 		(void *)ssl_ctx,
1bda73a7
 		pkcs11_id_management ? 1 : 0,
18597b93
 		pkcs11_id
ce98fd24
 	);
 
1bda73a7
 	if (pkcs11_id_management) {
 		struct user_pass id_resp;
 
 		CLEAR (id_resp);
 
 		id_resp.defined = false;
 		id_resp.nocache = true;
 		openvpn_snprintf (
 			id_resp.username,
 			sizeof (id_resp.username),
 			"Please specify PKCS#11 id to use"
 		);
 
 		if (
 			!get_user_pass (
 				&id_resp,
 				NULL,
 				"pkcs11-id-request",
 				GET_USER_PASS_MANAGEMENT|GET_USER_PASS_NEED_STR|GET_USER_PASS_NOFATAL
 			)
 		) {
 			goto cleanup;
 		}
 
 		if (
 			(rv = pkcs11h_certificate_deserializeCertificateId (
 				&certificate_id,
 				id_resp.password
 			)) != CKR_OK
 		) {
 			msg (M_WARN, "PKCS#11: Cannot deserialize id %ld-'%s'", rv, pkcs11h_getMessage (rv));
 			goto cleanup;
 		}
 	}
 	else {
 		if (
 			(rv = pkcs11h_certificate_deserializeCertificateId (
 				&certificate_id,
 				pkcs11_id
 			)) != CKR_OK
 		) {
 			msg (M_WARN, "PKCS#11: Cannot deserialize id %ld-'%s'", rv, pkcs11h_getMessage (rv));
 			goto cleanup;
 		}
ce98fd24
 	}
 
 	if (
18597b93
 		(rv = pkcs11h_certificate_create (
 			certificate_id,
718526e0
 			NULL,
 			PKCS11H_PROMPT_MASK_ALLOW_ALL,
33c8c4d4
 			PKCS11H_PIN_CACHE_INFINITE,
18597b93
 			&certificate
ce98fd24
 		)) != CKR_OK
 	) {
18597b93
 		msg (M_WARN, "PKCS#11: Cannot get certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
718526e0
 		goto cleanup;
ce98fd24
 	}
 
718526e0
 	if ((openssl_session = pkcs11h_openssl_createSession (certificate)) == NULL	) {
18597b93
 		msg (M_WARN, "PKCS#11: Cannot initialize openssl session");
718526e0
 		goto cleanup;
18597b93
 	}
 
718526e0
 	/*
 	 * Will be released by openssl_session
 	 */
 	certificate = NULL;
ce98fd24
 
718526e0
 	if ((rsa = pkcs11h_openssl_session_getRSA (openssl_session)) == NULL) {
18597b93
 		msg (M_WARN, "PKCS#11: Unable get rsa object");
718526e0
 		goto cleanup;
18597b93
 	}
 
718526e0
 	if ((x509 = pkcs11h_openssl_session_getX509 (openssl_session)) == NULL) {
18597b93
 		msg (M_WARN, "PKCS#11: Unable get certificate object");
718526e0
 		goto cleanup;
ce98fd24
 	}
 
718526e0
 	if (!SSL_CTX_use_RSAPrivateKey (ssl_ctx, rsa)) {
18597b93
 		msg (M_WARN, "PKCS#11: Cannot set private key for openssl");
718526e0
 		goto cleanup;
ce98fd24
 	}
 
718526e0
 	if (!SSL_CTX_use_certificate (ssl_ctx, x509)) {
18597b93
 		msg (M_WARN, "PKCS#11: Cannot set certificate for openssl");
718526e0
 		goto cleanup;
ce98fd24
 	}
 
718526e0
 	ok = true;
 
 cleanup:
ce98fd24
 	/*
 	 * openssl objects have reference
 	 * count, so release them
 	 */
 
 	if (x509 != NULL) {
 		X509_free (x509);
 		x509 = NULL;
 	}
 
 	if (rsa != NULL) {
 		RSA_free (rsa);
 		rsa = NULL;
 	}
18597b93
 
 	if (certificate != NULL) {
718526e0
 		pkcs11h_certificate_freeCertificate (certificate);
18597b93
 		certificate = NULL;
 	}
 
 	if (certificate_id != NULL) {
718526e0
 		pkcs11h_certificate_freeCertificateId (certificate_id);
18597b93
 		certificate_id = NULL;
 	}
984cf003
 	
18597b93
 	if (openssl_session != NULL) {
 		pkcs11h_openssl_freeSession (openssl_session);
 		openssl_session = NULL;
ce98fd24
 	}
 
18597b93
 	dmsg (
 		D_PKCS11_DEBUG,
718526e0
 		"PKCS#11: SSL_CTX_use_pkcs11 - return ok=%d, rv=%ld",
 		ok ? 1 : 0,
ce98fd24
 		rv
 	);
 
718526e0
 	return ok ? 1 : 0;
ce98fd24
 }
 
718526e0
 static
 bool
 _pkcs11_openvpn_show_pkcs11_ids_pin_prompt (
 	void * const global_data,
 	void * const user_data,
 	const pkcs11h_token_id_t token,
 	const unsigned retry,
 	char * const pin,
 	const size_t pin_max
ce98fd24
 ) {
718526e0
 	struct gc_arena gc = gc_new ();
 	struct buffer pass_prompt = alloc_buf_gc (128, &gc);
 
 	(void)global_data;
 	(void)user_data;
 	(void)retry;
 
 	ASSERT (token!=NULL);
 
 	buf_printf (&pass_prompt, "Please enter '%s' token PIN or 'cancel': ", token->display);
 
 	if (!get_console_input (BSTR (&pass_prompt), false, pin, pin_max)) {
 		msg (M_FATAL, "Cannot read password from stdin");
 	}
 
 	gc_free (&gc);
 
 	if (!strcmp (pin, "cancel")) {
 		return FALSE;
 	}
 	else {
 		return TRUE;
 	}
ce98fd24
 }
 
 void
718526e0
 show_pkcs11_ids (
984cf003
 	const char * const provider,
718526e0
 	bool cert_private
ce98fd24
 ) {
718526e0
 	pkcs11h_certificate_id_list_t user_certificates = NULL;
 	pkcs11h_certificate_id_list_t current = NULL;
 	CK_RV rv = CKR_FUNCTION_FAILED;
 
 	if ((rv = pkcs11h_initialize ()) != CKR_OK) {
 		msg (M_FATAL, "PKCS#11: Cannot initialize %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	if ((rv = pkcs11h_setLogHook (_pkcs11_openvpn_log, NULL)) != CKR_OK) {
 		msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	pkcs11h_setLogLevel (_pkcs11_msg_openvpn2pkcs11 (get_debug_level ()));
 
 	if ((rv = pkcs11h_setProtectedAuthentication (TRUE)) != CKR_OK) {
 		msg (M_FATAL, "PKCS#11: Cannot set protected authentication %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	if ((rv = pkcs11h_setPINPromptHook (_pkcs11_openvpn_show_pkcs11_ids_pin_prompt, NULL)) != CKR_OK) {
 		msg (M_FATAL, "PKCS#11: Cannot set PIN hook %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	if (
 		(rv = pkcs11h_addProvider (
 			provider,
 			provider,
 			TRUE,
 			0,
 			FALSE,
 			0,
 			cert_private ? TRUE : FALSE
 		)) != CKR_OK
 	) {
 		msg (M_FATAL, "PKCS#11: Cannot add provider '%s' %ld-'%s'", provider, rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	if (
 		(rv = pkcs11h_certificate_enumCertificateIds (
 			PKCS11H_ENUM_METHOD_CACHE_EXIST,
 			NULL,
 			PKCS11H_PROMPT_MASK_ALLOW_ALL,
 			NULL,
 			&user_certificates
 		)) != CKR_OK
 	) {
 		msg (M_FATAL, "PKCS#11: Cannot enumerate certificates %ld-'%s'", rv, pkcs11h_getMessage (rv));
 		goto cleanup;
 	}
 
 	msg (
 		M_INFO|M_NOPREFIX|M_NOLF,
 		(
 			"\n"
 			"The following objects are available for use.\n"
 			"Each object shown below may be used as parameter to\n"
 			"--pkcs11-id option please remember to use single quote mark.\n"
 		)
ce98fd24
 	);
718526e0
 	for (current = user_certificates;current != NULL; current = current->next) {
 		pkcs11h_certificate_t certificate = NULL;
 		X509 *x509 = NULL;
 		BIO *bio = NULL;
 		char dn[1024] = {0};
 		char serial[1024] = {0};
 		char *ser = NULL;
 		size_t ser_len = 0;
 		int n;
 
 		if (
 			(rv = pkcs11h_certificate_serializeCertificateId (
 				NULL,
 				&ser_len,
 				current->certificate_id
 			)) != CKR_OK
 		) {
 			msg (M_FATAL, "PKCS#11: Cannot serialize certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
 			goto cleanup1;
 		}
 
 		if (
 			rv == CKR_OK &&
 			(ser = (char *)malloc (ser_len)) == NULL
 		) {
 			msg (M_FATAL, "PKCS#11: Cannot allocate memory");
 			goto cleanup1;
 		}
 
 		if (
 			(rv = pkcs11h_certificate_serializeCertificateId (
 				ser,
 				&ser_len,
 				current->certificate_id
 			)) != CKR_OK
 		) {
 			msg (M_FATAL, "PKCS#11: Cannot serialize certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
 			goto cleanup1;
 		}
 
 		if (
 			(rv = pkcs11h_certificate_create (
 				current->certificate_id,
 				NULL,
 				PKCS11H_PROMPT_MASK_ALLOW_ALL,
 				PKCS11H_PIN_CACHE_INFINITE,
 				&certificate
 			))
 		) {
 			msg (M_FATAL, "PKCS#11: Cannot create certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
 			goto cleanup1;
 		}
 
 		if ((x509 = pkcs11h_openssl_getX509 (certificate)) == NULL) {
 			msg (M_FATAL, "PKCS#11: Cannot get X509");
 			goto cleanup1;
 		}
 
 		X509_NAME_oneline (
 			X509_get_subject_name (x509),
 			dn,
 			sizeof (dn)
 		);
 
 		if ((bio = BIO_new (BIO_s_mem ())) == NULL) {
 			msg (M_FATAL, "PKCS#11: Cannot create BIO");
 			goto cleanup1;
 		}
 
 		i2a_ASN1_INTEGER(bio, X509_get_serialNumber (x509));
 		n = BIO_read (bio, serial, sizeof (serial)-1);
 		if (n<0) {
 			serial[0] = '\x0';
 		}
 		else {
 			serial[n] = 0;
 		}
 
 		msg (
 			M_INFO|M_NOPREFIX|M_NOLF,
 			(
 				"\n"
 				"Certificate\n"
 				"       DN:             %s\n"
 				"       Serial:         %s\n"
 				"       Serialized id:  %s\n"
 			),
 			dn,
 			serial,
 			ser
 		);
 
 	cleanup1:
 		if (x509 != NULL) {
 			X509_free (x509);
 			x509 = NULL;
 		}
 
 		if (certificate != NULL) {
 			pkcs11h_certificate_freeCertificate (certificate);
 			certificate = NULL;
 		}
 
 		if (ser != NULL) {
 			free (ser);
 			ser = NULL;
 		}
 	}
 
 cleanup:
 	if (user_certificates != NULL) {
 		pkcs11h_certificate_freeCertificateIdList (user_certificates);
 		user_certificates = NULL;
 	}
 
 	pkcs11h_terminate ();
ce98fd24
 }
 
 #else
 static void dummy (void) {}
6835555e
 #endif /* ENABLE_PKCS11 */