Annotation of embedaddon/sudo/plugins/sudoers/timestamp.c, revision 1.1.1.2

1.1       misho       1: /*
                      2:  * Copyright (c) 1993-1996,1998-2005, 2007-2013
                      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/time.h>
                     26: #include <sys/stat.h>
                     27: #ifndef __TANDEM
                     28: # include <sys/file.h>
                     29: #endif
                     30: #include <stdio.h>
                     31: #ifdef STDC_HEADERS
                     32: # include <stdlib.h>
                     33: # include <stddef.h>
                     34: #else
                     35: # ifdef HAVE_STDLIB_H
                     36: #  include <stdlib.h>
                     37: # endif
                     38: #endif /* STDC_HEADERS */
                     39: #ifdef HAVE_STRING_H
                     40: # include <string.h>
                     41: #endif /* HAVE_STRING_H */
                     42: #ifdef HAVE_STRINGS_H
                     43: # include <strings.h>
                     44: #endif /* HAVE_STRINGS_H */
                     45: #ifdef HAVE_UNISTD_H
                     46: # include <unistd.h>
                     47: #endif /* HAVE_UNISTD_H */
                     48: #if TIME_WITH_SYS_TIME
                     49: # include <time.h>
                     50: #endif
                     51: #include <errno.h>
                     52: #include <fcntl.h>
                     53: #include <signal.h>
                     54: #include <pwd.h>
                     55: #include <grp.h>
                     56: 
                     57: #include "sudoers.h"
                     58: #include "check.h"
                     59: 
                     60: static struct sudo_tty_info tty_info;
                     61: static char timestampdir[PATH_MAX];
                     62: static char timestampfile[PATH_MAX];
                     63: 
                     64: /*
                     65:  * Fills in timestampdir as well as timestampfile if using tty tickets.
                     66:  */
                     67: int
                     68: build_timestamp(struct passwd *pw)
                     69: {
                     70:     char *dirparent;
                     71:     struct stat sb;
                     72:     int len;
                     73:     debug_decl(build_timestamp, SUDO_DEBUG_AUTH)
                     74: 
                     75:     /* Stash the tty's device, session ID and ctime for ticket comparison. */
1.1.1.2 ! misho      76:     if (def_tty_tickets) {
        !            77:        if (user_ttypath && stat(user_ttypath, &sb) == 0) {
        !            78:            tty_info.dev = sb.st_dev;
        !            79:            tty_info.ino = sb.st_ino;
        !            80:            tty_info.rdev = sb.st_rdev;
        !            81:            tty_info.uid = sb.st_uid;
        !            82:            tty_info.gid = sb.st_gid;
        !            83:        }
1.1       misho      84:        tty_info.sid = user_sid;
                     85:     }
                     86: 
                     87:     dirparent = def_timestampdir;
                     88:     timestampfile[0] = '\0';
                     89:     len = snprintf(timestampdir, sizeof(timestampdir), "%s/%s", dirparent,
                     90:        user_name);
                     91:     if (len <= 0 || len >= sizeof(timestampdir))
                     92:        goto bad;
                     93: 
                     94:     /*
                     95:      * Timestamp file may be a file in the directory or NUL to use
                     96:      * the directory as the timestamp.
                     97:      */
                     98:     if (def_tty_tickets) {
1.1.1.2 ! misho      99:        char pidbuf[sizeof("pid") + (((sizeof(pid_t) * 8) + 2) / 3)];
1.1       misho     100:        char *p;
                    101: 
1.1.1.2 ! misho     102:        if (user_ttypath == NULL) {
        !           103:            /* No tty, use parent pid. */
        !           104:            len = snprintf(pidbuf, sizeof(pidbuf), "pid%u",
        !           105:                (unsigned int)getppid());
        !           106:            if (len <= 0 || len >= sizeof(pidbuf))
        !           107:                goto bad;
        !           108:            p = pidbuf;
        !           109:        } else if ((p = strrchr(user_tty, '/'))) {
1.1       misho     110:            p++;
1.1.1.2 ! misho     111:        } else {
1.1       misho     112:            p = user_tty;
1.1.1.2 ! misho     113:        }
        !           114:        if (def_targetpw) {
1.1       misho     115:            len = snprintf(timestampfile, sizeof(timestampfile), "%s/%s/%s:%s",
                    116:                dirparent, user_name, p, runas_pw->pw_name);
1.1.1.2 ! misho     117:        } else {
1.1       misho     118:            len = snprintf(timestampfile, sizeof(timestampfile), "%s/%s/%s",
                    119:                dirparent, user_name, p);
1.1.1.2 ! misho     120:        }
1.1       misho     121:        if (len <= 0 || len >= sizeof(timestampfile))
                    122:            goto bad;
                    123:     } else if (def_targetpw) {
                    124:        len = snprintf(timestampfile, sizeof(timestampfile), "%s/%s/%s",
                    125:            dirparent, user_name, runas_pw->pw_name);
                    126:        if (len <= 0 || len >= sizeof(timestampfile))
                    127:            goto bad;
                    128:     }
                    129:     sudo_debug_printf(SUDO_DEBUG_INFO, "using timestamp file %s", timestampfile);
                    130: 
                    131:     debug_return_int(len);
                    132: bad:
                    133:     log_fatal(0, N_("timestamp path too long: %s"),
                    134:        *timestampfile ? timestampfile : timestampdir);
                    135:     /* NOTREACHED */
                    136:     debug_return_int(-1);
                    137: }
                    138: 
                    139: /*
                    140:  * Update the time on the timestamp file/dir or create it if necessary.
                    141:  */
                    142: bool
                    143: update_timestamp(struct passwd *pw)
                    144: {
                    145:     debug_decl(update_timestamp, SUDO_DEBUG_AUTH)
                    146: 
                    147:     if (timestamp_uid != 0)
                    148:        set_perms(PERM_TIMESTAMP);
                    149:     if (*timestampfile) {
                    150:        /*
                    151:         * Store tty info in timestamp file
                    152:         */
                    153:        int fd = open(timestampfile, O_WRONLY|O_CREAT, 0600);
                    154:        if (fd == -1)
                    155:            log_warning(USE_ERRNO, N_("unable to open %s"), timestampfile);
                    156:        else {
                    157:            lock_file(fd, SUDO_LOCK);
                    158:            if (write(fd, &tty_info, sizeof(tty_info)) != sizeof(tty_info))
                    159:                log_warning(USE_ERRNO, N_("unable to write to %s"), timestampfile);
                    160:            close(fd);
                    161:        }
                    162:     } else {
                    163:        if (touch(-1, timestampdir, NULL) == -1) {
                    164:            if (mkdir(timestampdir, 0700) == -1) {
                    165:                log_warning(USE_ERRNO, N_("unable to mkdir %s"),
                    166:                    timestampdir);
                    167:            }
                    168:        }
                    169:     }
                    170:     if (timestamp_uid != 0)
                    171:        restore_perms();
                    172:     debug_return_bool(true);
                    173: }
                    174: 
                    175: /*
                    176:  * Check the timestamp file and directory and return their status.
                    177:  */
                    178: static int
                    179: timestamp_status_internal(bool removing)
                    180: {
                    181:     struct stat sb;
                    182:     struct timeval boottime, mtime;
                    183:     time_t now;
                    184:     char *dirparent = def_timestampdir;
                    185:     int status = TS_ERROR;             /* assume the worst */
                    186:     debug_decl(timestamp_status_internal, SUDO_DEBUG_AUTH)
                    187: 
                    188:     if (timestamp_uid != 0)
                    189:        set_perms(PERM_TIMESTAMP);
                    190: 
                    191:     /*
                    192:      * Sanity check dirparent and make it if it doesn't already exist.
                    193:      * We start out assuming the worst (that the dir is not sane) and
                    194:      * if it is ok upgrade the status to ``no timestamp file''.
                    195:      * Note that we don't check the parent(s) of dirparent for
                    196:      * sanity since the sudo dir is often just located in /tmp.
                    197:      */
                    198:     if (lstat(dirparent, &sb) == 0) {
                    199:        if (!S_ISDIR(sb.st_mode))
                    200:            log_warning(0, N_("%s exists but is not a directory (0%o)"),
                    201:                dirparent, (unsigned int) sb.st_mode);
                    202:        else if (sb.st_uid != timestamp_uid)
                    203:            log_warning(0, N_("%s owned by uid %u, should be uid %u"),
                    204:                dirparent, (unsigned int) sb.st_uid,
                    205:                (unsigned int) timestamp_uid);
                    206:        else if ((sb.st_mode & 0000022))
                    207:            log_warning(0,
                    208:                N_("%s writable by non-owner (0%o), should be mode 0700"),
                    209:                dirparent, (unsigned int) sb.st_mode);
                    210:        else {
                    211:            if ((sb.st_mode & 0000777) != 0700)
                    212:                (void) chmod(dirparent, 0700);
                    213:            status = TS_MISSING;
                    214:        }
                    215:     } else if (errno != ENOENT) {
                    216:        log_warning(USE_ERRNO, N_("unable to stat %s"), dirparent);
                    217:     } else {
                    218:        /* No dirparent, try to make one. */
                    219:        if (!removing) {
                    220:            if (mkdir(dirparent, S_IRWXU))
                    221:                log_warning(USE_ERRNO, N_("unable to mkdir %s"),
                    222:                    dirparent);
                    223:            else
                    224:                status = TS_MISSING;
                    225:        }
                    226:     }
                    227:     if (status == TS_ERROR)
                    228:        goto done;
                    229: 
                    230:     /*
                    231:      * Sanity check the user's ticket dir.  We start by downgrading
                    232:      * the status to TS_ERROR.  If the ticket dir exists and is sane
                    233:      * this will be upgraded to TS_OLD.  If the dir does not exist,
                    234:      * it will be upgraded to TS_MISSING.
                    235:      */
                    236:     status = TS_ERROR;                 /* downgrade status again */
                    237:     if (lstat(timestampdir, &sb) == 0) {
                    238:        if (!S_ISDIR(sb.st_mode)) {
                    239:            if (S_ISREG(sb.st_mode)) {
                    240:                /* convert from old style */
                    241:                if (unlink(timestampdir) == 0)
                    242:                    status = TS_MISSING;
                    243:            } else
                    244:                log_warning(0, N_("%s exists but is not a directory (0%o)"),
                    245:                    timestampdir, (unsigned int) sb.st_mode);
                    246:        } else if (sb.st_uid != timestamp_uid)
                    247:            log_warning(0, N_("%s owned by uid %u, should be uid %u"),
                    248:                timestampdir, (unsigned int) sb.st_uid,
                    249:                (unsigned int) timestamp_uid);
                    250:        else if ((sb.st_mode & 0000022))
                    251:            log_warning(0,
                    252:                N_("%s writable by non-owner (0%o), should be mode 0700"),
                    253:                timestampdir, (unsigned int) sb.st_mode);
                    254:        else {
                    255:            if ((sb.st_mode & 0000777) != 0700)
                    256:                (void) chmod(timestampdir, 0700);
                    257:            status = TS_OLD;            /* do date check later */
                    258:        }
                    259:     } else if (errno != ENOENT) {
                    260:        log_warning(USE_ERRNO, N_("unable to stat %s"), timestampdir);
                    261:     } else
                    262:        status = TS_MISSING;
                    263: 
                    264:     /*
                    265:      * If there is no user ticket dir, AND we are in tty ticket mode,
                    266:      * AND we are not just going to remove it, create the user ticket dir.
                    267:      */
                    268:     if (status == TS_MISSING && *timestampfile && !removing) {
                    269:        if (mkdir(timestampdir, S_IRWXU) == -1) {
                    270:            status = TS_ERROR;
                    271:            log_warning(USE_ERRNO, N_("unable to mkdir %s"), timestampdir);
                    272:        }
                    273:     }
                    274: 
                    275:     /*
                    276:      * Sanity check the tty ticket file if it exists.
                    277:      */
                    278:     if (*timestampfile && status != TS_ERROR) {
                    279:        if (status != TS_MISSING)
                    280:            status = TS_NOFILE;                 /* dir there, file missing */
                    281:        if (lstat(timestampfile, &sb) == 0) {
                    282:            if (!S_ISREG(sb.st_mode)) {
                    283:                status = TS_ERROR;
                    284:                log_warning(0, N_("%s exists but is not a regular file (0%o)"),
                    285:                    timestampfile, (unsigned int) sb.st_mode);
                    286:            } else {
                    287:                /* If bad uid or file mode, complain and kill the bogus file. */
                    288:                if (sb.st_uid != timestamp_uid) {
                    289:                    log_warning(0,
                    290:                        N_("%s owned by uid %u, should be uid %u"),
                    291:                        timestampfile, (unsigned int) sb.st_uid,
                    292:                        (unsigned int) timestamp_uid);
                    293:                    (void) unlink(timestampfile);
                    294:                } else if ((sb.st_mode & 0000022)) {
                    295:                    log_warning(0,
                    296:                        N_("%s writable by non-owner (0%o), should be mode 0600"),
                    297:                        timestampfile, (unsigned int) sb.st_mode);
                    298:                    (void) unlink(timestampfile);
                    299:                } else {
                    300:                    /* If not mode 0600, fix it. */
                    301:                    if ((sb.st_mode & 0000777) != 0600)
                    302:                        (void) chmod(timestampfile, 0600);
                    303: 
                    304:                    /*
                    305:                     * Check for stored tty info.  If the file is zero-sized
                    306:                     * it is an old-style timestamp with no tty info in it.
                    307:                     * If removing, we don't care about the contents.
                    308:                     * The actual mtime check is done later.
                    309:                     */
                    310:                    if (removing) {
                    311:                        status = TS_OLD;
                    312:                    } else if (sb.st_size != 0) {
                    313:                        struct sudo_tty_info info;
                    314:                        int fd = open(timestampfile, O_RDONLY, 0644);
                    315:                        if (fd != -1) {
                    316:                            if (read(fd, &info, sizeof(info)) == sizeof(info) &&
                    317:                                memcmp(&info, &tty_info, sizeof(info)) == 0) {
                    318:                                status = TS_OLD;
                    319:                            }
                    320:                            close(fd);
                    321:                        }
                    322:                    }
                    323:                }
                    324:            }
                    325:        } else if (errno != ENOENT) {
                    326:            log_warning(USE_ERRNO, N_("unable to stat %s"), timestampfile);
                    327:            status = TS_ERROR;
                    328:        }
                    329:     }
                    330: 
                    331:     /*
                    332:      * If the file/dir exists and we are not removing it, check its mtime.
                    333:      */
                    334:     if (status == TS_OLD && !removing) {
                    335:        mtim_get(&sb, &mtime);
                    336:        if (timevalisset(&mtime)) {
                    337:            /* Negative timeouts only expire manually (sudo -k). */
                    338:            if (def_timestamp_timeout < 0) {
                    339:                status = TS_CURRENT;
                    340:            } else {
                    341:                time(&now);
                    342:                if (def_timestamp_timeout &&
                    343:                    now - mtime.tv_sec < 60 * def_timestamp_timeout) {
                    344:                    /*
                    345:                     * Check for bogus time on the stampfile.  The clock may
                    346:                     * have been set back or user could be trying to spoof us.
                    347:                     */
                    348:                    if (mtime.tv_sec > now + 60 * def_timestamp_timeout * 2) {
                    349:                        time_t tv_sec = (time_t)mtime.tv_sec;
                    350:                        log_warning(0,
                    351:                            N_("timestamp too far in the future: %20.20s"),
                    352:                            4 + ctime(&tv_sec));
                    353:                        if (*timestampfile)
                    354:                            (void) unlink(timestampfile);
                    355:                        else
                    356:                            (void) rmdir(timestampdir);
                    357:                        status = TS_MISSING;
                    358:                    } else if (get_boottime(&boottime) &&
                    359:                        timevalcmp(&mtime, &boottime, <)) {
                    360:                        status = TS_OLD;
                    361:                    } else {
                    362:                        status = TS_CURRENT;
                    363:                    }
                    364:                }
                    365:            }
                    366:        }
                    367:     }
                    368: 
                    369: done:
                    370:     if (timestamp_uid != 0)
                    371:        restore_perms();
                    372:     debug_return_int(status);
                    373: }
                    374: 
                    375: int
                    376: timestamp_status(struct passwd *pw)
                    377: {
                    378:     return timestamp_status_internal(false);
                    379: }
                    380: 
                    381: /*
                    382:  * Remove the timestamp ticket file/dir.
                    383:  */
                    384: void
                    385: remove_timestamp(bool remove)
                    386: {
                    387:     struct timeval tv;
                    388:     char *path;
                    389:     int status;
                    390:     debug_decl(remove_timestamp, SUDO_DEBUG_AUTH)
                    391: 
                    392:     if (build_timestamp(NULL) == -1)
                    393:        debug_return;
                    394: 
                    395:     status = timestamp_status_internal(true);
                    396:     if (status != TS_MISSING && status != TS_ERROR) {
                    397:        path = *timestampfile ? timestampfile : timestampdir;
                    398:        if (remove) {
                    399:            if (*timestampfile)
                    400:                status = unlink(timestampfile);
                    401:            else
                    402:                status = rmdir(timestampdir);
                    403:            if (status == -1 && errno != ENOENT) {
                    404:                log_warning(0,
1.1.1.2 ! misho     405:                    N_("unable to remove %s, will reset to the Unix epoch"),
        !           406:                    path);
1.1       misho     407:                remove = false;
                    408:            }
                    409:        }
                    410:        if (!remove) {
                    411:            timevalclear(&tv);
                    412:            if (touch(-1, path, &tv) == -1 && errno != ENOENT)
1.1.1.2 ! misho     413:                fatal(_("unable to reset %s to the Unix epoch"), path);
1.1       misho     414:        }
                    415:     }
                    416: 
                    417:     debug_return;
                    418: }
                    419: 
                    420: /*
                    421:  * Lecture status is currently implied by the timestamp status but
                    422:  * may be stored separately in a future release.
                    423:  */
                    424: bool
                    425: set_lectured(void)
                    426: {
                    427:     return true;
                    428: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>