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