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