1/*
2 * Copyright (c) 1999-2005, 2008-2010 Todd C. Miller <Todd.Miller@courtesan.com>
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#include <config.h>
22
23#include <sys/types.h>
24#include <sys/param.h>
25#include <stdio.h>
26#ifdef STDC_HEADERS
27# include <stdlib.h>
28# include <stddef.h>
29#else
30# ifdef HAVE_STDLIB_H
31#  include <stdlib.h>
32# endif
33#endif /* STDC_HEADERS */
34#ifdef HAVE_STRING_H
35# include <string.h>
36#endif /* HAVE_STRING_H */
37#ifdef HAVE_STRINGS_H
38# include <strings.h>
39#endif /* HAVE_STRINGS_H */
40#ifdef HAVE_UNISTD_H
41# include <unistd.h>
42#endif /* HAVE_UNISTD_H */
43#include <pwd.h>
44#include <time.h>
45#include <signal.h>
46
47#include "sudo.h"
48#include "sudo_auth.h"
49#include "insults.h"
50
51extern char **NewArgv; /* XXX */
52
53sudo_auth auth_switch[] = {
54#ifdef AUTH_STANDALONE
55    AUTH_STANDALONE
56#else
57#  ifndef WITHOUT_PASSWD
58    AUTH_ENTRY(0, "passwd", passwd_init, NULL, passwd_verify, passwd_cleanup)
59#  endif
60#  if defined(HAVE_GETPRPWNAM) && !defined(WITHOUT_PASSWD)
61    AUTH_ENTRY(0, "secureware", secureware_init, NULL, secureware_verify, secureware_cleanup)
62#  endif
63#  ifdef HAVE_AFS
64    AUTH_ENTRY(0, "afs", NULL, NULL, afs_verify, NULL)
65#  endif
66#  ifdef HAVE_DCE
67    AUTH_ENTRY(0, "dce", NULL, NULL, dce_verify, NULL)
68#  endif
69#  ifdef HAVE_KERB4
70    AUTH_ENTRY(0, "kerb4", kerb4_init, NULL, kerb4_verify, NULL)
71#  endif
72#  ifdef HAVE_KERB5
73    AUTH_ENTRY(0, "kerb5", kerb5_init, kerb5_setup, kerb5_verify, kerb5_cleanup)
74#  endif
75#  ifdef HAVE_SKEY
76    AUTH_ENTRY(0, "S/Key", NULL, rfc1938_setup, rfc1938_verify, NULL)
77#  endif
78#  ifdef HAVE_OPIE
79    AUTH_ENTRY(0, "OPIE", NULL, rfc1938_setup, rfc1938_verify, NULL)
80#  endif
81#endif /* AUTH_STANDALONE */
82    AUTH_ENTRY(0, NULL, NULL, NULL, NULL, NULL)
83};
84
85/*
86 * Initialize sudoers authentication method(s).
87 * Returns 0 on success and -1 on error.
88 */
89int
90sudo_auth_init(pw)
91    struct passwd *pw;
92{
93    sudo_auth *auth;
94    int status = AUTH_SUCCESS;
95
96    if (auth_switch[0].name == NULL)
97	return 0;
98
99    /* Set FLAG_ONEANDONLY if there is only one auth method. */
100    if (auth_switch[1].name == NULL)
101	SET(auth_switch[0].flags, FLAG_ONEANDONLY);
102
103    /* Initialize auth methods and unconfigure the method if necessary. */
104    for (auth = auth_switch; auth->name; auth++) {
105	if (auth->init && IS_CONFIGURED(auth)) {
106	    if (NEEDS_USER(auth))
107		set_perms(PERM_USER);
108
109	    status = (auth->init)(pw, auth);
110
111	    if (NEEDS_USER(auth))
112		set_perms(PERM_ROOT);
113
114	    /* Disable if it failed to init unless there was a fatal error. */
115	    if (status == AUTH_FAILURE)
116		CLR(auth->flags, FLAG_CONFIGURED);
117	    else if (status == AUTH_FATAL)
118		break;		/* assume error msg already printed */
119	}
120    }
121    return status == AUTH_FATAL ? -1 : 0;
122}
123
124/*
125 * Cleanup all authentication methods.
126 * Returns 0 on success and -1 on error.
127 */
128int
129sudo_auth_cleanup(pw)
130    struct passwd *pw;
131{
132    sudo_auth *auth;
133    int status = AUTH_SUCCESS;
134
135    /* Call cleanup routines. */
136    for (auth = auth_switch; auth->name; auth++) {
137	if (auth->cleanup && IS_CONFIGURED(auth)) {
138	    if (NEEDS_USER(auth))
139		set_perms(PERM_USER);
140
141	    status = (auth->cleanup)(pw, auth);
142
143	    if (NEEDS_USER(auth))
144		set_perms(PERM_ROOT);
145
146	    if (status == AUTH_FATAL)
147		break;		/* assume error msg already printed */
148	}
149    }
150    return status == AUTH_FATAL ? -1 : 0;
151}
152
153/*
154 * Verify the specified user.
155 * Returns TRUE if verified, FALSE if not or -1 on error.
156 */
157int
158verify_user(pw, prompt, validated)
159    struct passwd *pw;
160    char *prompt;
161    int validated;
162{
163    int counter = def_passwd_tries + 1;
164    int success = AUTH_FAILURE;
165    int status, rval;
166    char *p;
167    sudo_auth *auth;
168    sigaction_t sa, osa;
169
170    /* Enable suspend during password entry. */
171    sigemptyset(&sa.sa_mask);
172    sa.sa_flags = SA_RESTART;
173    sa.sa_handler = SIG_DFL;
174    (void) sigaction(SIGTSTP, &sa, &osa);
175
176    /* Make sure we have at least one auth method. */
177    /* XXX - check FLAG_DISABLED too */
178    if (auth_switch[0].name == NULL) {
179	audit_failure(NewArgv, "no authentication methods");
180    	log_error(0, "%s  %s %s",
181	    "There are no authentication methods compiled into sudo!",
182	    "If you want to turn off authentication, use the",
183	    "--disable-authentication configure option.");
184	return -1;
185    }
186
187    while (--counter) {
188	/* Do any per-method setup and unconfigure the method if needed */
189	for (auth = auth_switch; auth->name; auth++) {
190	    if (auth->setup && IS_CONFIGURED(auth)) {
191		if (NEEDS_USER(auth))
192		    set_perms(PERM_USER);
193
194		status = (auth->setup)(pw, &prompt, auth);
195
196		if (NEEDS_USER(auth))
197		    set_perms(PERM_ROOT);
198
199		if (status == AUTH_FAILURE)
200		    CLR(auth->flags, FLAG_CONFIGURED);
201		else if (status == AUTH_FATAL) {
202		    goto done;		/* assume error msg already printed */
203		}
204	    }
205	}
206
207	/* Get the password unless the auth function will do it for us */
208#ifdef AUTH_STANDALONE
209	p = prompt;
210#else
211	p = (char *) tgetpass(prompt, def_passwd_timeout * 60,
212	    tgetpass_flags);
213#endif /* AUTH_STANDALONE */
214
215	/* Call authentication functions. */
216	for (auth = auth_switch; p && auth->name; auth++) {
217	    if (!IS_CONFIGURED(auth))
218		continue;
219
220	    if (NEEDS_USER(auth))
221		set_perms(PERM_USER);
222
223	    success = auth->status = (auth->verify)(pw, (char *)p, auth);
224
225	    if (NEEDS_USER(auth))
226		set_perms(PERM_ROOT);
227
228	    if (auth->status != AUTH_FAILURE)
229		goto done;
230	}
231#ifndef AUTH_STANDALONE
232	if (p == NULL)
233	    break;
234	zero_bytes(p, strlen(p));
235#endif
236	if (!ISSET(tgetpass_flags, TGP_ASKPASS))
237	    pass_warn(stderr);
238    }
239
240done:
241    switch (success) {
242	case AUTH_SUCCESS:
243	    (void) sigaction(SIGTSTP, &osa, NULL);
244	    rval = TRUE;
245	    break;
246	case AUTH_INTR:
247	case AUTH_FAILURE:
248	    if (counter != def_passwd_tries)
249		validated |= FLAG_BAD_PASSWORD;
250	    log_auth_failure(validated, def_passwd_tries - counter);
251	    rval = FALSE;
252	    break;
253	case AUTH_FATAL:
254	default:
255	    log_auth_failure(validated | FLAG_AUTH_ERROR, 0);
256	    rval = -1;
257	    break;
258    }
259    return rval;
260}
261
262void
263pass_warn(fp)
264    FILE *fp;
265{
266
267#ifdef INSULT
268    if (def_insults)
269	(void) fprintf(fp, "%s\n", INSULT);
270    else
271#endif
272	(void) fprintf(fp, "%s\n", def_badpass_message);
273}
274
275void
276dump_auth_methods()
277{
278    sudo_auth *auth;
279
280    (void) fputs("Authentication methods:", stdout);
281    for (auth = auth_switch; auth->name; auth++)
282        (void) printf(" '%s'", auth->name);
283    (void) putchar('\n');
284}
285