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