Add OpenVPN 2.1rc12 source (unconfigured)
[tomato.git] / release / src / router / openvpn / cryptoapi.c
blob9406c7a538f3752ca01c07f111986a6e9f18682c
1 /*
2 * Copyright (c) 2004 Peter 'Luna' Runestig <peter@runestig.com>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without modifi-
6 * cation, are permitted provided that the following conditions are met:
8 * o Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
11 * o Redistributions in binary form must reproduce the above copyright no-
12 * tice, this list of conditions and the following disclaimer in the do-
13 * cumentation and/or other materials provided with the distribution.
15 * o The names of the contributors may not be used to endorse or promote
16 * products derived from this software without specific prior written
17 * permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LI-
23 * ABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUEN-
24 * TIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEV-
26 * ER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABI-
27 * LITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "syshead.h"
33 #if defined(WIN32) && defined(USE_CRYPTO) && defined(USE_SSL)
35 #include <openssl/ssl.h>
36 #include <openssl/err.h>
37 #include <windows.h>
38 #include <wincrypt.h>
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <assert.h>
43 #ifdef __MINGW32_VERSION
44 /* MinGW w32api is incomplete when it comes to CryptoAPI, as per version 3.1
45 * anyway. This is a hack around that problem. */
46 #define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5)
47 #define CERT_SYSTEM_STORE_LOCATION_SHIFT 16
48 #define CERT_SYSTEM_STORE_CURRENT_USER_ID 1
49 #define CERT_SYSTEM_STORE_CURRENT_USER (CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT)
50 #define CERT_STORE_READONLY_FLAG 0x00008000
51 #define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
52 #define CRYPT_ACQUIRE_COMPARE_KEY_FLAG 0x00000004
53 static HINSTANCE crypt32dll = NULL;
54 static BOOL WINAPI (*CryptAcquireCertificatePrivateKey) (PCCERT_CONTEXT pCert, DWORD dwFlags,
55 void *pvReserved, HCRYPTPROV *phCryptProv, DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) = NULL;
56 #endif
58 /* Size of an SSL signature: MD5+SHA1 */
59 #define SSL_SIG_LENGTH 36
61 /* try to funnel any Windows/CryptoAPI error messages to OpenSSL ERR_... */
62 #define ERR_LIB_CRYPTOAPI (ERR_LIB_USER + 69) /* 69 is just a number... */
63 #define CRYPTOAPIerr(f) err_put_ms_error(GetLastError(), (f), __FILE__, __LINE__)
64 #define CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE 100
65 #define CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE 101
66 #define CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY 102
67 #define CRYPTOAPI_F_CRYPT_CREATE_HASH 103
68 #define CRYPTOAPI_F_CRYPT_GET_HASH_PARAM 104
69 #define CRYPTOAPI_F_CRYPT_SET_HASH_PARAM 105
70 #define CRYPTOAPI_F_CRYPT_SIGN_HASH 106
71 #define CRYPTOAPI_F_LOAD_LIBRARY 107
72 #define CRYPTOAPI_F_GET_PROC_ADDRESS 108
74 static ERR_STRING_DATA CRYPTOAPI_str_functs[] = {
75 { ERR_PACK(ERR_LIB_CRYPTOAPI, 0, 0), "microsoft cryptoapi"},
76 { ERR_PACK(0, CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE, 0), "CertOpenSystemStore" },
77 { ERR_PACK(0, CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE, 0), "CertFindCertificateInStore" },
78 { ERR_PACK(0, CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY, 0), "CryptAcquireCertificatePrivateKey" },
79 { ERR_PACK(0, CRYPTOAPI_F_CRYPT_CREATE_HASH, 0), "CryptCreateHash" },
80 { ERR_PACK(0, CRYPTOAPI_F_CRYPT_GET_HASH_PARAM, 0), "CryptGetHashParam" },
81 { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SET_HASH_PARAM, 0), "CryptSetHashParam" },
82 { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SIGN_HASH, 0), "CryptSignHash" },
83 { ERR_PACK(0, CRYPTOAPI_F_LOAD_LIBRARY, 0), "LoadLibrary" },
84 { ERR_PACK(0, CRYPTOAPI_F_GET_PROC_ADDRESS, 0), "GetProcAddress" },
85 { 0, NULL }
88 typedef struct _CAPI_DATA {
89 const CERT_CONTEXT *cert_context;
90 HCRYPTPROV crypt_prov;
91 DWORD key_spec;
92 BOOL free_crypt_prov;
93 } CAPI_DATA;
95 static char *ms_error_text(DWORD ms_err)
97 LPVOID lpMsgBuf = NULL;
98 char *rv = NULL;
100 FormatMessage(
101 FORMAT_MESSAGE_ALLOCATE_BUFFER |
102 FORMAT_MESSAGE_FROM_SYSTEM |
103 FORMAT_MESSAGE_IGNORE_INSERTS,
104 NULL, ms_err,
105 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
106 (LPTSTR) &lpMsgBuf, 0, NULL);
107 if (lpMsgBuf) {
108 char *p;
109 rv = strdup(lpMsgBuf);
110 LocalFree(lpMsgBuf);
111 /* trim to the left */
112 if (rv)
113 for (p = rv + strlen(rv) - 1; p >= rv; p--) {
114 if (isspace(*p))
115 *p = '\0';
116 else
117 break;
120 return rv;
123 static void err_put_ms_error(DWORD ms_err, int func, const char *file, int line)
125 static int init = 0;
126 # define ERR_MAP_SZ 16
127 static struct {
128 int err;
129 DWORD ms_err; /* I don't think we get more than 16 *different* errors */
130 } err_map[ERR_MAP_SZ]; /* in here, before we give up the whole thing... */
131 int i;
133 if (ms_err == 0)
134 /* 0 is not an error */
135 return;
136 if (!init) {
137 ERR_load_strings(ERR_LIB_CRYPTOAPI, CRYPTOAPI_str_functs);
138 memset(&err_map, 0, sizeof(err_map));
139 init++;
141 /* since MS error codes are 32 bit, and the ones in the ERR_... system is
142 * only 12, we must have a mapping table between them. */
143 for (i = 0; i < ERR_MAP_SZ; i++) {
144 if (err_map[i].ms_err == ms_err) {
145 ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line);
146 break;
147 } else if (err_map[i].ms_err == 0 ) {
148 /* end of table, add new entry */
149 ERR_STRING_DATA *esd = calloc(2, sizeof(*esd));
150 if (esd == NULL)
151 break;
152 err_map[i].ms_err = ms_err;
153 err_map[i].err = esd->error = i + 100;
154 esd->string = ms_error_text(ms_err);
155 ERR_load_strings(ERR_LIB_CRYPTOAPI, esd);
156 ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line);
157 break;
162 /* encrypt */
163 static int rsa_pub_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
165 /* I haven't been able to trigger this one, but I want to know if it happens... */
166 assert(0);
168 return 0;
171 /* verify arbitrary data */
172 static int rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
174 /* I haven't been able to trigger this one, but I want to know if it happens... */
175 assert(0);
177 return 0;
180 /* sign arbitrary data */
181 static int rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
183 CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data;
184 HCRYPTHASH hash;
185 DWORD hash_size, len, i;
186 unsigned char *buf;
188 if (cd == NULL) {
189 RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER);
190 return 0;
192 if (padding != RSA_PKCS1_PADDING) {
193 /* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */
194 RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
195 return 0;
197 /* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would
198 * be way to straightforward for M$, I guess... So we have to do it this
199 * tricky way instead, by creating a "Hash", and load the already-made hash
200 * from 'from' into it. */
201 /* For now, we only support NID_md5_sha1 */
202 if (flen != SSL_SIG_LENGTH) {
203 RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH);
204 return 0;
206 if (!CryptCreateHash(cd->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) {
207 CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_CREATE_HASH);
208 return 0;
210 len = sizeof(hash_size);
211 if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, 0)) {
212 CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_GET_HASH_PARAM);
213 CryptDestroyHash(hash);
214 return 0;
216 if ((int) hash_size != flen) {
217 RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH);
218 CryptDestroyHash(hash);
219 return 0;
221 if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) {
222 CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SET_HASH_PARAM);
223 CryptDestroyHash(hash);
224 return 0;
227 len = RSA_size(rsa);
228 buf = malloc(len);
229 if (buf == NULL) {
230 RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
231 CryptDestroyHash(hash);
232 return 0;
234 if (!CryptSignHash(hash, cd->key_spec, NULL, 0, buf, &len)) {
235 CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SIGN_HASH);
236 CryptDestroyHash(hash);
237 free(buf);
238 return 0;
240 /* and now, we have to reverse the byte-order in the result from CryptSignHash()... */
241 for (i = 0; i < len; i++)
242 to[i] = buf[len - i - 1];
243 free(buf);
245 CryptDestroyHash(hash);
246 return len;
249 /* decrypt */
250 static int rsa_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
252 /* I haven't been able to trigger this one, but I want to know if it happens... */
253 assert(0);
255 return 0;
258 /* called at RSA_new */
259 static int init(RSA *rsa)
262 return 0;
265 /* called at RSA_free */
266 static int finish(RSA *rsa)
268 CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data;
270 if (cd == NULL)
271 return 0;
272 if (cd->crypt_prov && cd->free_crypt_prov)
273 CryptReleaseContext(cd->crypt_prov, 0);
274 if (cd->cert_context)
275 CertFreeCertificateContext(cd->cert_context);
276 free(rsa->meth->app_data);
277 free((char *) rsa->meth);
278 rsa->meth = NULL;
279 return 1;
282 static const CERT_CONTEXT *find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
284 /* Find, and use, the desired certificate from the store. The
285 * 'cert_prop' certificate search string can look like this:
286 * SUBJ:<certificate substring to match>
287 * THUMB:<certificate thumbprint hex value>, e.g.
288 * THUMB:f6 49 24 41 01 b4 fb 44 0c ce f4 36 ae d0 c4 c9 df 7a b6 28
290 const CERT_CONTEXT *rv = NULL;
292 if (!strncmp(cert_prop, "SUBJ:", 5)) {
293 /* skip the tag */
294 cert_prop += 5;
295 rv = CertFindCertificateInStore(cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
296 0, CERT_FIND_SUBJECT_STR_A, cert_prop, NULL);
298 } else if (!strncmp(cert_prop, "THUMB:", 6)) {
299 unsigned char hash[255];
300 char *p;
301 int i, x = 0;
302 CRYPT_HASH_BLOB blob;
304 /* skip the tag */
305 cert_prop += 6;
306 for (p = (char *) cert_prop, i = 0; *p && i < sizeof(hash); i++) {
307 if (*p >= '0' && *p <= '9')
308 x = (*p - '0') << 4;
309 else if (*p >= 'A' && *p <= 'F')
310 x = (*p - 'A' + 10) << 4;
311 else if (*p >= 'a' && *p <= 'f')
312 x = (*p - 'a' + 10) << 4;
313 if (!*++p) /* unexpected end of string */
314 break;
315 if (*p >= '0' && *p <= '9')
316 x += *p - '0';
317 else if (*p >= 'A' && *p <= 'F')
318 x += *p - 'A' + 10;
319 else if (*p >= 'a' && *p <= 'f')
320 x += *p - 'a' + 10;
321 hash[i] = x;
322 /* skip any space(s) between hex numbers */
323 for (p++; *p && *p == ' '; p++);
325 blob.cbData = i;
326 blob.pbData = (unsigned char *) &hash;
327 rv = CertFindCertificateInStore(cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
328 0, CERT_FIND_HASH, &blob, NULL);
332 return rv;
335 int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
337 HCERTSTORE cs;
338 X509 *cert = NULL;
339 RSA *rsa = NULL, *pub_rsa;
340 CAPI_DATA *cd = calloc(1, sizeof(*cd));
341 RSA_METHOD *my_rsa_method = calloc(1, sizeof(*my_rsa_method));
343 if (cd == NULL || my_rsa_method == NULL) {
344 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
345 goto err;
347 /* search CURRENT_USER first, then LOCAL_MACHINE */
348 cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER |
349 CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY");
350 if (cs == NULL) {
351 CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE);
352 goto err;
354 cd->cert_context = find_certificate_in_store(cert_prop, cs);
355 CertCloseStore(cs, 0);
356 if (!cd->cert_context) {
357 cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE |
358 CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY");
359 if (cs == NULL) {
360 CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE);
361 goto err;
363 cd->cert_context = find_certificate_in_store(cert_prop, cs);
364 CertCloseStore(cs, 0);
365 if (cd->cert_context == NULL) {
366 CRYPTOAPIerr(CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE);
367 goto err;
371 /* cert_context->pbCertEncoded is the cert X509 DER encoded. */
372 cert = d2i_X509(NULL, (const unsigned char **) &cd->cert_context->pbCertEncoded,
373 cd->cert_context->cbCertEncoded);
374 if (cert == NULL) {
375 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB);
376 goto err;
379 /* set up stuff to use the private key */
380 #ifdef __MINGW32_VERSION
381 /* MinGW w32api is incomplete when it comes to CryptoAPI, as per version 3.1
382 * anyway. This is a hack around that problem. */
383 if (crypt32dll == NULL) {
384 crypt32dll = LoadLibrary("crypt32");
385 if (crypt32dll == NULL) {
386 CRYPTOAPIerr(CRYPTOAPI_F_LOAD_LIBRARY);
387 goto err;
390 if (CryptAcquireCertificatePrivateKey == NULL) {
391 CryptAcquireCertificatePrivateKey = GetProcAddress(crypt32dll,
392 "CryptAcquireCertificatePrivateKey");
393 if (CryptAcquireCertificatePrivateKey == NULL) {
394 CRYPTOAPIerr(CRYPTOAPI_F_GET_PROC_ADDRESS);
395 goto err;
398 #endif
399 if (!CryptAcquireCertificatePrivateKey(cd->cert_context, CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
400 NULL, &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) {
401 /* if we don't have a smart card reader here, and we try to access a
402 * smart card certificate, we get:
403 * "Error 1223: The operation was canceled by the user." */
404 CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY);
405 goto err;
407 /* here we don't need to do CryptGetUserKey() or anything; all necessary key
408 * info is in cd->cert_context, and then, in cd->crypt_prov. */
410 my_rsa_method->name = "Microsoft CryptoAPI RSA Method";
411 my_rsa_method->rsa_pub_enc = rsa_pub_enc;
412 my_rsa_method->rsa_pub_dec = rsa_pub_dec;
413 my_rsa_method->rsa_priv_enc = rsa_priv_enc;
414 my_rsa_method->rsa_priv_dec = rsa_priv_dec;
415 /* my_rsa_method->init = init; */
416 my_rsa_method->finish = finish;
417 my_rsa_method->flags = RSA_METHOD_FLAG_NO_CHECK;
418 my_rsa_method->app_data = (char *) cd;
420 rsa = RSA_new();
421 if (rsa == NULL) {
422 SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
423 goto err;
426 /* cert->cert_info->key->pkey is NULL until we call SSL_CTX_use_certificate(),
427 * so we do it here then... */
428 if (!SSL_CTX_use_certificate(ssl_ctx, cert))
429 goto err;
430 /* the public key */
431 pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
432 /* SSL_CTX_use_certificate() increased the reference count in 'cert', so
433 * we decrease it here with X509_free(), or it will never be cleaned up. */
434 X509_free(cert);
435 cert = NULL;
437 /* I'm not sure about what we have to fill in in the RSA, trying out stuff... */
438 /* rsa->n indicates the key size */
439 rsa->n = BN_dup(pub_rsa->n);
440 rsa->flags |= RSA_FLAG_EXT_PKEY;
441 if (!RSA_set_method(rsa, my_rsa_method))
442 goto err;
444 if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa))
445 goto err;
446 /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so
447 * we decrease it here with RSA_free(), or it will never be cleaned up. */
448 RSA_free(rsa);
449 return 1;
451 err:
452 if (cert)
453 X509_free(cert);
454 if (rsa)
455 RSA_free(rsa);
456 else {
457 if (my_rsa_method)
458 free(my_rsa_method);
459 if (cd) {
460 if (cd->free_crypt_prov && cd->crypt_prov)
461 CryptReleaseContext(cd->crypt_prov, 0);
462 if (cd->cert_context)
463 CertFreeCertificateContext(cd->cert_context);
464 free(cd);
467 return 0;
470 #else
471 static void dummy (void) {}
472 #endif /* WIN32 */