Home | History | Annotate | Line # | Download | only in amd
      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