Hi all,
From the mailinglist I'm not the first one that wanted dynamic key-id
detection for pkcs11 authentication.
I first started to write a script, but it had to be platform independent.
Working on two scripts (bash and vbs) was stupid as they really became to
big and created extra security risks.
That's why I started to work on this patch.
(see attach)
In short this is what I do:
- New pkcs11-match variable in the openvpn.conf. This is the substring of
the key-id you want to match. It works with a 'first match wins' principle.
- options.c => I add the variable and the checks, as it's either
pkcs11-id or pkcs11-match
- ssl.c => if pkcs11_match is set, do the checks and give error if
necessary. Store the key-id in options->pkcs11_id.
I had to do some black magic here. options->pkcs11_id is defined as
const. So I had to un-const this variable to be able to change its value.
This is just a compiler-bypass but a long-term solution should be sought
by changing some parts of the design of the const-ness of some variables.
I think this is something _you_ should decide.
- pkcs11.c => I copy-pasted some code of the find-pkcs11-ids(),
refactored it and added a check for the substring.
First I wanted to add a regex-check, but then I had to depend on new libs
and that's probably something you do not want. The behavior is the same
as the cryptoapicert variable for windows. (where also a substring is
needed).
I did my best to try to prevent memory leaks, but please double check
that. Worst case only a few bytes should be lost, but hey better check :-)
Thanks for considering including this patch. I (and probably other
people) _need_ this functionality to make openVPN more userfriendly.
The computer/config will not be locked to the smartcard. This enables
people to:
- deploy the same configuration on different computers
- log-in to the tunnel with different cards on the same machine.
(different users that share the same computer)
- With eID's (what I use) the key-id contains similar characteristics.
(like mine where 'BELPIC\x20\x28Basic\x20PIN\x29/02' is the end of the
Authentication key.
Axalto/Belgium\x20eID/6CFF2491AB111E14/BELPIC\x20\x28Basic\x20PIN\x29/02 )
All input on the patch is welcome.
Thanks
Christophe
PS: It's for my OpenVPN-plans with the eID...
<http://christophe.vandeplas.com/2008/02/03/openvpn-belgian-eid>
<http://christophe.vandeplas.com/2008/02/08/database-authorization-openvpn-eid>
--
mailto:christo...@vandeplas.com
http://christophe.vandeplas.com
diff -Naur openvpn-2.1_rc7/options.c openvpn/options.c
--- openvpn-2.1_rc7/options.c 2008-01-24 01:55:18.000000000 +0100
+++ openvpn/options.c 2008-03-16 16:22:11.000000000 +0100
@@ -520,6 +520,7 @@
"--pkcs11-pin-cache seconds : Number of seconds to cache PIN. The default
is -1\n"
" cache until token is removed.\n"
"--pkcs11-id serialized-id : Identity to use, get using standalone
--show-pkcs11-ids\n"
+ "--pkcs11-match string : String to match the Identity to use, see
pkcs11-id\n"
#endif /* ENABLE_PKCS11 */
"\n"
"SSL Library information:\n"
@@ -1766,8 +1767,11 @@
if (options->pkcs11_providers[0])
{
notnull (options->ca_file, "CA file (--ca)");
- notnull (options->pkcs11_id, "PKCS#11 id (--pkcs11-id)");
-
+// notnull (options->pkcs11_id, "PKCS#11 id (--pkcs11-id)");
+ if (!options->pkcs11_id && !options->pkcs11_match)
+ msg(M_USAGE, "You must define PKCS#11 id (--pkcs11-id) or string to
match (--pkcs11-match).");
+ if (options->pkcs11_id && options->pkcs11_match)
+ msg(M_USAGE, "Parameter --pkcs11-match cannot not be used when
--pkcs11-id is also specified.");
if (options->cert_file)
msg(M_USAGE, "Parameter --cert cannot be used when --pkcs11-provider
is also specified.");
if (options->priv_key_file)
@@ -5137,6 +5141,11 @@
VERIFY_PERMISSION (OPT_P_GENERAL);
options->pkcs11_id = p[1];
}
+ else if (streq (p[0], "pkcs11-match") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->pkcs11_match = p[1];
+ }
#endif
#ifdef TUNSETPERSIST
else if (streq (p[0], "rmtun"))
diff -Naur openvpn-2.1_rc7/options.h openvpn/options.h
--- openvpn-2.1_rc7/options.h 2008-01-23 22:08:41.000000000 +0100
+++ openvpn/options.h 2008-03-16 11:59:45.000000000 +0100
@@ -419,6 +419,7 @@
bool pkcs11_cert_private[MAX_PARMS];
int pkcs11_pin_cache_period;
const char *pkcs11_id;
+ const char *pkcs11_match;
#endif
#ifdef WIN32
diff -Naur openvpn-2.1_rc7/pkcs11.c openvpn/pkcs11.c
--- openvpn-2.1_rc7/pkcs11.c 2008-01-23 22:08:41.000000000 +0100
+++ openvpn/pkcs11.c 2008-03-16 16:25:37.000000000 +0100
@@ -543,6 +543,135 @@
}
}
+char *
+find_pkcs11_id (
+ const char * const pkcs11_match
+) {
+ ASSERT (pkcs11_match!=NULL);
+
+ char *ret = NULL;
+
+ pkcs11h_certificate_id_list_t user_certificates = NULL;
+ pkcs11h_certificate_id_list_t current = NULL;
+ CK_RV rv = CKR_FUNCTION_FAILED;
+
+ 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;
+ }
+
+ // loop over the certs till a) the last one b) we have a hit
+ for (current = user_certificates;current != NULL && ret == NULL;
current = current->next) {
+ pkcs11h_certificate_t certificate = NULL;
+ X509 *x509 = NULL;
+ char dn[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)
+ );
+
+ /*msg (
+ M_INFO|M_NOPREFIX|M_NOLF,
+ (
+ "Certificate\n"
+ " DN: %s\n"
+ " Serialized id: %s\n"
+ ),
+ dn,
+ ser
+ );*/
+
+ // if we have a match, save it to the ret value.
+ if (NULL!=strstr(ser, pkcs11_match)) {
+ ret = malloc (ser_len);
+ strcpy(ret, 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;
+ }
+ return ret;
+}
+
void
show_pkcs11_ids (
const char * const provider,
diff -Naur openvpn-2.1_rc7/pkcs11.h openvpn/pkcs11.h
--- openvpn-2.1_rc7/pkcs11.h 2008-01-23 22:08:41.000000000 +0100
+++ openvpn/pkcs11.h 2008-03-16 16:10:21.000000000 +0100
@@ -58,6 +58,12 @@
const char * const pkcs11_id
);
+char *
+find_pkcs11_id (
+ const char * const pkcs11_match
+);
+
+
void
show_pkcs11_ids (
const char * const provider,
diff -Naur openvpn-2.1_rc7/ssl.c openvpn/ssl.c
--- openvpn-2.1_rc7/ssl.c 2008-01-23 22:08:41.000000000 +0100
+++ openvpn/ssl.c 2008-03-16 16:53:02.000000000 +0100
@@ -30,6 +30,8 @@
* over the same TCP/UDP port.
*/
+# define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+
#ifdef WIN32
#include "config-win32.h"
#else
@@ -1075,7 +1077,7 @@
* All files are in PEM format.
*/
SSL_CTX *
-init_ssl (const struct options *options)
+init_ssl (const struct options *options)
{
SSL_CTX *ctx = NULL;
DH *dh;
@@ -1195,10 +1197,20 @@
else
{
/* Use seperate PEM files for key, cert and CA certs */
-
+
#ifdef ENABLE_PKCS11
if (options->pkcs11_providers[0])
{
+ if (options->pkcs11_match)
+ {
+ __UNCONST( options->pkcs11_id) =
find_pkcs11_id(options->pkcs11_match);
+ msg (M_WARN, "PKCS#11-id has been found: %s",
options->pkcs11_id);
+
+ if (options->pkcs11_id==NULL) {
+ msg (M_WARN, "Cannot find valid certificate ID using
PKCS#11 interface and string to match");
+ goto err;
+ }
+ }
/* Load Certificate and Private Key */
if (!SSL_CTX_use_pkcs11 (ctx, options->pkcs11_id))
{