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