1/*
2 * Copyright (c) 2000-2005, 2007-2008, 2010
3 *	Todd C. Miller <Todd.Miller@courtesan.com>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * Sponsored in part by the Defense Advanced Research Projects
18 * Agency (DARPA) and Air Force Research Laboratory, Air Force
19 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20 */
21
22#include <config.h>
23
24#include <sys/types.h>
25#include <sys/param.h>
26#include <stdio.h>
27#ifdef STDC_HEADERS
28# include <stdlib.h>
29# include <stddef.h>
30#else
31# ifdef HAVE_STDLIB_H
32#  include <stdlib.h>
33# endif
34#endif /* STDC_HEADERS */
35#ifdef HAVE_STRING_H
36# include <string.h>
37#endif /* HAVE_STRING_H */
38#ifdef HAVE_STRINGS_H
39# include <strings.h>
40#endif /* HAVE_STRING_H */
41#ifdef HAVE_UNISTD_H
42# include <unistd.h>
43#endif /* HAVE_UNISTD_H */
44#include <ctype.h>
45#include <pwd.h>
46#include <signal.h>
47
48#include <login_cap.h>
49#include <bsd_auth.h>
50
51#include "sudo.h"
52#include "sudo_auth.h"
53
54#ifndef LOGIN_DEFROOTCLASS
55# define LOGIN_DEFROOTCLASS	"daemon"
56#endif
57
58extern char *login_style;		/* from sudo.c */
59
60struct bsdauth_state {
61    auth_session_t *as;
62    login_cap_t *lc;
63};
64
65int
66bsdauth_init(pw, auth)
67    struct passwd *pw;
68    sudo_auth *auth;
69{
70    static struct bsdauth_state state;
71
72    /* Get login class based on auth user, which may not be invoking user. */
73    if (pw->pw_class && *pw->pw_class)
74       state.lc = login_getclass(pw->pw_class);
75    else
76       state.lc = login_getclass(pw->pw_uid ? LOGIN_DEFCLASS : LOGIN_DEFROOTCLASS);
77    if (state.lc == NULL) {
78	log_error(USE_ERRNO|NO_MAIL,
79	    "unable to get login class for user %s", pw->pw_name);
80	return AUTH_FATAL;
81    }
82
83    if ((state.as = auth_open()) == NULL) {
84	log_error(USE_ERRNO|NO_MAIL,
85	    "unable to begin bsd authentication");
86	login_close(state.lc);
87	return AUTH_FATAL;
88    }
89
90    /* XXX - maybe sanity check the auth style earlier? */
91    login_style = login_getstyle(state.lc, login_style, "auth-sudo");
92    if (login_style == NULL) {
93	log_error(NO_MAIL, "invalid authentication type");
94	auth_close(state.as);
95	login_close(state.lc);
96	return AUTH_FATAL;
97    }
98
99     if (auth_setitem(state.as, AUTHV_STYLE, login_style) < 0 ||
100	auth_setitem(state.as, AUTHV_NAME, pw->pw_name) < 0 ||
101	auth_setitem(state.as, AUTHV_CLASS, login_class) < 0) {
102	log_error(NO_MAIL, "unable to setup authentication");
103	auth_close(state.as);
104	login_close(state.lc);
105	return AUTH_FATAL;
106    }
107
108    auth->data = (void *) &state;
109    return AUTH_SUCCESS;
110}
111
112int
113bsdauth_verify(pw, prompt, auth)
114    struct passwd *pw;
115    char *prompt;
116    sudo_auth *auth;
117{
118    char *pass;
119    char *s;
120    size_t len;
121    int authok = 0;
122    sigaction_t sa, osa;
123    auth_session_t *as = ((struct bsdauth_state *) auth->data)->as;
124
125    /* save old signal handler */
126    sigemptyset(&sa.sa_mask);
127    sa.sa_flags = SA_RESTART;
128    sa.sa_handler = SIG_DFL;
129    (void) sigaction(SIGCHLD, &sa, &osa);
130
131    /*
132     * If there is a challenge then print that instead of the normal
133     * prompt.  If the user just hits return we prompt again with echo
134     * turned on, which is useful for challenge/response things like
135     * S/Key.
136     */
137    if ((s = auth_challenge(as)) == NULL) {
138	pass = tgetpass(prompt, def_passwd_timeout * 60, tgetpass_flags);
139    } else {
140	pass = tgetpass(s, def_passwd_timeout * 60, tgetpass_flags);
141	if (pass && *pass == '\0') {
142	    if ((prompt = strrchr(s, '\n')))
143		prompt++;
144	    else
145		prompt = s;
146
147	    /*
148	     * Append '[echo on]' to the last line of the challenge and
149	     * reprompt with echo turned on.
150	     */
151	    len = strlen(prompt) - 1;
152	    while (isspace(prompt[len]) || prompt[len] == ':')
153		prompt[len--] = '\0';
154	    easprintf(&s, "%s [echo on]: ", prompt);
155	    pass = tgetpass(s, def_passwd_timeout * 60,
156		tgetpass_flags | TGP_ECHO);
157	    free(s);
158	}
159    }
160
161    if (pass) {
162	authok = auth_userresponse(as, pass, 1);
163	zero_bytes(pass, strlen(pass));
164    }
165
166    /* restore old signal handler */
167    (void) sigaction(SIGCHLD, &osa, NULL);
168
169    if (authok)
170	return AUTH_SUCCESS;
171
172    if (!pass)
173	return AUTH_INTR;
174
175    if ((s = auth_getvalue(as, "errormsg")) != NULL)
176	log_error(NO_MAIL, "%s", s);
177    return AUTH_FAILURE;
178}
179
180int
181bsdauth_cleanup(pw, auth)
182    struct passwd *pw;
183    sudo_auth *auth;
184{
185    struct bsdauth_state *state = auth->data;
186
187    if (state != NULL) {
188	auth_close(state->as);
189	login_close(state->lc);
190    }
191
192    return AUTH_SUCCESS;
193}
194