]> git.armaanb.net Git - opendoas.git/commitdiff
Add proper pam session handling
authorDuncaen <mail@duncano.de>
Sun, 8 May 2016 14:15:58 +0000 (16:15 +0200)
committerDuncaen <mail@duncano.de>
Sun, 8 May 2016 14:15:58 +0000 (16:15 +0200)
configure
doas.c
doas_pam.c [new file with mode: 0644]
includes.h
libopenbsd/auth_userokay.c [deleted file]
libopenbsd/openbsd.h

index 98699daa0dfd6dc7af9165036f5c5ddc6a800585..16d37ee8c68d4bba116fe3a3f397447efa3187ff 100755 (executable)
--- a/configure
+++ b/configure
@@ -228,9 +228,21 @@ src='
 int main(void) {
        return 0;
 }'
-check_func "bsd_auth_h" "$src" || {
-       printf 'OPENBSD  +=     auth_userokay.c\n' >>$CONFIG_MK
-}
+check_func "bsd_auth_h" "$src" && \
+       have_bsd_auth_h=1
+
+#
+# Check for pam_appl.h.
+#
+src='
+#include <security/pam_appl.h>
+int main(void) {
+       return 0;
+}'
+[ -z "$have_bsd_auth_h" ] && \
+       check_func "pam_appl_h" "$src" && {
+               printf 'SRCS     +=     doas_pam.c\n' >>$CONFIG_MK
+       }
 
 #
 # Check for login_cap.h.
diff --git a/doas.c b/doas.c
index 8094aad4efd7cd0826b68929456f06a4a4d078ef..cceeac17d07317224bb396a420e2204ee168cb13 100644 (file)
--- a/doas.c
+++ b/doas.c
@@ -438,11 +438,11 @@ main(int argc, char **argv, char **envp)
                errc(1, EPERM, NULL);
        }
 
+#ifdef HAVE_BSD_AUTH_H
        if (!(rule->options & NOPASS)) {
                if (nflag)
                        errx(1, "Authorization required");
 
-#ifdef HAVE_BSD_AUTH_H
                char *challenge = NULL, *response, rbuf[1024], cbuf[128];
                auth_session_t *as;
 
@@ -470,14 +470,17 @@ main(int argc, char **argv, char **envp)
                        errc(1, EPERM, NULL);
                }
                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);
+               errc(1, EPERM, NULL);
+       }
 #else
-               if (!auth_userokay(myname, NULL, NULL, NULL)) {
-                       syslog(LOG_AUTHPRIV | LOG_NOTICE,
-                           "failed auth for %s", myname);
-                       errc(1, EPERM, NULL);
-               }
+       if (!(rule->options & NOPASS)) {
+                       errx(1, "Authorization required");
 #endif /* HAVE_BSD_AUTH_H */
-       }
 
        if (pledge("stdio rpath getpw exec id", NULL) == -1)
                err(1, "pledge");
diff --git a/doas_pam.c b/doas_pam.c
new file mode 100644 (file)
index 0000000..df6a097
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * 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/types.h>
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <security/pam_appl.h>
+
+#include "doas.h"
+#include "includes.h"
+
+#define PAM_SERVICE_NAME "doas"
+
+static pam_handle_t *pamh = NULL;
+static sig_atomic_t volatile caught_signal = 0;
+
+static char *
+prompt(const char *msg, int echo_on, int *pam)
+{
+       char buf[PAM_MAX_RESP_SIZE];
+       int flags = RPP_REQUIRE_TTY | (echo_on ? RPP_ECHO_ON : RPP_ECHO_OFF);
+       char *ret = readpassphrase(msg, buf, sizeof(buf), flags);
+       if (!ret)
+               *pam = PAM_CONV_ERR;
+       else if (!(ret = strdup(ret)))
+               *pam = PAM_BUF_ERR;
+       explicit_bzero(buf, sizeof(buf));
+       return ret;
+}
+
+static int
+doas_pam_conv(int nmsgs, const struct pam_message **msgs,
+               struct pam_response **rsps, __UNUSED void *ptr)
+{
+       struct pam_response *rsp;
+       int i, style;
+       int ret = PAM_SUCCESS;
+
+       if (!(rsp = calloc(nmsgs, sizeof(struct pam_response))))
+               errx(1, "couldn't malloc 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 = prompt(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 (style = 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;
+       }
+
+       return PAM_CONV_ERR;
+}
+
+static void
+catchsig(int sig)
+{
+       caught_signal = sig;
+}
+
+int
+doas_pam(char *name, int interactive, int nopass)
+{
+       static const struct pam_conv conv = {
+               .conv = doas_pam_conv,
+               .appdata_ptr = NULL,
+       };
+       pid_t child;
+       int ret;
+
+       if (!name)
+               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);
+
+       if (!nopass) {
+               if (!interactive)
+                       errx(1, "Authorization required");
+               /* 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));
+                       return 1;
+               }
+       }
+
+       ret = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+       if (ret != PAM_SUCCESS)
+               errx(1, "pam_setcred(?, PAM_ESTABLISH_CRED): %s\n",
+                               pam_strerror(pamh, ret));
+
+       ret = pam_acct_mgmt(pamh, 0);
+       if (ret != PAM_SUCCESS)
+               errx(1, "pam_setcred(): %s\n", pam_strerror(pamh, ret));
+
+       /* open session */
+       ret = pam_open_session(pamh, 0);
+       if (ret != PAM_SUCCESS)
+               errx(1, "pam_open_session(): %s\n", pam_strerror(pamh, ret));
+
+       if ((child = fork()) == -1) {
+               ret = pam_close_session(pamh, 0);
+               if (ret != PAM_SUCCESS)
+                       errx(1, "pam_close_session(): %s\n", pam_strerror(pamh, ret));
+
+               ret = pam_end(pamh, PAM_ABORT);
+               if (ret != PAM_SUCCESS)
+                       errx(1, "pam_end(): %s\n", pam_strerror(pamh, ret));
+
+               errx(1, "fork()");
+       }
+
+       /* return as child */
+       if (child == 0) {
+               return 1;
+       }
+
+       /* parent watches for signals and closes session */
+       sigset_t sigs;
+       struct sigaction act, oldact;
+       int status;
+
+       /* block signals */
+       sigfillset(&sigs);
+       if (sigprocmask(SIG_BLOCK, &sigs, NULL)) {
+               errx(1, "sigprocmask()");
+       }
+
+       /* setup signal handler */
+       act.sa_handler = catchsig;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       sigemptyset(&sigs);
+
+       /* unblock SIGTERM and SIGALRM to catch them */
+       if(sigaddset(&sigs, SIGTERM) ||
+                       sigaddset(&sigs, SIGALRM) ||
+                       sigaction(SIGTERM, &act, &oldact) ||
+                       sigprocmask(SIG_UNBLOCK, &sigs, NULL)) {
+               errx(1, "failed to set signal handler");
+       }
+
+       /* 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;
+
+       if (caught_signal) {
+               fprintf(stderr, "\nSession terminated, killing shell\n");
+               kill(child, SIGTERM);
+       }
+
+       /* close session */
+       ret = pam_close_session(pamh, 0);
+       if (ret != PAM_SUCCESS)
+               errx(1, "pam_close_session(): %s\n", pam_strerror(pamh, ret));
+
+       ret = pam_end(pamh, PAM_SUCCESS);
+       if (ret != PAM_SUCCESS)
+               errx(1, "pam_end(): %s\n", pam_strerror(pamh, ret));
+
+       if (caught_signal) {
+               /* 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);
+
+       return 0;
+}
index b08e93c707064954313932850f52926f2daab6db..e9ebf6364da8d6365097b9511862bcc8ec76d62a 100644 (file)
@@ -19,4 +19,8 @@
 
 #include "openbsd.h"
 
+#ifdef HAVE_PAM_APPL_H
+int doas_pam(char *name, int interactive, int nopass);
+#endif
+
 #endif /* INCLUDES_H */
diff --git a/libopenbsd/auth_userokay.c b/libopenbsd/auth_userokay.c
deleted file mode 100644 (file)
index 6a9841b..0000000
+++ /dev/null
@@ -1,112 +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/types.h>
-#include <err.h>
-#include <errno.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <security/pam_appl.h>
-
-#include "includes.h"
-
-#define PAM_SERVICE_NAME "doas"
-
-static char *
-pam_prompt(const char *msg, int echo_on, int *pam)
-{
-       char buf[PAM_MAX_RESP_SIZE];
-       int flags = RPP_REQUIRE_TTY | (echo_on ? RPP_ECHO_ON : RPP_ECHO_OFF);
-       char *ret = readpassphrase(msg, buf, sizeof(buf), flags);
-       if (!ret)
-               *pam = PAM_CONV_ERR;
-       else if (!(ret = strdup(ret)))
-               *pam = PAM_BUF_ERR;
-       explicit_bzero(buf, sizeof(buf));
-       return ret;
-}
-
-static int
-pam_conv(int nmsgs, const struct pam_message **msgs,
-               struct pam_response **rsps, __UNUSED void *ptr)
-{
-       struct pam_response *rsp;
-       int i, style;
-       int pam = PAM_SUCCESS;
-
-       if (!(rsp = calloc(nmsgs, sizeof(struct pam_response))))
-               errx(1, "couldn't malloc pam_response");
-       *rsps = rsp;
-
-       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 = pam_prompt(msgs[i]->msg,
-                                       style == PAM_PROMPT_ECHO_ON, &pam);
-                       break;
-
-               case PAM_ERROR_MSG:
-               case PAM_TEXT_INFO:
-                       if (fprintf(style == PAM_ERROR_MSG ? stderr : stdout,
-                                       "%s\n", msgs[i]->msg) < 0)
-                               pam = PAM_CONV_ERR;
-                       break;
-
-               default:
-                       errx(1, "invalid PAM msg_style %d", style);
-               }
-       }
-
-       return PAM_SUCCESS;
-}
-
-int
-auth_userokay(char *name, char *style, char *type, char *password)
-{
-       static const struct pam_conv conv = {
-               .conv = pam_conv,
-               .appdata_ptr = NULL,
-       };
-
-       int ret, auth;
-       pam_handle_t *pamh = NULL;
-
-       if (!name)
-               return 0;
-       if (style || type || password)
-               errx(1, "auth_userokay(name, NULL, NULL, NULL)!\n");
-
-       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);
-
-       auth = pam_authenticate(pamh, 0);
-
-       ret = pam_open_session(pamh, 0);
-       if (ret != PAM_SUCCESS)
-               errx(1, "pam_open_session(): %s\n", pam_strerror(pamh, ret));
-
-       ret = pam_close_session(pamh, 0);
-       if (ret != PAM_SUCCESS)
-               errx(1, "pam_close_session(): %s\n", pam_strerror(pamh, ret));
-
-       return auth == PAM_SUCCESS;
-}
index 1e5ff9776048a28d95a8f949ca4a98814acf7283..f0eec3df135b1ec03efe1621670bd2f72fe57fd2 100644 (file)
@@ -9,11 +9,6 @@
 
 /* API definitions lifted from OpenBSD src/include */
 
-/* bsd_auth.h */
-#ifndef HAVE_BSD_AUTH_H
-int auth_userokay(char *, char *, char *, char *);
-#endif /* !HAVE_BSD_AUTH_H */
-
 /* login_cap.h */
 #ifndef HAVE_LOGIN_CAP_H
 #define        LOGIN_SETGROUP          0x0001  /* Set group */