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