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