From: Armaan Bhojwani Date: Mon, 3 May 2021 15:26:18 +0000 (-0400) Subject: Remove PAM authentication option X-Git-Url: https://git.armaanb.net/?p=opendoas.git;a=commitdiff_plain;h=22203dec632c0a030e3c83c39ae59feff8d4f8b0 Remove PAM authentication option I don't use it and don't want the potential security risk of having it there --- diff --git a/GNUmakefile b/GNUmakefile index 2eef88e..f19deab 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -27,7 +27,6 @@ install: ${PROG} ${MAN} uninstall: rm -f ${DESTDIR}${BINDIR}/${PROG} - rm -f ${DESTDIR}${PAMDIR}/doas rm -f ${DESTDIR}${MANDIR}/man1/doas.1 rm -f ${DESTDIR}${MANDIR}/man5/doas.conf.5 diff --git a/README.md b/README.md index 0fc94cf..104cf7d 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,9 @@ There are a few steps you have to carefully consider before building and install opendoas: * There are less eyes on random doas ports, just because sudo had a vulnerability - does not mean random doas ports are more secure if they are not reviewed - or pam is configured incorrectly. -* If you want to use pam; You have to [configure pam](#pam-configuration) - and failing to do so correctly might leave a big open door. + does not mean random doas ports are more secure if they are not reviewed. * Use the configure script. * Use the default make target. -* If you really want to install a setuid binary that depends on - pam being correctly configured, use the make install target - to install the software. ## About the port @@ -32,25 +26,13 @@ This is not an official port/project from OpenBSD! 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 or shadow based authentication glommed on to it. +shadow based authentication glommed on to it. Compatibility functions in libopenbsd come from openbsd directly (`strtonum.c`, `reallocarray.c`, `strlcpy.c`, `strlcat.c`), from openssh (`readpassphrase.c`) or from sudo (`closefrom.c`). -The PAM and shadow authentication code does not come from the OpenBSD project. - -### pam configuration - -I will not ship pam configuration files, they are distribution specific and -its simply not safe or productive to ship and install those files. - -If you want to use opendoas on your system and there is no package that -ships with a working pam configuration file, then you have to write and -test it yourself. - -A good starting point is probably the distribution maintained `/etc/pam.d/sudo` -file. +The shadow authentication code does not come from the OpenBSD project. ### Persist/Timestamp/Timeout diff --git a/configure b/configure index b337e6a..b421527 100755 --- a/configure +++ b/configure @@ -23,9 +23,6 @@ usage: configure [options] --enable-debug enable debugging --enable-static prepare for static build - --without-pam disable pam support - --without-shadow disable shadow support - --with-timestamp enable timestamp support --without-insults disable insults @@ -58,10 +55,6 @@ for x; do --target) TARGET=$var ;; --enable-debug) DEBUG=yes ;; --enable-static) BUILD_STATIC=yes ;; - --with-pam) WITHOUT_PAM=; WITHOUT_SHADOW=yes ;; - --with-shadow) WITHOUT_SHADOW=; WITHOUT_PAM=yes ;; - --without-pam) WITHOUT_PAM=yes ;; - --without-shadow) WITHOUT_SHADOW=yes ;; --without-insults) WITHOUT_INSULTS=yes ;; --with-timestamp) WITHOUT_TIMESTAMP= ;; --without-timestamp) WITHOUT_TIMESTAMP=yes ;; @@ -189,39 +182,15 @@ check_func() { } authmethod() { - # - # Check for pam_appl.h. - # - src=' -#include -int main(void) { - return 0; -}' - [ -z "$WITHOUT_PAM" ] && check_func "pam_appl_h" "$src" && { - printf 'SRCS += pam.c\n' >>$CONFIG_MK - printf 'LDLIBS += -lpam\n' >>$CONFIG_MK - printf '#define USE_PAM\n' >>$CONFIG_H - printf 'pam\n' - return 0 - } - - # - # Check for shadow.h. - # src=' #include int main(void) { return 0; }' - [ -z "$WITHOUT_SHADOW" ] && check_func "shadow_h" "$src" && { - printf 'SRCS += shadow.c\n' >>$CONFIG_MK - printf 'LDLIBS += -lcrypt\n' >>$CONFIG_MK - printf '#define USE_SHADOW\n' >>$CONFIG_H - printf 'shadow\n' - return 0 - } - return 1 +printf 'SRCS += shadow.c\n' >>$CONFIG_MK +printf 'LDLIBS += -lcrypt\n' >>$CONFIG_MK +return 0 } definsults() { @@ -554,12 +523,6 @@ check_func "__attribute__" "$src" || { } auth=$(authmethod) -if [ $? -eq 0 ]; then - printf 'Using auth method\t\t\t%s.\n' "$auth" >&2 -else - printf 'Error auth method\t\t\n' >&2 - exit 1 -fi insults=$(definsults) diff --git a/doas.c b/doas.c index a99b964..2cd227d 100644 --- a/doas.c +++ b/doas.c @@ -362,7 +362,6 @@ main(int argc, char **argv) errc(1, EPERM, NULL); } -#if defined(USE_SHADOW) if (!(rule->options & NOPASS)) { if (nflag) errx(1, "Authentication required"); @@ -371,12 +370,6 @@ main(int argc, char **argv) if (ret == 5) authfail(rule->options & INSULT); } -#elif !defined(USE_PAM) - /* no authentication provider, only allow NOPASS rules */ - (void) nflag; - if (!(rule->options & NOPASS)) - errx(1, "Authentication required"); -#endif if ((p = getenv("PATH")) != NULL) formerpath = strdup(p); @@ -394,11 +387,6 @@ main(int argc, char **argv) if (targpw == NULL) errx(1, "no passwd entry for target"); -#if defined(USE_PAM) - pamauth(targpw->pw_name, mypw->pw_name, !nflag, rule->options & NOPASS, - rule->options & PERSIST); -#endif - #ifdef HAVE_LOGIN_CAP_H if (setusercontext(NULL, targpw, target, LOGIN_SETGROUP | LOGIN_SETPATH | diff --git a/doas.h b/doas.h index b1fbac0..ecf0db2 100644 --- a/doas.h +++ b/doas.h @@ -45,13 +45,7 @@ char **prepenv(const struct rule *, const struct passwd *, #define NOLOG 0x8 #define INSULT 0x10 -#ifdef USE_PAM -int pamauth(const char *, const char *, int, int, int); -#endif - -#ifdef USE_SHADOW int shadowauth(const char *, int); -#endif #ifdef USE_TIMESTAMP int timestamp_open(int *, int); diff --git a/pam.c b/pam.c deleted file mode 100644 index f1aae2d..0000000 --- a/pam.c +++ /dev/null @@ -1,350 +0,0 @@ -/* - * 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 "config.h" - -#include -#include - -#include -#include -#include -#include -#ifdef HAVE_READPASSPHRASE -# include -#else -# include "sys-readpassphrase.h" -#endif -#include -#include -#include -#include -#include -#include - -#include - -#include "openbsd.h" -#include "doas.h" - -#ifndef HOST_NAME_MAX -#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX -#endif - -#define PAM_SERVICE_NAME "doas" - -static pam_handle_t *pamh = NULL; -static char doas_prompt[128]; -static sig_atomic_t volatile caught_signal = 0; - -static void -catchsig(int sig) -{ - caught_signal = sig; -} - -static char * -pamprompt(const char *msg, int echo_on, int *ret) -{ - const char *prompt; - char *pass, buf[PAM_MAX_RESP_SIZE]; - int flags = RPP_REQUIRE_TTY | (echo_on ? RPP_ECHO_ON : RPP_ECHO_OFF); - - /* overwrite default prompt if it matches "Password:[ ]" */ - if (strncmp(msg,"Password:", 9) == 0 && - (msg[9] == '\0' || (msg[9] == ' ' && msg[10] == '\0'))) - prompt = doas_prompt; - else - prompt = msg; - - pass = readpassphrase(prompt, buf, sizeof(buf), flags); - if (!pass) - *ret = PAM_CONV_ERR; - else if (!(pass = strdup(pass))) - *ret = PAM_BUF_ERR; - else - *ret = PAM_SUCCESS; - - explicit_bzero(buf, sizeof(buf)); - return pass; -} - -static int -pamconv(int nmsgs, const struct pam_message **msgs, - struct pam_response **rsps, __UNUSED void *ptr) -{ - struct pam_response *rsp; - int i, style; - int ret; - - if (!(rsp = calloc(nmsgs, sizeof(struct pam_response)))) - err(1, "could not allocate pam_response"); - - for (i = 0; i < nmsgs; i++) { - switch (style = msgs[i]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - case PAM_PROMPT_ECHO_ON: - rsp[i].resp = pamprompt(msgs[i]->msg, style == PAM_PROMPT_ECHO_ON, &ret); - if (ret != PAM_SUCCESS) - goto fail; - break; - - case PAM_ERROR_MSG: - case PAM_TEXT_INFO: - if (fprintf(style == PAM_ERROR_MSG ? stderr : stdout, - "%s\n", msgs[i]->msg) < 0) - goto fail; - break; - - default: - errx(1, "invalid PAM msg_style %d", style); - } - } - - *rsps = rsp; - rsp = NULL; - - return PAM_SUCCESS; - -fail: - /* overwrite and free response buffers */ - for (i = 0; i < nmsgs; i++) { - if (rsp[i].resp == NULL) - continue; - switch (msgs[i]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - case PAM_PROMPT_ECHO_ON: - explicit_bzero(rsp[i].resp, strlen(rsp[i].resp)); - free(rsp[i].resp); - } - rsp[i].resp = NULL; - } - free(rsp); - - return PAM_CONV_ERR; -} - -void -pamcleanup(int ret, int sess, int cred) -{ - if (sess) { - ret = pam_close_session(pamh, 0); - if (ret != PAM_SUCCESS) - errx(1, "pam_close_session: %s", pam_strerror(pamh, ret)); - } - if (cred) { - ret = pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); - if (ret != PAM_SUCCESS) - warn("pam_setcred(?, PAM_DELETE_CRED | PAM_SILENT): %s", - pam_strerror(pamh, ret)); - } - pam_end(pamh, ret); -} - -void -watchsession(pid_t child, int sess, int cred) -{ - sigset_t sigs; - struct sigaction act, oldact; - int status = 1; - - /* block signals */ - sigfillset(&sigs); - if (sigprocmask(SIG_BLOCK, &sigs, NULL)) { - warn("failed to block signals"); - caught_signal = 1; - goto close; - } - - /* setup signal handler */ - act.sa_handler = catchsig; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - - /* unblock SIGTERM and SIGALRM to catch them */ - sigemptyset(&sigs); - if (sigaddset(&sigs, SIGTERM) || - sigaddset(&sigs, SIGALRM) || - sigaddset(&sigs, SIGTSTP) || - sigaction(SIGTERM, &act, &oldact) || - sigprocmask(SIG_UNBLOCK, &sigs, NULL)) { - warn("failed to set signal handler"); - caught_signal = 1; - goto close; - } - - /* wait for child to be terminated */ - if (waitpid(child, &status, 0) != -1) { - if (WIFSIGNALED(status)) { - fprintf(stderr, "%s%s\n", strsignal(WTERMSIG(status)), - WCOREDUMP(status) ? " (core dumped)" : ""); - status = WTERMSIG(status) + 128; - } else - status = WEXITSTATUS(status); - } - else if (caught_signal) - status = caught_signal + 128; - else - status = 1; - -close: - if (caught_signal && child != (pid_t)-1) { - fprintf(stderr, "\nSession terminated, killing shell\n"); - kill(child, SIGTERM); - } - - pamcleanup(PAM_SUCCESS, sess, cred); - - if (caught_signal) { - if (child != (pid_t)-1) { - /* kill child */ - sleep(2); - kill(child, SIGKILL); - fprintf(stderr, " ...killed.\n"); - } - - /* unblock cached signal and resend */ - sigaction(SIGTERM, &oldact, NULL); - if (caught_signal != SIGTERM) - caught_signal = SIGKILL; - kill(getpid(), caught_signal); - } - - exit(status); -} - -int -pamauth(const char *user, const char *myname, int interactive, int nopass, int persist) -{ - static const struct pam_conv conv = { - .conv = pamconv, - .appdata_ptr = NULL, - }; - const char *ttydev; - pid_t child; - int ret, sess = 0, cred = 0; - -#ifdef USE_TIMESTAMP - int fd = -1; - int valid = 0; -#else - (void) persist; -#endif - - if (!user || !myname) - return(5); - - ret = pam_start(PAM_SERVICE_NAME, myname, &conv, &pamh); - if (ret != PAM_SUCCESS) - errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed", - PAM_SERVICE_NAME, myname); - - ret = pam_set_item(pamh, PAM_RUSER, myname); - if (ret != PAM_SUCCESS) - warn("pam_set_item(?, PAM_RUSER, \"%s\"): %s", - pam_strerror(pamh, ret), myname); - - if (isatty(0) && (ttydev = ttyname(0)) != NULL) { - if (strncmp(ttydev, "/dev/", 5) == 0) - ttydev += 5; - - ret = pam_set_item(pamh, PAM_TTY, ttydev); - if (ret != PAM_SUCCESS) - warn("pam_set_item(?, PAM_TTY, \"%s\"): %s", - ttydev, pam_strerror(pamh, ret)); - } - - -#ifdef USE_TIMESTAMP - if (persist) - fd = timestamp_open(&valid, 5 * 60); - if (fd != -1 && valid == 1) - nopass = 1; -#endif - - if (!nopass) { - if (!interactive) - return(5); - - /* doas style prompt for pam */ - char host[HOST_NAME_MAX + 1]; - if (gethostname(host, sizeof(host))) - snprintf(host, sizeof(host), "?"); - snprintf(doas_prompt, sizeof(doas_prompt), - "\rdoas (%.32s@%.32s) password: ", myname, host); - - /* authenticate */ - ret = pam_authenticate(pamh, 0); - if (ret != PAM_SUCCESS) { - pamcleanup(ret, sess, cred); - syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - return(5); - } - } - - - ret = pam_acct_mgmt(pamh, 0); - if (ret == PAM_NEW_AUTHTOK_REQD) - ret = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); - - /* account not vaild or changing the auth token failed */ - if (ret != PAM_SUCCESS) { - pamcleanup(ret, sess, cred); - syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - return(5); - } - - /* set PAM_USER to the user we want to be */ - ret = pam_set_item(pamh, PAM_USER, user); - if (ret != PAM_SUCCESS) - warn("pam_set_item(?, PAM_USER, \"%s\"): %s", user, - pam_strerror(pamh, ret)); - - ret = pam_setcred(pamh, PAM_REINITIALIZE_CRED); - if (ret != PAM_SUCCESS) - warn("pam_setcred(?, PAM_REINITIALIZE_CRED): %s", pam_strerror(pamh, ret)); - else - cred = 1; - - /* open session */ - ret = pam_open_session(pamh, 0); - if (ret != PAM_SUCCESS) - errx(1, "pam_open_session: %s", pam_strerror(pamh, ret)); - sess = 1; - - if ((child = fork()) == -1) { - pamcleanup(PAM_ABORT, sess, cred); - err(1, "fork"); - } - - /* return as child */ - if (child == 0) { -#ifdef USE_TIMESTAMP - if (fd != -1) - close(fd); -#endif - return; - } - -#ifdef USE_TIMESTAMP - if (fd != -1) { - timestamp_set(fd, 5 * 60); - close(fd); - } -#endif - watchsession(child, sess, cred); - return(0); -}