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