diff -u sudo-1.7.2p1/sudoers.man.in sudo-1.7.2p1/sudoers.man.in --- sudo-1.7.2p1/sudoers.man.in +++ sudo-1.7.2p1/sudoers.man.in @@ -1257,10 +1257,45 @@ \&\fBLists that can be used in a boolean context\fR: .IP "env_check" 16 .IX Item "env_check" -Environment variables to be removed from the user's environment if -the variable's value contains \f(CW\*(C`%\*(C'\fR or \f(CW\*(C`/\*(C'\fR characters. This can +Environment variables to be removed from the user's environment +unless they are considered +\(lqsafe\(rq. +For all variables except +\fRTZ\fR, +\(lqsafe\(rq +means that the variable's value does not contain any +\f(CW\*(C`%\*(C'\fR or \f(CW\*(C`/\*(C'\fR characters. This can be used to guard against printf-style format vulnerabilities in -poorly-written programs. The argument may be a double-quoted, +poorly-written programs. +The +\fRTZ\fR +variable is considered unsafe if any of the following are true: +.PP +.RS 18n +.PD 0 +.TP 4n +\fB\(bu\fR +It consists of a fully-qualified path name, +optionally prefixed with a colon +(\(oq:\&\(cq), +that does not match the location of the +\fIzoneinfo\fR +directory. +.PD +.TP 4n +\fB\(bu\fR +It contains a +\fI..\fR +path element. +.TP 4n +\fB\(bu\fR +It contains white space or non-printable characters. +.TP 4n +\fB\(bu\fR +It is longer than the value of +\fRPATH_MAX\fR. +.PP +The argument may be a double-quoted, space-separated list or a single value without double-quotes. The list can be replaced, added to, deleted from, or disabled by using the \f(CW\*(C`=\*(C'\fR, \f(CW\*(C`+=\*(C'\fR, \f(CW\*(C`\-=\*(C'\fR, and \f(CW\*(C`!\*(C'\fR operators respectively. Regardless diff -u sudo-1.7.2p1/env.c sudo-1.7.2p1/env.c --- sudo-1.7.2p1/env.c +++ sudo-1.7.2p1/env.c @@ -192,6 +192,7 @@ "LC_*", "LINGUAS", "TERM", + "TZ", NULL }; @@ -209,7 +210,6 @@ "PATH", "PS1", "PS2", - "TZ", "XAUTHORITY", "XAUTHORIZATION", NULL @@ -517,6 +517,53 @@ } /* + * Sanity-check the TZ environment variable. + * On many systems it is possible to set this to a pathname. + */ +static int +tz_is_sane(const char *tzval) +{ + const char *cp; + char lastch; + + /* tzcode treats a value beginning with a ':' as a path. */ + if (tzval[0] == ':') + tzval++; + + /* Reject fully-qualified TZ that doesn't being with the zoneinfo dir. */ + if (tzval[0] == '/') { +#ifdef _PATH_ZONEINFO + if (strncmp(tzval, _PATH_ZONEINFO, sizeof(_PATH_ZONEINFO) - 1) != 0 || + tzval[sizeof(_PATH_ZONEINFO) - 1] != '/') + return FALSE; +#else + /* Assume the worst. */ + return FALSE; +#endif + } + + /* + * Make sure TZ only contains printable non-space characters + * and does not contain a '..' path element. + */ + lastch = '/'; + for (cp = tzval; *cp != '\0'; cp++) { + if (isspace((unsigned char)*cp) || !isprint((unsigned char)*cp)) + return FALSE; + if (lastch == '/' && cp[0] == '.' && cp[1] == '.' && + (cp[2] == '/' || cp[2] == '\0')) + return FALSE; + lastch = *cp; + } + + /* Reject extra long TZ values (even if not a path). */ + if ((size_t)(cp - tzval) >= PATH_MAX) + return FALSE; + + return TRUE; +} + +/* * Apply the env_check list. * Returns TRUE if the variable is allowed, FALSE if denied * or -1 if no match. @@ -539,7 +586,12 @@ iswild = FALSE; if (strncmp(cur->value, var, len) == 0 && (iswild || var[len] == '=')) { - keepit = !strpbrk(var, "/%"); + if (strncmp(var, "TZ=", 3) == 0) { + /* Special case for TZ */ + keepit = tz_is_sane(var + 3); + } else { + keepit = !strpbrk(var, "/%"); + } break; } } diff -u sudo-1.7.2p1/debian/changelog sudo-1.7.2p1/debian/changelog --- sudo-1.7.2p1/debian/changelog +++ sudo-1.7.2p1/debian/changelog @@ -1,3 +1,16 @@ +sudo (1.7.2p1-1ubuntu5.8) lucid-security; urgency=medium + + * SECURITY UPDATE: arbitrary file access via TZ + - configure, configure.in, doc/sudoers.cat, doc/sudoers.man.in, + pathnames.h.in, plugins/sudoers/env.c: sanity check TZ env variable. + - http://www.sudo.ws/repos/sudo/rev/650ac6938b59 + - http://www.sudo.ws/repos/sudo/rev/ac1467f71ac0 + - http://www.sudo.ws/repos/sudo/rev/91859f613b88 + - http://www.sudo.ws/repos/sudo/rev/579b02f0dbe0 + - CVE-2014-9680 + + -- Marc Deslauriers Thu, 12 Mar 2015 12:21:20 -0400 + sudo (1.7.2p1-1ubuntu5.7) lucid-security; urgency=medium * SECURITY UPDATE: security policy bypass when env_reset is disabled only in patch2: unchanged: --- sudo-1.7.2p1.orig/configure.in +++ sudo-1.7.2p1/configure.in @@ -692,6 +692,12 @@ ;; esac]) +AC_ARG_WITH(tzdir, [AS_HELP_STRING([--with-tzdir=DIR], [path to the time zone data directory])], +[case $with_tzdir in + yes) AC_MSG_ERROR(["must give --with-tzdir an argument."]) + ;; +esac]) + AC_ARG_WITH(sendmail, [AS_HELP_STRING([--with-sendmail], [set path to sendmail]) AS_HELP_STRING([--without-sendmail], [do not send mail at all])], [case $with_sendmail in @@ -2543,6 +2549,7 @@ dnl SUDO_LOGFILE SUDO_TIMEDIR +SUDO_TZDIR dnl dnl Use passwd (and secureware) auth modules? only in patch2: unchanged: --- sudo-1.7.2p1.orig/pathnames.h.in +++ sudo-1.7.2p1/pathnames.h.in @@ -131,3 +131,7 @@ #ifndef _PATH_NETSVC_CONF #undef _PATH_NETSVC_CONF #endif /* _PATH_NETSVC_CONF */ + +#ifndef _PATH_ZONEINFO +# undef _PATH_ZONEINFO +#endif /* _PATH_ZONEINFO */ only in patch2: unchanged: --- sudo-1.7.2p1.orig/configure +++ sudo-1.7.2p1/configure @@ -1559,6 +1559,7 @@ --with-badpass-message message the user sees when the password is wrong --with-fqdn expect fully qualified hosts in sudoers --with-timedir path to the sudo timestamp dir + --with-tzdir=DIR path to the time zone data directory --with-sendmail set path to sendmail --without-sendmail do not send mail at all --with-sudoers-mode mode of sudoers file (defaults to 0440) @@ -3144,6 +3145,16 @@ +# Check whether --with-tzdir was given. +if test "${with_tzdir+set}" = set; then : + withval=$with_tzdir; case $with_tzdir in + yes) as_fn_error $? "\"must give --with-tzdir an argument.\"" "$LINENO" 5 + ;; +esac +fi + + + # Check whether --with-sendmail was given. if test "${with_sendmail+set}" = set; then withval=$with_sendmail; case $with_sendmail in @@ -23926,6 +23937,27 @@ fi fi +{ echo "$as_me:$LINENO}: checking time zone data directory" >&5 +echo $ECHO_N "checking time zone data directory... $ECHO_C" >&6; } +tzdir="$with_tzdir" +if test -z "$tzdir"; then + tzdir=no + for d in /usr/share /usr/share/lib /usr/lib /etc; do + if test -d "$d/zoneinfo"; then + tzdir="$d/zoneinfo" + break + fi + done +fi +{ echo "$as_me:$LINENO}: result: $tzdir" >&5 +echo "${ECHO_T}$tzdir" >&6; } +if test "${tzdir}" != "no"; then + cat >>confdefs.h <&5 echo $ECHO_N "checking for log file location... $ECHO_C" >&6; } if test -n "$with_logpath"; then