1/* 2 * Copyright (c) 1997-2006 Erez Zadok 3 * Copyright (c) 1990 Jan-Simon Pendry 4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 5 * Copyright (c) 1990 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry at Imperial College, London. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgment: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * 40 * File: am-utils/amd/info_exec.c 41 * 42 */ 43 44/* 45 * Get info from executable map 46 * 47 * Original from Erik Kline, 2004. 48 */ 49 50#ifdef HAVE_CONFIG_H 51# include <config.h> 52#endif /* HAVE_CONFIG_H */ 53#include <am_defs.h> 54#include <amd.h> 55 56#define MAX_LINE_LEN 1500 57 58/* forward declarations */ 59int exec_init(mnt_map *m, char *map, time_t *tp); 60int exec_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp); 61 62 63/* 64 * a timed fgets() 65 */ 66static char * 67fgets_timed(char *s, int size, int rdfd, int secs) 68{ 69 fd_set fds; 70 struct timeval timeo; 71 time_t start, now; 72 int rval=0, i=0; 73 74 if (!s || size < 0 || rdfd < 0) 75 return 0; 76 77 s[0] = 0; 78 if (size == 0) 79 return s; 80 81 start = clocktime(NULL); 82 while (s[i] != '\n' && i < size-1) { 83 s[i+1] = 0; /* places the requisite trailing '\0' */ 84 85 /* ready for reading */ 86 rval = read(rdfd, (void *)(s+i), 1); 87 if (rval == 1) { 88 if (s[i] == 0) { 89 rval = 0; 90 break; 91 } 92 i++; 93 continue; 94 } else if (rval == 0) { 95 break; 96 } else if (rval < 0 && errno != EAGAIN && errno != EINTR) { 97 plog(XLOG_WARNING, "fgets_timed read error: %m"); 98 break; 99 } 100 101 timeo.tv_usec = 0; 102 now = clocktime(NULL) - start; 103 if (secs <= 0) 104 timeo.tv_sec = 0; 105 else if (now < secs) 106 timeo.tv_sec = secs - now; 107 else { 108 /* timed out (now>=secs) */ 109 plog(XLOG_WARNING, "executable map read timed out (> %d secs)", secs); 110 rval = -1; 111 break; 112 } 113 114 FD_ZERO(&fds); 115 FD_SET(rdfd, &fds); 116 117 rval = select(rdfd+1, &fds, 0, 0, &timeo); 118 if (rval < 0) { 119 /* error selecting */ 120 plog(XLOG_WARNING, "fgets_timed select error: %m"); 121 if (errno == EINTR) 122 continue; 123 rval = -1; 124 break; 125 } else if (rval == 0) { 126 /* timed out */ 127 plog(XLOG_WARNING, "executable map read timed out (> %d secs)", secs); 128 rval = -1; 129 break; 130 } 131 } 132 133 if (rval > 0) 134 return s; 135 136 close(rdfd); 137 return (rval == 0 ? s : 0); 138} 139 140 141static int 142read_line(char *buf, int size, int fd) 143{ 144 int done = 0; 145 146 while (fgets_timed(buf, size, fd, gopt.exec_map_timeout)) { 147 int len = strlen(buf); 148 done += len; 149 if (len > 1 && buf[len - 2] == '\\' && 150 buf[len - 1] == '\n') { 151 buf += len - 2; 152 size -= len - 2; 153 *buf = '\n'; 154 buf[1] = '\0'; 155 } else { 156 return done; 157 } 158 } 159 160 return done; 161} 162 163 164/* 165 * Try to locate a value in a query answer 166 */ 167static int 168exec_parse_qanswer(int fd, char *map, char *key, char **pval, time_t *tp) 169{ 170 char qanswer[MAX_LINE_LEN], *dc = 0; 171 int chuck = 0; 172 int line_no = 0; 173 174 while (read_line(qanswer, sizeof(qanswer), fd)) { 175 char *cp; 176 char *hash; 177 int len = strlen(qanswer); 178 line_no++; 179 180 /* 181 * Make sure we got the whole line 182 */ 183 if (qanswer[len - 1] != '\n') { 184 plog(XLOG_WARNING, "line %d in \"%s\" is too long", line_no, map); 185 chuck = 1; 186 } else { 187 qanswer[len - 1] = '\0'; 188 } 189 190 /* 191 * Strip comments 192 */ 193 hash = strchr(qanswer, '#'); 194 if (hash) 195 *hash = '\0'; 196 197 /* 198 * Find beginning of value (query answer) 199 */ 200 for (cp = qanswer; *cp && !isascii((int)*cp) && !isspace((int)*cp); cp++) 201 ;; 202 203 /* Ignore blank lines */ 204 if (!*cp) 205 goto again; 206 207 /* 208 * Return a copy of the data 209 */ 210 dc = strdup(cp); 211 *pval = dc; 212 dlog("%s returns %s", key, dc); 213 214 close(fd); 215 return 0; 216 217 again: 218 /* 219 * If the last read didn't get a whole line then 220 * throw away the remainder before continuing... 221 */ 222 if (chuck) { 223 while (fgets_timed(qanswer, sizeof(qanswer), fd, gopt.exec_map_timeout) && 224 !strchr(qanswer, '\n')) ; 225 chuck = 0; 226 } 227 } 228 229 return ENOENT; 230} 231 232 233static int 234set_nonblock(int fd) 235{ 236 int val; 237 238 if (fd < 0) 239 return 0; 240 241 if ((val = fcntl(fd, F_GETFL, 0)) < 0) { 242 plog(XLOG_WARNING, "set_nonblock fcntl F_GETFL error: %m"); 243 return 0; 244 } 245 246 val |= O_NONBLOCK; 247 if (fcntl(fd, F_SETFL, val) < 0) { 248 plog(XLOG_WARNING, "set_nonblock fcntl F_SETFL error: %m"); 249 return 0; 250 } 251 252 return 1; 253} 254 255 256static int 257exec_map_open(char *emap, char *key) 258{ 259 pid_t p1, p2; 260 int pdes[2], nullfd, i; 261 char *argv[3]; 262 263 if (!emap) 264 return 0; 265 266 argv[0] = emap; 267 argv[1] = key; 268 argv[2] = NULL; 269 270 if ((nullfd = open("/dev/null", O_WRONLY|O_NOCTTY)) < 0) 271 return -1; 272 273 if (pipe(pdes) < 0) { 274 close(nullfd); 275 return -1; 276 } 277 278 switch ((p1 = vfork())) { 279 case -1: 280 /* parent: fork error */ 281 close(nullfd); 282 close(pdes[0]); 283 close(pdes[1]); 284 return -1; 285 case 0: 286 /* child #1 */ 287 p2 = vfork(); 288 switch (p2) { 289 case -1: 290 /* child #1: fork error */ 291 exit(errno); 292 case 0: 293 /* child #2: init will reap our status */ 294 if (pdes[1] != STDOUT_FILENO) { 295 dup2(pdes[1], STDOUT_FILENO); 296 close(pdes[1]); 297 } 298 299 if (nullfd != STDERR_FILENO) { 300 dup2(nullfd, STDERR_FILENO); 301 close(nullfd); 302 } 303 304 for (i=0; i<FD_SETSIZE; i++) 305 if (i != STDOUT_FILENO && i != STDERR_FILENO) 306 close(i); 307 308 /* make the write descriptor non-blocking */ 309 if (!set_nonblock(STDOUT_FILENO)) { 310 close(STDOUT_FILENO); 311 exit(-1); 312 } 313 314 execve(emap, argv, NULL); 315 exit(errno); /* in case execve failed */ 316 } 317 318 /* child #1 */ 319 exit(0); 320 } 321 322 /* parent */ 323 close(nullfd); 324 close(pdes[1]); 325 326 /* anti-zombie insurance */ 327 while (waitpid(p1,0,0) < 0) 328 if (errno != EINTR) 329 exit(errno); 330 331 /* make the read descriptor non-blocking */ 332 if (!set_nonblock(pdes[0])) { 333 close(pdes[0]); 334 return -1; 335 } 336 337 return pdes[0]; 338} 339 340 341/* 342 * Check for various permissions on executable map without trying to 343 * fork a new executable-map process. 344 * 345 * return: >0 (errno) if failed 346 * 0 if ok 347 */ 348static int 349exec_check_perm(char *map) 350{ 351 struct stat sb; 352 353 /* sanity and permission checks */ 354 if (!map) { 355 dlog("exec_check_permission got a NULL map"); 356 return EINVAL; 357 } 358 if (stat(map, &sb)) { 359 plog(XLOG_ERROR, "map \"%s\" stat failure: %m", map); 360 return errno; 361 } 362 if (!S_ISREG(sb.st_mode)) { 363 plog(XLOG_ERROR, "map \"%s\" should be regular file", map); 364 return EINVAL; 365 } 366 if (sb.st_uid != 0) { 367 plog(XLOG_ERROR, "map \"%s\" owned by uid %u (must be 0)", map, (u_int) sb.st_uid); 368 return EACCES; 369 } 370 if (!(sb.st_mode & S_IXUSR)) { 371 plog(XLOG_ERROR, "map \"%s\" should be executable", map); 372 return EACCES; 373 } 374 if (sb.st_mode & (S_ISUID|S_ISGID)) { 375 plog(XLOG_ERROR, "map \"%s\" should not be setuid/setgid", map); 376 return EACCES; 377 } 378 if (sb.st_mode & S_IWOTH) { 379 plog(XLOG_ERROR, "map \"%s\" should not be world writeable", map); 380 return EACCES; 381 } 382 383 return 0; /* all is well */ 384} 385 386 387int 388exec_init(mnt_map *m, char *map, time_t *tp) 389{ 390 /* 391 * Basically just test that the executable map can be found 392 * and has proper permissions. 393 */ 394 return exec_check_perm(map); 395} 396 397 398int 399exec_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) 400{ 401 int mapfd, ret; 402 403 if ((ret = exec_check_perm(map)) != 0) { 404 return ret; 405 } 406 407 if (!key) 408 return 0; 409 410 if (logfp) 411 fflush(logfp); 412 dlog("exec_search \"%s\", key: \"%s\"", map, key); 413 mapfd = exec_map_open(map, key); 414 415 if (mapfd >= 0) { 416 if (tp) 417 *tp = clocktime(NULL); 418 419 return exec_parse_qanswer(mapfd, map, key, pval, tp); 420 } 421 422 return errno; 423} 424