[BACK]Return to doas.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / doas

Annotation of src/usr.bin/doas/doas.c, Revision 1.1

1.1     ! tedu        1: /* $OpenBSD$ */
        !             2: /*
        !             3:  * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
        !             4:  *
        !             5:  * Permission to use, copy, modify, and distribute this software for any
        !             6:  * purpose with or without fee is hereby granted, provided that the above
        !             7:  * copyright notice and this permission notice appear in all copies.
        !             8:  *
        !             9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            16:  */
        !            17: #include <sys/types.h>
        !            18:
        !            19: #include <limits.h>
        !            20: #include <login_cap.h>
        !            21: #include <bsd_auth.h>
        !            22: #include <string.h>
        !            23: #include <stdio.h>
        !            24: #include <stdlib.h>
        !            25: #include <err.h>
        !            26: #include <unistd.h>
        !            27: #include <pwd.h>
        !            28: #include <grp.h>
        !            29: #include <syslog.h>
        !            30:
        !            31: #include "doas.h"
        !            32:
        !            33: static void __dead
        !            34: usage(void)
        !            35: {
        !            36:        fprintf(stderr, "usage: doas [-u user] command [args]\n");
        !            37:        exit(1);
        !            38: }
        !            39:
        !            40: size_t
        !            41: arraylen(const char **arr)
        !            42: {
        !            43:        size_t cnt = 0;
        !            44:        while (*arr) {
        !            45:                cnt++;
        !            46:                arr++;
        !            47:        }
        !            48:        return cnt;
        !            49: }
        !            50:
        !            51: static int
        !            52: parseuid(const char *s, uid_t *uid)
        !            53: {
        !            54:        struct passwd *pw;
        !            55:        const char *errstr;
        !            56:
        !            57:        if ((pw = getpwnam(s)) != NULL) {
        !            58:                *uid = pw->pw_uid;
        !            59:                return 0;
        !            60:        }
        !            61:        *uid = strtonum(s, 0, UID_MAX, &errstr);
        !            62:        if (errstr)
        !            63:                return -1;
        !            64:        return 0;
        !            65: }
        !            66:
        !            67: static int
        !            68: uidcheck(const char *s, uid_t desired)
        !            69: {
        !            70:        uid_t uid;
        !            71:
        !            72:        if (parseuid(s, &uid) != 0)
        !            73:                return -1;
        !            74:        if (uid != desired)
        !            75:                return -1;
        !            76:        return 0;
        !            77: }
        !            78:
        !            79: static gid_t
        !            80: strtogid(const char *s)
        !            81: {
        !            82:        struct group *gr;
        !            83:        const char *errstr;
        !            84:        gid_t gid;
        !            85:
        !            86:        if ((gr = getgrnam(s)) != NULL)
        !            87:                return gr->gr_gid;
        !            88:        gid = strtonum(s, 0, GID_MAX, &errstr);
        !            89:        if (errstr)
        !            90:                return -1;
        !            91:        return gid;
        !            92: }
        !            93:
        !            94: static int
        !            95: match(uid_t uid, gid_t *groups, int ngroups, uid_t target, const char *cmd,
        !            96:     struct rule *r)
        !            97: {
        !            98:        int i;
        !            99:
        !           100:        if (r->ident[0] == ':') {
        !           101:                gid_t rgid = strtogid(r->ident + 1);
        !           102:                if (rgid == -1)
        !           103:                        return 0;
        !           104:                for (i = 0; i < ngroups; i++) {
        !           105:                        if (rgid == groups[i])
        !           106:                                break;
        !           107:                }
        !           108:                if (i == ngroups)
        !           109:                        return 0;
        !           110:        } else {
        !           111:                if (uidcheck(r->ident, uid) != 0)
        !           112:                        return 0;
        !           113:        }
        !           114:        if (r->target && uidcheck(r->target, target) != 0)
        !           115:                return 0;
        !           116:        if (r->cmd && strcmp(r->cmd, cmd) != 0)
        !           117:                return 0;
        !           118:        return 1;
        !           119: }
        !           120:
        !           121: static int
        !           122: permit(uid_t uid, gid_t *groups, int ngroups, struct rule **lastr,
        !           123:     uid_t target, const char *cmd)
        !           124: {
        !           125:        int i;
        !           126:
        !           127:        *lastr = NULL;
        !           128:        for (i = 0; i < nrules; i++) {
        !           129:                if (match(uid, groups, ngroups, target, cmd, rules[i]))
        !           130:                        *lastr = rules[i];
        !           131:        }
        !           132:        if (!*lastr)
        !           133:                return 0;
        !           134:        return (*lastr)->action == PERMIT;
        !           135: }
        !           136:
        !           137: static void
        !           138: parseconfig(const char *filename)
        !           139: {
        !           140:        extern FILE *yyfp;
        !           141:        extern int yyparse(void);
        !           142:
        !           143:        yyfp = fopen(filename, "r");
        !           144:        if (!yyfp) {
        !           145:                fprintf(stderr, "doas is not enabled.\n");
        !           146:                exit(1);
        !           147:        }
        !           148:        yyparse();
        !           149:        fclose(yyfp);
        !           150: }
        !           151:
        !           152: static int
        !           153: copyenvhelper(const char **oldenvp, const char **safeset, int nsafe, char **envp, int ei)
        !           154: {
        !           155:        int i;
        !           156:        for (i = 0; i < nsafe; i++) {
        !           157:                const char **oe = oldenvp;
        !           158:                while (*oe) {
        !           159:                        size_t len = strlen(safeset[i]);
        !           160:                        if (strncmp(*oe, safeset[i], len) == 0 &&
        !           161:                            (*oe)[len] == '=') {
        !           162:                                if (!(envp[ei++] = strdup(*oe)))
        !           163:                                        err(1, "strdup");
        !           164:                                break;
        !           165:                        }
        !           166:                        oe++;
        !           167:                }
        !           168:        }
        !           169:        return ei;
        !           170: }
        !           171:
        !           172: static char **
        !           173: copyenv(const char **oldenvp, struct rule *rule)
        !           174: {
        !           175:        const char *safeset[] = {
        !           176:                "DISPLAY", "HOME", "LOGNAME", "MAIL", "SHELL",
        !           177:                "PATH", "TERM", "USER", "USERNAME",
        !           178:                NULL,
        !           179:        };
        !           180:        int nsafe;
        !           181:        int nextras = 0;
        !           182:        char **envp;
        !           183:        const char **extra;
        !           184:        int ei;
        !           185:        int i, j;
        !           186:
        !           187:        if ((rule->options & KEEPENV) && !rule->envlist) {
        !           188:                j = arraylen(oldenvp);
        !           189:                envp = reallocarray(NULL, j + 1, sizeof(char *));
        !           190:                for (i = 0; i < j; i++) {
        !           191:                        if (!(envp[i] = strdup(oldenvp[i])))
        !           192:                                err(1, "strdup");
        !           193:                }
        !           194:                envp[i] = NULL;
        !           195:                return envp;
        !           196:        }
        !           197:
        !           198:        nsafe = arraylen(safeset);
        !           199:        if ((extra = rule->envlist)) {
        !           200:                nextras = arraylen(extra);
        !           201:                for (i = 0; i < nsafe; i++) {
        !           202:                        for (j = 0; j < nextras; j++) {
        !           203:                                if (strcmp(extra[j], safeset[i]) == 0) {
        !           204:                                        extra[j--] = extra[nextras--];
        !           205:                                        extra[nextras] = NULL;
        !           206:                                }
        !           207:                        }
        !           208:                }
        !           209:        }
        !           210:
        !           211:        envp = reallocarray(NULL, nsafe + nextras + 1, sizeof(char *));
        !           212:        if (!envp)
        !           213:                err(1, "can't allocate new environment");
        !           214:
        !           215:        ei = 0;
        !           216:        ei = copyenvhelper(oldenvp, safeset, nsafe, envp, ei);
        !           217:        ei = copyenvhelper(oldenvp, rule->envlist, nextras, envp, ei);
        !           218:        envp[ei] = NULL;
        !           219:
        !           220:        return envp;
        !           221: }
        !           222:
        !           223: static void __dead
        !           224: fail(void)
        !           225: {
        !           226:        const char *msgs[] = {
        !           227:                "No lollygagging!",
        !           228:                "Better luck next time.",
        !           229:                "PEBKAC detected.",
        !           230:                "That's what happens when you're lazy.",
        !           231:                "It is clear that this has not been thought through.",
        !           232:                "That's the most ridiculous thing I've heard in the last two or three minutes!",
        !           233:                "No sane people allowed here.  Go home.",
        !           234:                "I would explain, but I am too drunk.",
        !           235:                "You're not allowed to have an opinion.",
        !           236:                "Complaint forms are handled in another department.",
        !           237:        };
        !           238:        const char *m;
        !           239:
        !           240:        m = msgs[arc4random_uniform(sizeof(msgs) / sizeof(msgs[0]))];
        !           241:        fprintf(stderr, m);
        !           242:        fprintf(stderr, "\n");
        !           243:        exit(1);
        !           244: }
        !           245:
        !           246: int
        !           247: main(int argc, char **argv, char **envp)
        !           248: {
        !           249:        char cmdline[1024];
        !           250:        char myname[32];
        !           251:        uid_t uid, target = 0;
        !           252:        gid_t groups[NGROUPS_MAX + 1];
        !           253:        int ngroups;
        !           254:        struct passwd *pw;
        !           255:        struct rule *rule;
        !           256:        const char *cmd;
        !           257:        int i, ch;
        !           258:        const char *safepath = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin";
        !           259:
        !           260:        parseconfig("/etc/doas.conf");
        !           261:
        !           262:        while ((ch = getopt(argc, argv, "u:")) != -1) {
        !           263:                switch (ch) {
        !           264:                case 'u':
        !           265:                        if (parseuid(optarg, &target) != 0)
        !           266:                                errx(1, "unknown user");
        !           267:                        break;
        !           268:                default:
        !           269:                        usage();
        !           270:                        break;
        !           271:                }
        !           272:        }
        !           273:        argv += optind;
        !           274:        argc -= optind;
        !           275:
        !           276:        if (!argc)
        !           277:                usage();
        !           278:
        !           279:        cmd = argv[0];
        !           280:        strlcpy(cmdline, argv[0], sizeof(cmdline));
        !           281:        for (i = 1; i < argc; i++) {
        !           282:                strlcat(cmdline, " ", sizeof(cmdline));
        !           283:                strlcat(cmdline, argv[i], sizeof(cmdline));
        !           284:        }
        !           285:
        !           286:        uid = getuid();
        !           287:        pw = getpwuid(uid);
        !           288:        if (!pw)
        !           289:                err(1, "getpwuid failed");
        !           290:        strlcpy(myname, pw->pw_name, sizeof(myname));
        !           291:        ngroups = getgroups(NGROUPS_MAX, groups);
        !           292:        if (ngroups == -1)
        !           293:                err(1, "can't get groups");
        !           294:        groups[ngroups++] = getgid();
        !           295:
        !           296:        if (!permit(uid, groups, ngroups, &rule, target, cmd)) {
        !           297:                syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed command for %s: %s", myname, cmdline);
        !           298:                fail();
        !           299:        }
        !           300:
        !           301:        if (!(rule->options & NOPASS)) {
        !           302:                if (!auth_userokay(myname, NULL, NULL, NULL)) {
        !           303:                        syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed password for %s", myname);
        !           304:                        fail();
        !           305:                }
        !           306:        }
        !           307:        envp = copyenv((const char **)envp, rule);
        !           308:
        !           309:        pw = getpwuid(target);
        !           310:        if (!pw)
        !           311:                errx(1, "no passwd entry for target");
        !           312:        if (setusercontext(NULL, pw, target, LOGIN_SETGROUP |
        !           313:            LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK |
        !           314:            LOGIN_SETUSER) != 0)
        !           315:                errx(1, "failed to set user context for target");
        !           316:
        !           317:        syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command as %s: %s", myname, pw->pw_name, cmdline);
        !           318:        setenv("PATH", safepath, 1);
        !           319:        execvpe(cmd, argv, envp);
        !           320:        err(1, "%s", cmd);
        !           321: }