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