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