2 * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
20 #include <sys/param.h>
31 #endif /* STDC_HEADERS */
34 #endif /* HAVE_STRING_H */
37 #endif /* HAVE_STRINGS_H */
40 #endif /* HAVE_UNISTD_H */
41 #if TIME_WITH_SYS_TIME
64 int len; /* buffer length (how much read in) */
65 int off; /* write position (how much already consumed) */
78 # define SESSID_MAX 2176782336U
80 # define SESSID_MAX (unsigned long)2176782336
83 static sigset_t ttyblock;
84 static struct timeval last_time;
85 static union io_fd io_fds[IOFD_MAX];
96 char pathbuf[PATH_MAX];
99 * Create _PATH_SUDO_IO_LOGDIR if it doesn't already exist.
101 if (stat(_PATH_SUDO_IO_LOGDIR, &sb) != 0) {
102 if (mkdir(_PATH_SUDO_IO_LOGDIR, S_IRWXU) != 0)
103 log_error(USE_ERRNO, "Can't mkdir %s", _PATH_SUDO_IO_LOGDIR);
104 } else if (!S_ISDIR(sb.st_mode)) {
105 log_error(0, "%s exists but is not a directory (0%o)",
106 _PATH_SUDO_IO_LOGDIR, (unsigned int) sb.st_mode);
112 len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", _PATH_SUDO_IO_LOGDIR);
113 if (len <= 0 || len >= sizeof(pathbuf)) {
114 errno = ENAMETOOLONG;
115 log_error(USE_ERRNO, "%s/seq", pathbuf);
117 fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
119 log_error(USE_ERRNO, "cannot open %s", pathbuf);
120 lock_file(fd, SUDO_LOCK);
122 /* Read seq number (base 36). */
123 nread = read(fd, buf, sizeof(buf));
126 log_error(USE_ERRNO, "cannot read %s", pathbuf);
127 id = strtoul(buf, &ep, 36);
128 if (buf == ep || id >= SESSID_MAX)
129 log_error(0, "invalid sequence number %s", pathbuf);
134 * Convert id to a string and stash in sudo_user.sessid.
135 * Note that that least significant digits go at the end of the string.
137 for (i = 5; i >= 0; i--) {
140 buf[i] = ch < 10 ? ch + '0' : ch - 10 + 'A';
144 /* Stash id logging purposes */
145 memcpy(sudo_user.sessid, buf, 6);
146 sudo_user.sessid[6] = '\0';
148 /* Rewind and overwrite old seq file. */
149 if (lseek(fd, 0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7)
150 log_error(USE_ERRNO, "Can't write to %s", pathbuf);
155 build_idpath(pathbuf, pathsize)
162 if (sudo_user.sessid[0] == '\0')
163 log_error(0, "tried to build a session id path without a session id");
166 * Path is of the form /var/log/sudo-session/00/00/01.
168 len = snprintf(pathbuf, pathsize, "%s/%c%c/%c%c/%c%c", _PATH_SUDO_IO_LOGDIR,
169 sudo_user.sessid[0], sudo_user.sessid[1], sudo_user.sessid[2],
170 sudo_user.sessid[3], sudo_user.sessid[4], sudo_user.sessid[5]);
171 if (len <= 0 && len >= pathsize) {
172 errno = ENAMETOOLONG;
173 log_error(USE_ERRNO, "%s/%s", _PATH_SUDO_IO_LOGDIR, sudo_user.sessid);
177 * Create the intermediate subdirs as needed.
179 for (i = 6; i > 0; i -= 3) {
180 pathbuf[len - i] = '\0';
181 if (stat(pathbuf, &sb) != 0) {
182 if (mkdir(pathbuf, S_IRWXU) != 0)
183 log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
184 } else if (!S_ISDIR(sb.st_mode)) {
185 log_error(0, "%s: %s", pathbuf, strerror(ENOTDIR));
187 pathbuf[len - i] = '/';
194 open_io_fd(pathbuf, len, suffix, docompress)
204 strlcat(pathbuf, suffix, PATH_MAX);
205 fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
207 fcntl(fd, F_SETFD, FD_CLOEXEC);
210 vfd = gzdopen(fd, "w");
213 vfd = fdopen(fd, "w");
221 char pathbuf[PATH_MAX];
225 if (!def_log_input && !def_log_output)
229 * Build a path containing the session id split into two-digit subdirs,
230 * so ID 000001 becomes /var/log/sudo-session/00/00/01.
232 len = build_idpath(pathbuf, sizeof(pathbuf));
236 if (mkdir(pathbuf, S_IRUSR|S_IWUSR|S_IXUSR) != 0)
237 log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
240 * We create 7 files: a log file, a timing file and 5 for input/output.
242 io_logfile = open_io_fd(pathbuf, len, "/log", FALSE);
243 if (io_logfile == NULL)
244 log_error(USE_ERRNO, "Can't create %s", pathbuf);
246 io_fds[IOFD_TIMING].v = open_io_fd(pathbuf, len, "/timing", def_compress_io);
247 if (io_fds[IOFD_TIMING].v == NULL)
248 log_error(USE_ERRNO, "Can't create %s", pathbuf);
251 io_fds[IOFD_TTYIN].v = open_io_fd(pathbuf, len, "/ttyin", def_compress_io);
252 if (io_fds[IOFD_TTYIN].v == NULL)
253 log_error(USE_ERRNO, "Can't create %s", pathbuf);
256 if (def_log_output) {
257 io_fds[IOFD_TTYOUT].v = open_io_fd(pathbuf, len, "/ttyout", def_compress_io);
258 if (io_fds[IOFD_TTYOUT].v == NULL)
259 log_error(USE_ERRNO, "Can't create %s", pathbuf);
263 io_fds[IOFD_STDIN].v = open_io_fd(pathbuf, len, "/stdin", def_compress_io);
264 if (io_fds[IOFD_STDIN].v == NULL)
265 log_error(USE_ERRNO, "Can't create %s", pathbuf);
268 if (def_log_output) {
269 io_fds[IOFD_STDOUT].v = open_io_fd(pathbuf, len, "/stdout", def_compress_io);
270 if (io_fds[IOFD_STDOUT].v == NULL)
271 log_error(USE_ERRNO, "Can't create %s", pathbuf);
274 if (def_log_output) {
275 io_fds[IOFD_STDERR].v = open_io_fd(pathbuf, len, "/stderr", def_compress_io);
276 if (io_fds[IOFD_STDERR].v == NULL)
277 log_error(USE_ERRNO, "Can't create %s", pathbuf);
280 /* So we can block tty-generated signals */
281 sigemptyset(&ttyblock);
282 sigaddset(&ttyblock, SIGINT);
283 sigaddset(&ttyblock, SIGQUIT);
284 sigaddset(&ttyblock, SIGTSTP);
285 sigaddset(&ttyblock, SIGTTIN);
286 sigaddset(&ttyblock, SIGTTOU);
288 gettimeofday(&last_time, NULL);
290 /* XXX - log more stuff? window size? environment? */
291 fprintf(io_logfile, "%ld:%s:%s:%s:%s\n", (long)last_time.tv_sec, user_name,
292 runas_pw->pw_name, runas_gr ? runas_gr->gr_name : "", user_tty);
293 fprintf(io_logfile, "%s\n", user_cwd);
294 fprintf(io_logfile, "%s%s%s\n", user_cmnd, user_args ? " " : "",
295 user_args ? user_args : "");
306 for (i = 0; i < IOFD_MAX; i++) {
307 if (io_fds[i].v == NULL)
311 gzclose(io_fds[i].g);
319 log_io(buf, len, idx)
324 struct timeval now, delay;
327 gettimeofday(&now, NULL);
329 sigprocmask(SIG_BLOCK, &ttyblock, &omask);
333 gzwrite(io_fds[idx].g, buf, len);
336 fwrite(buf, 1, len, io_fds[idx].f);
337 delay.tv_sec = now.tv_sec;
338 delay.tv_usec = now.tv_usec;
339 timevalsub(&delay, &last_time);
342 gzprintf(io_fds[IOFD_TIMING].g, "%d %f %d\n", idx,
343 delay.tv_sec + ((double)delay.tv_usec / 1000000), len);
346 fprintf(io_fds[IOFD_TIMING].f, "%d %f %d\n", idx,
347 delay.tv_sec + ((double)delay.tv_usec / 1000000), len);
348 last_time.tv_sec = now.tv_sec;
349 last_time.tv_usec = now.tv_usec;
351 sigprocmask(SIG_SETMASK, &omask, NULL);
361 if (!io_fds[IOFD_TTYIN].v)
363 return log_io(buf, len, IOFD_TTYIN);
371 if (!io_fds[IOFD_TTYOUT].v)
373 return log_io(buf, len, IOFD_TTYOUT);
381 if (!io_fds[IOFD_STDIN].v)
383 return log_io(buf, len, IOFD_STDIN);
391 if (!io_fds[IOFD_STDOUT].v)
393 return log_io(buf, len, IOFD_STDOUT);
401 if (!io_fds[IOFD_STDOUT].v)
403 return log_io(buf, len, IOFD_STDERR);