diff options
author | Heiko Hund <heiko.hund@sophos.com> | 2012-02-10 15:13:42 +0100 |
---|---|---|
committer | David Sommerseth <davids@redhat.com> | 2012-02-13 17:11:50 +0100 |
commit | 71bbbd76c62630c88441237d72fe5b61f0b45b2a (patch) | |
tree | 715f8c8183c6e47bb26f0a10c0f6b14f9b9f83a2 | |
parent | 2ee0dc2bd72ec318fcc227af54e5ca7e1384a6cc (diff) | |
download | openvpn-71bbbd76c62630c88441237d72fe5b61f0b45b2a.tar.gz openvpn-71bbbd76c62630c88441237d72fe5b61f0b45b2a.tar.xz openvpn-71bbbd76c62630c88441237d72fe5b61f0b45b2a.zip |
handle Windows unicode paths
Openvpn for Windows is not compiled as a Unicode binary and thus cannot
handle paths which contain non-ASCII characters using the argv vector.
Characters that are not present in the system codepage are simply replaced
with a question mark, e.g. if started as 'openvpn --config домой.ovpn'
the file '?????.ovpn' is tried to be opened as configuration.
The same applies to paths in config files which need to be UTF-8
encoded if they contain non ASCII characters. The option line
'key лев.pem' will lead to openvpn trying to open 'лев.pem' on a
system with codepage 1252.
This patch makes openvpn read the command line in UCS-2 and convert
it to UTF-8 internally. Windows stores names in the filesystem in UCS-2.
When using a paths openvpn converts it from UTF-8 to UCS-2 and uses the
wide character Windows API function.
Signed-off-by: Heiko Hund <heiko.hund@sophos.com>
Acked-by: David Sommerseth <davids@redhat.com>
Signed-off-by: David Sommerseth <davids@redhat.com>
-rw-r--r-- | buffer.c | 3 | ||||
-rw-r--r-- | crypto.c | 6 | ||||
-rw-r--r-- | error.c | 17 | ||||
-rw-r--r-- | manage.c | 2 | ||||
-rw-r--r-- | misc.c | 41 | ||||
-rw-r--r-- | misc.h | 31 | ||||
-rw-r--r-- | options.c | 37 | ||||
-rw-r--r-- | packet_id.c | 6 | ||||
-rw-r--r-- | pf.c | 2 | ||||
-rw-r--r-- | plugin.c | 3 | ||||
-rw-r--r-- | ps.c | 2 | ||||
-rw-r--r-- | ssl_openssl.c | 459 | ||||
-rw-r--r-- | ssl_verify.c | 2 | ||||
-rw-r--r-- | ssl_verify_openssl.c | 6 | ||||
-rw-r--r-- | status.c | 48 | ||||
-rw-r--r-- | syshead.h | 1 | ||||
-rw-r--r-- | win32.c | 60 | ||||
-rw-r--r-- | win32.h | 3 |
18 files changed, 367 insertions, 362 deletions
@@ -28,6 +28,7 @@ #include "buffer.h" #include "error.h" #include "mtu.h" +#include "misc.h" #include "memdbg.h" @@ -1073,7 +1074,7 @@ buffer_list_advance (struct buffer_list *ol, int n) struct buffer_list * buffer_list_file (const char *fn, int max_line_len) { - FILE *fp = fopen (fn, "r"); + FILE *fp = openvpn_fopen (fn, "r"); struct buffer_list *bl = NULL; if (fp) @@ -862,7 +862,7 @@ read_key_file (struct key2 *key2, const char *file, const unsigned int flags) #endif { in = alloc_buf_gc (2048, &gc); - fd = open (file, O_RDONLY); + fd = openvpn_open (file, O_RDONLY, 0); if (fd == -1) msg (M_ERR, "Cannot open file key file '%s'", file); size = read (fd, in.data, in.capacity); @@ -1023,7 +1023,7 @@ read_passphrase_hash (const char *passphrase_file, const int min_passphrase_size = 8; uint8_t buf[64]; int total_size = 0; - int fd = open (passphrase_file, O_RDONLY); + int fd = openvpn_open (passphrase_file, O_RDONLY, 0); if (fd == -1) msg (M_ERR, "Cannot open passphrase file: '%s'", passphrase_file); @@ -1073,7 +1073,7 @@ write_key_file (const int nkeys, const char *filename) const int bytes_per_line = 16; /* open key file */ - fd = open (filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); + fd = openvpn_open (filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); if (fd == -1) msg (M_ERR, "Cannot open shared secret file '%s' for write", filename); @@ -465,6 +465,7 @@ redirect_stdout_stderr (const char *file, bool append) #if defined(WIN32) if (!std_redir) { + struct gc_arena gc = gc_new (); HANDLE log_handle; int log_fd; @@ -473,13 +474,15 @@ redirect_stdout_stderr (const char *file, bool append) saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; - log_handle = CreateFile (file, - GENERIC_WRITE, - FILE_SHARE_READ, - &saAttr, - append ? OPEN_ALWAYS : CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); + log_handle = CreateFileW (wide_string (file, &gc), + GENERIC_WRITE, + FILE_SHARE_READ, + &saAttr, + append ? OPEN_ALWAYS : CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + gc_free (&gc); if (log_handle == INVALID_HANDLE_VALUE) { @@ -1423,7 +1423,7 @@ man_record_peer_info (struct management *man) { const in_addr_t a = ntohl (addr.sin_addr.s_addr); const int p = ntohs (addr.sin_port); - FILE *fp = fopen (man->settings.write_peer_info_file, "w"); + FILE *fp = openvpn_fopen (man->settings.write_peer_info_file, "w"); if (fp) { fprintf (fp, "%s\n%d\n", print_in_addr_t (a, 0, &gc), p); @@ -246,7 +246,7 @@ get_pid_file (const char* filename, struct pid_state *state) CLEAR (*state); if (filename) { - state->fp = fopen (filename, "w"); + state->fp = openvpn_fopen (filename, "w"); if (!state->fp) msg (M_ERR, "Open error on pid file %s", filename); state->filename = filename; @@ -356,7 +356,15 @@ int openvpn_chdir (const char* dir) { #ifdef HAVE_CHDIR +#ifdef TARGET_WIN32 + int res; + struct gc_arena gc = gc_new (); + res = _wchdir (wide_string (dir, &gc)); + gc_free (&gc); + return res; +#else return chdir (dir); +#endif #else return -1; #endif @@ -571,6 +579,7 @@ openvpn_system (const char *command, const struct env_set *es, unsigned int flag { #ifdef HAVE_SYSTEM int ret; + struct gc_arena gc; perf_push (PERF_SCRIPT); @@ -589,7 +598,13 @@ openvpn_system (const char *command, const struct env_set *es, unsigned int flag /* * execute the command */ +#ifdef TARGET_WIN32 + gc = gc_new (); + ret = _wsystem (wide_string (command, &gc)); + gc_free (&gc); +#else ret = system (command); +#endif /* debugging */ dmsg (D_SCRIPT, "SYSTEM return=%u", ret); @@ -609,6 +624,19 @@ openvpn_system (const char *command, const struct env_set *es, unsigned int flag #endif } +int +openvpn_access (const char *path, int mode) +{ +#ifdef TARGET_WIN32 + struct gc_arena gc = gc_new (); + int ret = _waccess (wide_string (path, &gc), mode); + gc_free (&gc); + return ret; +#else + return access (path, mode); +#endif +} + /* * Run execve() inside a fork(), duping stdout. Designed to replicate the semantics of popen() but * in a safer way that doesn't require the invocation of a shell or the risks @@ -1200,7 +1228,7 @@ test_file (const char *filename) bool ret = false; if (filename) { - FILE *fp = fopen (filename, "r"); + FILE *fp = openvpn_fopen (filename, "r"); if (fp) { fclose (fp); @@ -1248,7 +1276,7 @@ create_temp_file (const char *directory, const char *prefix, struct gc_arena *gc /* Atomically create the file. Errors out if the file already exists. */ - fd = open (retfname, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); + fd = openvpn_open (retfname, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); if (fd != -1) { close (fd); @@ -1349,7 +1377,10 @@ bool delete_file (const char *filename) { #if defined(WIN32) - return (DeleteFile (filename) != 0); + struct gc_arena gc = gc_new (); + BOOL ret = DeleteFileW (wide_string (filename, &gc)); + gc_free (&gc); + return (ret != 0); #elif defined(HAVE_UNLINK) return (unlink (filename) == 0); #else @@ -1647,7 +1678,7 @@ get_user_pass_cr (struct user_pass *up, warn_if_group_others_accessible (auth_file); - fp = fopen (auth_file, "r"); + fp = openvpn_fopen (auth_file, "r"); if (!fp) msg (M_ERR, "Error opening '%s' auth file: %s", prefix, auth_file); @@ -136,6 +136,7 @@ int openvpn_execve (const struct argv *a, const struct env_set *es, const unsign bool openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message); bool openvpn_execve_allowed (const unsigned int flags); int openvpn_system (const char *command, const struct env_set *es, unsigned int flags); +int openvpn_access (const char *path, int mode); static inline bool openvpn_run_script (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *hook) @@ -146,6 +147,36 @@ openvpn_run_script (const struct argv *a, const struct env_set *es, const unsign return openvpn_execve_check(a, es, flags | S_SCRIPT, msg); }; +#ifdef TARGET_WIN32 +FILE * openvpn_fopen (const char *path, const char *mode); +#else +static inline FILE * +openvpn_fopen (const char *path, const char *mode) +{ + return fopen (path, mode); +} +#endif + +#ifdef TARGET_WIN32 +int openvpn_open (const char *path, int flags, mode_t mode); +#else +static inline int +openvpn_open (const char *path, int flags, mode_t mode) +{ + return open (path, flags, mode); +} +#endif + +#ifdef TARGET_WIN32 +int openvpn_stat (const char *path, struct stat *buf); +#else +static inline int +openvpn_stat (const char *path, struct stat *buf) +{ + return stat (path, buf); +} +#endif + #ifdef HAVE_STRERROR /* a thread-safe version of strerror */ const char* strerror_ts (int errnum, struct gc_arena *gc); @@ -2620,18 +2620,18 @@ check_file_access(const int type, const char *file, const int mode, const char * char *fullpath = strdup(file); /* POSIX dirname() implementaion may modify its arguments */ char *dirpath = dirname(fullpath); - if (access (dirpath, mode|X_OK) != 0) + if (openvpn_access (dirpath, mode|X_OK) != 0) errcode = errno; free(fullpath); } /* Is the file itself accessible? */ - if (!errcode && (type & CHKACC_FILE) && (access (file, mode) != 0) ) + if (!errcode && (type & CHKACC_FILE) && (openvpn_access (file, mode) != 0) ) errcode = errno; /* If the file exists and is accessible, is it writable? */ - if (!errcode && (type & CHKACC_FILEXSTWR) && (access (file, F_OK) == 0) ) - if (access (file, W_OK) != 0) + if (!errcode && (type & CHKACC_FILEXSTWR) && (openvpn_access (file, F_OK) == 0) ) + if (openvpn_access (file, W_OK) != 0) errcode = errno; /* Scream if an error is found */ @@ -3737,7 +3737,7 @@ read_config_file (struct options *options, if (streq (file, "stdin")) fp = stdin; else - fp = fopen (file, "r"); + fp = openvpn_fopen (file, "r"); if (fp) { line_num = 0; @@ -3814,6 +3814,33 @@ parse_argv (struct options *options, { int i, j; +#ifdef WIN32 + /* + * Windows replaces Unicode characters in argv[] that are not present + * in the current codepage with '?'. Get the wide char command line and + * convert it to UTF-8 ourselves. + */ + int wargc; + WCHAR **wargv; + char **uargv; + + wargv = CommandLineToArgvW (GetCommandLineW (), &wargc); + if (wargv == NULL || wargc != argc) + usage (); + + uargv = gc_malloc (wargc * sizeof (*uargv), false, &options->gc); + + for (i = 0; i < wargc; i++) + { + int n = WideCharToMultiByte (CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, NULL); + uargv[i] = gc_malloc (n, false, &options->gc); + WideCharToMultiByte (CP_UTF8, 0, wargv[i], -1, uargv[i], n, NULL, NULL); + } + + LocalFree (wargv); + argv = uargv; +#endif + /* usage message */ if (argc <= 1) usage (); diff --git a/packet_id.c b/packet_id.c index 024b4f3..ba8973a 100644 --- a/packet_id.c +++ b/packet_id.c @@ -362,9 +362,9 @@ packet_id_persist_load (struct packet_id_persist *p, const char *filename) if (!packet_id_persist_enabled (p)) { /* open packet-id persist file for both read and write */ - p->fd = open (filename, - O_CREAT | O_RDWR | O_BINARY, - S_IRUSR | S_IWUSR); + p->fd = openvpn_open (filename, + O_CREAT | O_RDWR | O_BINARY, + S_IRUSR | S_IWUSR); if (p->fd == -1) { msg (D_PID_PERSIST | M_ERRNO, @@ -499,7 +499,7 @@ pf_check_reload (struct context *c) && event_timeout_trigger (&c->c2.pf.reload, &c->c2.timeval, ETT_DEFAULT)) { struct stat s; - if (!stat (c->c2.pf.filename, &s)) + if (!openvpn_stat (c->c2.pf.filename, &s)) { if (s.st_mtime > c->c2.pf.file_last_mod) { @@ -30,6 +30,7 @@ #include "error.h" #include "misc.h" #include "plugin.h" +#include "win32.h" #include "memdbg.h" @@ -222,7 +223,7 @@ plugin_init_item (struct plugin *p, const struct plugin_option *o) #elif defined(USE_LOAD_LIBRARY) rel = !absolute_pathname (p->so_pathname); - p->module = LoadLibrary (p->so_pathname); + p->module = LoadLibraryW (wide_string (p->so_pathname, &gc)); if (!p->module) msg (M_ERR, "PLUGIN_INIT: could not load plugin DLL: %s", p->so_pathname); @@ -331,7 +331,7 @@ journal_add (const char *journal_dir, struct proxy_connection *pc, struct proxy_ check_malloc_return (jfn); openvpn_snprintf (jfn, fnlen, "%s/%s", journal_dir, t); dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: client origin %s -> %s", jfn, f); - fd = open (jfn, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP); + fd = openvpn_open (jfn, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP); if (fd != -1) { write(fd, f, strlen(f)); diff --git a/ssl_openssl.c b/ssl_openssl.c index b95944c..1267e6b 100644 --- a/ssl_openssl.c +++ b/ssl_openssl.c @@ -274,7 +274,7 @@ tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, #endif { /* Load the PKCS #12 file */ - if (!(fp = fopen(pkcs12_file, "rb"))) + if (!(fp = openvpn_fopen(pkcs12_file, "rb"))) msg(M_SSLERR, "Error opening file %s", pkcs12_file); p12 = d2i_PKCS12_fp(fp, NULL); fclose(fp); @@ -343,46 +343,20 @@ tls_ctx_load_cryptoapi(struct tls_root_ctx *ctx, const char *cryptoapi_cert) } #endif /* WIN32 */ -/* - * Based on SSL_CTX_use_certificate_file, return an x509 object for the - * given file. - */ -static int -tls_ctx_read_certificate_file(SSL_CTX *ctx, const char *file, X509 **x509) +static void +tls_ctx_add_extra_certs (struct tls_root_ctx *ctx, BIO *bio) { - BIO *in; - int ret=0; - X509 *x=NULL; - - in=BIO_new(BIO_s_file_internal()); - if (in == NULL) - { - SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_BUF_LIB); - goto end; - } - - if (BIO_read_filename(in,file) <= 0) - { - SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_SYS_LIB); - goto end; - } - - x = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, - ctx->default_passwd_callback_userdata); - if (x == NULL) + X509 *cert; + for (;;) { - SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PEM_LIB); - goto end; + cert = NULL; + if (!PEM_read_bio_X509 (bio, &cert, 0, NULL)) /* takes ownership of cert */ + break; + if (!cert) + msg (M_SSLERR, "Error reading extra certificate"); + if (SSL_CTX_add_extra_chain_cert(ctx->ctx, cert) != 1) + msg (M_SSLERR, "Error adding extra certificate"); } - - end: - if (in != NULL) - BIO_free(in); - if (x509) - *x509 = x; - else if (x) - X509_free (x); - return(ret); } void @@ -393,48 +367,57 @@ tls_ctx_load_cert_file (struct tls_root_ctx *ctx, const char *cert_file, X509 **x509 ) { - ASSERT(NULL != ctx); + BIO *in = NULL; + X509 *x = NULL; + int ret = 0; + bool inline_file = false; + + ASSERT (NULL != ctx); if (NULL != x509) - ASSERT(NULL == *x509); + ASSERT (NULL == *x509); #if ENABLE_INLINE_FILES - if (!strcmp (cert_file, INLINE_FILE_TAG) && cert_file_inline) - { - BIO *in = NULL; - X509 *x = NULL; - int ret = 0; + inline_file = (strcmp (cert_file, INLINE_FILE_TAG) == 0); - in = BIO_new_mem_buf ((char *)cert_file_inline, -1); - if (in) - { - x = PEM_read_bio_X509 (in, - NULL, - ctx->ctx->default_passwd_callback, - ctx->ctx->default_passwd_callback_userdata); - BIO_free (in); - if (x) { - ret = SSL_CTX_use_certificate(ctx->ctx, x); - if (x509) - *x509 = x; - else - X509_free (x); - } - } - - if (!ret) - msg (M_SSLERR, "Cannot load inline certificate file"); - } + if (inline_file && cert_file_inline) + in = BIO_new_mem_buf ((char *)cert_file_inline, -1); else #endif /* ENABLE_INLINE_FILES */ + in = BIO_new_file (cert_file, "r"); + + if (in == NULL) { - if (!SSL_CTX_use_certificate_chain_file (ctx->ctx, cert_file)) - msg (M_SSLERR, "Cannot load certificate file %s", cert_file); - if (x509) - { - if (!tls_ctx_read_certificate_file(ctx->ctx, cert_file, x509)) - msg (M_SSLERR, "Cannot load certificate file %s", cert_file); - } + SSLerr (SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_SYS_LIB); + goto end; + } + + x = PEM_read_bio_X509 (in, NULL, ctx->ctx->default_passwd_callback, + ctx->ctx->default_passwd_callback_userdata); + if (x == NULL) + { + SSLerr (SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PEM_LIB); + goto end; } + + ret = SSL_CTX_use_certificate (ctx->ctx, x); + if (ret) + tls_ctx_add_extra_certs (ctx, in); + +end: + if (!ret) + { + if (inline_file) + msg (M_SSLERR, "Cannot load inline certificate file"); + else + msg (M_SSLERR, "Cannot load certificate file %s", cert_file); + } + + if (in != NULL) + BIO_free(in); + if (x509) + *x509 = x; + else if (x) + X509_free (x); } void @@ -443,36 +426,6 @@ tls_ctx_free_cert_file (X509 *x509) X509_free(x509); } -#if ENABLE_INLINE_FILES -static int -use_inline_PrivateKey_file (SSL_CTX *ctx, const char *key_string) -{ - BIO *in = NULL; - EVP_PKEY *pkey = NULL; - int ret = 0; - - in = BIO_new_mem_buf ((char *)key_string, -1); - if (!in) - goto end; - - pkey = PEM_read_bio_PrivateKey (in, - NULL, - ctx->default_passwd_callback, - ctx->default_passwd_callback_userdata); - if (!pkey) - goto end; - - ret = SSL_CTX_use_PrivateKey (ctx, pkey); - - end: - if (pkey) - EVP_PKEY_free (pkey); - if (in) - BIO_free (in); - return ret; -} -#endif /* ENABLE_INLINE_FILES */ - int tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file #if ENABLE_INLINE_FILES @@ -481,35 +434,53 @@ tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file ) { int status; + SSL_CTX *ssl_ctx = NULL; + BIO *in = NULL; + EVP_PKEY *pkey = NULL; + int ret = 1; ASSERT(NULL != ctx); + ssl_ctx = ctx->ctx; + #if ENABLE_INLINE_FILES if (!strcmp (priv_key_file, INLINE_FILE_TAG) && priv_key_file_inline) - { - status = use_inline_PrivateKey_file (ctx->ctx, priv_key_file_inline); - } + in = BIO_new_mem_buf ((char *)priv_key_file_inline, -1); else #endif /* ENABLE_INLINE_FILES */ - { - status = SSL_CTX_use_PrivateKey_file (ctx->ctx, priv_key_file, SSL_FILETYPE_PEM); - } - if (!status) + in = BIO_new_file (priv_key_file, "r"); + + if (!in) + goto end; + + pkey = PEM_read_bio_PrivateKey (in, NULL, + ssl_ctx->default_passwd_callback, + ssl_ctx->default_passwd_callback_userdata); + if (!pkey) + goto end; + + if (!SSL_CTX_use_PrivateKey (ssl_ctx, pkey)) { #ifdef ENABLE_MANAGEMENT if (management && (ERR_GET_REASON (ERR_peek_error()) == EVP_R_BAD_DECRYPT)) - management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL); + management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL); #endif msg (M_WARN|M_SSL, "Cannot load private key file %s", priv_key_file); - return 1; + goto end; } warn_if_group_others_accessible (priv_key_file); /* Check Private Key */ - if (!SSL_CTX_check_private_key (ctx->ctx)) + if (!SSL_CTX_check_private_key (ssl_ctx)) msg (M_SSLERR, "Private key does not match the certificate"); - return 0; + ret = 0; +end: + if (pkey) + EVP_PKEY_free (pkey); + if (in) + BIO_free (in); + return ret; } #ifdef MANAGMENT_EXTERNAL_KEY @@ -650,111 +621,11 @@ tls_ctx_use_external_private_key (struct tls_root_ctx *ctx, X509 *cert) #endif -#if ENABLE_INLINE_FILES -static int -xname_cmp(const X509_NAME * const *a, const X509_NAME * const *b) -{ - return(X509_NAME_cmp(*a,*b)); -} - -static STACK_OF(X509_NAME) * -use_inline_load_client_CA_file (SSL_CTX *ctx, const char *ca_string) -{ - BIO *in = NULL; - X509 *x = NULL; - X509_NAME *xn = NULL; - STACK_OF(X509_NAME) *ret = NULL, *sk; - - sk=sk_X509_NAME_new(xname_cmp); - - in = BIO_new_mem_buf ((char *)ca_string, -1); - if (!in) - goto err; - - if ((sk == NULL) || (in == NULL)) - goto err; - - for (;;) - { - if (PEM_read_bio_X509(in,&x,NULL,NULL) == NULL) - break; - if (ret == NULL) - { - ret = sk_X509_NAME_new_null(); - if (ret == NULL) - goto err; - } - if ((xn=X509_get_subject_name(x)) == NULL) goto err; - /* check for duplicates */ - xn=X509_NAME_dup(xn); - if (xn == NULL) goto err; - if (sk_X509_NAME_find(sk,xn) >= 0) - X509_NAME_free(xn); - else - { - sk_X509_NAME_push(sk,xn); - sk_X509_NAME_push(ret,xn); - } - } - - if (0) - { - err: - if (ret != NULL) sk_X509_NAME_pop_free(ret,X509_NAME_free); - ret=NULL; - } - if (sk != NULL) sk_X509_NAME_free(sk); - if (in != NULL) BIO_free(in); - if (x != NULL) X509_free(x); - if (ret != NULL) - ERR_clear_error(); - return(ret); -} - static int -use_inline_load_verify_locations (SSL_CTX *ctx, const char *ca_string) +sk_x509_name_cmp(const X509_NAME * const *a, const X509_NAME * const *b) { - X509_STORE *store = NULL; - X509* cert = NULL; - BIO *in = NULL; - int ret = 0; - - in = BIO_new_mem_buf ((char *)ca_string, -1); - if (!in) - goto err; - - for (;;) - { - if (!PEM_read_bio_X509 (in, &cert, 0, NULL)) - { - ret = 1; - break; - } - if (!cert) - break; - - store = SSL_CTX_get_cert_store (ctx); - if (!store) - break; - - if (!X509_STORE_add_cert (store, cert)) - break; - - if (cert) - { - X509_free (cert); - cert = NULL; - } - } - - err: - if (cert) - X509_free (cert); - if (in) - BIO_free (in); - return ret; + return X509_NAME_cmp (*a, *b); } -#endif /* ENABLE_INLINE_FILES */ void tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file, @@ -764,66 +635,99 @@ tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file, const char *ca_path, bool tls_server ) { - int status; + STACK_OF(X509_INFO) *info_stack = NULL; + STACK_OF(X509_NAME) *cert_names = NULL; + X509_LOOKUP *lookup = NULL; + X509_STORE *store = NULL; + X509_NAME *xn = NULL; + BIO *in = NULL; + int i, added = 0; ASSERT(NULL != ctx); + store = SSL_CTX_get_cert_store(ctx->ctx); + if (!store) + msg(M_SSLERR, "Cannot get certificate store (SSL_CTX_get_cert_store)"); + + /* Try to add certificates and CRLs from ca_file */ + if (ca_file) + { #if ENABLE_INLINE_FILES - if (ca_file && !strcmp (ca_file, INLINE_FILE_TAG) && ca_file_inline) - { - status = use_inline_load_verify_locations (ctx->ctx, ca_file_inline); - } - else + if (!strcmp (ca_file, INLINE_FILE_TAG) && ca_file_inline) + in = BIO_new_mem_buf ((char *)ca_file_inline, -1); + else #endif - { - /* Load CA file for verifying peer supplied certificate */ - status = SSL_CTX_load_verify_locations (ctx->ctx, ca_file, ca_path); - } + in = BIO_new_file (ca_file, "r"); - if (!status) - msg (M_SSLERR, "Cannot load CA certificate file %s path %s (SSL_CTX_load_verify_locations)", np(ca_file), np(ca_path)); + if (in) + info_stack = PEM_X509_INFO_read_bio (in, NULL, NULL, NULL); + + if (info_stack) + { + for (i = 0; i < sk_X509_INFO_num (info_stack); i++) + { + X509_INFO *info = sk_X509_INFO_value (info_stack, i); + if (info->crl) + X509_STORE_add_crl (store, info->crl); + + if (info->x509) + { + X509_STORE_add_cert (store, info->x509); + added++; + + if (!tls_server) + continue; + + /* Use names of CAs as a client CA list */ + if (cert_names == NULL) + { + cert_names = sk_X509_NAME_new (sk_x509_name_cmp); + if (!cert_names) + continue; + } + + xn = X509_get_subject_name (info->x509); + if (!xn) + continue; + + /* Don't add duplicate CA names */ + if (sk_X509_NAME_find (cert_names, xn) == -1) + { + xn = X509_NAME_dup (xn); + if (!xn) + continue; + sk_X509_NAME_push (cert_names, xn); + } + } + } + sk_X509_INFO_pop_free (info_stack, X509_INFO_free); + } + + if (tls_server) + SSL_CTX_set_client_CA_list (ctx->ctx, cert_names); + + if (!added || (tls_server && sk_X509_NAME_num (cert_names) != added)) + msg (M_SSLERR, "Cannot load CA certificate file %s", np(ca_file)); + if (in) + BIO_free (in); + } /* Set a store for certs (CA & CRL) with a lookup on the "capath" hash directory */ - if (ca_path) { - X509_STORE *store = SSL_CTX_get_cert_store(ctx->ctx); - - if (store) - { - X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); - if (!X509_LOOKUP_add_dir(lookup, ca_path, X509_FILETYPE_PEM)) - X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT); - else - msg(M_WARN, "WARNING: experimental option --capath %s", ca_path); + if (ca_path) + { + lookup = X509_STORE_add_lookup (store, X509_LOOKUP_hash_dir ()); + if (lookup && X509_LOOKUP_add_dir (lookup, ca_path, X509_FILETYPE_PEM)) + msg(M_WARN, "WARNING: experimental option --capath %s", ca_path); + else + msg(M_SSLERR, "Cannot add lookup at --capath %s", ca_path); #if OPENSSL_VERSION_NUMBER >= 0x00907000L - X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + X509_STORE_set_flags (store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); #else - msg(M_WARN, "WARNING: this version of OpenSSL cannot handle CRL files in capath"); -#endif - } - else - msg(M_SSLERR, "Cannot get certificate store (SSL_CTX_get_cert_store)"); - } - - /* Load names of CAs from file and use it as a client CA list */ - if (ca_file && tls_server) { - STACK_OF(X509_NAME) *cert_names = NULL; -#if ENABLE_INLINE_FILES - if (!strcmp (ca_file, INLINE_FILE_TAG) && ca_file_inline) - { - cert_names = use_inline_load_client_CA_file (ctx->ctx, ca_file_inline); - } - else + msg(M_WARN, "WARNING: this version of OpenSSL cannot handle CRL files in capath"); #endif - { - cert_names = SSL_load_client_CA_file (ca_file); - } - if (!cert_names) - msg (M_SSLERR, "Cannot load CA certificate file %s (SSL_load_client_CA_file)", ca_file); - SSL_CTX_set_client_CA_list (ctx->ctx, cert_names); - } + } } - void tls_ctx_load_extra_certs (struct tls_root_ctx *ctx, const char *extra_certs_file #if ENABLE_INLINE_FILES @@ -831,31 +735,20 @@ tls_ctx_load_extra_certs (struct tls_root_ctx *ctx, const char *extra_certs_file #endif ) { - BIO *bio; - X509 *cert; + BIO *in; #if ENABLE_INLINE_FILES if (!strcmp (extra_certs_file, INLINE_FILE_TAG) && extra_certs_file_inline) - { - bio = BIO_new_mem_buf ((char *)extra_certs_file_inline, -1); - } + in = BIO_new_mem_buf ((char *)extra_certs_file_inline, -1); else #endif - { - bio = BIO_new(BIO_s_file()); - if (BIO_read_filename(bio, extra_certs_file) <= 0) - msg (M_SSLERR, "Cannot load extra-certs file: %s", extra_certs_file); - } - for (;;) - { - cert = NULL; - if (!PEM_read_bio_X509 (bio, &cert, 0, NULL)) /* takes ownership of cert */ - break; - if (!cert) - msg (M_SSLERR, "Error reading extra-certs certificate"); - if (SSL_CTX_add_extra_chain_cert(ctx->ctx, cert) != 1) - msg (M_SSLERR, "Error adding extra-certs certificate"); - } - BIO_free (bio); + in = BIO_new_file (extra_certs_file, "r"); + + if (in == NULL) + msg (M_SSLERR, "Cannot load extra-certs file: %s", extra_certs_file); + else + tls_ctx_add_extra_certs (ctx, in); + + BIO_free (in); } /* ************************************** diff --git a/ssl_verify.c b/ssl_verify.c index 37d4982..a7b361f 100644 --- a/ssl_verify.c +++ b/ssl_verify.c @@ -545,7 +545,7 @@ verify_check_crl_dir(const char *crl_dir, x509_cert_t *cert) x509_free_serial(serial); return FAILURE; } - fd = open (fn, O_RDONLY); + fd = openvpn_open (fn, O_RDONLY, 0); if (fd >= 0) { msg (D_HANDSHAKE, "VERIFY CRL: certificate serial number %s is revoked", serial); diff --git a/ssl_verify_openssl.c b/ssl_verify_openssl.c index b2c01a3..200a570 100644 --- a/ssl_verify_openssl.c +++ b/ssl_verify_openssl.c @@ -584,13 +584,9 @@ x509_verify_crl(const char *crl_file, X509 *peer_cert, const char *subject) int n,i; result_t retval = FAILURE; - in=BIO_new(BIO_s_file()); + in = BIO_new_file (crl_file, "r"); if (in == NULL) { - msg (M_ERR, "CRL: BIO err"); - goto end; - } - if (BIO_read_filename(in, crl_file) <= 0) { msg (M_ERR, "CRL: cannot read: %s", crl_file); goto end; } @@ -67,45 +67,27 @@ status_open (const char *filename, buf_reset (&so->read_buf); event_timeout_clear (&so->et); if (filename) - { - switch (so->flags) - { -#ifdef _MSC_VER + { + switch (so->flags) + { case STATUS_OUTPUT_WRITE: - so->fd = open (filename, - O_CREAT | O_TRUNC | O_WRONLY, - _S_IREAD | _S_IWRITE); + so->fd = openvpn_open (filename, + O_CREAT | O_TRUNC | O_WRONLY, + S_IRUSR | S_IWUSR); break; case STATUS_OUTPUT_READ: - so->fd = open (filename, - O_RDONLY, - _S_IREAD | _S_IWRITE); + so->fd = openvpn_open (filename, + O_RDONLY, + S_IRUSR | S_IWUSR); break; case STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE: - so->fd = open (filename, - O_CREAT | O_RDWR, - _S_IREAD | _S_IWRITE); + so->fd = openvpn_open (filename, + O_CREAT | O_RDWR, + S_IRUSR | S_IWUSR); break; -#else - case STATUS_OUTPUT_WRITE: - so->fd = open (filename, - O_CREAT | O_TRUNC | O_WRONLY, - S_IRUSR | S_IWUSR); - break; - case STATUS_OUTPUT_READ: - so->fd = open (filename, - O_RDONLY, - S_IRUSR | S_IWUSR); - break; - case STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE: - so->fd = open (filename, - O_CREAT | O_RDWR, - S_IRUSR | S_IWUSR); - break; -#endif - default: - ASSERT (0); - } + default: + ASSERT (0); + } if (so->fd >= 0) { so->filename = string_alloc (filename, NULL); @@ -356,6 +356,7 @@ #include <iphlpapi.h> #include <ntddndis.h> #include <wininet.h> +#include <shellapi.h> /* The following two headers are needed of PF_INET6 */ #include <winsock2.h> #include <ws2tcpip.h> @@ -931,8 +931,8 @@ env_block (const struct env_set *es) return NULL; } -static char * -cmd_line (const struct argv *a) +static WCHAR * +wide_cmd_line (const struct argv *a, struct gc_arena *gc) { size_t nchars = 1; size_t maxlen = 0; @@ -952,9 +952,9 @@ cmd_line (const struct argv *a) maxlen = len; } - work = (char *) malloc (maxlen + 1); + work = gc_malloc (maxlen + 1, false, gc); check_malloc_return (work); - buf = alloc_buf (nchars); + buf = alloc_buf_gc (nchars, gc); for (i = 0; i < a->argc; ++i) { @@ -969,8 +969,7 @@ cmd_line (const struct argv *a) buf_printf (&buf, "\"%s\"", work); } - free (work); - return BSTR(&buf); + return wide_string (BSTR (&buf), gc); } /* @@ -988,23 +987,24 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i { if (script_method == SM_EXECVE) { - STARTUPINFO start_info; + struct gc_arena gc = gc_new (); + STARTUPINFOW start_info; PROCESS_INFORMATION proc_info; char *env = env_block (es); - char *cl = cmd_line (a); - char *cmd = a->argv[0]; + WCHAR *cl = wide_cmd_line (a, &gc); + WCHAR *cmd = wide_string (a->argv[0], &gc); CLEAR (start_info); CLEAR (proc_info); /* fill in STARTUPINFO struct */ - GetStartupInfo(&start_info); + GetStartupInfoW(&start_info); start_info.cb = sizeof(start_info); start_info.dwFlags = STARTF_USESHOWWINDOW; start_info.wShowWindow = SW_HIDE; - if (CreateProcess (cmd, cl, NULL, NULL, FALSE, 0, env, NULL, &start_info, &proc_info)) + if (CreateProcessW (cmd, cl, NULL, NULL, FALSE, 0, env, NULL, &start_info, &proc_info)) { DWORD exit_status = 0; CloseHandle (proc_info.hThread); @@ -1019,8 +1019,8 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i { msg (M_WARN|M_ERRNO, "openvpn_execve: CreateProcess %s failed", cmd); } - free (cl); free (env); + gc_free (&gc); } else if (script_method == SM_SYSTEM) { @@ -1045,6 +1045,42 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i return ret; } +WCHAR * +wide_string (const char* utf8, struct gc_arena *gc) +{ + int n = MultiByteToWideChar (CP_UTF8, 0, utf8, -1, NULL, 0); + WCHAR *ucs16 = gc_malloc (n * sizeof (WCHAR), false, gc); + MultiByteToWideChar (CP_UTF8, 0, utf8, -1, ucs16, n); + return ucs16; +} + +FILE * +openvpn_fopen (const char *path, const char *mode) +{ + struct gc_arena gc = gc_new (); + FILE *f = _wfopen (wide_string (path, &gc), wide_string (mode, &gc)); + gc_free (&gc); + return f; +} + +int +openvpn_open (const char *path, int flags, mode_t mode) +{ + struct gc_arena gc = gc_new (); + int fd = _wopen (wide_string (path, &gc), flags, mode); + gc_free (&gc); + return fd; +} + +int +openvpn_stat (const char *path, struct stat *buf) +{ + struct gc_arena gc = gc_new (); + int res = wstat (wide_string (path, &gc), buf); + gc_free (&gc); + return res; +} + /* * call ourself in another process */ @@ -286,5 +286,8 @@ int openvpn_inet_pton(int af, const char *src, void *dst); /* Find temporary directory */ const char *win_get_tempdir(); +/* Convert a string from UTF-8 to UCS-2 */ +WCHAR *wide_string (const char* utf8, struct gc_arena *gc); + #endif #endif |