summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiko Hund <heiko.hund@sophos.com>2012-02-10 15:13:42 +0100
committerDavid Sommerseth <davids@redhat.com>2012-02-13 17:11:50 +0100
commit71bbbd76c62630c88441237d72fe5b61f0b45b2a (patch)
tree715f8c8183c6e47bb26f0a10c0f6b14f9b9f83a2
parent2ee0dc2bd72ec318fcc227af54e5ca7e1384a6cc (diff)
downloadopenvpn-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.c3
-rw-r--r--crypto.c6
-rw-r--r--error.c17
-rw-r--r--manage.c2
-rw-r--r--misc.c41
-rw-r--r--misc.h31
-rw-r--r--options.c37
-rw-r--r--packet_id.c6
-rw-r--r--pf.c2
-rw-r--r--plugin.c3
-rw-r--r--ps.c2
-rw-r--r--ssl_openssl.c459
-rw-r--r--ssl_verify.c2
-rw-r--r--ssl_verify_openssl.c6
-rw-r--r--status.c48
-rw-r--r--syshead.h1
-rw-r--r--win32.c60
-rw-r--r--win32.h3
18 files changed, 367 insertions, 362 deletions
diff --git a/buffer.c b/buffer.c
index fca6a90..6800e6e 100644
--- a/buffer.c
+++ b/buffer.c
@@ -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)
diff --git a/crypto.c b/crypto.c
index e628578..5af92a0 100644
--- a/crypto.c
+++ b/crypto.c
@@ -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);
diff --git a/error.c b/error.c
index 06b383f..ede33d0 100644
--- a/error.c
+++ b/error.c
@@ -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)
{
diff --git a/manage.c b/manage.c
index ce6fae7..23e32db 100644
--- a/manage.c
+++ b/manage.c
@@ -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);
diff --git a/misc.c b/misc.c
index 1aa1be4..58096fa 100644
--- a/misc.c
+++ b/misc.c
@@ -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);
diff --git a/misc.h b/misc.h
index b1badd9..2413f53 100644
--- a/misc.h
+++ b/misc.h
@@ -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);
diff --git a/options.c b/options.c
index a0b3431..42b0b52 100644
--- a/options.c
+++ b/options.c
@@ -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,
diff --git a/pf.c b/pf.c
index 79915fa..a0e9fc8 100644
--- a/pf.c
+++ b/pf.c
@@ -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)
{
diff --git a/plugin.c b/plugin.c
index 737a868..4cc9c36 100644
--- a/plugin.c
+++ b/plugin.c
@@ -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);
diff --git a/ps.c b/ps.c
index 98a20ad..182925b 100644
--- a/ps.c
+++ b/ps.c
@@ -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;
}
diff --git a/status.c b/status.c
index 4bbea28..5f32a83 100644
--- a/status.c
+++ b/status.c
@@ -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);
diff --git a/syshead.h b/syshead.h
index 98be0c3..0235abd 100644
--- a/syshead.h
+++ b/syshead.h
@@ -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>
diff --git a/win32.c b/win32.c
index 2cc8c2a..5b38918 100644
--- a/win32.c
+++ b/win32.c
@@ -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
*/
diff --git a/win32.h b/win32.h
index 23c04be..2492e35 100644
--- a/win32.h
+++ b/win32.h
@@ -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