mirror of
https://github.com/slicer69/doas
synced 2024-10-15 20:22:42 +00:00
Merge branch 'katakk-up' Brings us up to speed with OpenBSD 5.9.
This commit is contained in:
commit
31f072078a
5
doas.1
5
doas.1
|
@ -25,6 +25,7 @@
|
||||||
.Op Fl a Ar style
|
.Op Fl a Ar style
|
||||||
.Op Fl C Ar config
|
.Op Fl C Ar config
|
||||||
.Op Fl u Ar user
|
.Op Fl u Ar user
|
||||||
|
.Op Fl -
|
||||||
.Ar command
|
.Ar command
|
||||||
.Op Ar args
|
.Op Ar args
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
|
@ -80,6 +81,8 @@ or
|
||||||
Execute the command as
|
Execute the command as
|
||||||
.Ar user .
|
.Ar user .
|
||||||
The default is root.
|
The default is root.
|
||||||
|
.It Fl -
|
||||||
|
Any dashes after a combined double dash (--) will be interpreted as part of the command to be run or its paramters. Not an argument passed to doas itself.
|
||||||
.El
|
.El
|
||||||
.Sh EXIT STATUS
|
.Sh EXIT STATUS
|
||||||
.Ex -std doas
|
.Ex -std doas
|
||||||
|
@ -88,7 +91,7 @@ It may fail for one of the following reasons:
|
||||||
.Bl -bullet -compact
|
.Bl -bullet -compact
|
||||||
.It
|
.It
|
||||||
The config file
|
The config file
|
||||||
.Pa /etc/doas.conf
|
.Pa /usr/local/etc/doas.conf
|
||||||
could not be parsed.
|
could not be parsed.
|
||||||
.It
|
.It
|
||||||
The user attempted to run a command which is not permitted.
|
The user attempted to run a command which is not permitted.
|
||||||
|
|
118
doas.c
118
doas.c
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#if defined(HAVE_INTTYPES_H)
|
#if defined(HAVE_INTTYPES_H)
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#if defined(HAVE_LOGIN_CAP_H)
|
#if defined(HAVE_LOGIN_CAP_H)
|
||||||
#include <login_cap.h>
|
#include <login_cap.h>
|
||||||
|
@ -83,18 +85,6 @@ errc(int eval, int code, const char *format)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
size_t
|
|
||||||
arraylen(const char **arr)
|
|
||||||
{
|
|
||||||
size_t cnt = 0;
|
|
||||||
|
|
||||||
while (*arr) {
|
|
||||||
cnt++;
|
|
||||||
arr++;
|
|
||||||
}
|
|
||||||
return cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
parseuid(const char *s, uid_t *uid)
|
parseuid(const char *s, uid_t *uid)
|
||||||
{
|
{
|
||||||
|
@ -254,6 +244,54 @@ checkconfig(const char *confpath, int argc, char **argv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_BSD_AUTH)
|
||||||
|
static void
|
||||||
|
authuser(char *myname, char *login_style, int persist)
|
||||||
|
{
|
||||||
|
char *challenge = NULL, *response, rbuf[1024], cbuf[128];
|
||||||
|
auth_session_t *as;
|
||||||
|
int fd = -1;
|
||||||
|
|
||||||
|
if (persist)
|
||||||
|
fd = open("/dev/tty", O_RDWR);
|
||||||
|
if (fd != -1) {
|
||||||
|
if (ioctl(fd, TIOCCHKVERAUTH) == 0)
|
||||||
|
goto good;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(as = auth_userchallenge(myname, login_style, "auth-doas",
|
||||||
|
&challenge)))
|
||||||
|
errx(1, "Authorization failed");
|
||||||
|
if (!challenge) {
|
||||||
|
char host[HOST_NAME_MAX + 1];
|
||||||
|
if (gethostname(host, sizeof(host)))
|
||||||
|
snprintf(host, sizeof(host), "?");
|
||||||
|
snprintf(cbuf, sizeof(cbuf),
|
||||||
|
"\rdoas (%.32s@%.32s) password: ", myname, host);
|
||||||
|
challenge = cbuf;
|
||||||
|
}
|
||||||
|
response = readpassphrase(challenge, rbuf, sizeof(rbuf),
|
||||||
|
RPP_REQUIRE_TTY);
|
||||||
|
if (response == NULL && errno == ENOTTY) {
|
||||||
|
syslog(LOG_AUTHPRIV | LOG_NOTICE,
|
||||||
|
"tty required for %s", myname);
|
||||||
|
errx(1, "a tty is required");
|
||||||
|
}
|
||||||
|
if (!auth_userresponse(as, response, 0)) {
|
||||||
|
syslog(LOG_AUTHPRIV | LOG_NOTICE,
|
||||||
|
"failed auth for %s", myname);
|
||||||
|
errc(1, EPERM, NULL);
|
||||||
|
}
|
||||||
|
explicit_bzero(rbuf, sizeof(rbuf));
|
||||||
|
good:
|
||||||
|
if (fd != -1) {
|
||||||
|
int secs = 5 * 60;
|
||||||
|
ioctl(fd, TIOCSETVERAUTH, &secs);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
@ -283,11 +321,6 @@ main(int argc, char **argv)
|
||||||
setprogname("doas");
|
setprogname("doas");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
if (pledge("stdio rpath getpw tty proc exec id", NULL) == -1)
|
|
||||||
err(1, "pledge");
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef linux
|
#ifndef linux
|
||||||
closefrom(STDERR_FILENO + 1);
|
closefrom(STDERR_FILENO + 1);
|
||||||
#endif
|
#endif
|
||||||
|
@ -295,6 +328,7 @@ main(int argc, char **argv)
|
||||||
uid = getuid();
|
uid = getuid();
|
||||||
|
|
||||||
while ((ch = getopt(argc, argv, "a:C:nsu:")) != -1) {
|
while ((ch = getopt(argc, argv, "a:C:nsu:")) != -1) {
|
||||||
|
/* while ((ch = getopt(argc, argv, "a:C:Lnsu:")) != -1) { */
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'a':
|
case 'a':
|
||||||
login_style = optarg;
|
login_style = optarg;
|
||||||
|
@ -302,6 +336,12 @@ main(int argc, char **argv)
|
||||||
case 'C':
|
case 'C':
|
||||||
confpath = optarg;
|
confpath = optarg;
|
||||||
break;
|
break;
|
||||||
|
/* case 'L':
|
||||||
|
i = open("/dev/tty", O_RDWR);
|
||||||
|
if (i != -1)
|
||||||
|
ioctl(i, TIOCCLRVERAUTH);
|
||||||
|
exit(i != -1);
|
||||||
|
*/
|
||||||
case 'u':
|
case 'u':
|
||||||
if (parseuid(optarg, &target) != 0)
|
if (parseuid(optarg, &target) != 0)
|
||||||
errx(1, "unknown user");
|
errx(1, "unknown user");
|
||||||
|
@ -343,9 +383,11 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
if (sflag) {
|
if (sflag) {
|
||||||
sh = getenv("SHELL");
|
sh = getenv("SHELL");
|
||||||
if (sh == NULL || *sh == '\0')
|
if (sh == NULL || *sh == '\0') {
|
||||||
shargv[0] = pw->pw_shell;
|
shargv[0] = strdup(pw->pw_shell);
|
||||||
else
|
if (shargv[0] == NULL)
|
||||||
|
err(1, NULL);
|
||||||
|
} else
|
||||||
shargv[0] = sh;
|
shargv[0] = sh;
|
||||||
argv = shargv;
|
argv = shargv;
|
||||||
argc = 1;
|
argc = 1;
|
||||||
|
@ -357,11 +399,14 @@ main(int argc, char **argv)
|
||||||
exit(1); /* fail safe */
|
exit(1); /* fail safe */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (geteuid())
|
||||||
|
errx(1, "not installed setuid");
|
||||||
|
|
||||||
parseconfig(DOAS_CONF, 1);
|
parseconfig(DOAS_CONF, 1);
|
||||||
|
|
||||||
/* cmdline is used only for logging, no need to abort on truncate */
|
/* cmdline is used only for logging, no need to abort on truncate */
|
||||||
#ifndef linux
|
#ifndef linux
|
||||||
(void) strlcpy(cmdline, argv[0], sizeof(cmdline));
|
(void)strlcpy(cmdline, argv[0], sizeof(cmdline));
|
||||||
for (i = 1; i < argc; i++) {
|
for (i = 1; i < argc; i++) {
|
||||||
if (strlcat(cmdline, " ", sizeof(cmdline)) >= sizeof(cmdline))
|
if (strlcat(cmdline, " ", sizeof(cmdline)) >= sizeof(cmdline))
|
||||||
break;
|
break;
|
||||||
|
@ -379,7 +424,7 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
cmd = argv[0];
|
cmd = argv[0];
|
||||||
if (!permit(uid, groups, ngroups, &rule, target, cmd,
|
if (!permit(uid, groups, ngroups, &rule, target, cmd,
|
||||||
(const char**)argv + 1)) {
|
(const char **)argv + 1)) {
|
||||||
syslog(LOG_AUTHPRIV | LOG_NOTICE,
|
syslog(LOG_AUTHPRIV | LOG_NOTICE,
|
||||||
"failed command for %s: %s", myname, cmdline);
|
"failed command for %s: %s", myname, cmdline);
|
||||||
errc(1, EPERM, NULL);
|
errc(1, EPERM, NULL);
|
||||||
|
@ -387,36 +432,10 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
if (!(rule->options & NOPASS)) {
|
if (!(rule->options & NOPASS)) {
|
||||||
#if defined(USE_BSD_AUTH)
|
#if defined(USE_BSD_AUTH)
|
||||||
char *challenge = NULL, *response, rbuf[1024], cbuf[128];
|
|
||||||
auth_session_t *as;
|
|
||||||
|
|
||||||
if (nflag)
|
if (nflag)
|
||||||
errx(1, "Authorization required");
|
errx(1, "Authorization required");
|
||||||
|
|
||||||
if (!(as = auth_userchallenge(myname, login_style, "auth-doas",
|
authuser(myname, login_style, rule->options & PERSIST);
|
||||||
&challenge)))
|
|
||||||
errx(1, "Authorization failed");
|
|
||||||
if (!challenge) {
|
|
||||||
char host[MAXHOSTNAME + 1];
|
|
||||||
if (gethostname(host, sizeof(host)))
|
|
||||||
snprintf(host, sizeof(host), "?");
|
|
||||||
snprintf(cbuf, sizeof(cbuf),
|
|
||||||
"\rdoas (%.32s@%.32s) password: ", myname, host);
|
|
||||||
challenge = cbuf;
|
|
||||||
}
|
|
||||||
response = readpassphrase(challenge, rbuf, sizeof(rbuf),
|
|
||||||
RPP_REQUIRE_TTY);
|
|
||||||
if (response == NULL && errno == ENOTTY) {
|
|
||||||
syslog(LOG_AUTHPRIV | LOG_NOTICE,
|
|
||||||
"tty required for %s", myname);
|
|
||||||
errx(1, "a tty is required");
|
|
||||||
}
|
|
||||||
if (!auth_userresponse(as, response, 0)) {
|
|
||||||
syslog(LOG_AUTHPRIV | LOG_NOTICE,
|
|
||||||
"failed auth for %s", myname);
|
|
||||||
errc(1, EPERM, NULL);
|
|
||||||
}
|
|
||||||
explicit_bzero(rbuf, sizeof(rbuf));
|
|
||||||
#elif defined(USE_PAM)
|
#elif defined(USE_PAM)
|
||||||
#define PAM_END(msg) do { \
|
#define PAM_END(msg) do { \
|
||||||
syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); \
|
syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); \
|
||||||
|
@ -518,7 +537,6 @@ main(int argc, char **argv)
|
||||||
#else
|
#else
|
||||||
#error No auth module!
|
#error No auth module!
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
38
doas.conf.5
38
doas.conf.5
|
@ -1,4 +1,4 @@
|
||||||
.\" $OpenBSD: doas.conf.5,v 1.26 2016/06/11 17:17:10 tedu Exp $
|
.\" $OpenBSD: doas.conf.5,v 1.31 2016/12/05 10:58:07 schwarze Exp $
|
||||||
.\"
|
.\"
|
||||||
.\"Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
|
.\"Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
|
||||||
.\"
|
.\"
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
.\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
.\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
.\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
.\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
.\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
.\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
.Dd $Mdocdate: June 11 2016 $
|
.Dd $Mdocdate: December 5 2016 $
|
||||||
.Dt DOAS.CONF 5
|
.Dt DOAS.CONF 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
@ -35,7 +35,7 @@ The rules have the following format:
|
||||||
.Op Ar options
|
.Op Ar options
|
||||||
.Ar identity
|
.Ar identity
|
||||||
.Op Ic as Ar target
|
.Op Ic as Ar target
|
||||||
.Op Ic cmd Ar command Op Ic args ...
|
.Op Ic cmd Ar command Op Ic args No ...
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
Rules consist of the following parts:
|
Rules consist of the following parts:
|
||||||
|
@ -47,6 +47,9 @@ Options are:
|
||||||
.Bl -tag -width keepenv
|
.Bl -tag -width keepenv
|
||||||
.It Ic nopass
|
.It Ic nopass
|
||||||
The user is not required to enter a password.
|
The user is not required to enter a password.
|
||||||
|
.It Ic persist
|
||||||
|
After the user successfully authenticates, do not ask for a password
|
||||||
|
again for some time. Works on OpenBSD only, persist is not available on Linux or FreeBSD.
|
||||||
.It Ic keepenv
|
.It Ic keepenv
|
||||||
The user's environment is maintained.
|
The user's environment is maintained.
|
||||||
The default is to reset the environment, except for the variables
|
The default is to reset the environment, except for the variables
|
||||||
|
@ -59,9 +62,18 @@ The default is to reset the environment, except for the variables
|
||||||
.Ev USER
|
.Ev USER
|
||||||
and
|
and
|
||||||
.Ev USERNAME .
|
.Ev USERNAME .
|
||||||
.It Ic keepenv { Oo Ar variable ... Oc Ic }
|
.It Ic setenv { Oo Ar variable ... Oc Oo Ar variable=value ... Oc Ic }
|
||||||
In addition to the variables mentioned above, keep the space-separated
|
In addition to the variables mentioned above, keep the space-separated
|
||||||
specified variables.
|
specified variables.
|
||||||
|
Variables may also be removed with a leading
|
||||||
|
.Sq -
|
||||||
|
or set using the latter syntax.
|
||||||
|
If the first character of
|
||||||
|
.Ar value
|
||||||
|
is a
|
||||||
|
.Ql $
|
||||||
|
then the value to be set is taken from the existing environment
|
||||||
|
variable of the same name.
|
||||||
.El
|
.El
|
||||||
.It Ar identity
|
.It Ar identity
|
||||||
The username to match.
|
The username to match.
|
||||||
|
@ -78,7 +90,7 @@ Be advised that it is best to specify absolute paths.
|
||||||
If a relative path is specified, only a restricted
|
If a relative path is specified, only a restricted
|
||||||
.Ev PATH
|
.Ev PATH
|
||||||
will be searched.
|
will be searched.
|
||||||
.It Ic args ...
|
.It Ic args Op Ar argument ...
|
||||||
Arguments to command.
|
Arguments to command.
|
||||||
The command arguments provided by the user need to match those specified.
|
The command arguments provided by the user need to match those specified.
|
||||||
The keyword
|
The keyword
|
||||||
|
@ -109,25 +121,27 @@ If quotes or backslashes are used in a word,
|
||||||
it is not considered a keyword.
|
it is not considered a keyword.
|
||||||
.El
|
.El
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
The following example permits users in group wsrc to build ports,
|
The following example permits users in group wsrc to build ports;
|
||||||
wheel to execute commands as any user while keeping the environment
|
wheel to execute commands as any user while keeping the environment
|
||||||
variables
|
variables
|
||||||
.Ev ENV ,
|
.Ev PS1
|
||||||
.Ev PS1 ,
|
|
||||||
and
|
and
|
||||||
.Ev SSH_AUTH_SOCK ,
|
.Ev SSH_AUTH_SOCK
|
||||||
permits tedu to run procmap as root without a password,
|
and
|
||||||
|
unsetting
|
||||||
|
.Ev ENV ;
|
||||||
|
permits tedu to run procmap as root without a password;
|
||||||
and additionally permits root to run unrestricted commands as itself.
|
and additionally permits root to run unrestricted commands as itself.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
# Non-exhaustive list of variables needed to
|
# Non-exhaustive list of variables needed to
|
||||||
# build release(8) and ports(7)
|
# build release(8) and ports(7)
|
||||||
permit nopass keepenv { \e
|
permit nopass setenv { \e
|
||||||
FTPMODE PKG_CACHE PKG_PATH SM_PATH SSH_AUTH_SOCK \e
|
FTPMODE PKG_CACHE PKG_PATH SM_PATH SSH_AUTH_SOCK \e
|
||||||
DESTDIR DISTDIR FETCH_CMD FLAVOR GROUP MAKE MAKECONF \e
|
DESTDIR DISTDIR FETCH_CMD FLAVOR GROUP MAKE MAKECONF \e
|
||||||
MULTI_PACKAGES NOMAN OKAY_FILES OWNER PKG_DBDIR \e
|
MULTI_PACKAGES NOMAN OKAY_FILES OWNER PKG_DBDIR \e
|
||||||
PKG_DESTDIR PKG_TMPDIR PORTSDIR RELEASEDIR SHARED_ONLY \e
|
PKG_DESTDIR PKG_TMPDIR PORTSDIR RELEASEDIR SHARED_ONLY \e
|
||||||
SUBPACKAGE WRKOBJDIR SUDO_PORT_V1 } :wsrc
|
SUBPACKAGE WRKOBJDIR SUDO_PORT_V1 } :wsrc
|
||||||
permit nopass keepenv { ENV PS1 SSH_AUTH_SOCK } :wheel
|
permit setenv { -ENV PS1=$DOAS_PS1 SSH_AUTH_SOCK } :wheel
|
||||||
permit nopass tedu as root cmd /usr/sbin/procmap
|
permit nopass tedu as root cmd /usr/sbin/procmap
|
||||||
permit nopass keepenv root as root
|
permit nopass keepenv root as root
|
||||||
.Ed
|
.Ed
|
||||||
|
|
23
doas.h
23
doas.h
|
@ -1,4 +1,20 @@
|
||||||
/* $OpenBSD: doas.h,v 1.8 2016/06/19 19:29:43 martijn Exp $ */
|
/* $OpenBSD: doas.h,v 1.12 2016/10/05 17:40:25 tedu Exp $ */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
struct rule {
|
struct rule {
|
||||||
int action;
|
int action;
|
||||||
int options;
|
int options;
|
||||||
|
@ -10,11 +26,9 @@ struct rule {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct rule **rules;
|
extern struct rule **rules;
|
||||||
extern int nrules, maxrules;
|
extern int nrules;
|
||||||
extern int parse_errors;
|
extern int parse_errors;
|
||||||
|
|
||||||
size_t arraylen(const char **);
|
|
||||||
|
|
||||||
char **prepenv(struct rule *);
|
char **prepenv(struct rule *);
|
||||||
|
|
||||||
#define PERMIT 1
|
#define PERMIT 1
|
||||||
|
@ -22,6 +36,7 @@ char **prepenv(struct rule *);
|
||||||
|
|
||||||
#define NOPASS 0x1
|
#define NOPASS 0x1
|
||||||
#define KEEPENV 0x2
|
#define KEEPENV 0x2
|
||||||
|
#define PERSIST 0x4
|
||||||
|
|
||||||
#ifndef UID_MAX
|
#ifndef UID_MAX
|
||||||
#define UID_MAX 65535
|
#define UID_MAX 65535
|
||||||
|
|
177
env.c
177
env.c
|
@ -1,4 +1,4 @@
|
||||||
/* $OpenBSD: env.c,v 1.2 2016/06/19 19:29:43 martijn Exp $ */
|
/* $OpenBSD: env.c,v 1.5 2016/09/15 00:58:23 deraadt Exp $ */
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Ted Unangst <tedu@openbsd.org>
|
* Copyright (c) 2016 Ted Unangst <tedu@openbsd.org>
|
||||||
*
|
*
|
||||||
|
@ -54,12 +54,31 @@ RB_PROTOTYPE_STATIC(envtree, envnode, node, envcmp);
|
||||||
#endif
|
#endif
|
||||||
RB_GENERATE_STATIC(envtree, envnode, node, envcmp)
|
RB_GENERATE_STATIC(envtree, envnode, node, envcmp)
|
||||||
|
|
||||||
struct env *createenv(char **);
|
static struct envnode *
|
||||||
struct env *filterenv(struct env *, struct rule *);
|
createnode(const char *key, const char *value)
|
||||||
char **flattenenv(struct env *);
|
{
|
||||||
|
struct envnode *node;
|
||||||
|
|
||||||
struct env *
|
node = malloc(sizeof(*node));
|
||||||
createenv(char **envp)
|
if (!node)
|
||||||
|
err(1, NULL);
|
||||||
|
node->key = strdup(key);
|
||||||
|
node->value = strdup(value);
|
||||||
|
if (!node->key || !node->value)
|
||||||
|
err(1, NULL);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
freenode(struct envnode *node)
|
||||||
|
{
|
||||||
|
free((char *)node->key);
|
||||||
|
free((char *)node->value);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct env *
|
||||||
|
createenv(struct rule *rule)
|
||||||
{
|
{
|
||||||
struct env *env;
|
struct env *env;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
@ -70,41 +89,47 @@ createenv(char **envp)
|
||||||
RB_INIT(&env->root);
|
RB_INIT(&env->root);
|
||||||
env->count = 0;
|
env->count = 0;
|
||||||
|
|
||||||
for (i = 0; envp[i] != NULL; i++) {
|
if (rule->options & KEEPENV) {
|
||||||
|
extern const char **environ;
|
||||||
|
|
||||||
|
for (i = 0; environ[i] != NULL; i++) {
|
||||||
struct envnode *node;
|
struct envnode *node;
|
||||||
const char *e, *eq;
|
const char *e, *eq;
|
||||||
|
size_t len;
|
||||||
|
char name[1024];
|
||||||
|
|
||||||
e = envp[i];
|
e = environ[i];
|
||||||
|
|
||||||
|
/* ignore invalid or overlong names */
|
||||||
if ((eq = strchr(e, '=')) == NULL || eq == e)
|
if ((eq = strchr(e, '=')) == NULL || eq == e)
|
||||||
continue;
|
continue;
|
||||||
node = malloc(sizeof(*node));
|
len = eq - e;
|
||||||
if (!node)
|
if (len > sizeof(name) - 1)
|
||||||
err(1, NULL);
|
continue;
|
||||||
node->key = strndup(envp[i], eq - e);
|
memcpy(name, e, len);
|
||||||
node->value = strdup(eq + 1);
|
name[len] = '\0';
|
||||||
if (!node->key || !node->value)
|
|
||||||
err(1, NULL);
|
node = createnode(name, eq + 1);
|
||||||
if (RB_FIND(envtree, &env->root, node)) {
|
if (RB_INSERT(envtree, &env->root, node)) {
|
||||||
free((char *)node->key);
|
/* ignore any later duplicates */
|
||||||
free((char *)node->value);
|
freenode(node);
|
||||||
free(node);
|
|
||||||
} else {
|
} else {
|
||||||
RB_INSERT(envtree, &env->root, node);
|
|
||||||
env->count++;
|
env->count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
char **
|
static char **
|
||||||
flattenenv(struct env *env)
|
flattenenv(struct env *env)
|
||||||
{
|
{
|
||||||
char **envp;
|
char **envp;
|
||||||
struct envnode *node;
|
struct envnode *node;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
|
||||||
envp = reallocarray(NULL, (env->count + 1), sizeof(char *));
|
envp = reallocarray(NULL, env->count + 1, sizeof(char *));
|
||||||
if (!envp)
|
if (!envp)
|
||||||
err(1, NULL);
|
err(1, NULL);
|
||||||
i = 0;
|
i = 0;
|
||||||
|
@ -118,72 +143,74 @@ flattenenv(struct env *env)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
copyenv(struct env *orig, struct env *copy, const char **envlist)
|
fillenv(struct env *env, const char **envlist)
|
||||||
{
|
{
|
||||||
struct envnode *node, key;
|
struct envnode *node, key;
|
||||||
|
const char *e, *eq;
|
||||||
|
const char *val;
|
||||||
|
char name[1024];
|
||||||
u_int i;
|
u_int i;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
for (i = 0; envlist[i]; i++) {
|
for (i = 0; envlist[i]; i++) {
|
||||||
key.key = envlist[i];
|
e = envlist[i];
|
||||||
if ((node = RB_FIND(envtree, &orig->root, &key))) {
|
|
||||||
RB_REMOVE(envtree, &orig->root, node);
|
/* parse out env name */
|
||||||
orig->count--;
|
if ((eq = strchr(e, '=')) == NULL)
|
||||||
RB_INSERT(envtree, ©->root, node);
|
len = strlen(e);
|
||||||
copy->count++;
|
else
|
||||||
|
len = eq - e;
|
||||||
|
if (len > sizeof(name) - 1)
|
||||||
|
continue;
|
||||||
|
memcpy(name, e, len);
|
||||||
|
name[len] = '\0';
|
||||||
|
|
||||||
|
/* delete previous copies */
|
||||||
|
key.key = name;
|
||||||
|
if (*name == '-')
|
||||||
|
key.key = name + 1;
|
||||||
|
if ((node = RB_FIND(envtree, &env->root, &key))) {
|
||||||
|
RB_REMOVE(envtree, &env->root, node);
|
||||||
|
freenode(node);
|
||||||
|
env->count--;
|
||||||
|
}
|
||||||
|
if (*name == '-')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* assign value or inherit from environ */
|
||||||
|
if (eq) {
|
||||||
|
val = eq + 1;
|
||||||
|
if (*val == '$')
|
||||||
|
val = getenv(val + 1);
|
||||||
|
} else {
|
||||||
|
val = getenv(name);
|
||||||
|
}
|
||||||
|
/* at last, we have something to insert */
|
||||||
|
if (val) {
|
||||||
|
node = createnode(name, val);
|
||||||
|
RB_INSERT(envtree, &env->root, node);
|
||||||
|
env->count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct env *
|
|
||||||
filterenv(struct env *orig, struct rule *rule)
|
|
||||||
{
|
|
||||||
const char *safeset[] = {
|
|
||||||
"DISPLAY", "HOME", "LOGNAME", "MAIL",
|
|
||||||
"PATH", "TERM", "USER", "USERNAME",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
const char *badset[] = {
|
|
||||||
"ENV",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
struct env *copy;
|
|
||||||
struct envnode *node, key;
|
|
||||||
u_int i;
|
|
||||||
|
|
||||||
if ((rule->options & KEEPENV) && !rule->envlist) {
|
|
||||||
for (i = 0; badset[i]; i++) {
|
|
||||||
key.key = badset[i];
|
|
||||||
if ((node = RB_FIND(envtree, &orig->root, &key))) {
|
|
||||||
RB_REMOVE(envtree, &orig->root, node);
|
|
||||||
free((char *)node->key);
|
|
||||||
free((char *)node->value);
|
|
||||||
free(node);
|
|
||||||
orig->count--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return orig;
|
|
||||||
}
|
|
||||||
|
|
||||||
copy = malloc(sizeof(*copy));
|
|
||||||
if (!copy)
|
|
||||||
err(1, NULL);
|
|
||||||
RB_INIT(©->root);
|
|
||||||
copy->count = 0;
|
|
||||||
|
|
||||||
if (rule->envlist)
|
|
||||||
copyenv(orig, copy, rule->envlist);
|
|
||||||
copyenv(orig, copy, safeset);
|
|
||||||
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
char **
|
char **
|
||||||
prepenv(struct rule *rule)
|
prepenv(struct rule *rule)
|
||||||
{
|
{
|
||||||
extern char **environ;
|
static const char *safeset[] = {
|
||||||
|
"DISPLAY", "HOME", "LOGNAME", "MAIL",
|
||||||
|
"PATH", "TERM", "USER", "USERNAME",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
struct env *env;
|
struct env *env;
|
||||||
|
|
||||||
env = createenv(environ);
|
env = createenv(rule);
|
||||||
env = filterenv(env, rule);
|
|
||||||
|
/* if we started with blank, fill some defaults then apply rules */
|
||||||
|
if (!(rule->options & KEEPENV))
|
||||||
|
fillenv(env, safeset);
|
||||||
|
if (rule->envlist)
|
||||||
|
fillenv(env, rule->envlist);
|
||||||
|
|
||||||
return flattenenv(env);
|
return flattenenv(env);
|
||||||
}
|
}
|
||||||
|
|
91
parse.y
91
parse.y
|
@ -1,4 +1,4 @@
|
||||||
/* $OpenBSD: parse.y,v 1.18 2016/06/07 16:49:23 tedu Exp $ */
|
/* $OpenBSD: parse.y,v 1.26 2017/01/02 01:40:20 tedu Exp $ */
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
|
* Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
|
||||||
*
|
*
|
||||||
|
@ -37,6 +37,7 @@ typedef struct {
|
||||||
const char **cmdargs;
|
const char **cmdargs;
|
||||||
const char **envlist;
|
const char **envlist;
|
||||||
};
|
};
|
||||||
|
const char **strlist;
|
||||||
const char *str;
|
const char *str;
|
||||||
};
|
};
|
||||||
int lineno;
|
int lineno;
|
||||||
|
@ -47,17 +48,30 @@ typedef struct {
|
||||||
FILE *yyfp;
|
FILE *yyfp;
|
||||||
|
|
||||||
struct rule **rules;
|
struct rule **rules;
|
||||||
int nrules, maxrules;
|
int nrules;
|
||||||
|
static int maxrules;
|
||||||
|
|
||||||
int parse_errors = 0;
|
int parse_errors = 0;
|
||||||
|
|
||||||
void yyerror(const char *, ...);
|
static void yyerror(const char *, ...);
|
||||||
int yylex(void);
|
static int yylex(void);
|
||||||
int yyparse(void);
|
|
||||||
|
static size_t
|
||||||
|
arraylen(const char **arr)
|
||||||
|
{
|
||||||
|
size_t cnt = 0;
|
||||||
|
|
||||||
|
while (*arr) {
|
||||||
|
cnt++;
|
||||||
|
arr++;
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%token TPERMIT TDENY TAS TCMD TARGS
|
%token TPERMIT TDENY TAS TCMD TARGS
|
||||||
%token TNOPASS TKEEPENV
|
%token TNOPASS TPERSIST TKEEPENV TSETENV
|
||||||
%token TSTRING
|
%token TSTRING
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
@ -98,15 +112,23 @@ action: TPERMIT options {
|
||||||
$$.envlist = $2.envlist;
|
$$.envlist = $2.envlist;
|
||||||
} | TDENY {
|
} | TDENY {
|
||||||
$$.action = DENY;
|
$$.action = DENY;
|
||||||
|
$$.options = 0;
|
||||||
|
$$.envlist = NULL;
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
options: /* none */
|
options: /* none */ {
|
||||||
| options option {
|
$$.options = 0;
|
||||||
|
$$.envlist = NULL;
|
||||||
|
} | options option {
|
||||||
$$.options = $1.options | $2.options;
|
$$.options = $1.options | $2.options;
|
||||||
$$.envlist = $1.envlist;
|
$$.envlist = $1.envlist;
|
||||||
|
if (($$.options & (NOPASS|PERSIST)) == (NOPASS|PERSIST)) {
|
||||||
|
yyerror("can't combine nopass and persist");
|
||||||
|
YYERROR;
|
||||||
|
}
|
||||||
if ($2.envlist) {
|
if ($2.envlist) {
|
||||||
if ($$.envlist) {
|
if ($$.envlist) {
|
||||||
yyerror("can't have two keepenv sections");
|
yyerror("can't have two setenv sections");
|
||||||
YYERROR;
|
YYERROR;
|
||||||
} else
|
} else
|
||||||
$$.envlist = $2.envlist;
|
$$.envlist = $2.envlist;
|
||||||
|
@ -114,24 +136,29 @@ options: /* none */
|
||||||
} ;
|
} ;
|
||||||
option: TNOPASS {
|
option: TNOPASS {
|
||||||
$$.options = NOPASS;
|
$$.options = NOPASS;
|
||||||
|
$$.envlist = NULL;
|
||||||
|
} | TPERSIST {
|
||||||
|
$$.options = PERSIST;
|
||||||
|
$$.envlist = NULL;
|
||||||
} | TKEEPENV {
|
} | TKEEPENV {
|
||||||
$$.options = KEEPENV;
|
$$.options = KEEPENV;
|
||||||
} | TKEEPENV '{' envlist '}' {
|
$$.envlist = NULL;
|
||||||
$$.options = KEEPENV;
|
} | TSETENV '{' strlist '}' {
|
||||||
$$.envlist = $3.envlist;
|
$$.options = 0;
|
||||||
|
$$.envlist = $3.strlist;
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
envlist: /* empty */ {
|
strlist: /* empty */ {
|
||||||
if (!($$.envlist = calloc(1, sizeof(char *))))
|
if (!($$.strlist = calloc(1, sizeof(char *))))
|
||||||
errx(1, "can't allocate envlist");
|
errx(1, "can't allocate strlist");
|
||||||
} | envlist TSTRING {
|
} | strlist TSTRING {
|
||||||
int nenv = arraylen($1.envlist);
|
int nstr = arraylen($1.strlist);
|
||||||
if (!($$.envlist = reallocarray($1.envlist, nenv + 2,
|
if (!($$.strlist = reallocarray($1.strlist, nstr + 2,
|
||||||
sizeof(char *))))
|
sizeof(char *))))
|
||||||
errx(1, "can't allocate envlist");
|
errx(1, "can't allocate strlist");
|
||||||
$$.envlist[nenv] = $2.str;
|
$$.strlist[nstr] = $2.str;
|
||||||
$$.envlist[nenv + 1] = NULL;
|
$$.strlist[nstr + 1] = NULL;
|
||||||
}
|
} ;
|
||||||
|
|
||||||
|
|
||||||
ident: TSTRING {
|
ident: TSTRING {
|
||||||
|
@ -154,20 +181,8 @@ cmd: /* optional */ {
|
||||||
|
|
||||||
args: /* empty */ {
|
args: /* empty */ {
|
||||||
$$.cmdargs = NULL;
|
$$.cmdargs = NULL;
|
||||||
} | TARGS argslist {
|
} | TARGS strlist {
|
||||||
$$.cmdargs = $2.cmdargs;
|
$$.cmdargs = $2.strlist;
|
||||||
} ;
|
|
||||||
|
|
||||||
argslist: /* empty */ {
|
|
||||||
if (!($$.cmdargs = calloc(1, sizeof(char *))))
|
|
||||||
errx(1, "can't allocate args");
|
|
||||||
} | argslist TSTRING {
|
|
||||||
int nargs = arraylen($1.cmdargs);
|
|
||||||
if (!($$.cmdargs = reallocarray($1.cmdargs, nargs + 2,
|
|
||||||
sizeof(char *))))
|
|
||||||
errx(1, "can't allocate args");
|
|
||||||
$$.cmdargs[nargs] = $2.str;
|
|
||||||
$$.cmdargs[nargs + 1] = NULL;
|
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
@ -185,7 +200,7 @@ yyerror(const char *fmt, ...)
|
||||||
parse_errors++;
|
parse_errors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct keyword {
|
static struct keyword {
|
||||||
const char *word;
|
const char *word;
|
||||||
int token;
|
int token;
|
||||||
} keywords[] = {
|
} keywords[] = {
|
||||||
|
@ -195,7 +210,9 @@ struct keyword {
|
||||||
{ "cmd", TCMD },
|
{ "cmd", TCMD },
|
||||||
{ "args", TARGS },
|
{ "args", TARGS },
|
||||||
{ "nopass", TNOPASS },
|
{ "nopass", TNOPASS },
|
||||||
|
{ "persist", TPERSIST },
|
||||||
{ "keepenv", TKEEPENV },
|
{ "keepenv", TKEEPENV },
|
||||||
|
{ "setenv", TSETENV },
|
||||||
};
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
Loading…
Reference in a new issue