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

The attached patch fixes bug #330 which causes a regression from 2.2.x
where valid configs won't work in certain cases when using --chroot. A
more terse overview of the change is presented in the patch itself.

The additional error checking for files & scripts introduced for 2.3
don't take into account that the file path changes after the chroot
operation, preventing options_postprocess_filechecks() from checking the
proper paths.

The changes in this patch intelligently look at the pre-chroot path when
required by appending the "in-chroot" path after the specified chroot
dir. To bring in support for dynamic string/memory management and the
ability to see if chroot is used, some callers were also modified to
pass in pointers to the options struct. Additionally, some bitmask
#defines were included even with ENABLE_SMALL as the set_user_script()
callers in option_add() need the ability to define if the script in
question is accessed from within the chroot or not so the access checks
can be made properly.

Besides the low-impact #define changes, all the impacted code is not
built with ENABLE_SMALL, so this shouldn't be a problem for embedded
systems. I also tried to be helpful with the error messages returned
from check_file_access() by informing users if the failing path is
pre-chroot since the text won't match a direct string supplied but a
combination of two.

On a much smaller note, there seemed to be a mix of indent styles in a
few places where apparent alignment was made for tabs expanding to 8
spaces; I have aligned this patch assuming a tab is 2 spaces, leaving
the issue of whitespace cleanup for a separate patch. All new lines use
spaces only.


- --
Josh
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.20 (GNU/Linux)

iQGcBAEBAgAGBQJSNYEZAAoJENcx2Xpgb9Rjm6gL/1epNkDRCWMEjb/GGo86ECk4
092oQNmAS8fssD2y0YULJ7xKT5O6JUq+yCIsxjHMIpBpeRBmn+nwVTP33gPdFNFb
RYg1eIUb/EL0to56S7tSm+018aW80LH5fp8CyQcpTWHJd5sW1sYwxtLmk1Ho6cAb
4bKy709sOZfSIvqhBHe342izqFtPWsO0RV16NrFL7vKbr+ubc3hgGiEEB/f+U3n9
S/kbQXbrcBi5OUsjbulNAgOyR641rS3CcE7pde/dxMVo21OBmDiXkzksHFpugLlv
xhaa3JqM2KCiJb+x04N7WT9F+h7UPsvyc4P+gyccb/UVPgdE3SR2ERlwv8x2uSqy
7/sMrGaT9pwfd+P4bywBGvv+16VOeEwhItrX5sy9rGIsx1I0FiXpt7MROU2FERFB
sk/WRcEchXfB+Z9RSBZwtbeRf0w6dF7nCDOEr1NWTrLYGIX1Z+iZ4jXBzt+0qU3v
O518OD3QA7apvQqvptDl9ExY3LRu+H6y4WZWeapHwQ==
=V6Ck
-----END PGP SIGNATURE-----
>From 2b4a3b819ee2dd3d1bf1326d17b7baf8bfbf6f38 Mon Sep 17 00:00:00 2001
From: Josh Cepek <josh.ce...@usa.net>
List-Post: openvpn-devel@lists.sourceforge.net
Date: Sun, 15 Sep 2013 05:12:11 -0500
Subject: [PATCH] Fix file access checks when using --chroot

This fixes bug #330.

The additional error checking for files & scripts introduced for 2.3
don't take into account that the file path changes after the chroot
operation, preventing options_postprocess_filechecks() from checking the
proper paths.

The changes in this patch intelligently look at the pre-chroot path when
required by appending the "in-chroot" path after the specified chroot
dir. Error messages call attention to the use of the chroot and combined
path when there are access issues.

Signed-off-by: Josh Cepek <josh.ce...@usa.net>
---
 src/openvpn/options.c | 130 +++++++++++++++++++++++++++++++-------------------
 1 file changed, 81 insertions(+), 49 deletions(-)

diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 4d0271f..ae32afd 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -2560,16 +2560,19 @@ options_postprocess_mutate (struct options *o)
  *  Check file/directory sanity
  *
  */
-#ifndef ENABLE_SMALL  /** Expect people using the stripped down version to know what they do */
-
+#define CHKACC_NOOP 0            /** No-op - used by callers to add no additional checks */
 #define CHKACC_FILE (1<<0)       /** Check for a file/directory precense */
 #define CHKACC_DIRPATH (1<<1)    /** Check for directory precense where a file should reside */
 #define CHKACC_FILEXSTWR (1<<2)  /** If file exists, is it writable? */
 #define CHKACC_INLINE (1<<3)     /** File is present if it's an inline file */
 #define CHKACC_ACPTSTDIN (1<<4)  /** If filename is stdin, it's allowed and "exists" */
+#define CHKACC_INCHROOT (1<<5)   /** When chrooting, look for this file under the chroot dir */
+
+#ifndef ENABLE_SMALL  /** Expect people using the stripped down version to know what they do */

 static bool
-check_file_access(const int type, const char *file, const int mode, const char *opt)
+check_file_access(const int type, const char *file, const int mode, const char *opt,
+        struct options *o)
 {
   int errcode = 0;

@@ -2587,10 +2590,33 @@ check_file_access(const int type, const char *file, const int mode, const char *
   if( (type & CHKACC_ACPTSTDIN) && streq(file, "stdin") )
       return false;

+  struct gc_arena gc = gc_new();
+
+  /* Define the real file to look at based on possible chroot usage.
+   * If this this file is in used within a chroot, look there when chroot is enabled
+   */
+  int buf_size = strlen(file) + 1;
+  if (o->chroot_dir)
+    buf_size += strlen(o->chroot_dir) + strlen(PATH_SEPARATOR_STR);
+  struct buffer buf_file = alloc_buf_gc (buf_size, &gc);
+
+  char extra_msg[50] = ""; /* Extra error message details when in chroot */
+  if ((type & CHKACC_INCHROOT) && o->chroot_dir )
+    {
+      buf_printf (&buf_file, "%s%s%s", o->chroot_dir, PATH_SEPARATOR_STR, file);
+      openvpn_snprintf (extra_msg, sizeof(extra_msg),
+                        " (pre-chroot path shown)");
+    }
+  else
+    buf_puts (&buf_file, file);
+
+  /* Probably unnecessary, but make sure buf_printf() didn't overflow and stay empty */
+  ASSERT (buf_file.len > 0); 
+
   /* Is the directory path leading to the given file accessible? */
   if (type & CHKACC_DIRPATH)
     {
-      char *fullpath = strdup(file);  /* POSIX dirname() implementaion may modify its arguments */
+      char *fullpath = strdup(BSTR(&buf_file));  /* POSIX dirname() implementaion may modify its arguments */
       char *dirpath = dirname(fullpath);

       if (platform_access (dirpath, mode|X_OK) != 0)
@@ -2599,18 +2625,20 @@ check_file_access(const int type, const char *file, const int mode, const char *
     }

   /* Is the file itself accessible? */
-  if (!errcode && (type & CHKACC_FILE) && (platform_access (file, mode) != 0) )
+  if (!errcode && (type & CHKACC_FILE) && (platform_access (BSTR(&buf_file), mode) != 0) )
       errcode = errno;

   /* If the file exists and is accessible, is it writable? */
-  if (!errcode && (type & CHKACC_FILEXSTWR) && (platform_access (file, F_OK) == 0) )
-    if (platform_access (file, W_OK) != 0)
+  if (!errcode && (type & CHKACC_FILEXSTWR) && (platform_access (BSTR(&buf_file), F_OK) == 0) )
+    if (platform_access (BSTR(&buf_file), W_OK) != 0)
       errcode = errno;

   /* Scream if an error is found */
   if( errcode > 0 )
-    msg (M_NOPREFIX|M_OPTERR, "%s fails with '%s': %s",
-         opt, file, strerror(errno));
+    msg (M_NOPREFIX|M_OPTERR, "%s fails with '%s': %s%s",
+         opt, BSTR(&buf_file), strerror(errno), extra_msg);
+
+  gc_free(&gc);

   /* Return true if an error occured */
   return (errcode != 0 ? true : false);
@@ -2633,7 +2661,7 @@ check_file_access(const int type, const char *file, const int mode, const char *
  * check_file_access() arguments.
  */
 static bool
-check_cmd_access(const char *command, const char *opt)
+check_cmd_access(const char *command, const char *opt, struct options *options, int access_flags)
 {
   struct argv argv;
   bool return_code;
@@ -2652,7 +2680,7 @@ check_cmd_access(const char *command, const char *opt)
      * only requires X_OK to function on Unix - a scenario not unlikely to
      * be seen on suid binaries.
      */
-    return_code = check_file_access(CHKACC_FILE, argv.argv[0], X_OK, opt);
+    return_code = check_file_access(CHKACC_FILE|access_flags, argv.argv[0], X_OK, opt, options);
   else
     {
       msg (M_NOPREFIX|M_OPTERR, "%s fails with '%s': No path to executable.",
@@ -2676,74 +2704,74 @@ options_postprocess_filechecks (struct options *options)

   /* ** SSL/TLS/crypto related files ** */
 #ifdef ENABLE_SSL
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->dh_file, R_OK, "--dh");
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->ca_file, R_OK, "--ca");
-  errs |= check_file_access (CHKACC_FILE, options->ca_path, R_OK, "--capath");
-  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->cert_file, R_OK, "--cert");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->dh_file, R_OK, "--dh", options);
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->ca_file, R_OK, "--ca", options);
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INCHROOT, options->ca_path, R_OK, "--capath", options);
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->cert_file, R_OK, "--cert", options);
   errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->extra_certs_file, R_OK,
-                             "--extra-certs");
+                             "--extra-certs", options);
 #ifdef MANAGMENT_EXTERNAL_KEY
   if(!(options->management_flags & MF_EXTERNAL_KEY))
 #endif
      errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->priv_key_file, R_OK,
-                             "--key");
+                             "--key", options);
   errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->pkcs12_file, R_OK,
-                             "--pkcs12");
+                             "--pkcs12", options);

   if (options->ssl_flags & SSLF_CRL_VERIFY_DIR)
-    errs |= check_file_access (CHKACC_FILE, options->crl_file, R_OK|X_OK,
-                               "--crl-verify directory");
+    errs |= check_file_access (CHKACC_FILE|CHKACC_INCHROOT, options->crl_file, R_OK|X_OK,
+                               "--crl-verify directory", options);
   else
-    errs |= check_file_access (CHKACC_FILE, options->crl_file, R_OK,
-                               "--crl-verify");
+    errs |= check_file_access (CHKACC_FILE|CHKACC_INCHROOT, options->crl_file, R_OK,
+                               "--crl-verify", options);

   errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->tls_auth_file, R_OK,
-                             "--tls-auth");
+                             "--tls-auth", options);
 #endif /* ENABLE_SSL */
 #ifdef ENABLE_CRYPTO
   errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->shared_secret_file, R_OK,
-                             "--secret");
+                             "--secret", options);
   errs |= check_file_access (CHKACC_DIRPATH|CHKACC_FILEXSTWR,
-                             options->packet_id_file, R_OK|W_OK, "--replay-persist");
+                             options->packet_id_file, R_OK|W_OK, "--replay-persist", options);
 #endif /* ENABLE_CRYPTO */


   /* ** Password files ** */
 #ifdef ENABLE_SSL
   errs |= check_file_access (CHKACC_FILE, options->key_pass_file, R_OK,
-                             "--askpass");
+                             "--askpass", options);
 #endif /* ENABLE_SSL */
 #ifdef ENABLE_MANAGEMENT
   errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN,
                              options->management_user_pass, R_OK,
-                             "--management user/password file");
+                             "--management user/password file", options);
 #endif /* ENABLE_MANAGEMENT */
 #if P2MP
   errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN,
                              options->auth_user_pass_file, R_OK,
-                             "--auth-user-pass");
+                             "--auth-user-pass", options);
 #endif /* P2MP */

   /* ** System related ** */
   errs |= check_file_access (CHKACC_FILE, options->chroot_dir,
-                             R_OK|X_OK, "--chroot directory");
+                             R_OK|X_OK, "--chroot directory", options);
   errs |= check_file_access (CHKACC_DIRPATH|CHKACC_FILEXSTWR, options->writepid,
-                             R_OK|W_OK, "--writepid");
+                             R_OK|W_OK, "--writepid", options);

   /* ** Log related ** */
   errs |= check_file_access (CHKACC_DIRPATH|CHKACC_FILEXSTWR, options->status_file,
-                             R_OK|W_OK, "--status");
+                             R_OK|W_OK, "--status", options);

   /* ** Config related ** */
 #ifdef ENABLE_SSL
-  errs |= check_file_access (CHKACC_FILE, options->tls_export_cert,
-                             R_OK|W_OK|X_OK, "--tls-export-cert");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INCHROOT, options->tls_export_cert,
+                             R_OK|W_OK|X_OK, "--tls-export-cert", options);
 #endif /* ENABLE_SSL */
 #if P2MP_SERVER
-  errs |= check_file_access (CHKACC_FILE, options->client_config_dir,
-                             R_OK|X_OK, "--client-config-dir");
-  errs |= check_file_access (CHKACC_FILE, options->tmp_dir,
-                             R_OK|W_OK|X_OK, "Temporary directory (--tmp-dir)");
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INCHROOT, options->client_config_dir,
+                             R_OK|X_OK, "--client-config-dir", options);
+  errs |= check_file_access (CHKACC_FILE|CHKACC_INCHROOT, options->tmp_dir,
+                             R_OK|W_OK|X_OK, "Temporary directory (--tmp-dir)", options);

 #endif /* P2MP_SERVER */

@@ -2758,6 +2786,7 @@ options_postprocess_filechecks (struct options *options)
  * options.
  */
 void
+
 options_postprocess (struct options *options)
 {
   options_postprocess_mutate (options);
@@ -4007,7 +4036,8 @@ static void
 set_user_script (struct options *options,
 		 const char **script,
 		 const char *new_script,
-		 const char *type)
+		 const char *type,
+     int access_flags)
 {
   if (*script) {
     msg (M_WARN, "Multiple --%s scripts defined.  "
@@ -4022,7 +4052,7 @@ set_user_script (struct options *options,
     openvpn_snprintf (script_name, sizeof(script_name),
                       "--%s script", type);

-    if (check_cmd_access (*script, script_name))
+    if (check_cmd_access (*script, script_name, options, access_flags))
       msg (M_USAGE, "Please correct this error.");
   }
 #endif
@@ -4541,7 +4571,8 @@ add_option (struct options *options,
       set_user_script (options,
 		       &options->ipchange,
 		       string_substitute (p[1], ',', ' ', &options->gc),
-		       "ipchange");
+		       "ipchange",
+           CHKACC_INCHROOT);
     }
   else if (streq (p[0], "float"))
     {
@@ -4587,14 +4618,14 @@ add_option (struct options *options,
       VERIFY_PERMISSION (OPT_P_SCRIPT);
       if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
 	goto err;
-      set_user_script (options, &options->up_script, p[1], "up");
+      set_user_script (options, &options->up_script, p[1], "up", CHKACC_NOOP);
     }
   else if (streq (p[0], "down") && p[1])
     {
       VERIFY_PERMISSION (OPT_P_SCRIPT);
       if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
 	goto err;
-      set_user_script (options, &options->down_script, p[1], "down");
+      set_user_script (options, &options->down_script, p[1], "down", CHKACC_INCHROOT);
     }
   else if (streq (p[0], "down-pre"))
     {
@@ -5275,7 +5306,7 @@ add_option (struct options *options,
       VERIFY_PERMISSION (OPT_P_SCRIPT);
       if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
 	goto err;
-      set_user_script (options, &options->route_script, p[1], "route-up");
+      set_user_script (options, &options->route_script, p[1], "route-up", CHKACC_NOOP);
     }
   else if (streq (p[0], "route-pre-down") && p[1])
     {
@@ -5285,7 +5316,8 @@ add_option (struct options *options,
       set_user_script (options,
 		       &options->route_predown_script,
 		       p[1],
-		       "route-pre-down");
+		       "route-pre-down",
+           CHKACC_INCHROOT);
     }
   else if (streq (p[0], "route-noexec"))
     {
@@ -5654,7 +5686,7 @@ add_option (struct options *options,
 	}
       set_user_script (options,
 		       &options->auth_user_pass_verify_script,
-		       p[1], "auth-user-pass-verify");
+		       p[1], "auth-user-pass-verify", CHKACC_INCHROOT);
     }
   else if (streq (p[0], "client-connect") && p[1])
     {
@@ -5662,7 +5694,7 @@ add_option (struct options *options,
       if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
 	goto err;
       set_user_script (options, &options->client_connect_script,
-		       p[1], "client-connect");
+		       p[1], "client-connect", CHKACC_INCHROOT);
     }
   else if (streq (p[0], "client-disconnect") && p[1])
     {
@@ -5670,7 +5702,7 @@ add_option (struct options *options,
       if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
 	goto err;
       set_user_script (options, &options->client_disconnect_script,
-		       p[1], "client-disconnect");
+		       p[1], "client-disconnect", CHKACC_INCHROOT);
     }
   else if (streq (p[0], "learn-address") && p[1])
     {
@@ -5678,7 +5710,7 @@ add_option (struct options *options,
       if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
 	goto err;
       set_user_script (options, &options->learn_address_script,
-		       p[1], "learn-address");
+		       p[1], "learn-address", CHKACC_INCHROOT);
     }
   else if (streq (p[0], "tmp-dir") && p[1])
     {
@@ -6638,7 +6670,7 @@ add_option (struct options *options,
 	goto err;
       set_user_script (options, &options->tls_verify,
 		       string_substitute (p[1], ',', ' ', &options->gc),
-		       "tls-verify");
+		       "tls-verify", CHKACC_INCHROOT);
     }
 #ifndef ENABLE_CRYPTO_POLARSSL
   else if (streq (p[0], "tls-export-cert") && p[1])
-- 
1.8.1.5

Attachment: Fix-chroot-checks.patch.sig
Description: PGP signature

Reply via email to