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