]> git.armaanb.net Git - opendoas.git/blobdiff - doas_pam.c
specify that default is deny if no rule matches
[opendoas.git] / doas_pam.c
index 5c6fb288b9e36d357ba47d44cc3b3f77bce28d2c..4f2731d5e133d12b664a63709c470b17d025a9be 100644 (file)
@@ -24,6 +24,9 @@
 #include <unistd.h>
 #include <sys/wait.h>
 #include <signal.h>
+#ifdef __linux__
+#include <limits.h>
+#endif
 
 #include <security/pam_appl.h>
 
 
 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)
 {
-       char buf[PAM_MAX_RESP_SIZE];
+       const char *prompt;
+       char *ret, 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);
+
+       /* 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;
+
+       ret = readpassphrase(prompt, 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;
 }
@@ -72,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;
 
@@ -103,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,
@@ -120,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))
@@ -146,31 +159,45 @@ 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) {
                if (!interactive)
                        errx(1, "Authorization required");
+
+               /* 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: ", 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));
-                       return 1;
+                       pam_end(pamh, ret);
+                       return 0;
                }
        }
 
-       ret = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+       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)
-               errx(1, "pam_setcred(?, PAM_ESTABLISH_CRED): %s\n",
-                               pam_strerror(pamh, ret));
+               return 0;
 
-       ret = pam_acct_mgmt(pamh, 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(): %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);
@@ -178,14 +205,8 @@ doas_pam(char *name, int interactive, int nopass)
                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));
-
+               pam_close_session(pamh, 0);
+               pam_end(pamh, PAM_ABORT);
                errx(1, "fork()");
        }
 
@@ -202,37 +223,41 @@ doas_pam(char *name, int interactive, int nopass)
        /* block signals */
        sigfillset(&sigs);
        if (sigprocmask(SIG_BLOCK, &sigs, NULL)) {
-               errx(1, "sigprocmask()");
+               warn("failed to block signals");
+               caught_signal = 1;
        }
 
        /* 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");
+       sigemptyset(&sigs);
+       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;
        }
 
-       /* 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);
+       if (!caught_signal) {
+               /* 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;
        }
-       else if (caught_signal)
-               status = caught_signal + 128;
-       else
-               status = 1;
 
        if (caught_signal) {
                fprintf(stderr, "\nSession terminated, killing shell\n");
@@ -244,9 +269,7 @@ doas_pam(char *name, int interactive, int nopass)
        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));
+       pam_end(pamh, PAM_SUCCESS);
 
        if (caught_signal) {
                /* kill child */
@@ -262,6 +285,5 @@ doas_pam(char *name, int interactive, int nopass)
        }
 
        exit(status);
-
        return 0;
 }