1/* 2 * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com> 3 * 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. 7 * 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. 15 */ 16 17#include <config.h> 18 19#include <sys/types.h> 20#include <sys/param.h> 21#include <sys/stat.h> 22#include <sys/time.h> 23#include <stdio.h> 24#ifdef STDC_HEADERS 25# include <stdlib.h> 26# include <stddef.h> 27#else 28# ifdef HAVE_STDLIB_H 29# include <stdlib.h> 30# endif 31#endif /* STDC_HEADERS */ 32#ifdef HAVE_STRING_H 33# include <string.h> 34#endif /* HAVE_STRING_H */ 35#ifdef HAVE_STRINGS_H 36# include <strings.h> 37#endif /* HAVE_STRINGS_H */ 38#ifdef HAVE_UNISTD_H 39# include <unistd.h> 40#endif /* HAVE_UNISTD_H */ 41#if TIME_WITH_SYS_TIME 42# include <time.h> 43#endif 44#include <errno.h> 45#include <fcntl.h> 46#include <signal.h> 47#include <pwd.h> 48#include <grp.h> 49#ifdef HAVE_ZLIB_H 50# include <zlib.h> 51#endif 52 53#include "sudo.h" 54 55union io_fd { 56 FILE *f; 57#ifdef HAVE_ZLIB_H 58 gzFile g; 59#endif 60 void *v; 61}; 62 63struct script_buf { 64 int len; /* buffer length (how much read in) */ 65 int off; /* write position (how much already consumed) */ 66 char buf[16 * 1024]; 67}; 68 69#define IOFD_STDIN 0 70#define IOFD_STDOUT 1 71#define IOFD_STDERR 2 72#define IOFD_TTYIN 3 73#define IOFD_TTYOUT 4 74#define IOFD_TIMING 5 75#define IOFD_MAX 6 76 77#ifdef __STDC__ 78# define SESSID_MAX 2176782336U 79#else 80# define SESSID_MAX (unsigned long)2176782336 81#endif 82 83static sigset_t ttyblock; 84static struct timeval last_time; 85static union io_fd io_fds[IOFD_MAX]; 86 87void 88io_nextid() 89{ 90 struct stat sb; 91 char buf[32], *ep; 92 int fd, i; 93 unsigned long id = 0; 94 int len; 95 ssize_t nread; 96 char pathbuf[PATH_MAX]; 97 static const char b36char[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 98 99 /* 100 * Create I/O log directory if it doesn't already exist. 101 */ 102 if (stat(def_iolog_dir, &sb) != 0) { 103 if (mkdir(def_iolog_dir, S_IRWXU) != 0) 104 log_fatal(USE_ERRNO, "Can't mkdir %s", def_iolog_dir); 105 (void) chown(def_iolog_dir, (uid_t)-1, ROOT_GID); 106 } else if (!S_ISDIR(sb.st_mode)) { 107 log_fatal(0, "%s exists but is not a directory (0%o)", 108 def_iolog_dir, (unsigned int) sb.st_mode); 109 } 110 111 /* 112 * Open sequence file 113 */ 114 len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", def_iolog_dir); 115 if (len <= 0 || len >= sizeof(pathbuf)) { 116 errno = ENAMETOOLONG; 117 log_fatal(USE_ERRNO, "%s/seq", pathbuf); 118 } 119 fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); 120 if (fd == -1) 121 log_fatal(USE_ERRNO, "cannot open %s", pathbuf); 122 lock_file(fd, SUDO_LOCK); 123 124 /* Read seq number (base 36). */ 125 nread = read(fd, buf, sizeof(buf)); 126 if (nread != 0) { 127 if (nread == -1) 128 log_fatal(USE_ERRNO, "cannot read %s", pathbuf); 129 id = strtoul(buf, &ep, 36); 130 if (buf == ep || id >= SESSID_MAX) 131 log_fatal(0, "invalid sequence number %s", pathbuf); 132 } 133 id++; 134 135 /* 136 * Convert id to a string and stash in sudo_user.sessid. 137 * Note that that least significant digits go at the end of the string. 138 */ 139 for (i = 5; i >= 0; i--) { 140 buf[i] = b36char[id % 36]; 141 id /= 36; 142 } 143 buf[6] = '\n'; 144 145 /* Stash id for logging purposes. */ 146 memcpy(sudo_user.sessid, buf, 6); 147 sudo_user.sessid[6] = '\0'; 148 149 /* Rewind and overwrite old seq file. */ 150 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7) 151 log_fatal(USE_ERRNO, "Can't write to %s", pathbuf); 152 close(fd); 153} 154 155static int 156build_idpath(pathbuf, pathsize) 157 char *pathbuf; 158 size_t pathsize; 159{ 160 struct stat sb; 161 int i, len; 162 163 if (sudo_user.sessid[0] == '\0') 164 log_fatal(0, "tried to build a session id path without a session id"); 165 166 /* 167 * Path is of the form /var/log/sudo-io/00/00/01. 168 */ 169 len = snprintf(pathbuf, pathsize, "%s/%c%c/%c%c/%c%c", def_iolog_dir, 170 sudo_user.sessid[0], sudo_user.sessid[1], sudo_user.sessid[2], 171 sudo_user.sessid[3], sudo_user.sessid[4], sudo_user.sessid[5]); 172 if (len <= 0 && len >= pathsize) { 173 errno = ENAMETOOLONG; 174 log_fatal(USE_ERRNO, "%s/%s", def_iolog_dir, sudo_user.sessid); 175 } 176 177 /* 178 * Create the intermediate subdirs as needed. 179 */ 180 for (i = 6; i > 0; i -= 3) { 181 pathbuf[len - i] = '\0'; 182 if (stat(pathbuf, &sb) != 0) { 183 if (mkdir(pathbuf, S_IRWXU) != 0) 184 log_fatal(USE_ERRNO, "Can't mkdir %s", pathbuf); 185 (void) chown(pathbuf, (uid_t)-1, ROOT_GID); 186 } else if (!S_ISDIR(sb.st_mode)) { 187 log_fatal(0, "%s: %s", pathbuf, strerror(ENOTDIR)); 188 } 189 pathbuf[len - i] = '/'; 190 } 191 192 return len; 193} 194 195static void * 196open_io_fd(pathbuf, len, suffix, docompress) 197 char *pathbuf; 198 int len; 199 const char *suffix; 200 int docompress; 201{ 202 void *vfd = NULL; 203 int fd; 204 205 pathbuf[len] = '\0'; 206 strlcat(pathbuf, suffix, PATH_MAX); 207 fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); 208 if (fd != -1) { 209 fcntl(fd, F_SETFD, FD_CLOEXEC); 210#ifdef HAVE_ZLIB_H 211 if (docompress) 212 vfd = gzdopen(fd, "w"); 213 else 214#endif 215 vfd = fdopen(fd, "w"); 216 } 217 return vfd; 218} 219 220int 221io_log_open() 222{ 223 char pathbuf[PATH_MAX]; 224 FILE *io_logfile; 225 int len; 226 227 if (!def_log_input && !def_log_output) 228 return FALSE; 229 230 /* 231 * Build a path containing the session id split into two-digit subdirs, 232 * so ID 000001 becomes /var/log/sudo-io/00/00/01. 233 */ 234 len = build_idpath(pathbuf, sizeof(pathbuf)); 235 if (len == -1) 236 return -1; 237 238 if (mkdir(pathbuf, S_IRUSR|S_IWUSR|S_IXUSR) != 0) 239 log_fatal(USE_ERRNO, "Can't mkdir %s", pathbuf); 240 (void) chown(pathbuf, (uid_t)-1, ROOT_GID); 241 242 /* 243 * We create 7 files: a log file, a timing file and 5 for input/output. 244 */ 245 io_logfile = open_io_fd(pathbuf, len, "/log", FALSE); 246 if (io_logfile == NULL) 247 log_fatal(USE_ERRNO, "Can't create %s", pathbuf); 248 249 io_fds[IOFD_TIMING].v = open_io_fd(pathbuf, len, "/timing", def_compress_io); 250 if (io_fds[IOFD_TIMING].v == NULL) 251 log_fatal(USE_ERRNO, "Can't create %s", pathbuf); 252 253 if (def_log_input) { 254 io_fds[IOFD_TTYIN].v = open_io_fd(pathbuf, len, "/ttyin", def_compress_io); 255 if (io_fds[IOFD_TTYIN].v == NULL) 256 log_fatal(USE_ERRNO, "Can't create %s", pathbuf); 257 } 258 259 if (def_log_output) { 260 io_fds[IOFD_TTYOUT].v = open_io_fd(pathbuf, len, "/ttyout", def_compress_io); 261 if (io_fds[IOFD_TTYOUT].v == NULL) 262 log_fatal(USE_ERRNO, "Can't create %s", pathbuf); 263 } 264 265 if (def_log_input) { 266 io_fds[IOFD_STDIN].v = open_io_fd(pathbuf, len, "/stdin", def_compress_io); 267 if (io_fds[IOFD_STDIN].v == NULL) 268 log_fatal(USE_ERRNO, "Can't create %s", pathbuf); 269 } 270 271 if (def_log_output) { 272 io_fds[IOFD_STDOUT].v = open_io_fd(pathbuf, len, "/stdout", def_compress_io); 273 if (io_fds[IOFD_STDOUT].v == NULL) 274 log_fatal(USE_ERRNO, "Can't create %s", pathbuf); 275 } 276 277 if (def_log_output) { 278 io_fds[IOFD_STDERR].v = open_io_fd(pathbuf, len, "/stderr", def_compress_io); 279 if (io_fds[IOFD_STDERR].v == NULL) 280 log_fatal(USE_ERRNO, "Can't create %s", pathbuf); 281 } 282 283 /* So we can block tty-generated signals */ 284 sigemptyset(&ttyblock); 285 sigaddset(&ttyblock, SIGINT); 286 sigaddset(&ttyblock, SIGQUIT); 287 sigaddset(&ttyblock, SIGTSTP); 288 sigaddset(&ttyblock, SIGTTIN); 289 sigaddset(&ttyblock, SIGTTOU); 290 291 gettimeofday(&last_time, NULL); 292 293 /* XXX - log more stuff? window size? environment? */ 294 fprintf(io_logfile, "%ld:%s:%s:%s:%s\n", (long)last_time.tv_sec, user_name, 295 runas_pw->pw_name, runas_gr ? runas_gr->gr_name : "", user_tty); 296 fprintf(io_logfile, "%s\n", user_cwd); 297 fprintf(io_logfile, "%s%s%s\n", user_cmnd, user_args ? " " : "", 298 user_args ? user_args : ""); 299 fclose(io_logfile); 300 301 return TRUE; 302} 303 304void 305io_log_close() 306{ 307 int i; 308 309 for (i = 0; i < IOFD_MAX; i++) { 310 if (io_fds[i].v == NULL) 311 continue; 312#ifdef HAVE_ZLIB_H 313 if (def_compress_io) 314 gzclose(io_fds[i].g); 315 else 316#endif 317 fclose(io_fds[i].f); 318 } 319} 320 321static int 322log_io(buf, len, idx) 323 const char *buf; 324 unsigned int len; 325 int idx; 326{ 327 struct timeval now, delay; 328 sigset_t omask; 329 330 gettimeofday(&now, NULL); 331 332 sigprocmask(SIG_BLOCK, &ttyblock, &omask); 333 334#ifdef HAVE_ZLIB_H 335 if (def_compress_io) 336 ignore_result(gzwrite(io_fds[idx].g, (const voidp)buf, len)); 337 else 338#endif 339 ignore_result(fwrite(buf, 1, len, io_fds[idx].f)); 340 delay.tv_sec = now.tv_sec; 341 delay.tv_usec = now.tv_usec; 342 timevalsub(&delay, &last_time); 343#ifdef HAVE_ZLIB_H 344 if (def_compress_io) 345 gzprintf(io_fds[IOFD_TIMING].g, "%d %f %d\n", idx, 346 delay.tv_sec + ((double)delay.tv_usec / 1000000), len); 347 else 348#endif 349 fprintf(io_fds[IOFD_TIMING].f, "%d %f %d\n", idx, 350 delay.tv_sec + ((double)delay.tv_usec / 1000000), len); 351 last_time.tv_sec = now.tv_sec; 352 last_time.tv_usec = now.tv_usec; 353 354 sigprocmask(SIG_SETMASK, &omask, NULL); 355 356 return TRUE; 357} 358 359int 360log_ttyin(buf, len) 361 const char *buf; 362 unsigned int len; 363{ 364 if (!io_fds[IOFD_TTYIN].v) 365 return TRUE; 366 return log_io(buf, len, IOFD_TTYIN); 367} 368 369int 370log_ttyout(buf, len) 371 const char *buf; 372 unsigned int len; 373{ 374 if (!io_fds[IOFD_TTYOUT].v) 375 return TRUE; 376 return log_io(buf, len, IOFD_TTYOUT); 377} 378 379int 380log_stdin(buf, len) 381 const char *buf; 382 unsigned int len; 383{ 384 if (!io_fds[IOFD_STDIN].v) 385 return TRUE; 386 return log_io(buf, len, IOFD_STDIN); 387} 388 389int 390log_stdout(buf, len) 391 const char *buf; 392 unsigned int len; 393{ 394 if (!io_fds[IOFD_STDOUT].v) 395 return TRUE; 396 return log_io(buf, len, IOFD_STDOUT); 397} 398 399int 400log_stderr(buf, len) 401 const char *buf; 402 unsigned int len; 403{ 404 if (!io_fds[IOFD_STDOUT].v) 405 return TRUE; 406 return log_io(buf, len, IOFD_STDERR); 407} 408