opendoas

A portable version of the OpenBSD `doas` command
git clone https://pi.duncano.de/git/opendoas.git
Log | Files | Refs | README | LICENSE

commit 4f7ed3854a4db241401f5b84cbb6246a06a17561
parent 21c6e427af5275a1879cd027b5534e63528e1349
Author: Duncaen <mail@duncano.de>
Date:   Wed,  8 Jun 2016 17:50:28 +0200

open pam sessions with right user and remove setusercontext shim

before this change the sessions were opened as the user running doas.
Now it sets its uid to root and then opens a pam session for the target
user.
The setusercontext shim was removed, because pam handles all this and
its easier to just call setresuid and setresgid instead.

Diffstat:
configure | 12------------
doas.c | 22+++++++++++++++-------
doas_pam.c | 67++++++++++++++++++++++++++++++++++---------------------------------
includes.h | 2+-
libopenbsd/openbsd.h | 17-----------------
libopenbsd/setusercontext.c | 66------------------------------------------------------------------
pam.d__doas__linux | 5++---
7 files changed, 52 insertions(+), 139 deletions(-)

diff --git a/configure b/configure @@ -281,18 +281,6 @@ int main(void) { } # -# Check for login_cap.h. -# -src=' -#include <login_cap.h> -int main(void) { - return 0; -}' -check_func "login_cap_h" "$src" || { - printf 'OPENBSD += setusercontext.o\n' >>$CONFIG_MK -} - -# # Check for execvpe(). # src=' diff --git a/doas.c b/doas.c @@ -439,6 +439,10 @@ main(int argc, char **argv, char **envp) errc(1, EPERM, NULL); } + pw = getpwuid(target); + if (!pw) + errx(1, "no passwd entry for target"); + #ifdef HAVE_BSD_AUTH_H if (!(rule->options & NOPASS)) { if (nflag) @@ -473,9 +477,8 @@ main(int argc, char **argv, char **envp) explicit_bzero(rbuf, sizeof(rbuf)); } #elif HAVE_PAM_APPL_H - if (!doas_pam(myname, !nflag, rule->options & NOPASS)) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "failed auth for %s", myname); + if (!doas_pam(pw->pw_name, myname, !nflag, rule->options & NOPASS)) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); errc(1, EPERM, NULL); } #else @@ -486,14 +489,19 @@ main(int argc, char **argv, char **envp) if (pledge("stdio rpath getpw exec id", NULL) == -1) err(1, "pledge"); - pw = getpwuid(target); - if (!pw) - errx(1, "no passwd entry for target"); - +#ifdef HAVE_BSD_AUTH_H if (setusercontext(NULL, pw, target, LOGIN_SETGROUP | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | LOGIN_SETUSER) != 0) errx(1, "failed to set user context for target"); +#else + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) + errx(1, "setgid"); + if (initgroups(pw->pw_name, pw->pw_gid) != 0) + errx(1, "initgroups"); + if (setresuid(target, target, target) != 0) + errx(1, "setuid"); +#endif if (pledge("stdio rpath exec", NULL) == -1) err(1, "pledge"); diff --git a/doas_pam.c b/doas_pam.c @@ -39,6 +39,12 @@ static pam_handle_t *pamh = NULL; static sig_atomic_t volatile caught_signal = 0; static char doas_prompt[128]; +static void +catchsig(int sig) +{ + caught_signal = sig; +} + static char * prompt(const char *msg, int echo_on, int *pam) { @@ -48,7 +54,7 @@ prompt(const char *msg, int echo_on, int *pam) /* overwrite default prompt if it matches "Password:[ ]" */ if (strncmp(msg,"Password:", 9) == 0 && - (msg[9] == '\0' || (msg[9] == ' ' && msg[10] == '\0'))) + (msg[9] == '\0' || (msg[9] == ' ' && msg[10] == '\0'))) prompt = doas_prompt; else prompt = msg; @@ -86,7 +92,7 @@ doas_pam_conv(int nmsgs, const struct pam_message **msgs, case PAM_ERROR_MSG: case PAM_TEXT_INFO: if (fprintf(style == PAM_ERROR_MSG ? stderr : stdout, - "%s\n", msgs[i]->msg) < 0) + "%s\n", msgs[i]->msg) < 0) goto fail; break; @@ -117,14 +123,8 @@ fail: return PAM_CONV_ERR; } -static void -catchsig(int sig) -{ - caught_signal = sig; -} - int -doas_pam(char *name, int interactive, int nopass) +doas_pam(const char *user, const char* ruser, int interactive, int nopass) { static const struct pam_conv conv = { .conv = doas_pam_conv, @@ -134,23 +134,22 @@ doas_pam(char *name, int interactive, int nopass) pid_t child; int ret; - if (!name) + if (!user || !ruser) return 0; - ret = pam_start(PAM_SERVICE_NAME, name, &conv, &pamh); - if (ret != PAM_SUCCESS) - errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed\n", - PAM_SERVICE_NAME, name); + /* pam needs the real root */ + if(setuid(0)) + errx(1, "setuid"); - ret = pam_set_item(pamh, PAM_USER, name); + ret = pam_start(PAM_SERVICE_NAME, ruser, &conv, &pamh); if (ret != PAM_SUCCESS) - errx(1, "pam_set_item(?, PAM_USER, \"%s\"): %s\n", - name, pam_strerror(pamh, ret)); + errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed\n", + PAM_SERVICE_NAME, ruser); - ret = pam_set_item(pamh, PAM_RUSER, name); + ret = pam_set_item(pamh, PAM_RUSER, ruser); if (ret != PAM_SUCCESS) - errx(1, "pam_set_item(?, PAM_RUSER, \"%s\"): %s\n", - name, pam_strerror(pamh, ret)); + warn("pam_set_item(?, PAM_RUSER, \"%s\"): %s\n", + pam_strerror(pamh, ret), ruser); if (isatty(0) && (ttydev = ttyname(0)) != NULL) { if (strncmp(ttydev, "/dev/", 5)) @@ -160,8 +159,8 @@ doas_pam(char *name, int interactive, int nopass) ret = pam_set_item(pamh, PAM_TTY, tty); if (ret != PAM_SUCCESS) - errx(1, "pam_set_item(?, PAM_TTY, \"%s\"): %s\n", - tty, pam_strerror(pamh, ret)); + warn("pam_set_item(?, PAM_TTY, \"%s\"): %s\n", + tty, pam_strerror(pamh, ret)); } if (!nopass) { @@ -173,14 +172,12 @@ doas_pam(char *name, int interactive, int nopass) if (gethostname(host, sizeof(host))) snprintf(host, sizeof(host), "?"); snprintf(doas_prompt, sizeof(doas_prompt), - "\rdoas (%.32s@%.32s) password: ", name, host); + "\rdoas (%.32s@%.32s) password: ", ruser, host); /* authenticate */ ret = pam_authenticate(pamh, 0); if (ret != PAM_SUCCESS) { - ret = pam_end(pamh, ret); - if (ret != PAM_SUCCESS) - errx(1, "pam_end(): %s\n", pam_strerror(pamh, ret)); + pam_end(pamh, ret); return 0; } } @@ -193,10 +190,14 @@ doas_pam(char *name, int interactive, int nopass) if (ret != PAM_SUCCESS) return 0; + ret = pam_set_item(pamh, PAM_USER, user); + if (ret != PAM_SUCCESS) + warn("pam_set_item(?, PAM_USER, \"%s\"): %s\n", user, + pam_strerror(pamh, ret)); + ret = pam_setcred(pamh, PAM_ESTABLISH_CRED); if (ret != PAM_SUCCESS) - errx(1, "pam_setcred(?, PAM_ESTABLISH_CRED): %s\n", - pam_strerror(pamh, ret)); + warn("pam_setcred(?, PAM_ESTABLISH_CRED): %s\n", pam_strerror(pamh, ret)); /* open session */ ret = pam_open_session(pamh, 0); @@ -233,10 +234,10 @@ doas_pam(char *name, int interactive, int nopass) /* unblock SIGTERM and SIGALRM to catch them */ sigemptyset(&sigs); - if(sigaddset(&sigs, SIGTERM) || - sigaddset(&sigs, SIGALRM) || - sigaction(SIGTERM, &act, &oldact) || - sigprocmask(SIG_UNBLOCK, &sigs, NULL)) { + if (sigaddset(&sigs, SIGTERM) || + sigaddset(&sigs, SIGALRM) || + sigaction(SIGTERM, &act, &oldact) || + sigprocmask(SIG_UNBLOCK, &sigs, NULL)) { warn("failed to set signal handler"); caught_signal = 1; } @@ -246,7 +247,7 @@ doas_pam(char *name, int interactive, int nopass) if (waitpid(child, &status, 0) != -1) { if (WIFSIGNALED(status)) { fprintf(stderr, "%s%s\n", strsignal(WTERMSIG(status)), - WCOREDUMP(status) ? " (core dumped)" : ""); + WCOREDUMP(status) ? " (core dumped)" : ""); status = WTERMSIG(status) + 128; } else { status = WEXITSTATUS(status); diff --git a/includes.h b/includes.h @@ -20,7 +20,7 @@ #include "openbsd.h" #ifdef HAVE_PAM_APPL_H -int doas_pam(char *name, int interactive, int nopass); +int doas_pam(const char *user, const char *ruser, int interactive, int nopass); #endif #endif /* INCLUDES_H */ diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h @@ -8,23 +8,6 @@ /* API definitions lifted from OpenBSD src/include */ -/* login_cap.h */ -#ifndef HAVE_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); -#endif /* !HAVE_LOGIN_CAP_H */ - /* pwd.h */ #define _PW_NAME_LEN 63 diff --git a/libopenbsd/setusercontext.c b/libopenbsd/setusercontext.c @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2015 Nathan Holstein <nathan.holstein@gmail.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/resource.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <errno.h> -#include <pwd.h> -#include <stdlib.h> -#include <unistd.h> -#include <grp.h> - -#include "includes.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/pam.d__doas__linux b/pam.d__doas__linux @@ -1,10 +1,9 @@ #%PAM-1.0 -auth sufficient pam_timestamp.so timestamp_timeout=300 verbose debug -auth sufficient pam_rootok.so +auth sufficient pam_timestamp.so timestamp_timeout=300 auth required pam_unix.so account required pam_unix.so session optional pam_xauth.so session optional pam_umask.so usergroups umask=022 -session optional pam_timestamp.so timestamp_timeout=300 debug +session optional pam_timestamp.so timestamp_timeout=300 session required pam_env.so session required pam_unix.so