Annotation of embedaddon/sudo/plugins/sudoers/timestamp.c, revision 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>