Attempt to create .deps directory every time we build objects.
[doas.git] / pam.c
blobd8a7a3e29fa64ca7323889547b0c1b51746d9465
1 /*
2 * Copyright (c) 2015 Nathan Holstein <nathan.holstein@gmail.com>
3 * Copyright (c) 2021-2022 Sergey Sushilin <sergeysushilin@protonmail.com>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <sys/types.h>
19 #include <sys/wait.h>
21 #include <err.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <pwd.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <unistd.h>
32 #include <security/pam_appl.h>
34 #include "compat.h"
35 #include "readpassphrase.h"
36 #include "wrappers.h"
38 #ifndef HOST_NAME_MAX
39 # define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
40 #endif
42 #define PAM_SERVICE_NAME "doas"
44 static pam_handle_t *pamh = NULL;
46 static char const *doas_prompt;
48 static __nonnull((1, 3)) char *pamprompt(char const *restrict msg, bool echo_on, int *restrict ret)
50 char const *prompt;
51 char *pass, buf[PAM_MAX_RESP_SIZE];
52 int flags = RPP_REQUIRE_TTY | (echo_on ? RPP_ECHO_ON : RPP_ECHO_OFF);
54 /* Overwrite default prompt if it matches "Password:[ ]". */
55 if (strneq(msg, "Password:", 9) && (msg[9] == '\0' || (msg[9] == ' ' && msg[10] == '\0')))
56 prompt = doas_prompt;
57 else
58 prompt = msg;
60 pass = readpassphrase(prompt, buf, sizeof(buf), flags);
62 if (pass == NULL)
63 *ret = PAM_CONV_ERR;
64 else {
65 pass = xstrdup(pass);
66 *ret = PAM_SUCCESS;
69 explicit_bzero(buf, sizeof(buf));
70 return pass;
73 static int pamconv(int nmsgs, struct pam_message const **restrict msgs, struct pam_response **restrict rsps, void *restrict ptr __unused)
75 int i, style;
76 struct pam_response *rsp = xcalloc(nmsgs, sizeof(*rsp));
78 for (i = 0; i < nmsgs; i++) {
79 switch (style = msgs[i]->msg_style) {
80 case PAM_PROMPT_ECHO_OFF:
81 case PAM_PROMPT_ECHO_ON: {
82 int ret;
84 rsp[i].resp = pamprompt(msgs[i]->msg,
85 style == PAM_PROMPT_ECHO_ON,
86 &ret);
88 if (ret != PAM_SUCCESS)
89 goto lfail;
91 break;
93 case PAM_ERROR_MSG:
94 case PAM_TEXT_INFO: {
95 int fd = (style != PAM_ERROR_MSG ? STDOUT_FILENO : STDERR_FILENO);
96 size_t msglen = strlen(msgs[i]->msg);
98 if (full_write(fd, msgs[i]->msg, msglen) != msglen)
99 goto lfail;
101 break;
103 default:
104 errx(EXIT_FAILURE, "invalid PAM msg_style %d", style);
108 *rsps = rsp;
109 rsp = NULL;
110 return PAM_SUCCESS;
112 lfail:
113 /* Overwrite and free response buffers. */
114 for (i = 0; i < nmsgs; i++) {
115 if (rsp[i].resp == NULL)
116 continue;
118 switch (msgs[i]->msg_style) {
119 case PAM_PROMPT_ECHO_OFF:
120 case PAM_PROMPT_ECHO_ON:
121 explicit_bzero(rsp[i].resp, strlen(rsp[i].resp));
122 xfree(rsp[i].resp);
125 rsp[i].resp = NULL;
128 xfree(rsp);
129 return PAM_CONV_ERR;
132 static void pamcleanup(int ret, bool sess, bool cred)
134 if (sess) {
135 ret = pam_close_session(pamh, 0);
137 if (ret != PAM_SUCCESS)
138 errx(EXIT_FAILURE, "pam_close_session: %s", pam_strerror(pamh, ret));
141 if (cred) {
142 ret = pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT);
144 if (ret != PAM_SUCCESS)
145 warn("pam_setcred(?, PAM_DELETE_CRED | PAM_SILENT): %s", pam_strerror(pamh, ret));
148 pam_end(pamh, ret);
151 void pamauth(char const *restrict prompt, char const *target_name, char const *original_name)
153 static struct pam_conv const conv = { .conv = pamconv, .appdata_ptr = NULL };
154 char const *ttydev;
155 int ret;
156 bool sess = false, cred = false;
158 doas_prompt = prompt;
160 if (target_name == NULL || original_name == NULL)
161 errx(EXIT_FAILURE, "Authentication failed");
163 ret = pam_start(PAM_SERVICE_NAME, original_name, &conv, &pamh);
165 if (ret != PAM_SUCCESS)
166 errx(EXIT_FAILURE, "pam_start(\"%s\", \"%s\", ?, ?): failed", PAM_SERVICE_NAME, original_name);
168 ret = pam_set_item(pamh, PAM_RUSER, original_name);
170 if (ret != PAM_SUCCESS)
171 warn("pam_set_item(?, PAM_RUSER, \"%s\"): %s", pam_strerror(pamh, ret), original_name);
173 if (isatty(STDIN_FILENO) && (ttydev = ttyname(STDIN_FILENO)) != NULL) {
174 if (strneq(ttydev, "/dev/", 5))
175 ttydev += 5;
177 ret = pam_set_item(pamh, PAM_TTY, ttydev);
179 if (ret != PAM_SUCCESS)
180 warn("pam_set_item(?, PAM_TTY, \"%s\"): %s", ttydev, pam_strerror(pamh, ret));
183 /* Authenticate. */
184 ret = pam_authenticate(pamh, 0);
186 if (ret != PAM_SUCCESS) {
187 pamcleanup(ret, sess, cred);
188 syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", original_name);
189 errx(EXIT_FAILURE, "Authentication failed");
192 ret = pam_acct_mgmt(pamh, 0);
194 if (ret == PAM_NEW_AUTHTOK_REQD)
195 ret = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
197 /* Account not vaild or changing the auth token failed. */
198 if (ret != PAM_SUCCESS) {
199 pamcleanup(ret, sess, cred);
200 syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", original_name);
201 errx(EXIT_FAILURE, "Authentication failed");
204 /* Set PAM_USER to the user we want to be. */
205 ret = pam_set_item(pamh, PAM_USER, target_name);
207 if (ret != PAM_SUCCESS)
208 warn("pam_set_item(?, PAM_USER, \"%s\"): %s", target_name, pam_strerror(pamh, ret));
210 ret = pam_setcred(pamh, PAM_REINITIALIZE_CRED);
212 if (ret != PAM_SUCCESS)
213 warn("pam_setcred(?, PAM_REINITIALIZE_CRED): %s", pam_strerror(pamh, ret));
214 else
215 cred = true;
217 /* Open session. */
218 ret = pam_open_session(pamh, 0);
220 if (ret != PAM_SUCCESS)
221 errx(EXIT_FAILURE, "pam_open_session: %s", pam_strerror(pamh, ret));
223 sess = true;
224 pamcleanup(PAM_SUCCESS, sess, cred);