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