Fixed issue with port of doas searching the user's full path for

commands matching the "cmd" parameter in doas.conf. The path
should be shortened to system-standard paths. This prevents
the user from injecting their own application with a familiar
name in their PATH variable and tricking doas into running it.
This commit is contained in:
Jesse Smith 2017-09-05 21:40:47 -03:00
parent 261c216449
commit 4bd6c1c178
6 changed files with 249 additions and 38 deletions

View file

@ -8,6 +8,7 @@ LDFLAGS+=-lpam
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
LDFLAGS+=-lpam_misc
OBJECTS+=strlcat.o strlcpy.o
endif
ifeq ($(UNAME_S),FreeBSD)
CFLAGS+=-DHAVE_LOGIN_CAP_H

16
doas.c
View file

@ -369,13 +369,9 @@ main(int argc, char **argv)
pw = getpwuid(uid);
if (!pw)
err(1, "getpwuid failed");
#ifndef linux
if (strlcpy(myname, pw->pw_name, sizeof(myname)) >= sizeof(myname))
errx(1, "pw_name too long");
#endif
#ifdef linux
strncpy(myname, pw->pw_name, sizeof(myname));
#endif
ngroups = getgroups(NGROUPS_MAX, groups);
if (ngroups == -1)
err(1, "can't get groups");
@ -405,7 +401,6 @@ main(int argc, char **argv)
parseconfig(DOAS_CONF, 1);
/* cmdline is used only for logging, no need to abort on truncate */
#ifndef linux
(void)strlcpy(cmdline, argv[0], sizeof(cmdline));
for (i = 1; i < argc; i++) {
if (strlcat(cmdline, " ", sizeof(cmdline)) >= sizeof(cmdline))
@ -413,14 +408,6 @@ main(int argc, char **argv)
if (strlcat(cmdline, argv[i], sizeof(cmdline)) >= sizeof(cmdline))
break;
}
#endif
#ifdef linux
strncpy(cmdline, argv[0], sizeof(cmdline) - 1);
for (i = 1; i < argc; i++) {
strncat(cmdline, " ", sizeof(cmdline) - strlen(cmdline) - 1);
strncat(cmdline, argv[i], sizeof(cmdline) - strlen(cmdline) - 1);
}
#endif
cmd = argv[0];
if (!permit(uid, groups, ngroups, &rule, target, cmd,
@ -590,3 +577,4 @@ main(int argc, char **argv)
errx(1, "%s: command not found", cmd);
err(1, "%s", cmd);
}

2
doas.h
View file

@ -62,4 +62,6 @@ int execvpe(const char *file, char * const *argv, char * const *envp);
#ifdef linux
void errc(int eval, int code, const char *format);
size_t strlcat(char *dst, const char *src, size_t dsize);
size_t strlcpy(char *dst, const char *src, size_t dsize);
#endif

161
execvpe.c
View file

@ -1,6 +1,7 @@
/* $OpenBSD: exec.c,v 1.23 2016/03/13 18:34:20 guenther Exp $ */
/*-
* Copyright (C) 2015 NONAKA Kimihiro <nonakap@gmail.com>
* All rights reserved.
* Copyright (c) 1991, 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
@ -10,37 +11,149 @@
* 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 AUTHOR ``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 AUTHOR 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.
* 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.
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <errno.h>
#include <limits.h>
#include <paths.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "doas.h"
#if !defined(HAVE_EXECVPE)
extern char **environ;
int
execvpe(const char *file, char * const *argv, char * const *envp)
execvpe(const char *name, char *const *argv, char *const *envp)
{
char **oldenvp = environ;
int error;
char **memp;
int cnt;
size_t lp, ln, len;
char *p;
int eacces = 0;
char *bp, *cur, *path, buf[PATH_MAX];
environ = (char **)(long)envp; /* XXX */
error = execvp(file, argv);
environ = oldenvp;
return error;
/*
* Do not allow null name
*/
if (name == NULL || *name == '\0') {
errno = ENOENT;
return (-1);
}
/* If it's an absolute or relative path name, it's easy. */
if (strchr(name, '/')) {
bp = (char *)name;
cur = path = NULL;
goto retry;
}
bp = buf;
/* Get the path we're searching. */
if (!(path = getenv("PATH")))
path = _PATH_DEFPATH;
len = strlen(path) + 1;
cur = alloca(len);
if (cur == NULL) {
errno = ENOMEM;
return (-1);
}
strlcpy(cur, path, len);
path = cur;
while ((p = strsep(&cur, ":"))) {
/*
* It's a SHELL path -- double, leading and trailing colons
* mean the current directory.
*/
if (!*p) {
p = ".";
lp = 1;
} else
lp = strlen(p);
ln = strlen(name);
/*
* If the path is too long complain. This is a possible
* security issue; given a way to make the path too long
* the user may execute the wrong program.
*/
if (lp + ln + 2 > sizeof(buf)) {
struct iovec iov[3];
iov[0].iov_base = "execvp: ";
iov[0].iov_len = 8;
iov[1].iov_base = p;
iov[1].iov_len = lp;
iov[2].iov_base = ": path too long\n";
iov[2].iov_len = 16;
(void)writev(STDERR_FILENO, iov, 3);
continue;
}
bcopy(p, buf, lp);
buf[lp] = '/';
bcopy(name, buf + lp + 1, ln);
buf[lp + ln + 1] = '\0';
retry: (void)execve(bp, argv, envp);
switch(errno) {
case E2BIG:
goto done;
case EISDIR:
case ELOOP:
case ENAMETOOLONG:
case ENOENT:
break;
case ENOEXEC:
for (cnt = 0; argv[cnt]; ++cnt)
;
memp = alloca((cnt + 2) * sizeof(char *));
if (memp == NULL)
goto done;
memp[0] = "sh";
memp[1] = bp;
bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
(void)execve(_PATH_BSHELL, memp, envp);
goto done;
case ENOMEM:
goto done;
case ENOTDIR:
break;
case ETXTBSY:
/*
* We used to retry here, but sh(1) doesn't.
*/
goto done;
case EACCES:
eacces = 1;
break;
default:
goto done;
}
}
if (eacces)
errno = EACCES;
else if (!errno)
errno = ENOENT;
done:
return (-1);
}
#endif /* !HAVE_EXECVPE */

56
strlcat.c Normal file
View file

@ -0,0 +1,56 @@
/* $OpenBSD: strlcat.c,v 1.18 2016/10/16 17:37:39 dtucker Exp $ */
/*
* Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
/*
* Appends src to string dst of size dsize (unlike strncat, dsize is the
* full size of dst, not space left). At most dsize-1 characters
* will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
* Returns strlen(src) + MIN(dsize, strlen(initial dst)).
* If retval >= dsize, truncation occurred.
*/
size_t
strlcat(char *dst, const char *src, size_t dsize)
{
const char *odst = dst;
const char *osrc = src;
size_t n = dsize;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end. */
while (n-- != 0 && *dst != '\0')
dst++;
dlen = dst - odst;
n = dsize - dlen;
if (n-- == 0)
return(dlen + strlen(src));
while (*src != '\0') {
if (n != 0) {
*dst++ = *src;
n--;
}
src++;
}
*dst = '\0';
return(dlen + (src - osrc)); /* count does not include NUL */
}

51
strlcpy.c Normal file
View file

@ -0,0 +1,51 @@
/* $OpenBSD: strlcpy.c,v 1.15 2016/10/16 17:37:39 dtucker Exp $ */
/*
* Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
/*
* Copy string src to buffer dst of size dsize. At most dsize-1
* chars will be copied. Always NUL terminates (unless dsize == 0).
* Returns strlen(src); if retval >= dsize, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t dsize)
{
const char *osrc = src;
size_t nleft = dsize;
/* Copy as many bytes as will fit. */
if (nleft != 0) {
while (--nleft != 0) {
if ((*dst++ = *src++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src. */
if (nleft == 0) {
if (dsize != 0)
*dst = '\0'; /* NUL-terminate dst */
while (*src++)
;
}
return(src - osrc - 1); /* count does not include NUL */
}