Skip to contentSkip to conversation

handle Windows unicode paths

PATCH Superseded Heiko Hund 3 participants 7 messages Dec 7, 2011 – Feb 13, 2012

lore
Share & tools
lore

Heiko Hund

author
#0 lore raw

PATCH 1/1 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.

---
 buffer.c             |    3 +-
 crypto.c             |    6 +-
 error.c              |   17 +-
 manage.c             |    2 +-
 misc.c               |   28 +++-
 misc.h               |   30 ++++
 options.c            |   29 +++-
 packet_id.c          |    6 +-
 pf.c                 |    2 +-
 plugin.c             |    3 +-
 ps.c                 |    2 +-
 ssl_openssl.c        |  462 +++++++++++++++++++-------------------------------
 ssl_verify.c         |    2 +-
 ssl_verify_openssl.c |    6 +-
 status.c             |   48 ++----
 syshead.h            |    1 +
 win32.c              |   60 +++++--
 win32.h              |    3 +
 18 files changed, 350 insertions(+), 360 deletions(-)
buffer.c +2 -1
index 2f8e4b8..fd89522 100644
@@ -28,6 +28,7 @@
 #include "buffer.h"
 #include "error.h"
 #include "mtu.h"
+#include "misc.h"
 
 #include "memdbg.h"
 
@@ -1058,7 +1059,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)
crypto.c +3 -3
index d17f706..189bd9c 100644
@@ -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);
error.c +10 -7
index a0de53e..1ed7dd6 100644
@@ -464,6 +464,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;
 
@@ -472,13 +473,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)
 	{
manage.c +1 -1
index ce6fae7..23e32db 100644
@@ -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);
misc.c +23 -5
index 99e5bc5..ad74357 100644
@@ -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);
@@ -1133,7 +1148,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);
@@ -1181,7 +1196,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);
@@ -1282,7 +1297,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
@@ -1525,7 +1543,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);
 
misc.h +30 -0
index b2e6f91..0054e24 100644
@@ -146,6 +146,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);
options.c +28 -1
index a01ae76..e9a89aa 100644
@@ -3560,7 +3560,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;
@@ -3637,6 +3637,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 ();
packet_id.c +3 -3
index 024b4f3..ba8973a 100644
@@ -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,
pf.c +1 -1
index 6b4cba4..e16cd85 100644
@@ -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)
 	    {
plugin.c +2 -1
index 737a868..4cc9c36 100644
@@ -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);
 
ps.c +1 -1
index 98a20ad..182925b 100644
@@ -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));
ssl_openssl.c +177 -285

560 lines not on this page · raw message

ssl_verify.c +1 -1
index 0b2a1fb..606c46f 100644
@@ -544,7 +544,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);
ssl_verify_openssl.c +1 -5
index 7a99469..017215b 100644
@@ -546,13 +546,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;
   }
status.c +15 -33

60 lines not on this page · raw message

syshead.h +1 -0
index f3c0ac9..0678587 100644
@@ -354,6 +354,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>
win32.c +48 -12

110 lines not on this page · raw message

win32.h +3 -0
index 5b18e3c..43325de 100644
@@ -286,5 +286,8 @@ int inet_pton(int af, const char *src, void *st);
 /* 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
Signature · 1 line
1.7.5.4
author
#1 lore raw

PATCH 1/1 set Windows environment variables as UCS-2

Windows uses 16 bit wide characters to represent Unicode in the process environment. Convert UTF-8 to UCS-2 and use the wide character API to set environment variables.

---
 misc.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
misc.c +2 -2
index ad74357..ddd5fdf 100644
@@ -1030,8 +1030,8 @@ setenv_str_ex (struct env_set *es,
     {
 #if defined(WIN32)
       {
-	/*msg (M_INFO, "SetEnvironmentVariable '%s' '%s'", name_tmp, val_tmp ? val_tmp : "NULL");*/
-	if (!SetEnvironmentVariable (name_tmp, val_tmp))
+        if (!SetEnvironmentVariableW (wide_string (name_tmp, &gc),
+                                      wide_string (val_tmp, &gc)))
 	  msg (M_WARN | M_ERRNO, "SetEnvironmentVariable failed, name='%s', value='%s'",
 	       name_tmp,
 	       val_tmp ? val_tmp : "NULL");
Signature · 1 line
1.7.5.4
author
#2 lore raw
In reply to Heiko Hund
Quoted 4 lines · On Wednesday 07 December 2011 17:58:51 Heiko Hund wrote:

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.

One thing I left out on purpose for now was handling --capath with a Unicode path. To have that work, openvpn would need to define a custom LOOKUP_METHOD that is Unicode-safe. The one that is currently used is at [1]. The problem here is the _stat call that needs to be _wstat. Please let me know if that's something that should be done or not and I'll provide a patch.

Heiko

[1] http://cvs.openssl.org/fileview?f=openssl/crypto/x509/by_dir.c&v=1.32

Signature · 8 lines
Heiko Hund | Software Engineer | Phone +49-721-25516-237 | Fax -200
Astaro a Sophos Company | Amalienbadstr. 41 Bau 52 | 76227 Karlsruhe | Germany
Commercial Register: Mannheim HRA 702710 | Headquarter Location: Karlsruhe
 
Represented by the General Partner Astaro Verwaltungs GmbH
Amalienbadstraße 41 Bau 52 | 76227 Karlsruhe | Germany 
Commercial Register: Mannheim HRB 708248 | Executive Board: Gert Hansen,
Markus Hennig, Jan Hichert, Günter Junk, Dr. Frank Nellissen
#3 lore raw
Quoted 12 lines · Heiko Hund
Quoted 4 lines · On Wednesday 07 December 2011 17:58:51 Heiko Hund wrote:

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.

One thing I left out on purpose for now was handling --capath with a Unicode path. To have that work, openvpn would need to define a custom  LOOKUP_METHOD that is Unicode-safe. The one that is currently used is at [1]. The problem here is the _stat call that needs to be _wstat. Please let me know if that's something that should be done or not and I'll provide a patch.

Quoted 3 lines

Heiko

[1] http://cvs.openssl.org/fileview?f=openssl/crypto/x509/by_dir.c&v=1.32

Personally, I think that you should split this loooong patch into several. Style changes, file io, openssl, etc...
For the openssl, I would split each change:dn, files.

It would be much easier to review and comment.

At first glance, the files are relatively ok.

Alon.

author
#4 lore raw

PATCH v2 1/1 handle Windows unicode paths

In reply to Heiko Hund

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.

---

This version of the patch was rebased to current master. It also handles the access(2) calls introduced in commit 0f2bc0dd by David correctly.

 buffer.c             |    3 +-
 crypto.c             |    6 +-
 error.c              |   17 +-
 manage.c             |    2 +-
 misc.c               |   41 ++++-
 misc.h               |   31 ++++
 options.c            |   37 ++++-
 packet_id.c          |    6 +-
 pf.c                 |    2 +-
 plugin.c             |    3 +-
 ps.c                 |    2 +-
 ssl_openssl.c        |  459 +++++++++++++++++++-------------------------------
 ssl_verify.c         |    2 +-
 ssl_verify_openssl.c |    6 +-
 status.c             |   48 ++----
 syshead.h            |    1 +
 win32.c              |   60 ++++++--
 win32.h              |    3 +
 18 files changed, 367 insertions(+), 362 deletions(-)
buffer.c +2 -1
index fca6a90..6800e6e 100644
@@ -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)
crypto.c +3 -3
index e628578..5af92a0 100644
@@ -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);
error.c +10 -7
index 06b383f..ede33d0 100644
@@ -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)
 	{
manage.c +1 -1
index ce6fae7..23e32db 100644
@@ -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);
misc.c +36 -5

97 lines not on this page · raw message

misc.h +31 -0
index b1badd9..2413f53 100644
@@ -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);
options.c +32 -5
index a0b3431..42b0b52 100644
@@ -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 ();
packet_id.c +3 -3
index 024b4f3..ba8973a 100644
@@ -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,
pf.c +1 -1
index 79915fa..a0e9fc8 100644
@@ -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)
 	    {
plugin.c +2 -1
index 737a868..4cc9c36 100644
@@ -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);
 
ps.c +1 -1
index 98a20ad..182925b 100644
@@ -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));
ssl_openssl.c +176 -283

556 lines not on this page · raw message

ssl_verify.c +1 -1
index 37d4982..a7b361f 100644
@@ -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);
ssl_verify_openssl.c +1 -5
index b2c01a3..200a570 100644
@@ -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;
   }
status.c +15 -33

60 lines not on this page · raw message

syshead.h +1 -0
index f121114..71d5d00 100644
@@ -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>
win32.c +48 -12

110 lines not on this page · raw message

win32.h +3 -0
index 23c04be..2492e35 100644
@@ -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
Signature · 1 line
1.7.8.3
#5 lore raw

Hello Heiko,

I think this patch is important, however as I wrote before it is very difficult to review when you send it as one chunk.

It is much better if you split this into trivial changes (the fopen->openvpn_fopen and such) and then focus on each complex change as in the openssl functions.

The trivial code can be easily merged even without the complete functionality, slowly making the openvpn Unicode complaint.

The configuration file format is also an issue, if I understand correctly you force configuration to be UTF-8, while in windows it is none standard as UCS-2 should used.

Alon.

Quoted 20 lines · Heiko Hund

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.

---

#6 lore raw
In reply to Heiko Hund

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 33 lines · Heiko Hund

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.

This version of the patch was rebased to current master. It also handles the access(2) calls introduced in commit 0f2bc0dd by David correctly.

buffer.c | 3 +- crypto.c | 6 +- error.c

|   17 +- manage.c             |    2 +- misc.c               |   41
++++- misc.h               |   31 ++++ options.c            |   37
++++- packet_id.c          |    6 +- pf.c                 |    2 +- 
plugin.c             |    3 +- ps.c                 |    2 +- 
ssl_openssl.c        |  459
+++++++++++++++++++------------------------------- ssl_verify.c
|    2 +- ssl_verify_openssl.c |    6 +- status.c             |   48
++---- syshead.h            |    1 + win32.c              |   60
++++++-- win32.h              |    3 + 18 files changed, 367
Quoted 2 lines

insertions(+), 362 deletions(-)

ACK and applied to master for -stable and -testing trees.

commit 71bbbd76c62630c88441237d72fe5b61f0b45b2a

Author: Heiko Hund <[email protected]...>
Date:   Fri Feb 10 15:13:42 2012 +0100
    handle Windows unicode paths

    Signed-off-by: Heiko Hund <[email protected]...>
    Acked-by: David Sommerseth <[email protected]...>
    Signed-off-by: David Sommerseth <[email protected]...>

kind regards,

David Sommerseth
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk85ZCwACgkQDC186MBRfrqGJQCdHMudYYgOM/ZT9trN4eNimO6i yeMAoIijsO70hmkaq+OeAWXV7Xn7cv2o
=68zX
-----END PGP SIGNATURE-----