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

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

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