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>