2 * Copyright (c) 1993-1996,1998-2005, 2007-2009
3 * Todd C. Miller <Todd.Miller@courtesan.com>
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.
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.
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.
24 #include <sys/types.h>
25 #include <sys/param.h>
28 # include <sys/file.h>
38 #endif /* STDC_HEADERS */
42 # ifdef HAVE_STRINGS_H
45 #endif /* HAVE_STRING_H */
48 #endif /* HAVE_UNISTD_H */
56 # include <emul/timespec.h>
62 __unused static const char rcsid[] = "$Sudo: check.c,v 1.247 2009/05/25 12:02:41 millert Exp $";
65 /* Status codes for timestamp_status() */
72 /* Flags for timestamp_status() */
73 #define TS_MAKE_DIRS 1
76 static void build_timestamp __P((char **, char **));
77 static int timestamp_status __P((char *, char *, char *, int));
78 static char *expand_prompt __P((char *, char *, char *));
79 static void lecture __P((int));
80 static void update_timestamp __P((char *, char *));
83 * This function only returns if the user can successfully
84 * verify who he/she is.
87 check_user(validated, mode)
91 char *timestampdir = NULL;
92 char *timestampfile = NULL;
96 if (mode & MODE_INVALIDATE) {
97 /* do not check or update timestamp */
100 if (user_uid == 0 || user_uid == runas_pw->pw_uid || user_is_exempt())
103 build_timestamp(×tampdir, ×tampfile);
104 status = timestamp_status(timestampdir, timestampfile, user_name,
107 if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) {
108 /* Bail out if we are non-interactive and a password is required */
109 if (ISSET(mode, MODE_NONINTERACTIVE))
110 errorx(1, "sorry, a password is required to run %s", getprogname());
112 /* If user specified -A, make sure we have an askpass helper. */
113 if (ISSET(tgetpass_flags, TGP_ASKPASS)) {
114 if (user_askpass == NULL)
116 "no askpass program specified, try setting SUDO_ASKPASS");
117 } else if (!ISSET(tgetpass_flags, TGP_STDIN)) {
118 /* If no tty but DISPLAY is set, use askpass if we have it. */
119 if (!user_ttypath && !tty_present()) {
120 if (user_askpass && user_display && *user_display != '\0') {
121 SET(tgetpass_flags, TGP_ASKPASS);
122 } else if (!def_visiblepw) {
124 "no tty present and no askpass program specified");
129 if (!ISSET(tgetpass_flags, TGP_ASKPASS))
132 /* Expand any escapes in the prompt. */
133 prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt,
134 user_name, user_shost);
136 verify_user(auth_pw, prompt);
138 /* Only update timestamp if user was validated. */
139 if (status != TS_ERROR && ISSET(validated, VALIDATE_OK))
140 update_timestamp(timestampdir, timestampfile);
142 efree(timestampfile);
146 * Standard sudo lecture.
156 if (def_lecture == never ||
157 (def_lecture == once && status != TS_MISSING && status != TS_ERROR))
160 if (def_lecture_file && (fp = fopen(def_lecture_file, "r")) != NULL) {
161 while ((nread = fread(buf, sizeof(char), sizeof(buf), fp)) != 0)
162 fwrite(buf, nread, 1, stderr);
166 We trust you have received the usual lecture from the local System\n\
167 Administrator. It usually boils down to these three things:\n\
169 #1) Respect the privacy of others.\n\
170 #2) Think before you type.\n\
171 #3) With great power comes great responsibility.\n\n",
177 * Update the time on the timestamp file/dir or create it if necessary.
180 update_timestamp(timestampdir, timestampfile)
184 if (timestamp_uid != 0)
185 set_perms(PERM_TIMESTAMP);
186 if (touch(-1, timestampfile ? timestampfile : timestampdir, NULL) == -1) {
188 int fd = open(timestampfile, O_WRONLY|O_CREAT|O_TRUNC, 0600);
191 log_error(NO_EXIT|USE_ERRNO, "Can't open %s", timestampfile);
195 if (mkdir(timestampdir, 0700) == -1)
196 log_error(NO_EXIT|USE_ERRNO, "Can't mkdir %s", timestampdir);
199 if (timestamp_uid != 0)
200 set_perms(PERM_ROOT);
204 * Expand %h and %u escapes in the prompt and pass back the dynamically
205 * allocated result. Returns the same string if there are no escapes.
208 expand_prompt(old_prompt, user, host)
215 char *p, *np, *new_prompt, *endp;
217 /* How much space do we need to malloc for the prompt? */
219 for (p = old_prompt, len = strlen(old_prompt); *p; p++) {
224 len += strlen(user_shost) - 2;
229 len += strlen(user_host) - 2;
236 else if (def_targetpw || def_runaspw)
237 len += strlen(runas_pw->pw_name) - 2;
239 len += strlen(user_name) - 2;
244 len += strlen(user_name) - 2;
249 len += strlen(runas_pw->pw_name) - 2;
264 new_prompt = (char *) emalloc(++len);
265 endp = new_prompt + len;
266 for (p = old_prompt, np = new_prompt; *p; p++) {
271 n = strlcpy(np, user_shost, np - endp);
278 n = strlcpy(np, user_host, np - endp);
286 n = strlcpy(np, "root", np - endp);
287 else if (def_targetpw || def_runaspw)
288 n = strlcpy(np, runas_pw->pw_name, np - endp);
290 n = strlcpy(np, user_name, np - endp);
297 n = strlcpy(np, user_name, np - endp);
304 n = strlcpy(np, runas_pw->pw_name, np - endp);
310 /* convert %% -> % */
324 new_prompt = old_prompt;
329 /* We pre-allocate enough space, so this should never happen. */
330 errorx(1, "internal error, expand_prompt() overflow");
334 * Checks if the user is exempt from supplying a password.
342 if (!def_exempt_group)
345 if (!(grp = sudo_getgrnam(def_exempt_group)))
348 if (user_gid == grp->gr_gid)
351 for (gr_mem = grp->gr_mem; *gr_mem; gr_mem++) {
352 if (strcmp(user_name, *gr_mem) == 0)
360 * Fills in timestampdir as well as timestampfile if using tty tickets.
363 build_timestamp(timestampdir, timestampfile)
365 char **timestampfile;
370 dirparent = def_timestampdir;
371 len = easprintf(timestampdir, "%s/%s", dirparent, user_name);
373 log_error(0, "timestamp path too long: %s", *timestampdir);
376 * Timestamp file may be a file in the directory or NUL to use
377 * the directory as the timestamp.
379 if (def_tty_tickets) {
382 if ((p = strrchr(user_tty, '/')))
387 len = easprintf(timestampfile, "%s/%s/%s:%s", dirparent, user_name,
388 p, runas_pw->pw_name);
390 len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, p);
392 log_error(0, "timestamp path too long: %s", *timestampfile);
393 } else if (def_targetpw) {
394 len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name,
397 log_error(0, "timestamp path too long: %s", *timestampfile);
399 *timestampfile = NULL;
403 * Check the timestamp file and directory and return their status.
406 timestamp_status(timestampdir, timestampfile, user, flags)
414 char *dirparent = def_timestampdir;
415 int status = TS_ERROR; /* assume the worst */
417 if (timestamp_uid != 0)
418 set_perms(PERM_TIMESTAMP);
421 * Sanity check dirparent and make it if it doesn't already exist.
422 * We start out assuming the worst (that the dir is not sane) and
423 * if it is ok upgrade the status to ``no timestamp file''.
424 * Note that we don't check the parent(s) of dirparent for
425 * sanity since the sudo dir is often just located in /tmp.
427 if (lstat(dirparent, &sb) == 0) {
428 if (!S_ISDIR(sb.st_mode))
429 log_error(NO_EXIT, "%s exists but is not a directory (0%o)",
430 dirparent, (unsigned int) sb.st_mode);
431 else if (sb.st_uid != timestamp_uid)
432 log_error(NO_EXIT, "%s owned by uid %lu, should be uid %lu",
433 dirparent, (unsigned long) sb.st_uid,
434 (unsigned long) timestamp_uid);
435 else if ((sb.st_mode & 0000022))
437 "%s writable by non-owner (0%o), should be mode 0700",
438 dirparent, (unsigned int) sb.st_mode);
440 if ((sb.st_mode & 0000777) != 0700)
441 (void) chmod(dirparent, 0700);
444 } else if (errno != ENOENT) {
445 log_error(NO_EXIT|USE_ERRNO, "can't stat %s", dirparent);
447 /* No dirparent, try to make one. */
448 if (ISSET(flags, TS_MAKE_DIRS)) {
449 if (mkdir(dirparent, S_IRWXU))
450 log_error(NO_EXIT|USE_ERRNO, "can't mkdir %s",
456 if (status == TS_ERROR) {
457 if (timestamp_uid != 0)
458 set_perms(PERM_ROOT);
463 * Sanity check the user's ticket dir. We start by downgrading
464 * the status to TS_ERROR. If the ticket dir exists and is sane
465 * this will be upgraded to TS_OLD. If the dir does not exist,
466 * it will be upgraded to TS_MISSING.
468 status = TS_ERROR; /* downgrade status again */
469 if (lstat(timestampdir, &sb) == 0) {
470 if (!S_ISDIR(sb.st_mode)) {
471 if (S_ISREG(sb.st_mode)) {
472 /* convert from old style */
473 if (unlink(timestampdir) == 0)
476 log_error(NO_EXIT, "%s exists but is not a directory (0%o)",
477 timestampdir, (unsigned int) sb.st_mode);
478 } else if (sb.st_uid != timestamp_uid)
479 log_error(NO_EXIT, "%s owned by uid %lu, should be uid %lu",
480 timestampdir, (unsigned long) sb.st_uid,
481 (unsigned long) timestamp_uid);
482 else if ((sb.st_mode & 0000022))
484 "%s writable by non-owner (0%o), should be mode 0700",
485 timestampdir, (unsigned int) sb.st_mode);
487 if ((sb.st_mode & 0000777) != 0700)
488 (void) chmod(timestampdir, 0700);
489 status = TS_OLD; /* do date check later */
491 } else if (errno != ENOENT) {
492 log_error(NO_EXIT|USE_ERRNO, "can't stat %s", timestampdir);
497 * If there is no user ticket dir, AND we are in tty ticket mode,
498 * AND the TS_MAKE_DIRS flag is set, create the user ticket dir.
500 if (status == TS_MISSING && timestampfile && ISSET(flags, TS_MAKE_DIRS)) {
501 if (mkdir(timestampdir, S_IRWXU) == -1) {
503 log_error(NO_EXIT|USE_ERRNO, "can't mkdir %s", timestampdir);
508 * Sanity check the tty ticket file if it exists.
510 if (timestampfile && status != TS_ERROR) {
511 if (status != TS_MISSING)
512 status = TS_NOFILE; /* dir there, file missing */
513 if (lstat(timestampfile, &sb) == 0) {
514 if (!S_ISREG(sb.st_mode)) {
516 log_error(NO_EXIT, "%s exists but is not a regular file (0%o)",
517 timestampfile, (unsigned int) sb.st_mode);
519 /* If bad uid or file mode, complain and kill the bogus file. */
520 if (sb.st_uid != timestamp_uid) {
522 "%s owned by uid %lu, should be uid %lu",
523 timestampfile, (unsigned long) sb.st_uid,
524 (unsigned long) timestamp_uid);
525 (void) unlink(timestampfile);
526 } else if ((sb.st_mode & 0000022)) {
528 "%s writable by non-owner (0%o), should be mode 0600",
529 timestampfile, (unsigned int) sb.st_mode);
530 (void) unlink(timestampfile);
532 /* If not mode 0600, fix it. */
533 if ((sb.st_mode & 0000777) != 0600)
534 (void) chmod(timestampfile, 0600);
536 status = TS_OLD; /* actually check mtime below */
539 } else if (errno != ENOENT) {
540 log_error(NO_EXIT|USE_ERRNO, "can't stat %s", timestampfile);
546 * If the file/dir exists and we are not removing it, check its mtime.
548 if (status == TS_OLD && !ISSET(flags, TS_REMOVE)) {
549 /* Negative timeouts only expire manually (sudo -k). */
550 if (def_timestamp_timeout < 0 && sb.st_mtime != 0)
553 /* XXX - should use timespec here */
555 if (def_timestamp_timeout &&
556 now - sb.st_mtime < 60 * def_timestamp_timeout) {
558 * Check for bogus time on the stampfile. The clock may
559 * have been set back or someone could be trying to spoof us.
561 if (sb.st_mtime > now + 60 * def_timestamp_timeout * 2) {
563 "timestamp too far in the future: %20.20s",
564 4 + ctime(&sb.st_mtime));
566 (void) unlink(timestampfile);
568 (void) rmdir(timestampdir);
576 if (timestamp_uid != 0)
577 set_perms(PERM_ROOT);
582 * Remove the timestamp ticket file/dir.
585 remove_timestamp(remove)
589 char *timestampdir, *timestampfile, *path;
592 build_timestamp(×tampdir, ×tampfile);
593 status = timestamp_status(timestampdir, timestampfile, user_name,
595 if (status == TS_OLD || status == TS_CURRENT) {
596 path = timestampfile ? timestampfile : timestampdir;
599 status = unlink(timestampfile);
601 status = rmdir(timestampdir);
602 if (status == -1 && errno != ENOENT) {
603 log_error(NO_EXIT, "can't remove %s (%s), will reset to Epoch",
604 path, strerror(errno));
609 if (touch(-1, path, &ts) == -1)
610 error(1, "can't reset %s to Epoch", path);
615 efree(timestampfile);