"Fossies" - the Fresh Open Source Software Archive 
Member "sudo-1.9.11p3/plugins/sudoers/check.c" (12 Jun 2022, 10675 Bytes) of package /linux/misc/sudo-1.9.11p3.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "check.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
1.9.10_vs_1.9.11rc1.
1 /*
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 1993-1996,1998-2005, 2007-2018
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 #include <sys/types.h> /* for ssize_t */
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <time.h>
38 #include <errno.h>
39 #include <pwd.h>
40 #include <grp.h>
41
42 #include "sudoers.h"
43 #include "check.h"
44
45 struct getpass_closure {
46 int tstat;
47 int lectured;
48 void *cookie;
49 struct passwd *auth_pw;
50 };
51
52 static struct passwd *get_authpw(int);
53
54 /*
55 * Called when getpass is suspended so we can drop the lock.
56 */
57 static int
58 getpass_suspend(int signo, void *vclosure)
59 {
60 struct getpass_closure *closure = vclosure;
61
62 timestamp_close(closure->cookie);
63 closure->cookie = NULL;
64 return 0;
65 }
66
67 /*
68 * Called when getpass is resumed so we can reacquire the lock.
69 */
70 static int
71 getpass_resume(int signo, void *vclosure)
72 {
73 struct getpass_closure *closure = vclosure;
74
75 closure->cookie = timestamp_open(user_name, user_sid);
76 if (closure->cookie == NULL)
77 return -1;
78 if (!timestamp_lock(closure->cookie, closure->auth_pw))
79 return -1;
80 return 0;
81 }
82
83 /*
84 * Returns true if the user successfully authenticates, false if not
85 * or -1 on fatal error.
86 */
87 static int
88 check_user_interactive(int validated, int mode, struct getpass_closure *closure)
89 {
90 struct sudo_conv_callback callback;
91 int ret = -1;
92 char *prompt;
93 debug_decl(check_user_interactive, SUDOERS_DEBUG_AUTH);
94
95 /* Construct callback for getpass function. */
96 memset(&callback, 0, sizeof(callback));
97 callback.version = SUDO_CONV_CALLBACK_VERSION;
98 callback.closure = closure;
99 callback.on_suspend = getpass_suspend;
100 callback.on_resume = getpass_resume;
101
102 /* Open, lock and read time stamp file if we are using it. */
103 if (!ISSET(mode, MODE_IGNORE_TICKET)) {
104 /* Open time stamp file and check its status. */
105 closure->cookie = timestamp_open(user_name, user_sid);
106 if (closure->cookie != NULL) {
107 if (timestamp_lock(closure->cookie, closure->auth_pw)) {
108 closure->tstat = timestamp_status(closure->cookie,
109 closure->auth_pw);
110 }
111 callback.on_suspend = getpass_suspend;
112 callback.on_resume = getpass_resume;
113 }
114 }
115
116 switch (closure->tstat) {
117 case TS_FATAL:
118 /* Fatal error (usually setuid failure), unsafe to proceed. */
119 goto done;
120
121 case TS_CURRENT:
122 /* Time stamp file is valid and current. */
123 if (!ISSET(validated, FLAG_CHECK_USER)) {
124 ret = true;
125 break;
126 }
127 sudo_debug_printf(SUDO_DEBUG_INFO,
128 "%s: check user flag overrides time stamp", __func__);
129 FALLTHROUGH;
130
131 default:
132 if (ISSET(mode, MODE_NONINTERACTIVE) && !def_noninteractive_auth) {
133 validated |= FLAG_NO_USER_INPUT;
134 log_auth_failure(validated, 0);
135 goto done;
136 }
137
138 /* Expand any escapes in the prompt. */
139 prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt,
140 closure->auth_pw->pw_name);
141 if (prompt == NULL)
142 goto done;
143
144 ret = verify_user(closure->auth_pw, prompt, validated, &callback);
145 if (ret == true && closure->lectured)
146 (void)set_lectured(); /* lecture error not fatal */
147 free(prompt);
148 break;
149 }
150
151 done:
152 debug_return_int(ret);
153 }
154
155 /*
156 * Returns true if the user successfully authenticates, false if not
157 * or -1 on error.
158 */
159 int
160 check_user(int validated, int mode)
161 {
162 struct getpass_closure closure = { TS_ERROR };
163 int ret = -1;
164 bool exempt = false;
165 debug_decl(check_user, SUDOERS_DEBUG_AUTH);
166
167 /*
168 * Init authentication system regardless of whether we need a password.
169 * Required for proper PAM session support.
170 */
171 if ((closure.auth_pw = get_authpw(mode)) == NULL)
172 goto done;
173 if (sudo_auth_init(closure.auth_pw, mode) == -1)
174 goto done;
175
176 /*
177 * Don't prompt for the root passwd or if the user is exempt.
178 * If the user is not changing uid/gid, no need for a password.
179 */
180 if (!def_authenticate || user_is_exempt()) {
181 sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %s", __func__,
182 !def_authenticate ? "authentication disabled" :
183 "user exempt from authentication");
184 exempt = true;
185 ret = true;
186 goto done;
187 }
188 if (user_uid == 0 || (user_uid == runas_pw->pw_uid &&
189 (!runas_gr || user_in_group(sudo_user.pw, runas_gr->gr_name)))) {
190 #ifdef HAVE_SELINUX
191 if (user_role == NULL && user_type == NULL)
192 #endif
193 #ifdef HAVE_APPARMOR
194 if (user_apparmor_profile == NULL)
195 #endif
196 #ifdef HAVE_PRIV_SET
197 if (runas_privs == NULL && runas_limitprivs == NULL)
198 #endif
199 {
200 sudo_debug_printf(SUDO_DEBUG_INFO,
201 "%s: user running command as self", __func__);
202 ret = true;
203 goto done;
204 }
205 }
206
207 ret = check_user_interactive(validated, mode, &closure);
208
209 done:
210 if (ret == true) {
211 /* The approval function may disallow a user post-authentication. */
212 ret = sudo_auth_approval(closure.auth_pw, validated, exempt);
213
214 /*
215 * Only update time stamp if user validated and was approved.
216 * Failure to update the time stamp is not a fatal error.
217 */
218 if (ret == true && closure.tstat != TS_ERROR) {
219 if (ISSET(validated, VALIDATE_SUCCESS))
220 (void)timestamp_update(closure.cookie, closure.auth_pw);
221 }
222 }
223 timestamp_close(closure.cookie);
224 sudo_auth_cleanup(closure.auth_pw, !ISSET(validated, VALIDATE_SUCCESS));
225 if (closure.auth_pw != NULL)
226 sudo_pw_delref(closure.auth_pw);
227
228 debug_return_int(ret);
229 }
230
231 /*
232 * Display sudo lecture (standard or custom).
233 * Returns true if the user was lectured, else false.
234 */
235 void
236 display_lecture(struct sudo_conv_callback *callback)
237 {
238 struct getpass_closure *closure;
239 struct sudo_conv_message msg;
240 struct sudo_conv_reply repl;
241 char buf[BUFSIZ];
242 struct stat sb;
243 ssize_t nread;
244 int fd;
245 debug_decl(lecture, SUDOERS_DEBUG_AUTH);
246
247 if (callback == NULL || (closure = callback->closure) == NULL)
248 debug_return;
249
250 if (closure->lectured)
251 debug_return;
252
253 if (def_lecture == never || (def_lecture == once && already_lectured()))
254 debug_return;
255
256 memset(&msg, 0, sizeof(msg));
257 memset(&repl, 0, sizeof(repl));
258
259 if (def_lecture_file) {
260 fd = open(def_lecture_file, O_RDONLY|O_NONBLOCK);
261 if (fd != -1 && fstat(fd, &sb) == 0) {
262 if (S_ISREG(sb.st_mode)) {
263 (void) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
264 while ((nread = read(fd, buf, sizeof(buf) - 1)) > 0) {
265 buf[nread] = '\0';
266 msg.msg_type = SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY;
267 msg.msg = buf;
268 sudo_conv(1, &msg, &repl, NULL);
269 }
270 if (nread == 0) {
271 close(fd);
272 goto done;
273 }
274 log_warning(SLOG_RAW_MSG,
275 N_("error reading lecture file %s"), def_lecture_file);
276 } else {
277 log_warningx(SLOG_RAW_MSG,
278 N_("ignoring lecture file %s: not a regular file"),
279 def_lecture_file);
280 }
281 } else {
282 log_warning(SLOG_RAW_MSG|SLOG_NO_STDERR, N_("unable to open %s"),
283 def_lecture_file);
284 }
285 if (fd != -1)
286 close(fd);
287 }
288
289 /* Default sudo lecture. */
290 msg.msg_type = SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY;
291 msg.msg = _("\n"
292 "We trust you have received the usual lecture from the local System\n"
293 "Administrator. It usually boils down to these three things:\n\n"
294 " #1) Respect the privacy of others.\n"
295 " #2) Think before you type.\n"
296 " #3) With great power comes great responsibility.\n\n");
297 sudo_conv(1, &msg, &repl, NULL);
298
299 done:
300 closure->lectured = true;
301 debug_return;
302 }
303
304 /*
305 * Checks if the user is exempt from supplying a password.
306 */
307 bool
308 user_is_exempt(void)
309 {
310 bool ret = false;
311 debug_decl(user_is_exempt, SUDOERS_DEBUG_AUTH);
312
313 if (ISSET(sudo_mode, MODE_POLICY_INTERCEPTED)) {
314 if (!def_intercept_authenticate)
315 ret = true;
316 }
317 if (def_exempt_group) {
318 if (user_in_group(sudo_user.pw, def_exempt_group))
319 ret = true;
320 }
321 debug_return_bool(ret);
322 }
323
324 /*
325 * Get passwd entry for the user we are going to authenticate as.
326 * By default, this is the user invoking sudo. In the most common
327 * case, this matches sudo_user.pw or runas_pw.
328 */
329 static struct passwd *
330 get_authpw(int mode)
331 {
332 struct passwd *pw = NULL;
333 debug_decl(get_authpw, SUDOERS_DEBUG_AUTH);
334
335 if (ISSET(mode, (MODE_CHECK|MODE_LIST))) {
336 /* In list mode we always prompt for the user's password. */
337 sudo_pw_addref(sudo_user.pw);
338 pw = sudo_user.pw;
339 } else {
340 if (def_rootpw) {
341 if ((pw = sudo_getpwuid(ROOT_UID)) == NULL) {
342 log_warningx(SLOG_SEND_MAIL, N_("unknown uid %u"), ROOT_UID);
343 }
344 } else if (def_runaspw) {
345 if ((pw = sudo_getpwnam(def_runas_default)) == NULL) {
346 log_warningx(SLOG_SEND_MAIL,
347 N_("unknown user %s"), def_runas_default);
348 }
349 } else if (def_targetpw) {
350 if (runas_pw->pw_name == NULL) {
351 /* This should never be NULL as we fake up the passwd struct */
352 log_warningx(SLOG_RAW_MSG, N_("unknown uid %u"),
353 (unsigned int) runas_pw->pw_uid);
354 } else {
355 sudo_pw_addref(runas_pw);
356 pw = runas_pw;
357 }
358 } else {
359 sudo_pw_addref(sudo_user.pw);
360 pw = sudo_user.pw;
361 }
362 }
363
364 debug_return_ptr(pw);
365 }
366
367 /*
368 * Returns true if the specified shell is allowed by /etc/shells, else false.
369 */
370 bool
371 check_user_shell(const struct passwd *pw)
372 {
373 const char *shell;
374 debug_decl(check_user_shell, SUDOERS_DEBUG_AUTH);
375
376 if (!def_runas_check_shell)
377 debug_return_bool(true);
378
379 sudo_debug_printf(SUDO_DEBUG_INFO,
380 "%s: checking /etc/shells for %s", __func__, pw->pw_shell);
381
382 setusershell();
383 while ((shell = getusershell()) != NULL) {
384 if (strcmp(shell, pw->pw_shell) == 0)
385 debug_return_bool(true);
386 }
387 endusershell();
388
389 debug_return_bool(false);
390 }