mirror of
https://github.com/OpenVPN/openvpn.git
synced 2025-05-09 21:51:05 +08:00

it into QCA (Qt Cryptographic Architecture), so that KDE 4 will finally be able to use smartcards. The changes allows the following features: 1. Thread safe, is activated if USE_PTHREAD. 2. Slot event - Will allow us in the future to disconnect VPN when smartcard is removed. In order to support this OpenVPN must support threading... At least SIGUSR1 from a different thread. Threading should be supported in both Windows and Linux. -- currently disabled. When I talk about threading support it is just support in configuration script and that the method that SIGUSR1 self can be called from a different thread. I already handle the monitor threads. 3. Certificate enumeration - Will allow us to finally have one configuration file for all users! When you add the plugin GUI stuff you talked about, we will be able to display a list of available certificates for the user to select. -- currently disabled. 4. Data object manipulation - Will allow us to store tls-auth on the smartcard as well. -- currently disabled. 5. Many other minor improvements. Alon Bar-Lev git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@990 e7ae566f-a301-0410-adde-c780ea21d3b5
549 lines
11 KiB
C
549 lines
11 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net>
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
/*
|
|
* The routines in this file deal with providing private key cryptography
|
|
* using RSA Security Inc. PKCS #11 Cryptographic Token Interface (Cryptoki).
|
|
*
|
|
*/
|
|
|
|
#if defined(WIN32)
|
|
#include "config-win32.h"
|
|
#else
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "syshead.h"
|
|
|
|
#if defined(ENABLE_PKCS11)
|
|
|
|
#define PKCS11H_NO_NEED_INCLUDE_CONFIG
|
|
|
|
#include "pkcs11-helper.h"
|
|
#include "pkcs11.h"
|
|
|
|
static
|
|
unsigned
|
|
_pkcs11_msg_pkcs112openvpn (
|
|
IN const unsigned flags
|
|
) {
|
|
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 (
|
|
IN const unsigned flags
|
|
) {
|
|
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
|
|
void
|
|
_pkcs11_openvpn_print (
|
|
IN const void *pData,
|
|
IN const char * const szFormat,
|
|
IN ...
|
|
) {
|
|
char Buffer[10*1024];
|
|
va_list args;
|
|
|
|
va_start (args, szFormat);
|
|
vsnprintf (Buffer, sizeof (Buffer), szFormat, args);
|
|
va_end (args);
|
|
Buffer[sizeof (Buffer)-1] = 0;
|
|
|
|
msg (M_INFO|M_NOPREFIX|M_NOLF, "%s", Buffer);
|
|
}
|
|
|
|
static
|
|
void
|
|
_pkcs11_openvpn_log (
|
|
IN const void *pData,
|
|
IN unsigned flags,
|
|
IN const char * const szFormat,
|
|
IN va_list args
|
|
) {
|
|
char Buffer[10*1024];
|
|
|
|
vsnprintf (Buffer, sizeof (Buffer), szFormat, args);
|
|
Buffer[sizeof (Buffer)-1] = 0;
|
|
|
|
msg (_pkcs11_msg_pkcs112openvpn (flags), "%s", Buffer);
|
|
}
|
|
|
|
static
|
|
bool
|
|
_pkcs11_openvpn_token_prompt (
|
|
IN const void *pData,
|
|
IN const pkcs11h_token_id_t token
|
|
) {
|
|
static struct user_pass token_resp;
|
|
|
|
ASSERT (token!=NULL);
|
|
|
|
CLEAR (token_resp);
|
|
token_resp.defined = false;
|
|
token_resp.nocache = true;
|
|
openvpn_snprintf (
|
|
token_resp.username,
|
|
sizeof (token_resp.username),
|
|
"Please insert %s token",
|
|
token->label
|
|
);
|
|
get_user_pass (
|
|
&token_resp,
|
|
NULL,
|
|
"token-insertion-request",
|
|
GET_USER_PASS_MANAGEMENT|GET_USER_PASS_NEED_OK
|
|
);
|
|
|
|
return strcmp (token_resp.password, "ok") == 0;
|
|
}
|
|
|
|
static
|
|
bool
|
|
_pkcs11_openvpn_pin_prompt (
|
|
IN const void *pData,
|
|
IN const pkcs11h_token_id_t token,
|
|
OUT char * const szPIN,
|
|
IN const size_t nMaxPIN
|
|
) {
|
|
static struct user_pass token_pass;
|
|
char szPrompt[1024];
|
|
|
|
ASSERT (token!=NULL);
|
|
|
|
openvpn_snprintf (szPrompt, sizeof (szPrompt), "%s token", token->label);
|
|
|
|
token_pass.defined = false;
|
|
token_pass.nocache = true;
|
|
get_user_pass (&token_pass, NULL, szPrompt, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_PASSWORD_ONLY);
|
|
strncpynt (szPIN, token_pass.password, nMaxPIN);
|
|
purge_user_pass (&token_pass, true);
|
|
|
|
if (strlen (szPIN) == 0) {
|
|
return false;
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
pkcs11_initialize (
|
|
IN const bool fProtectedAuthentication,
|
|
IN const int nPINCachePeriod
|
|
) {
|
|
CK_RV rv = CKR_OK;
|
|
|
|
dmsg (
|
|
D_PKCS11_DEBUG,
|
|
"PKCS#11: pkcs11_initialize - entered"
|
|
);
|
|
|
|
if (
|
|
rv == CKR_OK &&
|
|
(rv = pkcs11h_initialize ()) != CKR_OK
|
|
) {
|
|
msg (M_FATAL, "PKCS#11: Cannot initialize %ld-'%s'", rv, pkcs11h_getMessage (rv));
|
|
}
|
|
|
|
if (
|
|
rv == CKR_OK &&
|
|
(rv = pkcs11h_setLogHook (_pkcs11_openvpn_log, NULL)) != CKR_OK
|
|
) {
|
|
msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
|
|
}
|
|
|
|
if (rv == CKR_OK) {
|
|
pkcs11h_setLogLevel (_pkcs11_msg_openvpn2pkcs11 (get_debug_level ()));
|
|
}
|
|
|
|
if (
|
|
rv == CKR_OK &&
|
|
(rv = pkcs11h_setTokenPromptHook (_pkcs11_openvpn_token_prompt, NULL)) != CKR_OK
|
|
) {
|
|
msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
|
|
}
|
|
|
|
if (
|
|
rv == CKR_OK &&
|
|
(rv = pkcs11h_setPINPromptHook (_pkcs11_openvpn_pin_prompt, NULL)) != CKR_OK
|
|
) {
|
|
msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
|
|
}
|
|
|
|
if (
|
|
rv == CKR_OK &&
|
|
(rv = pkcs11h_setProtectedAuthentication (fProtectedAuthentication)) != CKR_OK
|
|
) {
|
|
msg (M_FATAL, "PKCS#11: Cannot set protected authentication mode %ld-'%s'", rv, pkcs11h_getMessage (rv));
|
|
}
|
|
|
|
if (
|
|
rv == CKR_OK &&
|
|
(rv = pkcs11h_setPINCachePeriod (nPINCachePeriod)) != CKR_OK
|
|
) {
|
|
msg (M_FATAL, "PKCS#11: Cannot set PIN cache period %ld-'%s'", rv, pkcs11h_getMessage (rv));
|
|
}
|
|
|
|
dmsg (
|
|
D_PKCS11_DEBUG,
|
|
"PKCS#11: pkcs11_initialize - return %ld-'%s'",
|
|
rv,
|
|
pkcs11h_getMessage (rv)
|
|
);
|
|
|
|
return rv == CKR_OK;
|
|
}
|
|
|
|
void
|
|
pkcs11_terminate () {
|
|
dmsg (
|
|
D_PKCS11_DEBUG,
|
|
"PKCS#11: pkcs11_terminate - entered"
|
|
);
|
|
|
|
pkcs11h_terminate ();
|
|
|
|
dmsg (
|
|
D_PKCS11_DEBUG,
|
|
"PKCS#11: pkcs11_terminate - return"
|
|
);
|
|
}
|
|
|
|
void
|
|
pkcs11_forkFixup () {
|
|
pkcs11h_forkFixup ();
|
|
}
|
|
|
|
bool
|
|
pkcs11_addProvider (
|
|
IN const char * const provider,
|
|
IN const bool fProtectedAuthentication,
|
|
IN const char * const sign_mode,
|
|
IN const bool fCertIsPrivate
|
|
) {
|
|
unsigned maskSignMode = 0;
|
|
|
|
CK_RV rv = CKR_OK;
|
|
|
|
ASSERT (provider!=NULL);
|
|
/*ASSERT (sign_mode!=NULL); NULL is default */
|
|
|
|
dmsg (
|
|
D_PKCS11_DEBUG,
|
|
"PKCS#11: pkcs11_addProvider - entered - provider='%s', sign_mode='%s'",
|
|
provider,
|
|
sign_mode == NULL ? "default" : sign_mode
|
|
);
|
|
|
|
msg (
|
|
M_INFO,
|
|
"PKCS#11: Adding PKCS#11 provider '%s'",
|
|
provider
|
|
);
|
|
|
|
if (rv == CKR_OK) {
|
|
if (sign_mode == NULL || !strcmp (sign_mode, "auto")) {
|
|
maskSignMode = 0;
|
|
}
|
|
else if (!strcmp (sign_mode, "sign")) {
|
|
maskSignMode = PKCS11H_SIGNMODE_MASK_SIGN;
|
|
}
|
|
else if (!strcmp (sign_mode, "recover")) {
|
|
maskSignMode = PKCS11H_SIGNMODE_MASK_RECOVER;
|
|
}
|
|
else if (!strcmp (sign_mode, "any")) {
|
|
maskSignMode = (
|
|
PKCS11H_SIGNMODE_MASK_SIGN |
|
|
PKCS11H_SIGNMODE_MASK_RECOVER
|
|
);
|
|
}
|
|
else {
|
|
msg (M_FATAL, "PKCS#11: Invalid sign mode '%s'", sign_mode);
|
|
rv = CKR_ARGUMENTS_BAD;
|
|
}
|
|
}
|
|
|
|
if (
|
|
rv == CKR_OK &&
|
|
(rv = pkcs11h_addProvider (
|
|
provider,
|
|
provider,
|
|
fProtectedAuthentication,
|
|
maskSignMode,
|
|
PKCS11H_SLOTEVENT_METHOD_AUTO,
|
|
0,
|
|
fCertIsPrivate
|
|
)) != CKR_OK
|
|
) {
|
|
msg (M_WARN, "PKCS#11: Cannot initialize provider '%s' %ld-'%s'", provider, rv, pkcs11h_getMessage (rv));
|
|
}
|
|
|
|
dmsg (
|
|
D_PKCS11_DEBUG,
|
|
"PKCS#11: pkcs11_addProvider - return rv=%ld-'%s'",
|
|
rv,
|
|
pkcs11h_getMessage (rv)
|
|
);
|
|
|
|
return rv == CKR_OK;
|
|
}
|
|
|
|
int
|
|
SSL_CTX_use_pkcs11 (
|
|
IN OUT SSL_CTX * const ssl_ctx,
|
|
IN const char * const pkcs11_slot_type,
|
|
IN const char * const pkcs11_slot,
|
|
IN const char * const pkcs11_id_type,
|
|
IN const char * const pkcs11_id
|
|
) {
|
|
X509 *x509 = NULL;
|
|
RSA *rsa = NULL;
|
|
pkcs11h_certificate_id_t certificate_id = NULL;
|
|
pkcs11h_certificate_t certificate = NULL;
|
|
pkcs11h_openssl_session_t openssl_session = NULL;
|
|
CK_RV rv = CKR_OK;
|
|
|
|
bool fOK = true;
|
|
|
|
ASSERT (ssl_ctx!=NULL);
|
|
ASSERT (pkcs11_slot_type!=NULL);
|
|
ASSERT (pkcs11_slot!=NULL);
|
|
ASSERT (pkcs11_id_type!=NULL);
|
|
ASSERT (pkcs11_id!=NULL);
|
|
|
|
dmsg (
|
|
D_PKCS11_DEBUG,
|
|
"PKCS#11: SSL_CTX_use_pkcs11 - entered - ssl_ctx=%p, pkcs11_slot_type='%s', pkcs11_slot='%s', pkcs11_id_type='%s', pkcs11_id='%s'",
|
|
(void *)ssl_ctx,
|
|
pkcs11_slot_type,
|
|
pkcs11_slot,
|
|
pkcs11_id_type,
|
|
pkcs11_id
|
|
);
|
|
|
|
ASSERT (ssl_ctx!=NULL);
|
|
ASSERT (pkcs11_slot_type!=NULL);
|
|
ASSERT (pkcs11_slot!=NULL);
|
|
ASSERT (pkcs11_id_type!=NULL);
|
|
ASSERT (pkcs11_id!=NULL);
|
|
|
|
if (
|
|
fOK &&
|
|
(rv = pkcs11h_locate_certificate (
|
|
pkcs11_slot_type,
|
|
pkcs11_slot,
|
|
pkcs11_id_type,
|
|
pkcs11_id,
|
|
&certificate_id
|
|
)) != CKR_OK
|
|
) {
|
|
fOK = false;
|
|
msg (M_WARN, "PKCS#11: Cannot set parameters %ld-'%s'", rv, pkcs11h_getMessage (rv));
|
|
}
|
|
|
|
if (
|
|
fOK &&
|
|
(rv = pkcs11h_certificate_create (
|
|
certificate_id,
|
|
PKCS11H_PIN_CACHE_INFINITE,
|
|
&certificate
|
|
)) != CKR_OK
|
|
) {
|
|
fOK = false;
|
|
msg (M_WARN, "PKCS#11: Cannot get certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
|
|
}
|
|
|
|
if (
|
|
fOK &&
|
|
(openssl_session = pkcs11h_openssl_createSession (certificate)) == NULL
|
|
) {
|
|
fOK = false;
|
|
msg (M_WARN, "PKCS#11: Cannot initialize openssl session");
|
|
}
|
|
|
|
if (fOK) {
|
|
/*
|
|
* Will be released by openssl_session
|
|
*/
|
|
certificate = NULL;
|
|
}
|
|
|
|
if (
|
|
fOK &&
|
|
(rsa = pkcs11h_openssl_getRSA (openssl_session)) == NULL
|
|
) {
|
|
fOK = false;
|
|
msg (M_WARN, "PKCS#11: Unable get rsa object");
|
|
}
|
|
|
|
if (
|
|
fOK &&
|
|
(x509 = pkcs11h_openssl_getX509 (openssl_session)) == NULL
|
|
) {
|
|
fOK = false;
|
|
msg (M_WARN, "PKCS#11: Unable get certificate object");
|
|
}
|
|
|
|
if (
|
|
fOK &&
|
|
!SSL_CTX_use_RSAPrivateKey (ssl_ctx, rsa)
|
|
) {
|
|
fOK = false;
|
|
msg (M_WARN, "PKCS#11: Cannot set private key for openssl");
|
|
}
|
|
|
|
if (
|
|
fOK &&
|
|
!SSL_CTX_use_certificate (ssl_ctx, x509)
|
|
) {
|
|
fOK = false;
|
|
msg (M_WARN, "PKCS#11: Cannot set certificate for openssl");
|
|
}
|
|
|
|
/*
|
|
* openssl objects have reference
|
|
* count, so release them
|
|
*/
|
|
|
|
if (x509 != NULL) {
|
|
X509_free (x509);
|
|
x509 = NULL;
|
|
}
|
|
|
|
if (rsa != NULL) {
|
|
RSA_free (rsa);
|
|
rsa = NULL;
|
|
}
|
|
|
|
if (certificate != NULL) {
|
|
pkcs11h_freeCertificate (certificate);
|
|
certificate = NULL;
|
|
}
|
|
|
|
if (certificate_id != NULL) {
|
|
pkcs11h_freeCertificateId (certificate_id);
|
|
certificate_id = NULL;
|
|
}
|
|
|
|
if (openssl_session != NULL) {
|
|
pkcs11h_openssl_freeSession (openssl_session);
|
|
openssl_session = NULL;
|
|
}
|
|
|
|
dmsg (
|
|
D_PKCS11_DEBUG,
|
|
"PKCS#11: SSL_CTX_use_pkcs11 - return fOK=%d, rv=%ld",
|
|
fOK ? 1 : 0,
|
|
rv
|
|
);
|
|
|
|
return fOK ? 1 : 0;
|
|
}
|
|
|
|
void
|
|
show_pkcs11_slots (
|
|
const char * const provider
|
|
) {
|
|
pkcs11h_standalone_dump_slots (
|
|
_pkcs11_openvpn_print,
|
|
NULL,
|
|
provider
|
|
);
|
|
}
|
|
|
|
void
|
|
show_pkcs11_objects (
|
|
const char * const provider,
|
|
const char * const slot,
|
|
const char * const pin
|
|
) {
|
|
pkcs11h_standalone_dump_objects (
|
|
_pkcs11_openvpn_print,
|
|
NULL,
|
|
provider,
|
|
slot,
|
|
pin
|
|
);
|
|
}
|
|
|
|
#else
|
|
static void dummy (void) {}
|
|
#endif /* ENABLE_PKCS11 */
|