2 * Copyright 2019 Hans Leidekker for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "wine/port.h"
30 #ifdef HAVE_SYS_STAT_H
33 #ifdef HAVE_SECURITY_SECURITY_H
34 #include <Security/Security.h>
36 #ifdef SONAME_LIBGNUTLS
37 #include <gnutls/pkcs12.h>
41 #define WIN32_NO_STATUS
47 #include "crypt32_private.h"
49 #include "wine/debug.h"
50 #include "wine/heap.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(crypt
);
54 #ifdef SONAME_LIBGNUTLS
56 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
58 /* Not present in gnutls version < 3.0 */
59 int gnutls_pkcs12_simple_parse(gnutls_pkcs12_t p12
, const char *password
,
60 gnutls_x509_privkey_t
*key
, gnutls_x509_crt_t
**chain
, unsigned int *chain_len
,
61 gnutls_x509_crt_t
**extra_certs
, unsigned int *extra_certs_len
,
62 gnutls_x509_crl_t
* crl
, unsigned int flags
);
64 int gnutls_x509_privkey_get_pk_algorithm2(gnutls_x509_privkey_t
, unsigned int*);
66 static void *libgnutls_handle
;
67 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
68 MAKE_FUNCPTR(gnutls_global_deinit
);
69 MAKE_FUNCPTR(gnutls_global_init
);
70 MAKE_FUNCPTR(gnutls_global_set_log_function
);
71 MAKE_FUNCPTR(gnutls_global_set_log_level
);
72 MAKE_FUNCPTR(gnutls_perror
);
73 MAKE_FUNCPTR(gnutls_pkcs12_deinit
);
74 MAKE_FUNCPTR(gnutls_pkcs12_import
);
75 MAKE_FUNCPTR(gnutls_pkcs12_init
);
76 MAKE_FUNCPTR(gnutls_pkcs12_simple_parse
);
77 MAKE_FUNCPTR(gnutls_x509_crt_export
);
78 MAKE_FUNCPTR(gnutls_x509_privkey_export_rsa_raw2
);
79 MAKE_FUNCPTR(gnutls_x509_privkey_get_pk_algorithm2
);
82 static void gnutls_log( int level
, const char *msg
)
84 TRACE( "<%d> %s", level
, msg
);
87 BOOL
gnutls_initialize(void)
92 if ((env_str
= getenv("GNUTLS_SYSTEM_PRIORITY_FILE")))
94 WARN("GNUTLS_SYSTEM_PRIORITY_FILE is %s.\n", debugstr_a(env_str
));
98 WARN("Setting GNUTLS_SYSTEM_PRIORITY_FILE to \"/dev/null\".\n");
99 setenv("GNUTLS_SYSTEM_PRIORITY_FILE", "/dev/null", 0);
102 if (!(libgnutls_handle
= dlopen( SONAME_LIBGNUTLS
, RTLD_NOW
)))
104 ERR_(winediag
)( "failed to load libgnutls, no support for pfx import/export\n" );
108 #define LOAD_FUNCPTR(f) \
109 if (!(p##f = dlsym( libgnutls_handle, #f ))) \
111 ERR( "failed to load %s\n", #f ); \
115 LOAD_FUNCPTR(gnutls_global_deinit
)
116 LOAD_FUNCPTR(gnutls_global_init
)
117 LOAD_FUNCPTR(gnutls_global_set_log_function
)
118 LOAD_FUNCPTR(gnutls_global_set_log_level
)
119 LOAD_FUNCPTR(gnutls_perror
)
120 LOAD_FUNCPTR(gnutls_pkcs12_deinit
)
121 LOAD_FUNCPTR(gnutls_pkcs12_import
)
122 LOAD_FUNCPTR(gnutls_pkcs12_init
)
123 LOAD_FUNCPTR(gnutls_pkcs12_simple_parse
)
124 LOAD_FUNCPTR(gnutls_x509_crt_export
)
125 LOAD_FUNCPTR(gnutls_x509_privkey_export_rsa_raw2
)
126 LOAD_FUNCPTR(gnutls_x509_privkey_get_pk_algorithm2
)
129 if ((ret
= pgnutls_global_init()) != GNUTLS_E_SUCCESS
)
131 pgnutls_perror( ret
);
135 if (TRACE_ON( crypt
))
137 pgnutls_global_set_log_level( 4 );
138 pgnutls_global_set_log_function( gnutls_log
);
144 dlclose( libgnutls_handle
);
145 libgnutls_handle
= NULL
;
149 void gnutls_uninitialize(void)
151 pgnutls_global_deinit();
152 dlclose( libgnutls_handle
);
153 libgnutls_handle
= NULL
;
155 #define RSA_MAGIC_KEY ('R' | ('S' << 8) | ('A' << 16) | ('2' << 24))
156 #define RSA_PUBEXP 65537
158 static DWORD
import_key( gnutls_x509_privkey_t key
, DWORD flags
, void **data_ret
)
162 gnutls_datum_t m
, e
, d
, p
, q
, u
, e1
, e2
;
165 BYTE
*buf
, *src
, *dst
;
170 if ((ret
= pgnutls_x509_privkey_get_pk_algorithm2( key
, &bitlen
)) < 0)
172 pgnutls_perror( ret
);
176 if (ret
!= GNUTLS_PK_RSA
)
178 FIXME( "key algorithm %u not supported\n", ret
);
182 if ((ret
= pgnutls_x509_privkey_export_rsa_raw2( key
, &m
, &e
, &d
, &p
, &q
, &u
, &e1
, &e2
)) < 0)
184 pgnutls_perror( ret
);
188 size
= sizeof(*hdr
) + sizeof(*rsakey
) + (bitlen
* 9 / 16);
189 if (!(buf
= RtlAllocateHeap( GetProcessHeap(), 0, size
))) goto done
;
191 hdr
= (BLOBHEADER
*)buf
;
192 hdr
->bType
= PRIVATEKEYBLOB
;
193 hdr
->bVersion
= CUR_BLOB_VERSION
;
195 hdr
->aiKeyAlg
= CALG_RSA_KEYX
;
197 rsakey
= (RSAPUBKEY
*)(hdr
+ 1);
198 rsakey
->magic
= RSA_MAGIC_KEY
;
199 rsakey
->bitlen
= bitlen
;
200 rsakey
->pubexp
= RSA_PUBEXP
;
202 dst
= (BYTE
*)(rsakey
+ 1);
203 if (m
.size
== bitlen
/ 8 + 1 && !m
.data
[0]) src
= m
.data
+ 1;
204 else if (m
.size
!= bitlen
/ 8) goto done
;
206 for (i
= bitlen
/ 8 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
208 if (p
.size
== bitlen
/ 16 + 1 && !p
.data
[0]) src
= p
.data
+ 1;
209 else if (p
.size
!= bitlen
/ 16) goto done
;
211 for (i
= bitlen
/ 16 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
213 if (q
.size
== bitlen
/ 16 + 1 && !q
.data
[0]) src
= q
.data
+ 1;
214 else if (q
.size
!= bitlen
/ 16) goto done
;
216 for (i
= bitlen
/ 16 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
218 if (e1
.size
== bitlen
/ 16 + 1 && !e1
.data
[0]) src
= e1
.data
+ 1;
219 else if (e1
.size
!= bitlen
/ 16) goto done
;
221 for (i
= bitlen
/ 16 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
223 if (e2
.size
== bitlen
/ 16 + 1 && !e2
.data
[0]) src
= e2
.data
+ 1;
224 else if (e2
.size
!= bitlen
/ 16) goto done
;
226 for (i
= bitlen
/ 16 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
228 if (u
.size
== bitlen
/ 16 + 1 && !u
.data
[0]) src
= u
.data
+ 1;
229 else if (u
.size
!= bitlen
/ 16) goto done
;
231 for (i
= bitlen
/ 16 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
233 if (d
.size
== bitlen
/ 8 + 1 && !d
.data
[0]) src
= d
.data
+ 1;
234 else if (d
.size
!= bitlen
/ 8) goto done
;
236 for (i
= bitlen
/ 8 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
249 if (!*data_ret
) RtlFreeHeap( GetProcessHeap(), 0, buf
);
253 static char *password_to_ascii( const WCHAR
*str
)
258 if (!(ret
= malloc( (lstrlenW(str
) + 1) * sizeof(*ret
) ))) return NULL
;
261 if (*str
> 0x7f) WARN( "password contains non-ascii characters\n" );
268 static BOOL WINAPI
import_cert_store( CRYPT_DATA_BLOB
*pfx
, const WCHAR
*password
, DWORD flags
,
269 void **key_ret
, void ***chain_ret
, DWORD
*count_ret
)
272 gnutls_datum_t pfx_data
;
273 gnutls_x509_privkey_t key
;
274 gnutls_x509_crt_t
*chain
;
275 unsigned int chain_len
, i
;
279 if (password
&& !(pwd
= password_to_ascii( password
))) return FALSE
;
281 if ((ret
= pgnutls_pkcs12_init( &p12
)) < 0) goto error
;
283 pfx_data
.data
= pfx
->pbData
;
284 pfx_data
.size
= pfx
->cbData
;
285 if ((ret
= pgnutls_pkcs12_import( p12
, &pfx_data
, GNUTLS_X509_FMT_DER
, 0 )) < 0) goto error
;
287 if ((ret
= pgnutls_pkcs12_simple_parse( p12
, pwd
? pwd
: "", &key
, &chain
, &chain_len
, NULL
, NULL
, NULL
, 0 )) < 0)
290 if (!import_key( key
, flags
, key_ret
)) goto error
;
292 *chain_ret
= RtlAllocateHeap( GetProcessHeap(), 0, chain_len
* sizeof(*chain_ret
) );
293 *count_ret
= chain_len
;
294 for (i
= 0; i
< chain_len
; i
++)
298 if ((ret
= pgnutls_x509_crt_export( chain
[i
], GNUTLS_X509_FMT_DER
, NULL
, &size
)) != GNUTLS_E_SHORT_MEMORY_BUFFER
)
301 (*chain_ret
)[i
] = RtlAllocateHeap( GetProcessHeap(), 0, size
);
302 if ((ret
= pgnutls_x509_crt_export( chain
[i
], GNUTLS_X509_FMT_DER
, (*chain_ret
)[i
], &size
)) < 0)
305 while (i
) RtlFreeHeap( GetProcessHeap(), 0, (*chain_ret
)[--i
] );
306 RtlFreeHeap( GetProcessHeap(), 0, *chain_ret
);
310 pgnutls_pkcs12_deinit( p12
);
314 pgnutls_perror( ret
);
315 pgnutls_pkcs12_deinit( p12
);
320 #endif /* SONAME_LIBGNUTLS */
329 static struct list root_cert_list
= LIST_INIT(root_cert_list
);
331 static BYTE
*add_cert( SIZE_T size
)
333 struct root_cert
*cert
= malloc( offsetof( struct root_cert
, data
[size
] ));
335 if (!cert
) return NULL
;
337 list_add_tail( &root_cert_list
, &cert
->entry
);
348 static inline void reset_buffer(struct DynamicBuffer
*buffer
)
351 if (buffer
->data
) buffer
->data
[0] = 0;
354 static void add_line_to_buffer(struct DynamicBuffer
*buffer
, LPCSTR line
)
356 if (buffer
->used
+ strlen(line
) + 1 > buffer
->allocated
)
358 DWORD new_size
= max( max( buffer
->allocated
* 2, 1024 ), buffer
->used
+ strlen(line
) + 1 );
359 void *ptr
= realloc( buffer
->data
, new_size
);
362 buffer
->allocated
= new_size
;
363 if (!buffer
->used
) buffer
->data
[0] = 0;
365 strcpy( buffer
->data
+ buffer
->used
, line
);
366 buffer
->used
+= strlen(line
);
369 #define BASE64_DECODE_PADDING 0x100
370 #define BASE64_DECODE_WHITESPACE 0x200
371 #define BASE64_DECODE_INVALID 0x300
373 static inline int decodeBase64Byte(char c
)
375 int ret
= BASE64_DECODE_INVALID
;
377 if (c
>= 'A' && c
<= 'Z')
379 else if (c
>= 'a' && c
<= 'z')
381 else if (c
>= '0' && c
<= '9')
388 ret
= BASE64_DECODE_PADDING
;
389 else if (c
== ' ' || c
== '\t' || c
== '\r' || c
== '\n')
390 ret
= BASE64_DECODE_WHITESPACE
;
394 static BOOL
base64_to_cert( const char *str
)
396 DWORD i
, valid
, out
, hasPadding
;
397 BYTE block
[4], *data
;
399 for (i
= valid
= out
= hasPadding
= 0; str
[i
]; i
++)
401 int d
= decodeBase64Byte( str
[i
] );
402 if (d
== BASE64_DECODE_INVALID
) return FALSE
;
403 if (d
== BASE64_DECODE_WHITESPACE
) continue;
405 /* When padding starts, data is not acceptable */
406 if (hasPadding
&& d
!= BASE64_DECODE_PADDING
) return FALSE
;
408 /* Padding after a full block (like "VVVV=") is ok and stops decoding */
409 if (d
== BASE64_DECODE_PADDING
&& (valid
& 3) == 0) break;
412 if (d
== BASE64_DECODE_PADDING
)
415 /* When padding reaches a full block, stop decoding */
416 if ((valid
& 3) == 0) break;
420 /* out is incremented in the 4-char block as follows: "1-23" */
421 if ((valid
& 3) != 2) out
++;
423 /* Fail if the block has bad padding; omitting padding is fine */
424 if ((valid
& 3) != 0 && hasPadding
) return FALSE
;
426 if (!(data
= add_cert( out
))) return FALSE
;
427 for (i
= valid
= out
= 0; str
[i
]; i
++)
429 int d
= decodeBase64Byte( str
[i
] );
430 if (d
== BASE64_DECODE_WHITESPACE
) continue;
431 if (d
== BASE64_DECODE_PADDING
) break;
432 block
[valid
& 3] = d
;
437 data
[out
++] = (block
[0] << 2);
440 data
[out
-1] = (block
[0] << 2) | (block
[1] >> 4);
443 data
[out
++] = (block
[1] << 4) | (block
[2] >> 2);
446 data
[out
++] = (block
[2] << 6) | (block
[3] >> 0);
453 /* Reads the file fd, and imports any certificates in it into store. */
454 static void import_certs_from_file( int fd
)
456 FILE *fp
= fdopen(fd
, "r");
458 BOOL in_cert
= FALSE
;
459 struct DynamicBuffer saved_cert
= { 0, 0, NULL
};
464 while (fgets(line
, sizeof(line
), fp
))
466 static const char header
[] = "-----BEGIN CERTIFICATE-----";
467 static const char trailer
[] = "-----END CERTIFICATE-----";
469 if (!strncmp(line
, header
, strlen(header
)))
471 TRACE("begin new certificate\n");
473 reset_buffer(&saved_cert
);
475 else if (!strncmp(line
, trailer
, strlen(trailer
)))
477 TRACE("end of certificate, adding cert\n");
479 if (base64_to_cert( saved_cert
.data
)) num_certs
++;
481 else if (in_cert
) add_line_to_buffer(&saved_cert
, line
);
483 free( saved_cert
.data
);
484 TRACE("Read %d certs\n", num_certs
);
488 static void import_certs_from_path(LPCSTR path
, BOOL allow_dir
);
490 static BOOL
check_buffer_resize(char **ptr_buf
, size_t *buf_size
, size_t check_size
)
492 if (check_size
> *buf_size
)
494 void *ptr
= realloc(*ptr_buf
, check_size
);
496 if (!ptr
) return FALSE
;
497 *buf_size
= check_size
;
503 /* Opens path, which must be a directory, and imports certificates from every
504 * file in the directory into store.
505 * Returns TRUE if any certificates were successfully imported.
507 static void import_certs_from_dir( LPCSTR path
)
514 size_t path_len
= strlen(path
), bufsize
= 0;
515 char *filebuf
= NULL
;
517 struct dirent
*entry
;
518 while ((entry
= readdir(dir
)))
520 if (strcmp(entry
->d_name
, ".") && strcmp(entry
->d_name
, ".."))
522 size_t name_len
= strlen(entry
->d_name
);
524 if (!check_buffer_resize(&filebuf
, &bufsize
, path_len
+ 1 + name_len
+ 1)) break;
525 snprintf(filebuf
, bufsize
, "%s/%s", path
, entry
->d_name
);
526 import_certs_from_path(filebuf
, FALSE
);
534 /* Opens path, which may be a file or a directory, and imports any certificates
535 * it finds into store.
536 * Returns TRUE if any certificates were successfully imported.
538 static void import_certs_from_path(LPCSTR path
, BOOL allow_dir
)
542 TRACE("(%s, %d)\n", debugstr_a(path
), allow_dir
);
544 fd
= open(path
, O_RDONLY
);
549 if (fstat(fd
, &st
) == 0)
551 if (S_ISREG(st
.st_mode
))
552 import_certs_from_file(fd
);
553 else if (S_ISDIR(st
.st_mode
))
556 import_certs_from_dir(path
);
558 WARN("%s is a directory and directories are disallowed\n",
562 ERR("%s: invalid file type\n", path
);
568 static const char * const CRYPT_knownLocations
[] = {
569 "/etc/ssl/certs/ca-certificates.crt",
571 "/etc/pki/tls/certs/ca-bundle.crt",
572 "/usr/share/ca-certificates/ca-bundle.crt",
573 "/usr/local/share/certs/",
574 "/etc/sfw/openssl/certs",
575 "/etc/security/cacerts", /* Android */
578 static void load_root_certs(void)
582 #ifdef HAVE_SECURITY_SECURITY_H
584 CFArrayRef rootCerts
;
586 status
= SecTrustCopyAnchorCertificates(&rootCerts
);
589 for (i
= 0; i
< CFArrayGetCount(rootCerts
); i
++)
591 SecCertificateRef cert
= (SecCertificateRef
)CFArrayGetValueAtIndex(rootCerts
, i
);
593 if ((status
= SecKeychainItemExport(cert
, kSecFormatX509Cert
, 0, NULL
, &certData
)) == noErr
)
595 BYTE
*data
= add_cert( CFDataGetLength(certData
) );
596 if (data
) memcpy( data
, CFDataGetBytePtr(certData
), CFDataGetLength(certData
) );
600 WARN("could not export certificate %d to X509 format: 0x%08x\n", i
, (unsigned int)status
);
602 CFRelease(rootCerts
);
606 for (i
= 0; i
< ARRAY_SIZE(CRYPT_knownLocations
) && list_empty(&root_cert_list
); i
++)
607 import_certs_from_path( CRYPT_knownLocations
[i
], TRUE
);
610 static BOOL WINAPI
enum_root_certs( void *buffer
, SIZE_T size
, SIZE_T
*needed
)
614 struct root_cert
*cert
;
616 if (!loaded
) load_root_certs();
619 if (!(ptr
= list_head( &root_cert_list
))) return FALSE
;
620 cert
= LIST_ENTRY( ptr
, struct root_cert
, entry
);
621 *needed
= cert
->size
;
622 if (cert
->size
<= size
)
624 memcpy( buffer
, cert
->data
, cert
->size
);
625 list_remove( &cert
->entry
);
631 static struct unix_funcs funcs
=
637 NTSTATUS CDECL
__wine_init_unix_lib( HMODULE module
, DWORD reason
, const void *ptr_in
, void *ptr_out
)
641 case DLL_PROCESS_ATTACH
:
642 #ifdef SONAME_LIBGNUTLS
643 if (gnutls_initialize()) funcs
.import_cert_store
= import_cert_store
;
645 *(const struct unix_funcs
**)ptr_out
= &funcs
;
647 case DLL_PROCESS_DETACH
:
648 #ifdef SONAME_LIBGNUTLS
649 if (libgnutls_handle
) gnutls_uninitialize();
653 return STATUS_SUCCESS
;