From: Nathan Holstein Date: Wed, 5 Aug 2015 15:01:36 +0000 (-0400) Subject: Merge doas.c 1.34 from OpenBSD CVS. X-Git-Tag: v0.1~26 X-Git-Url: https://git.armaanb.net/?a=commitdiff_plain;h=5c60366f659313e46a5b22ad27f50a3ba3ab8f15;hp=b45246e2d368ac00f8558eacd84f1b751857db81;p=opendoas.git Merge doas.c 1.34 from OpenBSD CVS. --- diff --git a/.git-author-conv-file b/.git-author-conv-file new file mode 100644 index 0000000..7a0a29a --- /dev/null +++ b/.git-author-conv-file @@ -0,0 +1,11 @@ +bcallah=Brian Callahan +benno= +bentley= +deraadt=Theo de Raadt +doug= +espie=Marc Espie +jmc=Jean-Marie Cannie +nicm=Nicholas Marriott +schwarze=Ingo Schwarze +tedu=Ted Unangst +zhuk=Vadim Zhukov diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6fd68eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +doas + +*.a +*.o + +*.swp +*.swo diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0c16551 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +portions copyright (c) 2015 Nathan Holstein +portions copyright (c) 2015 Ted Unangst + +To the best of my knowledge, everything is released under the BSD license. + +Additional bits copyright of their respective authors, see individual +files for details. diff --git a/Makefile b/Makefile index 191d00f..f2277ab 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,11 @@ PROG= doas MAN= doas.1 doas.conf.5 BINOWN= root -BINMODE=4555 +BINGRP= wheel +BINMODE=4511 -CFLAGS+= -I${.CURDIR} -COPTS+= -Wall +CFLAGS+= -I${CURDIR} +COPTS+= -Wall -Wextra -Werror -pedantic -std=c11 +LDFLAGS+= -lpam -.include +include bsd.prog.mk diff --git a/README.md b/README.md new file mode 100644 index 0000000..ca08ca7 --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# OpenDoas: a portable version of OpenBSD's `doas` command + +`doas` is a minimal replacement for the venerable `sudo`. It was +initially [written by Ted Unangst](http://www.tedunangst.com/flak/post/doas) +of the OpenBSD project to provide 95% of the features of `sudo` with a +fraction of the codebase. + +This is still a work in progress! Please do not deploy yet in a critical +environment! Of note, `doas` semantics may yet change, and I haven't +completed PAM integration yet! + +## Building and installing + +Building `doas` should be just a simple `make` away. + +The included makefile also has an installation target. Installation +requires root access to properly set the executable permissions. You'll +also need to install a `doas.conf` file: + +``` +make && sudo make install +echo "permit :admin" | sudo tee /etc/doas.conf +``` + +Oh the irony, using `sudo` to install `doas`! + +## About the port + +As much as possible I've attempted to stick to `doas` as tedu desired +it. As things stand it's essentially just code lifted from OpenBSD with +PAM based authentication glommed on to it. + +I've used cvsync and git-cvsimport to retain the history of the core +source files. I may choose to go back and do the same with some of the +compatibility functions (such as reallocarray.c). + +I have found it necessary to make some fixes to the codebase. One was +a segfault due to differences in yacc/bison, others were just minor +fixes to warnings. Once this appears stable, I may try to upstream some +of these. + +Currently, this is only tested on MacOSX 10.10 with Clang. My next goal +is support for Fedora Linux as well. Contributions gladly accepted. ;-) + +## Copyright + +All code from OpenBSD is licensed under the BSD license, please see +individual files for details as the specific text varies from file to +file. + +All code I've written is licensed with the 2-clause BSD. diff --git a/bsd.prog.mk b/bsd.prog.mk new file mode 100644 index 0000000..197455a --- /dev/null +++ b/bsd.prog.mk @@ -0,0 +1,45 @@ +# Copyright 2015 Nathan Holstein + +BINDIR?=/usr/bin +MANDIR?=/usr/share/man + +default: ${PROG} + +OPENBSD:=reallocarray.c strtonum.c execvpe.c setresuid.c \ + auth_userokay.c setusercontext.c explicit_bzero.c +OPENBSD:=$(addprefix libopenbsd/,${OPENBSD:.c=.o}) +libopenbsd.a: ${OPENBSD} + ${AR} -r $@ $? + +CFLAGS:=${CFLAGS} -I${CURDIR}/libopenbsd ${COPTS} + +OBJS:=${SRCS:.y=.c} +OBJS:=${OBJS:.c=.o} + +${PROG}: ${OBJS} libopenbsd.a + ${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@ + +.%.chmod: % + cp $< $@ + chmod ${BINMODE} $@ + chown ${BINOWN}:${BINGRP} $@ + +${BINDIR}: + mkdir -pm 0755 $@ + +${BINDIR}/${PROG}: .${PROG}.chmod ${BINDIR} + mv $< $@ + +MAN:=$(join $(addprefix ${MANDIR}/man,$(patsubst .%,%/,$(suffix ${MAN}))),${MAN}) +$(foreach M,${MAN},$(eval $M: $(notdir $M); cp $$< $$@)) + +install: ${BINDIR}/${PROG} ${MAN} + +clean: + rm -f libopenbsd.a + rm -f ${OPENBSD} + rm -f ${OBJS} + rm -f ${PROG} + +.PHONY: default clean install man +.INTERMEDIATE: .${PROG}.chmod diff --git a/doas.c b/doas.c index a7c7be2..f0cbab2 100644 --- a/doas.c +++ b/doas.c @@ -19,8 +19,6 @@ #include #include -#include -#include #include #include #include @@ -31,6 +29,8 @@ #include #include +#include "openbsd.h" + #include "doas.h" static void __dead @@ -45,9 +45,11 @@ arraylen(const char **arr) { size_t cnt = 0; - while (*arr) { - cnt++; - arr++; + if (arr) { + while (*arr) { + cnt++; + arr++; + } } return cnt; } @@ -162,10 +164,7 @@ parseconfig(const char *filename, int checkperms) yyfp = fopen(filename, "r"); if (!yyfp) { - if (checkperms) - fprintf(stderr, "doas is not enabled.\n"); - else - warn("could not open config file"); + warn("could not open config file"); exit(1); } @@ -188,10 +187,10 @@ parseconfig(const char *filename, int checkperms) * Copy the environment variables in safeset from oldenvp to envp. */ static int -copyenvhelper(const char **oldenvp, const char **safeset, int nsafe, +copyenvhelper(const char **oldenvp, const char **safeset, size_t nsafe, char **envp, int ei) { - int i; + size_t i; for (i = 0; i < nsafe; i++) { const char **oe = oldenvp; @@ -224,8 +223,8 @@ copyenv(const char **oldenvp, struct rule *rule) char **envp; const char **extra; int ei; - int nsafe, nbad; - int nextras = 0; + size_t nsafe, nbad; + size_t nextras = 0; /* if there was no envvar whitelist, pass all except badset ones */ nbad = arraylen(badset); diff --git a/libopenbsd/auth_userokay.c b/libopenbsd/auth_userokay.c new file mode 100644 index 0000000..9c89625 --- /dev/null +++ b/libopenbsd/auth_userokay.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015 Nathan Holstein + * + * 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 +#include +#include +#include +#include +#include + +#include + +#include "openbsd.h" + +#define PAM_SERVICE "sudo" + +#define __UNUSED __attribute__ ((unused)) + +static int +pam_conv(__UNUSED int huh, __UNUSED const struct pam_message **msg, + __UNUSED struct pam_response **rsp, __UNUSED void *ptr) +{ + return 0; +} + +static struct pam_conv conv = { + .conv = pam_conv, + .appdata_ptr = NULL, +}; + +static int +check_pam(const char *user) +{ + fprintf(stderr, "check_pam(%s)\n", user); + + int ret; + pam_handle_t *pamh = NULL; + + ret = pam_start(PAM_SERVICE, user, &conv, &pamh); + if (ret != 0) { + fprintf(stderr, "pam_start(\"%s\", \"%s\", ?, ?): failed\n", + PAM_SERVICE, user); + return -1; + } + + if ((ret = pam_close_session(pamh, 0)) != 0) { + fprintf(stderr, "pam_close_session(): %s\n", pam_strerror(pamh, ret)); + return -1; + } + + return 0; +} + +int +auth_userokay(char *name, char *style, char *type, char *password) +{ + if (!name) + return 0; + if (style || type || password) { + fprintf(stderr, "auth_userokay(name, NULL, NULL, NULL)!\n"); + exit(1); + } + + int ret = check_pam(name); + if (ret != 0) { + fprintf(stderr, "PAM authentication failed\n"); + return 0; + } + + /* + char passbuf[256]; + if (readpassphrase("Password: ", passbuf, sizeof(passbuf), + RPP_REQUIRE_TTY) == NULL) + return 0; + + explicit_bzero(passbuf, sizeof(passbuf)); + */ + + fprintf(stderr, "failing auth check for %s\n", name); + return 0; +} + diff --git a/libopenbsd/execvpe.c b/libopenbsd/execvpe.c new file mode 100644 index 0000000..f080148 --- /dev/null +++ b/libopenbsd/execvpe.c @@ -0,0 +1,156 @@ +/* $OpenBSD: exec.c,v 1.20 2013/01/08 02:26:09 deraadt Exp $ */ +/*- + * 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 + * 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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int +execvpe(const char *name, char *const *argv, char *const *envp) +{ + char **memp; + int cnt; + size_t lp, ln, len; + char *p; + int eacces = 0; + char *bp, *cur, *path, buf[PATH_MAX]; + + /* + * 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); +} diff --git a/libopenbsd/explicit_bzero.c b/libopenbsd/explicit_bzero.c new file mode 100644 index 0000000..9a646c6 --- /dev/null +++ b/libopenbsd/explicit_bzero.c @@ -0,0 +1,21 @@ +/* $OpenBSD: explicit_bzero.c,v 1.2 2014/06/10 04:17:37 deraadt Exp $ */ +/* + * Public domain. + * Written by Matthew Dempsky. + */ + +#include + +#define __UNUSED __attribute__ ((unused)) + +__attribute__((weak)) void +__explicit_bzero_hook(__UNUSED void *buf, __UNUSED size_t len) +{ +} + +void +explicit_bzero(void *buf, size_t len) +{ + memset(buf, 0, len); + __explicit_bzero_hook(buf, len); +} diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h new file mode 100644 index 0000000..1fa73af --- /dev/null +++ b/libopenbsd/openbsd.h @@ -0,0 +1,41 @@ +#ifndef _LIB_OPENBSD_H_ +#define _LIB_OPENBSD_H_ + +#include + +/* API definitions lifted from OpenBSD src/include */ + +/* bsd_auth.h */ +int auth_userokay(char *, char *, char *, char *); + +/* login_cap.h */ +#define LOGIN_SETGROUP 0x0001 /* Set group */ +#define LOGIN_SETLOGIN 0x0002 /* Set login */ +#define LOGIN_SETPATH 0x0004 /* Set path */ +#define LOGIN_SETPRIORITY 0x0008 /* Set priority */ +#define LOGIN_SETRESOURCES 0x0010 /* Set resource limits */ +#define LOGIN_SETUMASK 0x0020 /* Set umask */ +#define LOGIN_SETUSER 0x0040 /* Set user */ +#define LOGIN_SETENV 0x0080 /* Set environment */ +#define LOGIN_SETALL 0x00ff /* Set all. */ + +typedef struct login_cap login_cap_t; +struct passwd; +int setusercontext(login_cap_t *, struct passwd *, uid_t, unsigned int); + +/* pwd.h */ +#define _PW_NAME_LEN 63 + +/* stdlib.h */ +void * reallocarray(void *optr, size_t nmemb, size_t size); +long long strtonum(const char *numstr, long long minval, + long long maxval, const char **errstrp); + +/* string.h */ +void explicit_bzero(void *, size_t); + +/* unistd.h */ +int execvpe(const char *, char *const *, char *const *); +int setresuid(uid_t, uid_t, uid_t); + +#endif diff --git a/libopenbsd/reallocarray.c b/libopenbsd/reallocarray.c new file mode 100644 index 0000000..aa70686 --- /dev/null +++ b/libopenbsd/reallocarray.c @@ -0,0 +1,38 @@ +/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * 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 +#include +#include +#include + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} diff --git a/libopenbsd/setresuid.c b/libopenbsd/setresuid.c new file mode 100644 index 0000000..a62b19a --- /dev/null +++ b/libopenbsd/setresuid.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015 Nathan Holstein + * + * 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 +#include +#include + +/* I don't think we can actually mimic the right semantics? */ +int +setresuid(uid_t ruid, uid_t euid, uid_t suid) +{ + int ret; + if (suid != ruid) { + errno = EPERM; + return -1; + } + if ((ret = setuid(ruid)) != 0) + return ret; + if ((ret = seteuid(euid)) != 0) + return ret; + return 0; +} + diff --git a/libopenbsd/setusercontext.c b/libopenbsd/setusercontext.c new file mode 100644 index 0000000..a6a9aef --- /dev/null +++ b/libopenbsd/setusercontext.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015 Nathan Holstein + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "openbsd.h" + +int +setusercontext(login_cap_t *lc, struct passwd *pw, uid_t uid, unsigned int flags) +{ + int ret; + + if (lc != NULL || pw == NULL || + (flags & ~(LOGIN_SETGROUP | LOGIN_SETPRIORITY | + LOGIN_SETRESOURCES | LOGIN_SETUMASK | + LOGIN_SETUSER)) != 0) { + errno = EINVAL; + return -1; + } + + if (flags & LOGIN_SETGROUP) { + if ((ret = setgid(pw->pw_gid)) != 0) + return ret; + if ((ret = initgroups(pw->pw_name, pw->pw_gid)) != 0) + return ret; + } + + if (flags & LOGIN_SETPRIORITY) { + if ((ret = setpriority(PRIO_PROCESS, getpid(), 0)) != 0) + return ret; + if ((ret = setpriority(PRIO_USER, uid, 0)) != 0) + return ret; + } + + if (flags & LOGIN_SETRESOURCES) { + } + + if (flags & LOGIN_SETUMASK) + umask(S_IWGRP | S_IWOTH); + + if (flags & LOGIN_SETUSER) + return setuid(uid); + + return 0; +} + diff --git a/libopenbsd/strtonum.c b/libopenbsd/strtonum.c new file mode 100644 index 0000000..3725177 --- /dev/null +++ b/libopenbsd/strtonum.c @@ -0,0 +1,65 @@ +/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ + +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * 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 +#include +#include + +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp) +{ + long long ll = 0; + int error = 0; + char *ep; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) { + error = INVALID; + } else { + ll = strtoll(numstr, &ep, 10); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ll = 0; + + return (ll); +} diff --git a/parse.y b/parse.y index ee5f11d..0307b0f 100644 --- a/parse.y +++ b/parse.y @@ -18,12 +18,15 @@ %{ #include #include -#include -#include +#include #include #include +#include +#include #include -#include +#include + +#include "openbsd.h" #include "doas.h" @@ -99,8 +102,10 @@ action: TPERMIT options { $$.action = DENY; } ; -options: /* none */ - | options option { +options: /* none */ { + $$.options = 0; + $$.envlist = NULL; + } | options option { $$.options = $1.options | $2.options; $$.envlist = $1.envlist; if ($2.envlist) { @@ -113,16 +118,17 @@ options: /* none */ } ; option: TNOPASS { $$.options = NOPASS; + $$.envlist = NULL; } | TKEEPENV { $$.options = KEEPENV; + $$.envlist = NULL; } | TKEEPENV '{' envlist '}' { $$.options = KEEPENV; $$.envlist = $3.envlist; } ; envlist: /* empty */ { - if (!($$.envlist = calloc(1, sizeof(char *)))) - errx(1, "can't allocate envlist"); + $$.envlist = NULL; } | envlist TSTRING { int nenv = arraylen($1.envlist); if (!($$.envlist = reallocarray($1.envlist, nenv + 2, @@ -158,8 +164,7 @@ args: /* empty */ { } ; argslist: /* empty */ { - if (!($$.cmdargs = calloc(1, sizeof(char *)))) - errx(1, "can't allocate args"); + $$.cmdargs = NULL; } | argslist TSTRING { int nargs = arraylen($1.cmdargs); if (!($$.cmdargs = reallocarray($1.cmdargs, nargs + 2, @@ -200,7 +205,7 @@ int yylex(void) { char buf[1024], *ebuf, *p, *str; - int i, c, quotes = 0, escape = 0, qpos = -1, nonkw = 0; + int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0; p = buf; ebuf = buf + sizeof(buf); @@ -303,6 +308,7 @@ eow: goto repeat; } if (!nonkw) { + size_t i; for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { if (strcmp(buf, keywords[i].word) == 0) return keywords[i].token;