1 /*
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 1999-2005, 2008-2020 Todd C. Miller <Todd.Miller@sudo.ws>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * Sponsored in part by the Defense Advanced Research Projects
19 * Agency (DARPA) and Air Force Research Laboratory, Air Force
20 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
21 */
22
23 /*
24 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
25 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
26 */
27
28 #include <config.h>
29
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #if defined(HAVE_STDINT_H)
34 # include <stdint.h>
35 #elif defined(HAVE_INTTYPES_H)
36 # include <inttypes.h>
37 #endif
38 #include <string.h>
39 #include <unistd.h>
40 #include <pwd.h>
41 #include <time.h>
42 #include <signal.h>
43
44 #include "sudoers.h"
45 #include "sudo_auth.h"
46 #include "insults.h"
47
48 static sudo_auth auth_switch[] = {
49 /* Standalone entries first */
50 #ifdef HAVE_AIXAUTH
51 AUTH_ENTRY("aixauth", FLAG_STANDALONE, sudo_aix_init, NULL, sudo_aix_verify, NULL, sudo_aix_cleanup, NULL, NULL)
52 #endif
53 #ifdef HAVE_PAM
54 AUTH_ENTRY("pam", FLAG_STANDALONE, sudo_pam_init, NULL, sudo_pam_verify, sudo_pam_approval, sudo_pam_cleanup, sudo_pam_begin_session, sudo_pam_end_session)
55 #endif
56 #ifdef HAVE_SECURID
57 AUTH_ENTRY("SecurId", FLAG_STANDALONE, sudo_securid_init, sudo_securid_setup, sudo_securid_verify, NULL, NULL, NULL, NULL)
58 #endif
59 #ifdef HAVE_SIA_SES_INIT
60 AUTH_ENTRY("sia", FLAG_STANDALONE, NULL, sudo_sia_setup, sudo_sia_verify, NULL, sudo_sia_cleanup, sudo_sia_begin_session, NULL)
61 #endif
62 #ifdef HAVE_FWTK
63 AUTH_ENTRY("fwtk", FLAG_STANDALONE, sudo_fwtk_init, NULL, sudo_fwtk_verify, NULL, sudo_fwtk_cleanup, NULL, NULL)
64 #endif
65 #ifdef HAVE_BSD_AUTH_H
66 AUTH_ENTRY("bsdauth", FLAG_STANDALONE, bsdauth_init, NULL, bsdauth_verify, bsdauth_approval, bsdauth_cleanup, NULL, NULL)
67 #endif
68
69 /* Non-standalone entries */
70 #ifndef WITHOUT_PASSWD
71 AUTH_ENTRY("passwd", 0, sudo_passwd_init, NULL, sudo_passwd_verify, NULL, sudo_passwd_cleanup, NULL, NULL)
72 #endif
73 #if defined(HAVE_GETPRPWNAM) && !defined(WITHOUT_PASSWD)
74 AUTH_ENTRY("secureware", 0, sudo_secureware_init, NULL, sudo_secureware_verify, NULL, sudo_secureware_cleanup, NULL, NULL)
75 #endif
76 #ifdef HAVE_AFS
77 AUTH_ENTRY("afs", 0, NULL, NULL, sudo_afs_verify, NULL, NULL, NULL, NULL)
78 #endif
79 #ifdef HAVE_DCE
80 AUTH_ENTRY("dce", 0, NULL, NULL, sudo_dce_verify, NULL, NULL, NULL, NULL)
81 #endif
82 #ifdef HAVE_KERB5
83 AUTH_ENTRY("kerb5", 0, sudo_krb5_init, sudo_krb5_setup, sudo_krb5_verify, NULL, sudo_krb5_cleanup, NULL, NULL)
84 #endif
85 #ifdef HAVE_SKEY
86 AUTH_ENTRY("S/Key", 0, NULL, sudo_rfc1938_setup, sudo_rfc1938_verify, NULL, NULL, NULL, NULL)
87 #endif
88 #ifdef HAVE_OPIE
89 AUTH_ENTRY("OPIE", 0, NULL, sudo_rfc1938_setup, sudo_rfc1938_verify, NULL, NULL, NULL, NULL)
90 #endif
91 AUTH_ENTRY(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
92 };
93
94 static bool standalone;
95
96 /*
97 * Initialize sudoers authentication method(s).
98 * Returns 0 on success and -1 on error.
99 */
100 int
sudo_auth_init(struct passwd * pw)101 sudo_auth_init(struct passwd *pw)
102 {
103 sudo_auth *auth;
104 int status = AUTH_SUCCESS;
105 debug_decl(sudo_auth_init, SUDOERS_DEBUG_AUTH);
106
107 if (auth_switch[0].name == NULL)
108 debug_return_int(0);
109
110 /* Initialize auth methods and unconfigure the method if necessary. */
111 for (auth = auth_switch; auth->name; auth++) {
112 if (auth->init && !IS_DISABLED(auth)) {
113 /* Disable if it failed to init unless there was a fatal error. */
114 status = (auth->init)(pw, auth);
115 if (status == AUTH_FAILURE)
116 SET(auth->flags, FLAG_DISABLED);
117 else if (status == AUTH_FATAL)
118 break; /* assume error msg already printed */
119 }
120 }
121
122 /*
123 * Make sure we haven't mixed standalone and shared auth methods.
124 * If there are multiple standalone methods, only use the first one.
125 */
126 if ((standalone = IS_STANDALONE(&auth_switch[0]))) {
127 bool found = false;
128 for (auth = auth_switch; auth->name; auth++) {
129 if (IS_DISABLED(auth))
130 continue;
131 if (!IS_STANDALONE(auth)) {
132 audit_failure(NewArgv, N_("invalid authentication methods"));
133 log_warningx(SLOG_SEND_MAIL,
134 N_("Invalid authentication methods compiled into sudo! "
135 "You may not mix standalone and non-standalone authentication."));
136 debug_return_int(-1);
137 }
138 if (!found) {
139 /* Found first standalone method. */
140 found = true;
141 continue;
142 }
143 /* Disable other standalone methods. */
144 SET(auth->flags, FLAG_DISABLED);
145 }
146 }
147
148 /* Set FLAG_ONEANDONLY if there is only one auth method. */
149 for (auth = auth_switch; auth->name; auth++) {
150 /* Find first enabled auth method. */
151 if (!IS_DISABLED(auth)) {
152 sudo_auth *first = auth;
153 /* Check for others. */
154 for (; auth->name; auth++) {
155 if (!IS_DISABLED(auth))
156 break;
157 }
158 if (auth->name == NULL)
159 SET(first->flags, FLAG_ONEANDONLY);
160 break;
161 }
162 }
163
164 debug_return_int(status == AUTH_FATAL ? -1 : 0);
165 }
166
167 /*
168 * Cleanup all authentication approval methods.
169 * Returns true on success, false on failure and -1 on error.
170 */
171 int
sudo_auth_approval(struct passwd * pw,int validated,bool exempt)172 sudo_auth_approval(struct passwd *pw, int validated, bool exempt)
173 {
174 sudo_auth *auth;
175 debug_decl(sudo_auth_approval, SUDOERS_DEBUG_AUTH);
176
177 /* Call approval routines. */
178 for (auth = auth_switch; auth->name; auth++) {
179 if (auth->approval && !IS_DISABLED(auth)) {
180 int status = (auth->approval)(pw, auth, exempt);
181 if (status != AUTH_SUCCESS) {
182 /* Assume error msg already printed. */
183 log_auth_failure(validated, 0);
184 debug_return_int(status == AUTH_FAILURE ? false : -1);
185 }
186 }
187 }
188 debug_return_int(true);
189 }
190
191 /*
192 * Cleanup all authentication methods.
193 * Returns 0 on success and -1 on error.
194 */
195 int
sudo_auth_cleanup(struct passwd * pw,bool force)196 sudo_auth_cleanup(struct passwd *pw, bool force)
197 {
198 sudo_auth *auth;
199 debug_decl(sudo_auth_cleanup, SUDOERS_DEBUG_AUTH);
200
201 /* Call cleanup routines. */
202 for (auth = auth_switch; auth->name; auth++) {
203 if (auth->cleanup && !IS_DISABLED(auth)) {
204 int status = (auth->cleanup)(pw, auth, force);
205 if (status == AUTH_FATAL) {
206 /* Assume error msg already printed. */
207 debug_return_int(-1);
208 }
209 }
210 }
211 debug_return_int(0);
212 }
213
214 static void
pass_warn(void)215 pass_warn(void)
216 {
217 const char *warning = def_badpass_message;
218 debug_decl(pass_warn, SUDOERS_DEBUG_AUTH);
219
220 #ifdef INSULT
221 if (def_insults)
222 warning = INSULT;
223 #endif
224 sudo_printf(SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY, "%s\n", warning);
225
226 debug_return;
227 }
228
229 static bool
user_interrupted(void)230 user_interrupted(void)
231 {
232 sigset_t mask;
233
234 return (sigpending(&mask) == 0 &&
235 (sigismember(&mask, SIGINT) || sigismember(&mask, SIGQUIT)));
236 }
237
238 /*
239 * Verify the specified user.
240 * Returns true if verified, false if not or -1 on error.
241 */
242 int
verify_user(struct passwd * pw,char * prompt,int validated,struct sudo_conv_callback * callback)243 verify_user(struct passwd *pw, char *prompt, int validated,
244 struct sudo_conv_callback *callback)
245 {
246 unsigned int ntries;
247 int ret, status, success = AUTH_FAILURE;
248 sudo_auth *auth;
249 sigset_t mask, omask;
250 struct sigaction sa, saved_sigtstp;
251 debug_decl(verify_user, SUDOERS_DEBUG_AUTH);
252
253 /* Make sure we have at least one auth method. */
254 if (auth_switch[0].name == NULL) {
255 audit_failure(NewArgv, N_("no authentication methods"));
256 log_warningx(SLOG_SEND_MAIL,
257 N_("There are no authentication methods compiled into sudo! "
258 "If you want to turn off authentication, use the "
259 "--disable-authentication configure option."));
260 debug_return_int(-1);
261 }
262
263 /* Enable suspend during password entry. */
264 sigemptyset(&sa.sa_mask);
265 sa.sa_flags = SA_RESTART;
266 sa.sa_handler = SIG_DFL;
267 (void) sigaction(SIGTSTP, &sa, &saved_sigtstp);
268
269 /*
270 * We treat authentication as a critical section and block
271 * keyboard-generated signals such as SIGINT and SIGQUIT
272 * which might otherwise interrupt a sleep(3).
273 * They are temporarily unblocked by auth_getpass().
274 */
275 sigemptyset(&mask);
276 sigaddset(&mask, SIGINT);
277 sigaddset(&mask, SIGQUIT);
278 (void) sigprocmask(SIG_BLOCK, &mask, &omask);
279
280 for (ntries = 0; ntries < def_passwd_tries; ntries++) {
281 int num_methods = 0;
282 char *pass = NULL;
283
284 /* If user attempted to interrupt password verify, quit now. */
285 if (user_interrupted())
286 goto done;
287
288 if (ntries != 0)
289 pass_warn();
290
291 /* Do any per-method setup and unconfigure the method if needed */
292 for (auth = auth_switch; auth->name; auth++) {
293 if (IS_DISABLED(auth))
294 continue;
295 num_methods++;
296 if (auth->setup != NULL) {
297 status = (auth->setup)(pw, &prompt, auth);
298 if (status == AUTH_FAILURE)
299 SET(auth->flags, FLAG_DISABLED);
300 else if (status == AUTH_FATAL || user_interrupted())
301 goto done; /* assume error msg already printed */
302 }
303 }
304 if (num_methods == 0) {
305 audit_failure(NewArgv, N_("no authentication methods"));
306 log_warningx(SLOG_SEND_MAIL,
307 N_("Unable to initialize authentication methods."));
308 debug_return_int(-1);
309 }
310
311 /* Get the password unless the auth function will do it for us */
312 if (!standalone) {
313 pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
314 if (pass == NULL)
315 break;
316 }
317
318 /* Call authentication functions. */
319 for (auth = auth_switch; auth->name; auth++) {
320 if (IS_DISABLED(auth))
321 continue;
322
323 success = auth->status =
324 (auth->verify)(pw, standalone ? prompt : pass, auth, callback);
325 if (success != AUTH_FAILURE)
326 break;
327 }
328 if (pass != NULL)
329 freezero(pass, strlen(pass));
330
331 if (success != AUTH_FAILURE)
332 goto done;
333 }
334
335 done:
336 /* Restore signal handlers and signal mask. */
337 (void) sigaction(SIGTSTP, &saved_sigtstp, NULL);
338 (void) sigprocmask(SIG_SETMASK, &omask, NULL);
339
340 switch (success) {
341 case AUTH_SUCCESS:
342 ret = true;
343 break;
344 case AUTH_INTR:
345 case AUTH_FAILURE:
346 if (ntries != 0)
347 validated |= FLAG_BAD_PASSWORD;
348 log_auth_failure(validated, ntries);
349 ret = false;
350 break;
351 case AUTH_FATAL:
352 default:
353 log_auth_failure(validated, 0);
354 ret = -1;
355 break;
356 }
357
358 debug_return_int(ret);
359 }
360
361 /*
362 * Call authentication method begin session hooks.
363 * Returns 1 on success and -1 on error.
364 */
365 int
sudo_auth_begin_session(struct passwd * pw,char ** user_env[])366 sudo_auth_begin_session(struct passwd *pw, char **user_env[])
367 {
368 sudo_auth *auth;
369 debug_decl(sudo_auth_begin_session, SUDOERS_DEBUG_AUTH);
370
371 for (auth = auth_switch; auth->name; auth++) {
372 if (auth->begin_session && !IS_DISABLED(auth)) {
373 int status = (auth->begin_session)(pw, user_env, auth);
374 if (status != AUTH_SUCCESS) {
375 /* Assume error msg already printed. */
376 debug_return_int(-1);
377 }
378 }
379 }
380 debug_return_int(1);
381 }
382
383 bool
sudo_auth_needs_end_session(void)384 sudo_auth_needs_end_session(void)
385 {
386 sudo_auth *auth;
387 bool needed = false;
388 debug_decl(sudo_auth_needs_end_session, SUDOERS_DEBUG_AUTH);
389
390 for (auth = auth_switch; auth->name; auth++) {
391 if (auth->end_session && !IS_DISABLED(auth)) {
392 needed = true;
393 break;
394 }
395 }
396 debug_return_bool(needed);
397 }
398
399 /*
400 * Call authentication method end session hooks.
401 * Returns 1 on success and -1 on error.
402 */
403 int
sudo_auth_end_session(struct passwd * pw)404 sudo_auth_end_session(struct passwd *pw)
405 {
406 sudo_auth *auth;
407 int status;
408 debug_decl(sudo_auth_end_session, SUDOERS_DEBUG_AUTH);
409
410 for (auth = auth_switch; auth->name; auth++) {
411 if (auth->end_session && !IS_DISABLED(auth)) {
412 status = (auth->end_session)(pw, auth);
413 if (status == AUTH_FATAL) {
414 /* Assume error msg already printed. */
415 debug_return_int(-1);
416 }
417 }
418 }
419 debug_return_int(1);
420 }
421
422 /*
423 * Prompts the user for a password using the conversation function.
424 * Returns the plaintext password or NULL.
425 * The user is responsible for freeing the returned value.
426 */
427 char *
auth_getpass(const char * prompt,int type,struct sudo_conv_callback * callback)428 auth_getpass(const char *prompt, int type, struct sudo_conv_callback *callback)
429 {
430 struct sudo_conv_message msg;
431 struct sudo_conv_reply repl;
432 sigset_t mask, omask;
433 debug_decl(auth_getpass, SUDOERS_DEBUG_AUTH);
434
435 /* Mask user input if pwfeedback set and echo is off. */
436 if (type == SUDO_CONV_PROMPT_ECHO_OFF && def_pwfeedback)
437 type = SUDO_CONV_PROMPT_MASK;
438
439 /* If visiblepw set, do not error out if there is no tty. */
440 if (def_visiblepw)
441 type |= SUDO_CONV_PROMPT_ECHO_OK;
442
443 /* Unblock SIGINT and SIGQUIT during password entry. */
444 /* XXX - do in tgetpass() itself instead? */
445 sigemptyset(&mask);
446 sigaddset(&mask, SIGINT);
447 sigaddset(&mask, SIGQUIT);
448 (void) sigprocmask(SIG_UNBLOCK, &mask, &omask);
449
450 /* Call conversation function. */
451 memset(&msg, 0, sizeof(msg));
452 msg.msg_type = type;
453 msg.timeout = def_passwd_timeout.tv_sec;
454 msg.msg = prompt;
455 memset(&repl, 0, sizeof(repl));
456 sudo_conv(1, &msg, &repl, callback);
457 /* XXX - check for ENOTTY? */
458
459 /* Restore previous signal mask. */
460 (void) sigprocmask(SIG_SETMASK, &omask, NULL);
461
462 debug_return_str_masked(repl.reply);
463 }
464
465 void
dump_auth_methods(void)466 dump_auth_methods(void)
467 {
468 sudo_auth *auth;
469 debug_decl(dump_auth_methods, SUDOERS_DEBUG_AUTH);
470
471 sudo_printf(SUDO_CONV_INFO_MSG, _("Authentication methods:"));
472 for (auth = auth_switch; auth->name; auth++)
473 sudo_printf(SUDO_CONV_INFO_MSG, " '%s'", auth->name);
474 sudo_printf(SUDO_CONV_INFO_MSG, "\n");
475
476 debug_return;
477 }
478