Imported Upstream version 1.8.5
[debian/sudo] / plugins / sudoers / check.c
1 /*
2  * Copyright (c) 1993-1996,1998-2005, 2007-2011
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 <sys/time.h>
27 #include <sys/stat.h>
28 #ifdef __linux__
29 # include <sys/vfs.h>
30 #endif
31 #if defined(__sun) && defined(__SVR4)
32 # include <sys/statvfs.h>
33 #endif
34 #ifndef __TANDEM
35 # include <sys/file.h>
36 #endif
37 #include <stdio.h>
38 #ifdef STDC_HEADERS
39 # include <stdlib.h>
40 # include <stddef.h>
41 #else
42 # ifdef HAVE_STDLIB_H
43 #  include <stdlib.h>
44 # endif
45 #endif /* STDC_HEADERS */
46 #ifdef HAVE_STRING_H
47 # include <string.h>
48 #endif /* HAVE_STRING_H */
49 #ifdef HAVE_STRINGS_H
50 # include <strings.h>
51 #endif /* HAVE_STRINGS_H */
52 #ifdef HAVE_UNISTD_H
53 # include <unistd.h>
54 #endif /* HAVE_UNISTD_H */
55 #if TIME_WITH_SYS_TIME
56 # include <time.h>
57 #endif
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <signal.h>
61 #include <pwd.h>
62 #include <grp.h>
63
64 #include "sudoers.h"
65
66 /* Status codes for timestamp_status() */
67 #define TS_CURRENT              0
68 #define TS_OLD                  1
69 #define TS_MISSING              2
70 #define TS_NOFILE               3
71 #define TS_ERROR                4
72
73 /* Flags for timestamp_status() */
74 #define TS_MAKE_DIRS            1
75 #define TS_REMOVE               2
76
77 /*
78  * Info stored in tty ticket from stat(2) to help with tty matching.
79  */
80 static struct tty_info {
81     dev_t dev;                  /* ID of device tty resides on */
82     dev_t rdev;                 /* tty device ID */
83     ino_t ino;                  /* tty inode number */
84     struct timeval ctime;       /* tty inode change time */
85 } tty_info;
86
87 static int   build_timestamp(char **, char **);
88 static int   timestamp_status(char *, char *, char *, int);
89 static char *expand_prompt(char *, char *, char *);
90 static void  lecture(int);
91 static void  update_timestamp(char *, char *);
92 static bool  tty_is_devpts(const char *);
93 static struct passwd *get_authpw(void);
94
95 /*
96  * Returns true if the user successfully authenticates, else false.
97  */
98 int
99 check_user(int validated, int mode)
100 {
101     struct passwd *auth_pw;
102     char *timestampdir = NULL;
103     char *timestampfile = NULL;
104     char *prompt;
105     struct stat sb;
106     int status, rval = true;
107     bool need_pass = def_authenticate;
108     debug_decl(check_user, SUDO_DEBUG_AUTH)
109
110     /*
111      * Init authentication system regardless of whether we need a password.
112      * Required for proper PAM session support.
113      */
114     auth_pw = get_authpw();
115     if (sudo_auth_init(auth_pw) == -1) {
116         rval = -1;
117         goto done;
118     }
119
120     if (need_pass) {
121         /* Always need a password when -k was specified with the command. */
122         if (ISSET(mode, MODE_IGNORE_TICKET)) {
123             SET(validated, FLAG_CHECK_USER);
124         } else {
125             /*
126              * Don't prompt for the root passwd or if the user is exempt.
127              * If the user is not changing uid/gid, no need for a password.
128              */
129             if (user_uid == 0 || (user_uid == runas_pw->pw_uid &&
130                 (!runas_gr || user_in_group(sudo_user.pw, runas_gr->gr_name)))
131                 || user_is_exempt())
132                 need_pass = false;
133         }
134     }
135     if (!need_pass)
136         goto done;
137
138     /* Stash the tty's ctime for tty ticket comparison. */
139     if (def_tty_tickets && user_ttypath && stat(user_ttypath, &sb) == 0) {
140         tty_info.dev = sb.st_dev;
141         tty_info.ino = sb.st_ino;
142         tty_info.rdev = sb.st_rdev;
143         if (tty_is_devpts(user_ttypath))
144             ctim_get(&sb, &tty_info.ctime);
145     }
146
147     if (build_timestamp(&timestampdir, &timestampfile) == -1) {
148         rval = -1;
149         goto done;
150     }
151
152     status = timestamp_status(timestampdir, timestampfile, user_name,
153         TS_MAKE_DIRS);
154
155     if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) {
156         /* Bail out if we are non-interactive and a password is required */
157         if (ISSET(mode, MODE_NONINTERACTIVE)) {
158             warningx(_("sorry, a password is required to run %s"), getprogname());
159             rval = -1;
160             goto done;
161         }
162
163         /* XXX - should not lecture if askpass helper is being used. */
164         lecture(status);
165
166         /* Expand any escapes in the prompt. */
167         prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt,
168             user_name, user_shost);
169
170         rval = verify_user(auth_pw, prompt);
171     }
172     /* Only update timestamp if user was validated. */
173     if (rval == true && ISSET(validated, VALIDATE_OK) &&
174         !ISSET(mode, MODE_IGNORE_TICKET) && status != TS_ERROR)
175         update_timestamp(timestampdir, timestampfile);
176     efree(timestampdir);
177     efree(timestampfile);
178
179 done:
180     sudo_auth_cleanup(auth_pw);
181     pw_delref(auth_pw);
182
183     debug_return_bool(rval);
184 }
185
186 #define DEFAULT_LECTURE "\n" \
187     "We trust you have received the usual lecture from the local System\n" \
188     "Administrator. It usually boils down to these three things:\n\n" \
189     "    #1) Respect the privacy of others.\n" \
190     "    #2) Think before you type.\n" \
191     "    #3) With great power comes great responsibility.\n\n"
192
193 /*
194  * Standard sudo lecture.
195  */
196 static void
197 lecture(int status)
198 {
199     FILE *fp;
200     char buf[BUFSIZ];
201     ssize_t nread;
202     struct sudo_conv_message msg;
203     struct sudo_conv_reply repl;
204     debug_decl(lecture, SUDO_DEBUG_AUTH)
205
206     if (def_lecture == never ||
207         (def_lecture == once && status != TS_MISSING && status != TS_ERROR))
208         debug_return;
209
210     memset(&msg, 0, sizeof(msg));
211     memset(&repl, 0, sizeof(repl));
212
213     if (def_lecture_file && (fp = fopen(def_lecture_file, "r")) != NULL) {
214         while ((nread = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) != 0) {
215             buf[nread] = '\0';
216             msg.msg_type = SUDO_CONV_ERROR_MSG;
217             msg.msg = buf;
218             sudo_conv(1, &msg, &repl);
219         }
220         fclose(fp);
221     } else {
222         msg.msg_type = SUDO_CONV_ERROR_MSG;
223         msg.msg = _(DEFAULT_LECTURE);
224         sudo_conv(1, &msg, &repl);
225     }
226     debug_return;
227 }
228
229 /*
230  * Update the time on the timestamp file/dir or create it if necessary.
231  */
232 static void
233 update_timestamp(char *timestampdir, char *timestampfile)
234 {
235     debug_decl(update_timestamp, SUDO_DEBUG_AUTH)
236
237     /* If using tty timestamps but we have no tty there is nothing to do. */
238     if (def_tty_tickets && !user_ttypath)
239         debug_return;
240
241     if (timestamp_uid != 0)
242         set_perms(PERM_TIMESTAMP);
243     if (timestampfile) {
244         /*
245          * Store tty info in timestamp file
246          */
247         int fd = open(timestampfile, O_WRONLY|O_CREAT, 0600);
248         if (fd == -1)
249             log_error(USE_ERRNO, _("unable to open %s"), timestampfile);
250         else {
251             lock_file(fd, SUDO_LOCK);
252             if (write(fd, &tty_info, sizeof(tty_info)) != sizeof(tty_info)) {
253                 log_error(USE_ERRNO, _("unable to write to %s"),
254                     timestampfile);
255             }
256             close(fd);
257         }
258     } else {
259         if (touch(-1, timestampdir, NULL) == -1) {
260             if (mkdir(timestampdir, 0700) == -1) {
261                 log_error(USE_ERRNO, _("unable to mkdir %s"),
262                     timestampdir);
263             }
264         }
265     }
266     if (timestamp_uid != 0)
267         restore_perms();
268     debug_return;
269 }
270
271 /*
272  * Expand %h and %u escapes in the prompt and pass back the dynamically
273  * allocated result.  Returns the same string if there are no escapes.
274  */
275 static char *
276 expand_prompt(char *old_prompt, char *user, char *host)
277 {
278     size_t len, n;
279     int subst;
280     char *p, *np, *new_prompt, *endp;
281     debug_decl(expand_prompt, SUDO_DEBUG_AUTH)
282
283     /* How much space do we need to malloc for the prompt? */
284     subst = 0;
285     for (p = old_prompt, len = strlen(old_prompt); *p; p++) {
286         if (p[0] =='%') {
287             switch (p[1]) {
288                 case 'h':
289                     p++;
290                     len += strlen(user_shost) - 2;
291                     subst = 1;
292                     break;
293                 case 'H':
294                     p++;
295                     len += strlen(user_host) - 2;
296                     subst = 1;
297                     break;
298                 case 'p':
299                     p++;
300                     if (def_rootpw)
301                             len += 2;
302                     else if (def_targetpw || def_runaspw)
303                             len += strlen(runas_pw->pw_name) - 2;
304                     else
305                             len += strlen(user_name) - 2;
306                     subst = 1;
307                     break;
308                 case 'u':
309                     p++;
310                     len += strlen(user_name) - 2;
311                     subst = 1;
312                     break;
313                 case 'U':
314                     p++;
315                     len += strlen(runas_pw->pw_name) - 2;
316                     subst = 1;
317                     break;
318                 case '%':
319                     p++;
320                     len--;
321                     subst = 1;
322                     break;
323                 default:
324                     break;
325             }
326         }
327     }
328
329     if (subst) {
330         new_prompt = emalloc(++len);
331         endp = new_prompt + len;
332         for (p = old_prompt, np = new_prompt; *p; p++) {
333             if (p[0] =='%') {
334                 switch (p[1]) {
335                     case 'h':
336                         p++;
337                         n = strlcpy(np, user_shost, np - endp);
338                         if (n >= np - endp)
339                             goto oflow;
340                         np += n;
341                         continue;
342                     case 'H':
343                         p++;
344                         n = strlcpy(np, user_host, np - endp);
345                         if (n >= np - endp)
346                             goto oflow;
347                         np += n;
348                         continue;
349                     case 'p':
350                         p++;
351                         if (def_rootpw)
352                                 n = strlcpy(np, "root", np - endp);
353                         else if (def_targetpw || def_runaspw)
354                                 n = strlcpy(np, runas_pw->pw_name, np - endp);
355                         else
356                                 n = strlcpy(np, user_name, np - endp);
357                         if (n >= np - endp)
358                                 goto oflow;
359                         np += n;
360                         continue;
361                     case 'u':
362                         p++;
363                         n = strlcpy(np, user_name, np - endp);
364                         if (n >= np - endp)
365                             goto oflow;
366                         np += n;
367                         continue;
368                     case 'U':
369                         p++;
370                         n = strlcpy(np,  runas_pw->pw_name, np - endp);
371                         if (n >= np - endp)
372                             goto oflow;
373                         np += n;
374                         continue;
375                     case '%':
376                         /* convert %% -> % */
377                         p++;
378                         break;
379                     default:
380                         /* no conversion */
381                         break;
382                 }
383             }
384             *np++ = *p;
385             if (np >= endp)
386                 goto oflow;
387         }
388         *np = '\0';
389     } else
390         new_prompt = old_prompt;
391
392     debug_return_str(new_prompt);
393
394 oflow:
395     /* We pre-allocate enough space, so this should never happen. */
396     errorx(1, _("internal error, expand_prompt() overflow"));
397 }
398
399 /*
400  * Checks if the user is exempt from supplying a password.
401  */
402 bool
403 user_is_exempt(void)
404 {
405     bool rval = false;
406     debug_decl(user_is_exempt, SUDO_DEBUG_AUTH)
407
408     if (def_exempt_group)
409         rval = user_in_group(sudo_user.pw, def_exempt_group);
410     debug_return_bool(rval);
411 }
412
413 /*
414  * Fills in timestampdir as well as timestampfile if using tty tickets.
415  */
416 static int
417 build_timestamp(char **timestampdir, char **timestampfile)
418 {
419     char *dirparent;
420     int len;
421     debug_decl(build_timestamp, SUDO_DEBUG_AUTH)
422
423     dirparent = def_timestampdir;
424     len = easprintf(timestampdir, "%s/%s", dirparent, user_name);
425     if (len >= PATH_MAX)
426         goto bad;
427
428     /*
429      * Timestamp file may be a file in the directory or NUL to use
430      * the directory as the timestamp.
431      */
432     if (def_tty_tickets) {
433         char *p;
434
435         if ((p = strrchr(user_tty, '/')))
436             p++;
437         else
438             p = user_tty;
439         if (def_targetpw)
440             len = easprintf(timestampfile, "%s/%s/%s:%s", dirparent, user_name,
441                 p, runas_pw->pw_name);
442         else
443             len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, p);
444         if (len >= PATH_MAX)
445             goto bad;
446     } else if (def_targetpw) {
447         len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name,
448             runas_pw->pw_name);
449         if (len >= PATH_MAX)
450             goto bad;
451     } else
452         *timestampfile = NULL;
453
454     debug_return_int(len);
455 bad:
456     log_fatal(0, _("timestamp path too long: %s"), *timestampfile);
457     debug_return_int(-1);
458 }
459
460 /*
461  * Check the timestamp file and directory and return their status.
462  */
463 static int
464 timestamp_status(char *timestampdir, char *timestampfile, char *user, int flags)
465 {
466     struct stat sb;
467     struct timeval boottime, mtime;
468     time_t now;
469     char *dirparent = def_timestampdir;
470     int status = TS_ERROR;              /* assume the worst */
471     debug_decl(timestamp_status, SUDO_DEBUG_AUTH)
472
473     if (timestamp_uid != 0)
474         set_perms(PERM_TIMESTAMP);
475
476     /*
477      * Sanity check dirparent and make it if it doesn't already exist.
478      * We start out assuming the worst (that the dir is not sane) and
479      * if it is ok upgrade the status to ``no timestamp file''.
480      * Note that we don't check the parent(s) of dirparent for
481      * sanity since the sudo dir is often just located in /tmp.
482      */
483     if (lstat(dirparent, &sb) == 0) {
484         if (!S_ISDIR(sb.st_mode))
485             log_error(0, _("%s exists but is not a directory (0%o)"),
486                 dirparent, (unsigned int) sb.st_mode);
487         else if (sb.st_uid != timestamp_uid)
488             log_error(0, _("%s owned by uid %u, should be uid %u"),
489                 dirparent, (unsigned int) sb.st_uid,
490                 (unsigned int) timestamp_uid);
491         else if ((sb.st_mode & 0000022))
492             log_error(0,
493                 _("%s writable by non-owner (0%o), should be mode 0700"),
494                 dirparent, (unsigned int) sb.st_mode);
495         else {
496             if ((sb.st_mode & 0000777) != 0700)
497                 (void) chmod(dirparent, 0700);
498             status = TS_MISSING;
499         }
500     } else if (errno != ENOENT) {
501         log_error(USE_ERRNO, _("unable to stat %s"), dirparent);
502     } else {
503         /* No dirparent, try to make one. */
504         if (ISSET(flags, TS_MAKE_DIRS)) {
505             if (mkdir(dirparent, S_IRWXU))
506                 log_error(USE_ERRNO, _("unable to mkdir %s"),
507                     dirparent);
508             else
509                 status = TS_MISSING;
510         }
511     }
512     if (status == TS_ERROR)
513         goto done;
514
515     /*
516      * Sanity check the user's ticket dir.  We start by downgrading
517      * the status to TS_ERROR.  If the ticket dir exists and is sane
518      * this will be upgraded to TS_OLD.  If the dir does not exist,
519      * it will be upgraded to TS_MISSING.
520      */
521     status = TS_ERROR;                  /* downgrade status again */
522     if (lstat(timestampdir, &sb) == 0) {
523         if (!S_ISDIR(sb.st_mode)) {
524             if (S_ISREG(sb.st_mode)) {
525                 /* convert from old style */
526                 if (unlink(timestampdir) == 0)
527                     status = TS_MISSING;
528             } else
529                 log_error(0, _("%s exists but is not a directory (0%o)"),
530                     timestampdir, (unsigned int) sb.st_mode);
531         } else if (sb.st_uid != timestamp_uid)
532             log_error(0, _("%s owned by uid %u, should be uid %u"),
533                 timestampdir, (unsigned int) sb.st_uid,
534                 (unsigned int) timestamp_uid);
535         else if ((sb.st_mode & 0000022))
536             log_error(0,
537                 _("%s writable by non-owner (0%o), should be mode 0700"),
538                 timestampdir, (unsigned int) sb.st_mode);
539         else {
540             if ((sb.st_mode & 0000777) != 0700)
541                 (void) chmod(timestampdir, 0700);
542             status = TS_OLD;            /* do date check later */
543         }
544     } else if (errno != ENOENT) {
545         log_error(USE_ERRNO, _("unable to stat %s"), timestampdir);
546     } else
547         status = TS_MISSING;
548
549     /*
550      * If there is no user ticket dir, AND we are in tty ticket mode,
551      * AND the TS_MAKE_DIRS flag is set, create the user ticket dir.
552      */
553     if (status == TS_MISSING && timestampfile && ISSET(flags, TS_MAKE_DIRS)) {
554         if (mkdir(timestampdir, S_IRWXU) == -1) {
555             status = TS_ERROR;
556             log_error(USE_ERRNO, _("unable to mkdir %s"), timestampdir);
557         }
558     }
559
560     /*
561      * Sanity check the tty ticket file if it exists.
562      */
563     if (timestampfile && status != TS_ERROR) {
564         if (status != TS_MISSING)
565             status = TS_NOFILE;                 /* dir there, file missing */
566         if (def_tty_tickets && !user_ttypath)
567             goto done;                          /* no tty, always prompt */
568         if (lstat(timestampfile, &sb) == 0) {
569             if (!S_ISREG(sb.st_mode)) {
570                 status = TS_ERROR;
571                 log_error(0, _("%s exists but is not a regular file (0%o)"),
572                     timestampfile, (unsigned int) sb.st_mode);
573             } else {
574                 /* If bad uid or file mode, complain and kill the bogus file. */
575                 if (sb.st_uid != timestamp_uid) {
576                     log_error(0,
577                         _("%s owned by uid %u, should be uid %u"),
578                         timestampfile, (unsigned int) sb.st_uid,
579                         (unsigned int) timestamp_uid);
580                     (void) unlink(timestampfile);
581                 } else if ((sb.st_mode & 0000022)) {
582                     log_error(0,
583                         _("%s writable by non-owner (0%o), should be mode 0600"),
584                         timestampfile, (unsigned int) sb.st_mode);
585                     (void) unlink(timestampfile);
586                 } else {
587                     /* If not mode 0600, fix it. */
588                     if ((sb.st_mode & 0000777) != 0600)
589                         (void) chmod(timestampfile, 0600);
590
591                     /*
592                      * Check for stored tty info.  If the file is zero-sized
593                      * it is an old-style timestamp with no tty info in it.
594                      * If removing, we don't care about the contents.
595                      * The actual mtime check is done later.
596                      */
597                     if (ISSET(flags, TS_REMOVE)) {
598                         status = TS_OLD;
599                     } else if (sb.st_size != 0) {
600                         struct tty_info info;
601                         int fd = open(timestampfile, O_RDONLY, 0644);
602                         if (fd != -1) {
603                             if (read(fd, &info, sizeof(info)) == sizeof(info) &&
604                                 memcmp(&info, &tty_info, sizeof(info)) == 0) {
605                                 status = TS_OLD;
606                             }
607                             close(fd);
608                         }
609                     }
610                 }
611             }
612         } else if (errno != ENOENT) {
613             log_error(USE_ERRNO, _("unable to stat %s"), timestampfile);
614             status = TS_ERROR;
615         }
616     }
617
618     /*
619      * If the file/dir exists and we are not removing it, check its mtime.
620      */
621     if (status == TS_OLD && !ISSET(flags, TS_REMOVE)) {
622         mtim_get(&sb, &mtime);
623         /* Negative timeouts only expire manually (sudo -k). */
624         if (def_timestamp_timeout < 0 && mtime.tv_sec != 0)
625             status = TS_CURRENT;
626         else {
627             now = time(NULL);
628             if (def_timestamp_timeout &&
629                 now - mtime.tv_sec < 60 * def_timestamp_timeout) {
630                 /*
631                  * Check for bogus time on the stampfile.  The clock may
632                  * have been set back or someone could be trying to spoof us.
633                  */
634                 if (mtime.tv_sec > now + 60 * def_timestamp_timeout * 2) {
635                     time_t tv_sec = (time_t)mtime.tv_sec;
636                     log_error(0,
637                         _("timestamp too far in the future: %20.20s"),
638                         4 + ctime(&tv_sec));
639                     if (timestampfile)
640                         (void) unlink(timestampfile);
641                     else
642                         (void) rmdir(timestampdir);
643                     status = TS_MISSING;
644                 } else if (get_boottime(&boottime) && timevalcmp(&mtime, &boottime, <)) {
645                     status = TS_OLD;
646                 } else {
647                     status = TS_CURRENT;
648                 }
649             }
650         }
651     }
652
653 done:
654     if (timestamp_uid != 0)
655         restore_perms();
656     debug_return_int(status);
657 }
658
659 /*
660  * Remove the timestamp ticket file/dir.
661  */
662 void
663 remove_timestamp(bool remove)
664 {
665     struct timeval tv;
666     char *timestampdir, *timestampfile, *path;
667     int status;
668     debug_decl(remove_timestamp, SUDO_DEBUG_AUTH)
669
670     if (build_timestamp(&timestampdir, &timestampfile) == -1)
671         debug_return;
672
673     status = timestamp_status(timestampdir, timestampfile, user_name,
674         TS_REMOVE);
675     if (status != TS_MISSING && status != TS_ERROR) {
676         path = timestampfile ? timestampfile : timestampdir;
677         if (remove) {
678             if (timestampfile)
679                 status = unlink(timestampfile);
680             else
681                 status = rmdir(timestampdir);
682             if (status == -1 && errno != ENOENT) {
683                 log_error(0,
684                     _("unable to remove %s (%s), will reset to the epoch"),
685                     path, strerror(errno));
686                 remove = false;
687             }
688         }
689         if (!remove) {
690             timevalclear(&tv);
691             if (touch(-1, path, &tv) == -1 && errno != ENOENT)
692                 error(1, _("unable to reset %s to the epoch"), path);
693         }
694     }
695     efree(timestampdir);
696     efree(timestampfile);
697
698     debug_return;
699 }
700
701 /*
702  * Returns true if tty lives on a devpts, /dev or /devices filesystem, else
703  * false.  Unlike most filesystems, the ctime of devpts nodes is not updated
704  * when the device node is written to, only when the inode's status changes,
705  * typically via the chmod, chown, link, rename, or utimes system calls.
706  * Since the ctime is "stable" in this case, we can stash it the tty ticket
707  * file and use it to determine whether the tty ticket file is stale.
708  */
709 static bool
710 tty_is_devpts(const char *tty)
711 {
712     bool retval = false;
713 #ifdef __linux__
714     struct statfs sfs;
715     debug_decl(tty_is_devpts, SUDO_DEBUG_PTY)
716
717 #ifndef DEVPTS_SUPER_MAGIC
718 # define DEVPTS_SUPER_MAGIC 0x1cd1
719 #endif
720
721     if (statfs(tty, &sfs) == 0) {
722         if (sfs.f_type == DEVPTS_SUPER_MAGIC)
723             retval = true;
724     }
725 #elif defined(__sun) && defined(__SVR4)
726     struct statvfs sfs;
727     debug_decl(tty_is_devpts, SUDO_DEBUG_PTY)
728
729     if (statvfs(tty, &sfs) == 0) {
730         if (strcmp(sfs.f_fstr, "dev") == 0 || strcmp(sfs.f_fstr, "devices") == 0)
731             retval = true;
732     }
733 #else
734     debug_decl(tty_is_devpts, SUDO_DEBUG_PTY)
735 #endif /* __linux__ */
736     debug_return_bool(retval);
737 }
738
739 /*
740  * Get passwd entry for the user we are going to authenticate as.
741  * By default, this is the user invoking sudo.  In the most common
742  * case, this matches sudo_user.pw or runas_pw.
743  */
744 static struct passwd *
745 get_authpw(void)
746 {
747     struct passwd *pw;
748     debug_decl(get_authpw, SUDO_DEBUG_AUTH)
749
750     if (def_rootpw) {
751         if ((pw = sudo_getpwuid(ROOT_UID)) == NULL)
752             log_fatal(0, _("unknown uid: %u"), ROOT_UID);
753     } else if (def_runaspw) {
754         if ((pw = sudo_getpwnam(def_runas_default)) == NULL)
755             log_fatal(0, _("unknown user: %s"), def_runas_default);
756     } else if (def_targetpw) {
757         if (runas_pw->pw_name == NULL)
758             log_fatal(NO_MAIL|MSG_ONLY, _("unknown uid: %u"),
759                 (unsigned int) runas_pw->pw_uid);
760         pw_addref(runas_pw);
761         pw = runas_pw;
762     } else {
763         pw_addref(sudo_user.pw);
764         pw = sudo_user.pw;
765     }
766
767     debug_return_ptr(pw);
768 }