1174294Sobrien/* 2174294Sobrien * Copyright (c) 1997-2006 Erez Zadok 3174294Sobrien * Copyright (c) 1990 Jan-Simon Pendry 4174294Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 5174294Sobrien * Copyright (c) 1990 The Regents of the University of California. 6174294Sobrien * All rights reserved. 7174294Sobrien * 8174294Sobrien * This code is derived from software contributed to Berkeley by 9174294Sobrien * Jan-Simon Pendry at Imperial College, London. 10174294Sobrien * 11174294Sobrien * Redistribution and use in source and binary forms, with or without 12174294Sobrien * modification, are permitted provided that the following conditions 13174294Sobrien * are met: 14174294Sobrien * 1. Redistributions of source code must retain the above copyright 15174294Sobrien * notice, this list of conditions and the following disclaimer. 16174294Sobrien * 2. Redistributions in binary form must reproduce the above copyright 17174294Sobrien * notice, this list of conditions and the following disclaimer in the 18174294Sobrien * documentation and/or other materials provided with the distribution. 19174294Sobrien * 3. All advertising materials mentioning features or use of this software 20174294Sobrien * must display the following acknowledgment: 21174294Sobrien * This product includes software developed by the University of 22174294Sobrien * California, Berkeley and its contributors. 23174294Sobrien * 4. Neither the name of the University nor the names of its contributors 24174294Sobrien * may be used to endorse or promote products derived from this software 25174294Sobrien * without specific prior written permission. 26174294Sobrien * 27174294Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28174294Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29174294Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30174294Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31174294Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32174294Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33174294Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34174294Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35174294Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36174294Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37174294Sobrien * SUCH DAMAGE. 38174294Sobrien * 39174294Sobrien * 40174294Sobrien * File: am-utils/amd/info_exec.c 41174294Sobrien * 42174294Sobrien */ 43174294Sobrien 44174294Sobrien/* 45174294Sobrien * Get info from executable map 46174294Sobrien * 47174294Sobrien * Original from Erik Kline, 2004. 48174294Sobrien */ 49174294Sobrien 50174294Sobrien#ifdef HAVE_CONFIG_H 51174294Sobrien# include <config.h> 52174294Sobrien#endif /* HAVE_CONFIG_H */ 53174294Sobrien#include <am_defs.h> 54174294Sobrien#include <amd.h> 55174294Sobrien 56174294Sobrien#define MAX_LINE_LEN 1500 57174294Sobrien 58174294Sobrien/* forward declarations */ 59174294Sobrienint exec_init(mnt_map *m, char *map, time_t *tp); 60174294Sobrienint exec_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp); 61174294Sobrien 62174294Sobrien 63174294Sobrien/* 64174294Sobrien * a timed fgets() 65174294Sobrien */ 66174294Sobrienstatic char * 67174294Sobrienfgets_timed(char *s, int size, int rdfd, int secs) 68174294Sobrien{ 69174294Sobrien fd_set fds; 70174294Sobrien struct timeval timeo; 71174294Sobrien time_t start, now; 72174294Sobrien int rval=0, i=0; 73174294Sobrien 74174294Sobrien if (!s || size < 0 || rdfd < 0) 75174294Sobrien return 0; 76174294Sobrien 77174294Sobrien s[0] = 0; 78174294Sobrien if (size == 0) 79174294Sobrien return s; 80174294Sobrien 81174294Sobrien start = clocktime(NULL); 82174294Sobrien while (s[i] != '\n' && i < size-1) { 83174294Sobrien s[i+1] = 0; /* places the requisite trailing '\0' */ 84174294Sobrien 85174294Sobrien /* ready for reading */ 86174294Sobrien rval = read(rdfd, (void *)(s+i), 1); 87174294Sobrien if (rval == 1) { 88174294Sobrien if (s[i] == 0) { 89174294Sobrien rval = 0; 90174294Sobrien break; 91174294Sobrien } 92174294Sobrien i++; 93174294Sobrien continue; 94174294Sobrien } else if (rval == 0) { 95174294Sobrien break; 96174294Sobrien } else if (rval < 0 && errno != EAGAIN && errno != EINTR) { 97174294Sobrien plog(XLOG_WARNING, "fgets_timed read error: %m"); 98174294Sobrien break; 99174294Sobrien } 100174294Sobrien 101174294Sobrien timeo.tv_usec = 0; 102174294Sobrien now = clocktime(NULL) - start; 103174294Sobrien if (secs <= 0) 104174294Sobrien timeo.tv_sec = 0; 105174294Sobrien else if (now < secs) 106174294Sobrien timeo.tv_sec = secs - now; 107174294Sobrien else { 108174294Sobrien /* timed out (now>=secs) */ 109174294Sobrien plog(XLOG_WARNING, "executable map read timed out (> %d secs)", secs); 110174294Sobrien rval = -1; 111174294Sobrien break; 112174294Sobrien } 113174294Sobrien 114174294Sobrien FD_ZERO(&fds); 115174294Sobrien FD_SET(rdfd, &fds); 116174294Sobrien 117174294Sobrien rval = select(rdfd+1, &fds, 0, 0, &timeo); 118174294Sobrien if (rval < 0) { 119174294Sobrien /* error selecting */ 120174294Sobrien plog(XLOG_WARNING, "fgets_timed select error: %m"); 121174294Sobrien if (errno == EINTR) 122174294Sobrien continue; 123174294Sobrien rval = -1; 124174294Sobrien break; 125174294Sobrien } else if (rval == 0) { 126174294Sobrien /* timed out */ 127174294Sobrien plog(XLOG_WARNING, "executable map read timed out (> %d secs)", secs); 128174294Sobrien rval = -1; 129174294Sobrien break; 130174294Sobrien } 131174294Sobrien } 132174294Sobrien 133174294Sobrien if (rval > 0) 134174294Sobrien return s; 135174294Sobrien 136174294Sobrien close(rdfd); 137174294Sobrien return (rval == 0 ? s : 0); 138174294Sobrien} 139174294Sobrien 140174294Sobrien 141174294Sobrienstatic int 142174294Sobrienread_line(char *buf, int size, int fd) 143174294Sobrien{ 144174294Sobrien int done = 0; 145174294Sobrien 146174294Sobrien while (fgets_timed(buf, size, fd, gopt.exec_map_timeout)) { 147174294Sobrien int len = strlen(buf); 148174294Sobrien done += len; 149174294Sobrien if (len > 1 && buf[len - 2] == '\\' && 150174294Sobrien buf[len - 1] == '\n') { 151174294Sobrien buf += len - 2; 152174294Sobrien size -= len - 2; 153174294Sobrien *buf = '\n'; 154174294Sobrien buf[1] = '\0'; 155174294Sobrien } else { 156174294Sobrien return done; 157174294Sobrien } 158174294Sobrien } 159174294Sobrien 160174294Sobrien return done; 161174294Sobrien} 162174294Sobrien 163174294Sobrien 164174294Sobrien/* 165174294Sobrien * Try to locate a value in a query answer 166174294Sobrien */ 167174294Sobrienstatic int 168174294Sobrienexec_parse_qanswer(int fd, char *map, char *key, char **pval, time_t *tp) 169174294Sobrien{ 170174294Sobrien char qanswer[MAX_LINE_LEN], *dc = 0; 171174294Sobrien int chuck = 0; 172174294Sobrien int line_no = 0; 173174294Sobrien 174174294Sobrien while (read_line(qanswer, sizeof(qanswer), fd)) { 175174294Sobrien char *cp; 176174294Sobrien char *hash; 177174294Sobrien int len = strlen(qanswer); 178174294Sobrien line_no++; 179174294Sobrien 180174294Sobrien /* 181174294Sobrien * Make sure we got the whole line 182174294Sobrien */ 183174294Sobrien if (qanswer[len - 1] != '\n') { 184174294Sobrien plog(XLOG_WARNING, "line %d in \"%s\" is too long", line_no, map); 185174294Sobrien chuck = 1; 186174294Sobrien } else { 187174294Sobrien qanswer[len - 1] = '\0'; 188174294Sobrien } 189174294Sobrien 190174294Sobrien /* 191174294Sobrien * Strip comments 192174294Sobrien */ 193174294Sobrien hash = strchr(qanswer, '#'); 194174294Sobrien if (hash) 195174294Sobrien *hash = '\0'; 196174294Sobrien 197174294Sobrien /* 198174294Sobrien * Find beginning of value (query answer) 199174294Sobrien */ 200174294Sobrien for (cp = qanswer; *cp && !isascii((int)*cp) && !isspace((int)*cp); cp++) 201174294Sobrien ;; 202174294Sobrien 203174294Sobrien /* Ignore blank lines */ 204174294Sobrien if (!*cp) 205174294Sobrien goto again; 206174294Sobrien 207174294Sobrien /* 208174294Sobrien * Return a copy of the data 209174294Sobrien */ 210174294Sobrien dc = strdup(cp); 211174294Sobrien *pval = dc; 212174294Sobrien dlog("%s returns %s", key, dc); 213174294Sobrien 214174294Sobrien close(fd); 215174294Sobrien return 0; 216174294Sobrien 217174294Sobrien again: 218174294Sobrien /* 219174294Sobrien * If the last read didn't get a whole line then 220174294Sobrien * throw away the remainder before continuing... 221174294Sobrien */ 222174294Sobrien if (chuck) { 223174294Sobrien while (fgets_timed(qanswer, sizeof(qanswer), fd, gopt.exec_map_timeout) && 224174294Sobrien !strchr(qanswer, '\n')) ; 225174294Sobrien chuck = 0; 226174294Sobrien } 227174294Sobrien } 228174294Sobrien 229174294Sobrien return ENOENT; 230174294Sobrien} 231174294Sobrien 232174294Sobrien 233174294Sobrienstatic int 234174294Sobrienset_nonblock(int fd) 235174294Sobrien{ 236174294Sobrien int val; 237174294Sobrien 238174294Sobrien if (fd < 0) 239174294Sobrien return 0; 240174294Sobrien 241174294Sobrien if ((val = fcntl(fd, F_GETFL, 0)) < 0) { 242174294Sobrien plog(XLOG_WARNING, "set_nonblock fcntl F_GETFL error: %m"); 243174294Sobrien return 0; 244174294Sobrien } 245174294Sobrien 246174294Sobrien val |= O_NONBLOCK; 247174294Sobrien if (fcntl(fd, F_SETFL, val) < 0) { 248174294Sobrien plog(XLOG_WARNING, "set_nonblock fcntl F_SETFL error: %m"); 249174294Sobrien return 0; 250174294Sobrien } 251174294Sobrien 252174294Sobrien return 1; 253174294Sobrien} 254174294Sobrien 255174294Sobrien 256174294Sobrienstatic int 257174294Sobrienexec_map_open(char *emap, char *key) 258174294Sobrien{ 259174294Sobrien pid_t p1, p2; 260174294Sobrien int pdes[2], nullfd, i; 261174294Sobrien char *argv[3]; 262174294Sobrien 263174294Sobrien if (!emap) 264174294Sobrien return 0; 265174294Sobrien 266174294Sobrien argv[0] = emap; 267174294Sobrien argv[1] = key; 268174294Sobrien argv[2] = NULL; 269174294Sobrien 270174294Sobrien if ((nullfd = open("/dev/null", O_WRONLY|O_NOCTTY)) < 0) 271174294Sobrien return -1; 272174294Sobrien 273174294Sobrien if (pipe(pdes) < 0) { 274174294Sobrien close(nullfd); 275174294Sobrien return -1; 276174294Sobrien } 277174294Sobrien 278174294Sobrien switch ((p1 = vfork())) { 279174294Sobrien case -1: 280174294Sobrien /* parent: fork error */ 281174294Sobrien close(nullfd); 282174294Sobrien close(pdes[0]); 283174294Sobrien close(pdes[1]); 284174294Sobrien return -1; 285174294Sobrien case 0: 286174294Sobrien /* child #1 */ 287174294Sobrien p2 = vfork(); 288174294Sobrien switch (p2) { 289174294Sobrien case -1: 290174294Sobrien /* child #1: fork error */ 291174294Sobrien exit(errno); 292174294Sobrien case 0: 293174294Sobrien /* child #2: init will reap our status */ 294174294Sobrien if (pdes[1] != STDOUT_FILENO) { 295174294Sobrien dup2(pdes[1], STDOUT_FILENO); 296174294Sobrien close(pdes[1]); 297174294Sobrien } 298174294Sobrien 299174294Sobrien if (nullfd != STDERR_FILENO) { 300174294Sobrien dup2(nullfd, STDERR_FILENO); 301174294Sobrien close(nullfd); 302174294Sobrien } 303174294Sobrien 304174294Sobrien for (i=0; i<FD_SETSIZE; i++) 305174294Sobrien if (i != STDOUT_FILENO && i != STDERR_FILENO) 306174294Sobrien close(i); 307174294Sobrien 308174294Sobrien /* make the write descriptor non-blocking */ 309174294Sobrien if (!set_nonblock(STDOUT_FILENO)) { 310174294Sobrien close(STDOUT_FILENO); 311174294Sobrien exit(-1); 312174294Sobrien } 313174294Sobrien 314174294Sobrien execve(emap, argv, NULL); 315174294Sobrien exit(errno); /* in case execve failed */ 316174294Sobrien } 317174294Sobrien 318174294Sobrien /* child #1 */ 319174294Sobrien exit(0); 320174294Sobrien } 321174294Sobrien 322174294Sobrien /* parent */ 323174294Sobrien close(nullfd); 324174294Sobrien close(pdes[1]); 325174294Sobrien 326174294Sobrien /* anti-zombie insurance */ 327174294Sobrien while (waitpid(p1,0,0) < 0) 328174294Sobrien if (errno != EINTR) 329174294Sobrien exit(errno); 330174294Sobrien 331174294Sobrien /* make the read descriptor non-blocking */ 332174294Sobrien if (!set_nonblock(pdes[0])) { 333174294Sobrien close(pdes[0]); 334174294Sobrien return -1; 335174294Sobrien } 336174294Sobrien 337174294Sobrien return pdes[0]; 338174294Sobrien} 339174294Sobrien 340174294Sobrien 341174294Sobrien/* 342174294Sobrien * Check for various permissions on executable map without trying to 343174294Sobrien * fork a new executable-map process. 344174294Sobrien * 345174294Sobrien * return: >0 (errno) if failed 346174294Sobrien * 0 if ok 347174294Sobrien */ 348174294Sobrienstatic int 349174294Sobrienexec_check_perm(char *map) 350174294Sobrien{ 351174294Sobrien struct stat sb; 352174294Sobrien 353174294Sobrien /* sanity and permission checks */ 354174294Sobrien if (!map) { 355174294Sobrien dlog("exec_check_permission got a NULL map"); 356174294Sobrien return EINVAL; 357174294Sobrien } 358174294Sobrien if (stat(map, &sb)) { 359174294Sobrien plog(XLOG_ERROR, "map \"%s\" stat failure: %m", map); 360174294Sobrien return errno; 361174294Sobrien } 362174294Sobrien if (!S_ISREG(sb.st_mode)) { 363174294Sobrien plog(XLOG_ERROR, "map \"%s\" should be regular file", map); 364174294Sobrien return EINVAL; 365174294Sobrien } 366174294Sobrien if (sb.st_uid != 0) { 367174294Sobrien plog(XLOG_ERROR, "map \"%s\" owned by uid %u (must be 0)", map, (u_int) sb.st_uid); 368174294Sobrien return EACCES; 369174294Sobrien } 370174294Sobrien if (!(sb.st_mode & S_IXUSR)) { 371174294Sobrien plog(XLOG_ERROR, "map \"%s\" should be executable", map); 372174294Sobrien return EACCES; 373174294Sobrien } 374174294Sobrien if (sb.st_mode & (S_ISUID|S_ISGID)) { 375174294Sobrien plog(XLOG_ERROR, "map \"%s\" should not be setuid/setgid", map); 376174294Sobrien return EACCES; 377174294Sobrien } 378174294Sobrien if (sb.st_mode & S_IWOTH) { 379174294Sobrien plog(XLOG_ERROR, "map \"%s\" should not be world writeable", map); 380174294Sobrien return EACCES; 381174294Sobrien } 382174294Sobrien 383174294Sobrien return 0; /* all is well */ 384174294Sobrien} 385174294Sobrien 386174294Sobrien 387174294Sobrienint 388174294Sobrienexec_init(mnt_map *m, char *map, time_t *tp) 389174294Sobrien{ 390174294Sobrien /* 391174294Sobrien * Basically just test that the executable map can be found 392174294Sobrien * and has proper permissions. 393174294Sobrien */ 394174294Sobrien return exec_check_perm(map); 395174294Sobrien} 396174294Sobrien 397174294Sobrien 398174294Sobrienint 399174294Sobrienexec_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) 400174294Sobrien{ 401174294Sobrien int mapfd, ret; 402174294Sobrien 403174294Sobrien if ((ret = exec_check_perm(map)) != 0) { 404174294Sobrien return ret; 405174294Sobrien } 406174294Sobrien 407174294Sobrien if (!key) 408174294Sobrien return 0; 409174294Sobrien 410174294Sobrien if (logfp) 411174294Sobrien fflush(logfp); 412174294Sobrien dlog("exec_search \"%s\", key: \"%s\"", map, key); 413174294Sobrien mapfd = exec_map_open(map, key); 414174294Sobrien 415174294Sobrien if (mapfd >= 0) { 416174294Sobrien if (tp) 417174294Sobrien *tp = clocktime(NULL); 418174294Sobrien 419174294Sobrien return exec_parse_qanswer(mapfd, map, key, pval, tp); 420174294Sobrien } 421174294Sobrien 422174294Sobrien return errno; 423174294Sobrien} 424