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