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