]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Sanitise the following characters using svis(3) with VIS_CTYLE and VIS_OCTAL:
authorRoy Marples <roy@marples.name>
Fri, 26 Sep 2014 20:07:07 +0000 (20:07 +0000)
committerRoy Marples <roy@marples.name>
Fri, 26 Sep 2014 20:07:07 +0000 (20:07 +0000)
   | ^ & ; < > ( ) $ ` \ " ' <tab> <newline>
This allows a non buggy unvis(1) to decode it 100% and stays compatible with
how dhcpcd used to handle encoding on most platforms.
For systems that supply svis(3) there is a code reduction, for systems that
do not, a slight code increase.

This change mitigates systems affected by bash CVE-2014-6271 and CVE-2014-7169.

compat/vis.c [new file with mode: 0644]
compat/vis.h [new file with mode: 0644]
configure
dhcp-common.c
dhcpcd.8.in

diff --git a/compat/vis.c b/compat/vis.c
new file mode 100644 (file)
index 0000000..8bdd0ba
--- /dev/null
@@ -0,0 +1,160 @@
+/*     $NetBSD: vis.c,v 1.44 2011/03/12 19:52:48 christos Exp $        */
+
+/*-
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * HEAVILY trimmed down for use only in dhcpcd.
+ * Please use the source in NetBSD for a fuller working copy.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vis.h"
+
+#undef BELL
+#define BELL '\a'
+
+#define isoctal(c)     (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
+#define iswhite(c)     (c == ' ' || c == '\t' || c == '\n')
+
+/*
+ * This is do_vis, the central code of vis.
+ * dst:              Pointer to the destination buffer
+ * c:        Character to encode
+ * flag:      Flag word
+ * nextc:     The character following 'c'
+ * extra:     Pointer to the list of extra characters to be
+ *           backslash-protected.
+ */
+char *
+svis(char *dst, int c, int flag, int nextc, const char *extra)
+{
+       int isextra;
+
+       isextra = strchr(extra, c) != NULL;
+       if (!isextra && isascii(c) && (isgraph(c) || iswhite(c))) {
+               *dst++ = (char)c;
+               return dst;
+       }
+       if (flag & VIS_CSTYLE) {
+               switch (c) {
+               case '\n':
+                       *dst++ = '\\'; *dst++ = 'n';
+                       return dst;
+               case '\r':
+                       *dst++ = '\\'; *dst++ = 'r';
+                       return dst;
+               case '\b':
+                       *dst++ = '\\'; *dst++ = 'b';
+                       return dst;
+               case BELL:
+                       *dst++ = '\\'; *dst++ = 'a';
+                       return dst;
+               case '\v':
+                       *dst++ = '\\'; *dst++ = 'v';
+                       return dst;
+               case '\t':
+                       *dst++ = '\\'; *dst++ = 't';
+                       return dst;
+               case '\f':
+                       *dst++ = '\\'; *dst++ = 'f';
+                       return dst;
+               case ' ':
+                       *dst++ = '\\'; *dst++ = 's';
+                       return dst;
+               case '\0':
+                       *dst++ = '\\'; *dst++ = '0';
+                       if (isoctal(nextc)) {
+                               *dst++ = '0';
+                               *dst++ = '0';
+                       }
+                       return dst;
+               case '$': /* vis(1) - l */
+                       break;
+               default:
+                       if (isgraph(c)) {
+                               *dst++ = '\\'; *dst++ = (char)c;
+                               return dst;
+                       }
+               }
+       }
+
+       *dst++ = '\\';
+       if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) {
+               *dst++ = (((unsigned char)c >> 6) & 03) + '0';
+               *dst++ = (((unsigned char)c >> 3) & 07) + '0';
+               *dst++ = ( (unsigned char)c       & 07) + '0';
+       } else {
+               if (c & 0200) {
+                       c &= 0177; *dst++ = 'M';
+               }
+
+               if (iscntrl(c)) {
+                       *dst++ = '^';
+                       if (c == 0177)
+                               *dst++ = '?';
+                       else
+                               *dst++ = (char)c + '@';
+               } else {
+                       *dst++ = '-'; *dst++ = (char)c;
+               }
+       }
+       return dst;
+}
diff --git a/compat/vis.h b/compat/vis.h
new file mode 100644 (file)
index 0000000..25c55b5
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef VIS_H
+#define VIS_H
+
+#define        VIS_OCTAL       0x0001  /* use octal \ddd format */
+#define        VIS_CSTYLE      0x0002  /* use \[nrft0..] where appropiate */
+
+char *svis(char *dst, int c, int flag, int nextc, const char *meta);
+
+#endif
index 294f49a0edb80711eb530d79365803ff45d935d1..b169337d7dc698f21239ab52309257bb7a7ffff5 100755 (executable)
--- a/configure
+++ b/configure
@@ -71,6 +71,7 @@ for x do
        --without-md5) MD5=no;;
        --without-sha2) SHA2=no;;
        --without-sha256) SHA2=no;;
+       --without-vis) VIS=no;;
        --without-dev) DEV=no;;
        --without-udev) UDEV=no;;
        --serviceexists) SERVICEEXISTS=$var;;
@@ -627,6 +628,31 @@ if [ "$STRLCPY" = no ]; then
        echo "#include          \"compat/strlcpy.h\"" >>$CONFIG_H
 fi
 
+if [ -z "$VIS" ]; then
+       printf "Testing for vis ... "
+       cat <<EOF >_vis.c
+#include <vis.h>
+int main(void) {
+       char s[10];
+       svis(s, 1, 2, 3, s);
+       return 0;
+}
+EOF
+       if $XCC _vis.c -o _vis 2>&3; then
+               VIS=yes
+       else
+               VIS=no
+       fi
+       echo "$VIS"
+       rm -f _svis.c _svis
+fi
+if [ "$VIS" = no ]; then
+       echo "COMPAT_SRCS+=     compat/vis.c" >>$CONFIG_MK
+       echo "#include          \"compat/vis.h\"" >>$CONFIG_H
+else
+       echo "#define HAVE_VIS_H" >>$CONFIG_H
+fi
+
 if [ -z "$DPRINTF" ]; then
        printf "Testing for dprintf ... "
        cat <<EOF >_dprintf.c
index 093540644838f369ac07385436c91b21e492ebe7..1e0d1003fa18e6f1c5bcfe2d331582d667e6f810 100644 (file)
 #include <unistd.h>
 
 #include "config.h"
+
+#ifdef HAVE_VIS_H
+#include <vis.h>
+#endif
+
 #include "common.h"
 #include "dhcp-common.h"
 #include "dhcp.h"
@@ -291,14 +296,28 @@ decode_rfc3397(char *out, size_t len, const uint8_t *p, size_t pl)
        return (ssize_t)count;
 }
 
+/*
+ * Escape these characters to avoid any nastiness passing to a POSIX shell.
+ * See IEEE Std 1003.1, 2004 Shell Command Language, 2.2 Quoting
+ * space is not escaped.
+ */
+#define ESCAPE_CHARS   "|&;<>()$`\\\"'\t\n"
+
+/*
+ * Prints a chunk of data to a string.
+ * Escapes some characters defnined above to try and avoid any loopholes
+ * in the shell we're passing to.
+ * Any non visible characters are escaped as an octal number.
+ */
 ssize_t
 print_string(char *s, size_t len, const uint8_t *data, size_t dl)
 {
        uint8_t c;
        const uint8_t *e, *p;
-       ssize_t bytes = 0;
-       ssize_t r;
+       size_t bytes;
+       char v[5], *vp, *ve;
 
+       bytes = 0;
        e = data + dl;
        while (data < e) {
                c = *data++;
@@ -310,51 +329,25 @@ print_string(char *s, size_t len, const uint8_t *data, size_t dl)
                        if (p == e)
                                break;
                }
-               if (!isascii(c) || !isprint(c)) {
-                       if (s) {
-                               if (len < 5) {
-                                       errno = ENOBUFS;
-                                       return -1;
-                               }
-                               r = snprintf(s, len, "\\%03o", c);
-                               len -= (size_t)r;
-                               bytes += r;
-                               s += r;
-                       } else
-                               bytes += 4;
-                       continue;
-               }
-               switch (c) {
-               case '"':  /* FALLTHROUGH */
-               case '\'': /* FALLTHROUGH */
-               case '$':  /* FALLTHROUGH */
-               case '`':  /* FALLTHROUGH */
-               case '\\': /* FALLTHROUGH */
-               case '|':  /* FALLTHROUGH */
-               case '&':
-                       if (s) {
-                               if (len < 3) {
-                                       errno = ENOBUFS;
-                                       return -1;
-                               }
-                               *s++ = '\\';
-                               len--;
-                       }
-                       bytes++;
-                       break;
+               ve = svis(v, c, VIS_CSTYLE | VIS_OCTAL,
+                   data <= e ? *data : 0, ESCAPE_CHARS);
+               if (len < (size_t)(ve - v) + 1) {
+                       errno = ENOBUFS;
+                       return -1;
                }
+               bytes += (size_t)(ve - v);
                if (s) {
-                       *s++ = (char)c;
-                       len--;
+                       vp = v;
+                       while (vp != ve)
+                               *s++ = *vp++;
                }
-               bytes++;
        }
 
        /* NULL */
        if (s)
                *s = '\0';
        bytes++;
-       return bytes;
+       return (ssize_t)bytes;
 }
 
 #define ADDRSZ         4
index 7a155ff17d3185ed84dda02b3226d30bba217946..215c73e4f4aa0a72ba8ed9618be96d3099a0fdc7 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd September 15, 2014
+.Dd September 26, 2014
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -203,6 +203,17 @@ See
 for details on how these scripts work.
 .Nm
 currently ignores the exit code of the script.
+.Pp
+.Nm
+sanitises each variable passed from the DHCP/RA message by encoding non
+printable characters in escaped octal and escaping the following characters:
+.D1 | \*[Am] \&; \*[Lt] \*[Gt] \&( \&) $ ` \e \*q ' \
+\*[Lt]tab\*[Gt] \*[Lt]newline\*[Gt]
+.Pp
+It is possible to reverse this encoding by passing the variable to
+.Xr unvis 1 ,
+like so:
+.D1 printf \*q%s\*q \*qvariable_name\*q | unvis
 .Ss Fine tuning
 You can fine-tune the behaviour of
 .Nm
@@ -631,6 +642,23 @@ to rebind, reconfigure or exit need to include the same restrictive flags
 so that
 .Nm
 knows which process to signal.
+.Pp
+.Nm
+sanitises variables using the
+.Xr svis 3
+function with the
+.Dv VIS_CTYPE
+and
+.Dv VIS_OCTAL
+flags which your libc may provide,
+or it's own compatible one if not.
+Some
+.Xr unvis 1
+implementations may not decode either correctly, depending on how they
+handle VIS_CTYPE in both
+.Xr vis 3
+and
+.Xr unvis 3 .
 .Sh FILES
 .Bl -ohang
 .It Pa @SYSCONFDIR@/dhcpcd.conf