]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/amd/amd/info_exec.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / amd / amd / info_exec.c
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 *
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
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
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
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
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
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
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
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 }