]> git.armaanb.net Git - opendoas.git/blobdiff - doas_pam.c
sync with upstream (setenv)
[opendoas.git] / doas_pam.c
index 473e1fde525829863109bf85722ffe6dee1aa051..f8b6e63f402595bf5bcd87db0138a45e17330f4f 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 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;
 }
@@ -153,6 +167,14 @@ doas_pam(char *name, int interactive, int nopass)
        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: ", name, host);
+
                /* authenticate */
                ret = pam_authenticate(pamh, 0);
                if (ret != PAM_SUCCESS) {
@@ -163,29 +185,27 @@ doas_pam(char *name, int interactive, int nopass)
                }
        }
 
+       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)
+               return 0;
+
        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));
-
+               pam_close_session(pamh, 0);
+               pam_end(pamh, PAM_ABORT);
                errx(1, "fork()");
        }
 
@@ -202,37 +222,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 */
+       sigemptyset(&sigs);
        if(sigaddset(&sigs, SIGTERM) ||
                        sigaddset(&sigs, SIGALRM) ||
                        sigaction(SIGTERM, &act, &oldact) ||
                        sigprocmask(SIG_UNBLOCK, &sigs, NULL)) {
-               errx(1, "failed to set signal handler");
+               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 +268,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 +284,5 @@ doas_pam(char *name, int interactive, int nopass)
        }
 
        exit(status);
-
        return 0;
 }