/* * 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-2010 OpenVPN Technologies, Inc. * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #elif defined(_MSC_VER) #include "config-msvc.h" #endif #include "syshead.h" #if defined(ENABLE_PKCS11) #include #include "basic.h" #include "error.h" #include "manage.h" #include "base64.h" #include "pkcs11.h" #include "misc.h" #include "otime.h" #include "console.h" #include "pkcs11_backend.h" static 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 unsigned _pkcs11_msg_pkcs112openvpn ( 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 ( 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_log ( void * const global_data, unsigned flags, const char * const szFormat, va_list args ) { char Buffer[10*1024]; (void)global_data; vsnprintf (Buffer, sizeof (Buffer), szFormat, args); Buffer[sizeof (Buffer)-1] = 0; msg (_pkcs11_msg_pkcs112openvpn (flags), "%s", Buffer); } static PKCS11H_BOOL _pkcs11_openvpn_token_prompt ( void * const global_data, void * const user_data, const pkcs11h_token_id_t token, const unsigned retry ) { struct user_pass token_resp; (void)global_data; (void)user_data; (void)retry; 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 ); 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; } } static PKCS11H_BOOL _pkcs11_openvpn_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 ) { struct user_pass token_pass; char prompt[1024]; (void)global_data; (void)user_data; (void)retry; ASSERT (token!=NULL); openvpn_snprintf (prompt, sizeof (prompt), "%s token", token->label); token_pass.defined = false; token_pass.nocache = true; if ( !get_user_pass ( &token_pass, NULL, prompt, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_PASSWORD_ONLY|GET_USER_PASS_NOFATAL ) ) { return false; } else { strncpynt (pin, token_pass.password, pin_max); purge_user_pass (&token_pass, true); if (strlen (pin) == 0) { return false; } else { return true; } } } bool pkcs11_initialize ( const bool protected_auth, const int nPINCachePeriod ) { CK_RV rv = CKR_FUNCTION_FAILED; dmsg ( D_PKCS11_DEBUG, "PKCS#11: pkcs11_initialize - entered" ); 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) { 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_setForkMode (TRUE)) != CKR_OK) { msg (M_FATAL, "PKCS#11: Cannot set fork mode %ld-'%s'", rv, pkcs11h_getMessage (rv)); goto cleanup; } if ((rv = pkcs11h_setTokenPromptHook (_pkcs11_openvpn_token_prompt, NULL)) != CKR_OK) { msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv)); goto cleanup; } if ((rv = pkcs11h_setPINPromptHook (_pkcs11_openvpn_pin_prompt, NULL)) != CKR_OK) { msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv)); goto cleanup; } if ((rv = pkcs11h_setProtectedAuthentication (protected_auth)) != CKR_OK) { msg (M_FATAL, "PKCS#11: Cannot set protected authentication mode %ld-'%s'", rv, pkcs11h_getMessage (rv)); goto cleanup; } if ((rv = pkcs11h_setPINCachePeriod (nPINCachePeriod)) != CKR_OK) { msg (M_FATAL, "PKCS#11: Cannot set Pcache period %ld-'%s'", rv, pkcs11h_getMessage (rv)); goto cleanup; } rv = CKR_OK; cleanup: 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" ); } bool pkcs11_addProvider ( const char * const provider, const bool protected_auth, const unsigned private_mode, const bool cert_private ) { CK_RV rv = CKR_OK; ASSERT (provider!=NULL); dmsg ( D_PKCS11_DEBUG, "PKCS#11: pkcs11_addProvider - entered - provider='%s', private_mode=%08x", provider, private_mode ); msg ( M_INFO, "PKCS#11: Adding PKCS#11 provider '%s'", provider ); if ( (rv = pkcs11h_addProvider ( provider, provider, protected_auth, private_mode, PKCS11H_SLOTEVENT_METHOD_AUTO, 0, cert_private )) != 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 pkcs11_logout() { return pkcs11h_logout () == CKR_OK; } int 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; #if 0 /* certificate_id seems to be unused -- JY */ pkcs11h_certificate_id_t certificate_id = NULL; #endif pkcs11h_certificate_t certificate = NULL; CK_RV rv = CKR_OK; unsigned char *certificate_blob = NULL; 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; } if ((certificate_blob = (unsigned char *)malloc (certificate_blob_size)) == NULL) { 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 (openvpn_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 tls_ctx_use_pkcs11 ( struct tls_root_ctx * const ssl_ctx, bool pkcs11_id_management, const char * const pkcs11_id ) { pkcs11h_certificate_id_t certificate_id = NULL; pkcs11h_certificate_t certificate = NULL; CK_RV rv = CKR_OK; bool ok = false; ASSERT (ssl_ctx!=NULL); ASSERT (pkcs11_id_management || pkcs11_id!=NULL); dmsg ( D_PKCS11_DEBUG, "PKCS#11: tls_ctx_use_pkcs11 - entered - ssl_ctx=%p, pkcs11_id_management=%d, pkcs11_id='%s'", (void *)ssl_ctx, pkcs11_id_management ? 1 : 0, pkcs11_id ); 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; } } if ( (rv = pkcs11h_certificate_create ( 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 ( (pkcs11_init_tls_session ( certificate, ssl_ctx )) ) { /* Handled by SSL context free */ certificate = NULL; goto cleanup; } /* Handled by SSL context free */ certificate = NULL; ok = true; cleanup: if (certificate != NULL) { pkcs11h_certificate_freeCertificate (certificate); certificate = NULL; } if (certificate_id != NULL) { pkcs11h_certificate_freeCertificateId (certificate_id); certificate_id = NULL; } dmsg ( D_PKCS11_DEBUG, "PKCS#11: tls_ctx_use_pkcs11 - return ok=%d, rv=%ld", ok ? 1 : 0, rv ); return ok ? 1 : 0; } static PKCS11H_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 ) { 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; } } void show_pkcs11_ids ( const char * const provider, bool cert_private ) { struct gc_arena gc = gc_new(); 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" ) ); for (current = user_certificates;current != NULL; current = current->next) { pkcs11h_certificate_t certificate = NULL; char *dn = NULL; char serial[1024] = {0}; char *ser = NULL; size_t ser_len = 0; 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 ( (dn = pkcs11_certificate_dn ( certificate, &gc )) == NULL ) { goto cleanup1; } if ( (pkcs11_certificate_serial ( certificate, serial, sizeof(serial) )) ) { goto cleanup1; } 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 (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 (); gc_free (&gc); } #else #ifdef _MSC_VER /* Dummy function needed to avoid empty file compiler warning in Microsoft VC */ static void dummy (void) {} #endif #endif /* ENABLE_PKCS11 */