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 *
fgets_timed(char * s,int size,int rdfd,int secs)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
read_line(char * buf,int size,int fd)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
exec_parse_qanswer(mnt_map * m,int fd,char * map,char * key,char ** pval,time_t * tp)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
set_nonblock(int fd)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
exec_map_open(char * emap,char * key)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
exec_check_perm(char * map)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
exec_init(mnt_map * m,char * map,time_t * tp)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
exec_search(mnt_map * m,char * map,char * key,char ** pval,time_t * tp)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