Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/plugins/sudoers/auth/sudo_auth.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 1999-2005, 2008-2020 Todd C. Miller <[email protected]>
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
#include <config.h>
24
25
#include <sys/types.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#if defined(HAVE_STDINT_H)
29
# include <stdint.h>
30
#elif defined(HAVE_INTTYPES_H)
31
# include <inttypes.h>
32
#endif
33
#include <string.h>
34
#include <unistd.h>
35
#include <pwd.h>
36
#include <time.h>
37
#include <signal.h>
38
39
#include <sudoers.h>
40
#include "sudo_auth.h"
41
#include <insults.h>
42
#include <timestamp.h>
43
44
static sudo_auth auth_switch[] = {
45
/* Standalone entries first */
46
#ifdef HAVE_AIXAUTH
47
AUTH_ENTRY("aixauth", FLAG_STANDALONE, sudo_aix_init, NULL, sudo_aix_verify, NULL, sudo_aix_cleanup, NULL, NULL)
48
#endif
49
#ifdef HAVE_PAM
50
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)
51
#endif
52
#ifdef HAVE_SECURID
53
AUTH_ENTRY("SecurId", FLAG_STANDALONE, sudo_securid_init, sudo_securid_setup, sudo_securid_verify, NULL, NULL, NULL, NULL)
54
#endif
55
#ifdef HAVE_SIA_SES_INIT
56
AUTH_ENTRY("sia", FLAG_STANDALONE, NULL, sudo_sia_setup, sudo_sia_verify, NULL, sudo_sia_cleanup, sudo_sia_begin_session, NULL)
57
#endif
58
#ifdef HAVE_BSD_AUTH_H
59
AUTH_ENTRY("bsdauth", FLAG_STANDALONE, bsdauth_init, NULL, bsdauth_verify, bsdauth_approval, bsdauth_cleanup, NULL, NULL)
60
#endif
61
62
/* Non-standalone entries */
63
#ifndef WITHOUT_PASSWD
64
AUTH_ENTRY("passwd", 0, sudo_passwd_init, NULL, sudo_passwd_verify, NULL, sudo_passwd_cleanup, NULL, NULL)
65
#endif
66
#if defined(HAVE_GETPRPWNAM) && !defined(WITHOUT_PASSWD)
67
AUTH_ENTRY("secureware", 0, sudo_secureware_init, NULL, sudo_secureware_verify, NULL, sudo_secureware_cleanup, NULL, NULL)
68
#endif
69
#ifdef HAVE_AFS
70
AUTH_ENTRY("afs", 0, NULL, NULL, sudo_afs_verify, NULL, NULL, NULL, NULL)
71
#endif
72
#ifdef HAVE_DCE
73
AUTH_ENTRY("dce", 0, NULL, NULL, sudo_dce_verify, NULL, NULL, NULL, NULL)
74
#endif
75
#ifdef HAVE_KERB5
76
AUTH_ENTRY("kerb5", 0, sudo_krb5_init, sudo_krb5_setup, sudo_krb5_verify, NULL, sudo_krb5_cleanup, NULL, NULL)
77
#endif
78
#ifdef HAVE_SKEY
79
AUTH_ENTRY("S/Key", 0, NULL, sudo_rfc1938_setup, sudo_rfc1938_verify, NULL, NULL, NULL, NULL)
80
#endif
81
#ifdef HAVE_OPIE
82
AUTH_ENTRY("OPIE", 0, NULL, sudo_rfc1938_setup, sudo_rfc1938_verify, NULL, NULL, NULL, NULL)
83
#endif
84
AUTH_ENTRY(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
85
};
86
87
static bool standalone;
88
89
/*
90
* Initialize sudoers authentication method(s).
91
* Returns AUTH_SUCCESS on success and AUTH_ERROR on error.
92
*/
93
int
94
sudo_auth_init(const struct sudoers_context *ctx, struct passwd *pw,
95
unsigned int mode)
96
{
97
sudo_auth *auth;
98
debug_decl(sudo_auth_init, SUDOERS_DEBUG_AUTH);
99
100
if (auth_switch[0].name == NULL)
101
debug_return_int(AUTH_SUCCESS);
102
103
/* Initialize auth methods and unconfigure the method if necessary. */
104
for (auth = auth_switch; auth->name; auth++) {
105
if (ISSET(mode, MODE_NONINTERACTIVE))
106
SET(auth->flags, FLAG_NONINTERACTIVE);
107
if (auth->init && !IS_DISABLED(auth)) {
108
/* Disable if it failed to init unless there was a fatal error. */
109
switch ((auth->init)(ctx, pw, auth)) {
110
case AUTH_SUCCESS:
111
break;
112
case AUTH_FAILURE:
113
SET(auth->flags, FLAG_DISABLED);
114
break;
115
default:
116
/* Assume error msg already printed. */
117
debug_return_int(AUTH_ERROR);
118
}
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(ctx, ctx->runas.argv,
133
N_("invalid authentication methods"));
134
log_warningx(ctx, SLOG_SEND_MAIL,
135
N_("Invalid authentication methods compiled into sudo! "
136
"You may not mix standalone and non-standalone authentication."));
137
debug_return_int(AUTH_ERROR);
138
}
139
if (!found) {
140
/* Found first standalone method. */
141
found = true;
142
continue;
143
}
144
/* Disable other standalone methods. */
145
SET(auth->flags, FLAG_DISABLED);
146
}
147
}
148
149
/* Set FLAG_ONEANDONLY if there is only one auth method. */
150
for (auth = auth_switch; auth->name; auth++) {
151
/* Find first enabled auth method. */
152
if (!IS_DISABLED(auth)) {
153
sudo_auth *first = auth;
154
/* Check for others. */
155
for (; auth->name; auth++) {
156
if (!IS_DISABLED(auth))
157
break;
158
}
159
if (auth->name == NULL)
160
SET(first->flags, FLAG_ONEANDONLY);
161
break;
162
}
163
}
164
165
debug_return_int(AUTH_SUCCESS);
166
}
167
168
/*
169
* Call all authentication approval methods, if any.
170
* Returns AUTH_SUCCESS, AUTH_FAILURE or AUTH_ERROR.
171
*/
172
int
173
sudo_auth_approval(const struct sudoers_context *ctx, struct passwd *pw,
174
unsigned int validated, bool exempt)
175
{
176
int ret = AUTH_SUCCESS;
177
sudo_auth *auth;
178
debug_decl(sudo_auth_approval, SUDOERS_DEBUG_AUTH);
179
180
/* Call approval routines. */
181
for (auth = auth_switch; auth->name; auth++) {
182
if (auth->approval && !IS_DISABLED(auth)) {
183
ret = (auth->approval)(ctx, pw, auth, exempt);
184
if (ret != AUTH_SUCCESS) {
185
/* Assume error msg already printed. */
186
log_auth_failure(ctx, validated, 0);
187
break;
188
}
189
}
190
}
191
debug_return_int(ret);
192
}
193
194
/*
195
* Cleanup all authentication methods.
196
* Returns AUTH_SUCCESS on success and AUTH_ERROR on error.
197
*/
198
int
199
sudo_auth_cleanup(const struct sudoers_context *ctx, struct passwd *pw,
200
bool force)
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)(ctx, pw, auth, force);
209
if (status != AUTH_SUCCESS) {
210
/* Assume error msg already printed. */
211
debug_return_int(AUTH_ERROR);
212
}
213
}
214
}
215
debug_return_int(AUTH_SUCCESS);
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|SUDO_CONV_PREFER_TTY, "%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
* Called when getpass is suspended so we can drop the lock.
244
*/
245
static int
246
getpass_suspend(int signo, void *vclosure)
247
{
248
struct getpass_closure *closure = vclosure;
249
250
timestamp_close(closure->cookie);
251
closure->cookie = NULL;
252
return 0;
253
}
254
255
/*
256
* Called when getpass is resumed so we can reacquire the lock.
257
*/
258
static int
259
getpass_resume(int signo, void *vclosure)
260
{
261
struct getpass_closure *closure = vclosure;
262
263
closure->cookie = timestamp_open(closure->ctx);
264
if (closure->cookie == NULL)
265
return -1;
266
if (!timestamp_lock(closure->cookie, closure->auth_pw))
267
return -1;
268
return 0;
269
}
270
271
/*
272
* Verify the specified user.
273
* Returns AUTH_SUCCESS, AUTH_FAILURE or AUTH_ERROR.
274
*/
275
int
276
verify_user(const struct sudoers_context *ctx, struct passwd *pw, char *prompt,
277
unsigned int validated, struct sudo_conv_callback *callback)
278
{
279
struct sigaction sa, saved_sigtstp;
280
int ret = AUTH_FAILURE;
281
unsigned int ntries;
282
sigset_t mask, omask;
283
sudo_auth *auth;
284
debug_decl(verify_user, SUDOERS_DEBUG_AUTH);
285
286
/* Make sure we have at least one auth method. */
287
if (auth_switch[0].name == NULL) {
288
audit_failure(ctx, ctx->runas.argv, N_("no authentication methods"));
289
log_warningx(ctx, SLOG_SEND_MAIL,
290
N_("There are no authentication methods compiled into sudo! "
291
"If you want to turn off authentication, use the "
292
"--disable-authentication configure option."));
293
debug_return_int(AUTH_ERROR);
294
}
295
296
/* Enable suspend during password entry. */
297
callback->on_suspend = getpass_suspend;
298
callback->on_resume = getpass_resume;
299
sigemptyset(&sa.sa_mask);
300
sa.sa_flags = SA_RESTART;
301
sa.sa_handler = SIG_DFL;
302
(void) sigaction(SIGTSTP, &sa, &saved_sigtstp);
303
304
/*
305
* We treat authentication as a critical section and block
306
* keyboard-generated signals such as SIGINT and SIGQUIT
307
* which might otherwise interrupt a sleep(3).
308
* They are temporarily unblocked by auth_getpass().
309
*/
310
sigemptyset(&mask);
311
sigaddset(&mask, SIGINT);
312
sigaddset(&mask, SIGQUIT);
313
(void) sigprocmask(SIG_BLOCK, &mask, &omask);
314
315
for (ntries = 0; ntries < def_passwd_tries; ntries++) {
316
int num_methods = 0;
317
char *pass = NULL;
318
319
/* If user attempted to interrupt password verify, quit now. */
320
if (user_interrupted())
321
goto done;
322
323
if (ntries != 0)
324
pass_warn();
325
326
/* Do any per-method setup and unconfigure the method if needed */
327
for (auth = auth_switch; auth->name; auth++) {
328
if (IS_DISABLED(auth))
329
continue;
330
num_methods++;
331
if (auth->setup != NULL) {
332
switch ((auth->setup)(ctx, pw, &prompt, auth)) {
333
case AUTH_SUCCESS:
334
if (user_interrupted())
335
goto done; /* assume error msg already printed */
336
break;
337
case AUTH_FAILURE:
338
SET(auth->flags, FLAG_DISABLED);
339
break;
340
case AUTH_NONINTERACTIVE:
341
/* Non-interactive mode, cannot prompt user. */
342
goto done;
343
default:
344
ret = AUTH_ERROR;
345
goto done;
346
}
347
}
348
}
349
if (num_methods == 0) {
350
audit_failure(ctx, ctx->runas.argv,
351
N_("no authentication methods"));
352
log_warningx(ctx, SLOG_SEND_MAIL,
353
N_("Unable to initialize authentication methods."));
354
debug_return_int(AUTH_ERROR);
355
}
356
357
/* Get the password unless the auth function will do it for us */
358
if (!standalone) {
359
if (IS_NONINTERACTIVE(&auth_switch[0])) {
360
ret = AUTH_NONINTERACTIVE;
361
goto done;
362
}
363
pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
364
if (pass == NULL)
365
break;
366
}
367
368
/* Call authentication functions. */
369
for (auth = auth_switch; auth->name; auth++) {
370
if (IS_DISABLED(auth))
371
continue;
372
373
ret = auth->status = (auth->verify)(ctx, pw,
374
standalone ? prompt : pass, auth, callback);
375
if (ret != AUTH_FAILURE)
376
break;
377
}
378
if (pass != NULL)
379
freezero(pass, strlen(pass));
380
381
if (ret != AUTH_FAILURE)
382
goto done;
383
}
384
385
done:
386
/* Restore signal handlers and signal mask. */
387
(void) sigaction(SIGTSTP, &saved_sigtstp, NULL);
388
(void) sigprocmask(SIG_SETMASK, &omask, NULL);
389
390
switch (ret) {
391
case AUTH_SUCCESS:
392
break;
393
case AUTH_INTR:
394
ret = AUTH_FAILURE;
395
FALLTHROUGH;
396
case AUTH_FAILURE:
397
if (ntries != 0)
398
SET(validated, FLAG_BAD_PASSWORD);
399
log_auth_failure(ctx, validated, ntries);
400
break;
401
case AUTH_NONINTERACTIVE:
402
SET(validated, FLAG_NO_USER_INPUT);
403
FALLTHROUGH;
404
default:
405
log_auth_failure(ctx, validated, 0);
406
ret = AUTH_ERROR;
407
break;
408
}
409
410
debug_return_int(ret);
411
}
412
413
/*
414
* Call authentication method begin session hooks.
415
* Returns true on success, false on failure and -1 on error.
416
*/
417
int
418
sudo_auth_begin_session(const struct sudoers_context *ctx, struct passwd *pw,
419
char **user_env[])
420
{
421
sudo_auth *auth;
422
int ret = true;
423
debug_decl(sudo_auth_begin_session, SUDOERS_DEBUG_AUTH);
424
425
for (auth = auth_switch; auth->name; auth++) {
426
if (auth->begin_session && !IS_DISABLED(auth)) {
427
int status = (auth->begin_session)(ctx, pw, user_env, auth);
428
switch (status) {
429
case AUTH_SUCCESS:
430
break;
431
case AUTH_FAILURE:
432
ret = false;
433
break;
434
default:
435
/* Assume error msg already printed. */
436
ret = -1;
437
break;
438
}
439
}
440
}
441
debug_return_int(ret);
442
}
443
444
bool
445
sudo_auth_needs_end_session(void)
446
{
447
sudo_auth *auth;
448
bool needed = false;
449
debug_decl(sudo_auth_needs_end_session, SUDOERS_DEBUG_AUTH);
450
451
for (auth = auth_switch; auth->name; auth++) {
452
if (auth->end_session && !IS_DISABLED(auth)) {
453
needed = true;
454
break;
455
}
456
}
457
debug_return_bool(needed);
458
}
459
460
/*
461
* Call authentication method end session hooks.
462
* Returns true on success, false on failure and -1 on error.
463
*/
464
int
465
sudo_auth_end_session(void)
466
{
467
sudo_auth *auth;
468
int ret = true;
469
int status;
470
debug_decl(sudo_auth_end_session, SUDOERS_DEBUG_AUTH);
471
472
for (auth = auth_switch; auth->name; auth++) {
473
if (auth->end_session && !IS_DISABLED(auth)) {
474
status = (auth->end_session)(auth);
475
switch (status) {
476
case AUTH_SUCCESS:
477
break;
478
case AUTH_FAILURE:
479
ret = false;
480
break;
481
default:
482
/* Assume error msg already printed. */
483
ret = -1;
484
break;
485
}
486
}
487
}
488
debug_return_int(ret);
489
}
490
491
/*
492
* Prompts the user for a password using the conversation function.
493
* Returns the plaintext password or NULL.
494
* The user is responsible for freeing the returned value.
495
*/
496
char *
497
auth_getpass(const char *prompt, int type, struct sudo_conv_callback *callback)
498
{
499
struct sudo_conv_message msg;
500
struct sudo_conv_reply repl;
501
sigset_t mask, omask;
502
debug_decl(auth_getpass, SUDOERS_DEBUG_AUTH);
503
504
/* Display lecture if needed and we haven't already done so. */
505
display_lecture(callback);
506
507
/* Mask user input if pwfeedback set and echo is off. */
508
if (type == SUDO_CONV_PROMPT_ECHO_OFF && def_pwfeedback)
509
type = SUDO_CONV_PROMPT_MASK;
510
511
/* If visiblepw set, do not error out if there is no tty. */
512
if (def_visiblepw)
513
type |= SUDO_CONV_PROMPT_ECHO_OK;
514
515
/* Unblock SIGINT and SIGQUIT during password entry. */
516
/* XXX - do in tgetpass() itself instead? */
517
sigemptyset(&mask);
518
sigaddset(&mask, SIGINT);
519
sigaddset(&mask, SIGQUIT);
520
(void) sigprocmask(SIG_UNBLOCK, &mask, &omask);
521
522
/* Call conversation function. */
523
memset(&msg, 0, sizeof(msg));
524
msg.msg_type = type;
525
msg.timeout = (int)def_passwd_timeout.tv_sec;
526
msg.msg = prompt;
527
memset(&repl, 0, sizeof(repl));
528
sudo_conv(1, &msg, &repl, callback);
529
/* XXX - check for ENOTTY? */
530
531
/* Restore previous signal mask. */
532
(void) sigprocmask(SIG_SETMASK, &omask, NULL);
533
534
debug_return_str_masked(repl.reply);
535
}
536
537
void
538
dump_auth_methods(void)
539
{
540
sudo_auth *auth;
541
debug_decl(dump_auth_methods, SUDOERS_DEBUG_AUTH);
542
543
sudo_printf(SUDO_CONV_INFO_MSG, _("Authentication methods:"));
544
for (auth = auth_switch; auth->name; auth++)
545
sudo_printf(SUDO_CONV_INFO_MSG, " '%s'", auth->name);
546
sudo_printf(SUDO_CONV_INFO_MSG, "\n");
547
548
debug_return;
549
}
550
551