pt_pipe.c revision 143389
1/*-
2 * Copyright (C) 1005 Diomidis Spinellis. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/usr.sbin/mount_portalfs/pt_pipe.c 143389 2005-03-10 22:10:16Z dds $");
29
30#include <ctype.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37
38#include <sys/types.h>
39#include <sys/param.h>
40#include <sys/syslog.h>
41
42#include "portald.h"
43
44/* Usage conventions for the pipe's endpoints. */
45#define READ_END	0
46#define WRITE_END	1
47
48static int  errlog(void);
49static int  parse_argv(char *args, char **argv);
50
51int portal_pipe(pcr, key, v, so, fdp)
52struct portal_cred *pcr;
53char *key;
54char **v;
55int so;
56int *fdp;
57{
58	int fd[2];		/* Pipe endpoints. */
59	int caller_end;		/* The pipe end we will use. */
60	int process_end;	/* The pipe end the spawned process will use. */
61	int redirect_fd;	/* The fd to redirect on the spawned process. */
62	char pbuf[MAXPATHLEN];
63	int error = 0;
64	int i;
65	char **argv;
66	int argc;
67	/* Variables used to save the the caller's credentials. */
68	uid_t old_uid;
69	int ngroups;
70	gid_t old_groups[NGROUPS_MAX];
71
72	/* Validate open mode, and assign roles. */
73	if ((pcr->pcr_flag & FWRITE) && (pcr->pcr_flag & FREAD))
74		/* Don't allow both on a single fd. */
75		return (EINVAL);
76	else if (pcr->pcr_flag & FREAD) {
77		/*
78		 * The caller reads from the pipe,
79		 * the spawned process writes to it.
80		 */
81		caller_end = READ_END;
82		process_end = WRITE_END;
83		redirect_fd = STDOUT_FILENO;
84	} else if (pcr->pcr_flag & FWRITE) {
85		/*
86		 * The caller writes to the pipe,
87		 * the spawned process reads from it.
88		 */
89		caller_end = WRITE_END;
90		process_end = READ_END;
91		redirect_fd = STDIN_FILENO;
92	} else
93		return (EINVAL);
94
95	/* Get and check command line. */
96	pbuf[0] = '/';
97	strcpy(pbuf+1, key + (v[1] ? strlen(v[1]) : 0));
98	argc = parse_argv(pbuf, NULL);
99	if (argc == 0)
100		return (ENOENT);
101
102	/* Swap priviledges. */
103	old_uid = geteuid();
104	if ((ngroups = getgroups(NGROUPS_MAX, old_groups)) < 0)
105		return (errno);
106	if (setgroups(pcr->pcr_ngroups, pcr->pcr_groups) < 0)
107		return (errno);
108	if (seteuid(pcr->pcr_uid) < 0)
109		return (errno);
110
111	/* Redirect and spawn the specified process. */
112	fd[READ_END] = fd[WRITE_END] = -1;
113	if (pipe(fd) < 0) {
114		error = errno;
115		goto done;
116	}
117	switch (fork()) {
118	case -1: /* Error */
119		error = errno;
120		break;
121	default: /* Parent */
122		(void)close(fd[process_end]);
123		break;
124	case 0: /* Child */
125		argv = (char **)malloc((argc + 1) * sizeof(char *));
126		if (argv == 0) {
127			syslog(LOG_ALERT,
128			    "malloc: failed to get space for %d pointers",
129			    argc + 1);
130			exit(EXIT_FAILURE);
131		}
132		parse_argv(pbuf, argv);
133
134		if (dup2(fd[process_end], redirect_fd) < 0) {
135			syslog(LOG_ERR, "dup2: %m");
136			exit(EXIT_FAILURE);
137		}
138		(void)close(fd[caller_end]);
139		(void)close(fd[process_end]);
140		if (errlog() < 0) {
141			syslog(LOG_ERR, "errlog: %m");
142			exit(EXIT_FAILURE);
143		}
144		if (execv(argv[0], argv) < 0) {
145			syslog(LOG_ERR, "execv(%s): %m", argv[0]);
146			exit(EXIT_FAILURE);
147		}
148		/* NOTREACHED */
149	}
150
151done:
152	/* Re-establish our priviledges. */
153	if (seteuid(old_uid) < 0) {
154		error = errno;
155		syslog(LOG_ERR, "seteuid: %m");
156	}
157	if (setgroups(ngroups, old_groups) < 0) {
158		error = errno;
159		syslog(LOG_ERR, "setgroups: %m");
160	}
161
162	/* Set return fd value. */
163	if (error == 0)
164		*fdp = fd[caller_end];
165	else {
166		for (i = 0; i < 2; i++)
167			if (fd[i] >= 0)
168				(void)close(fd[i]);
169		*fdp = -1;
170	}
171
172	return (error);
173}
174
175/*
176 * Redirect stderr to the system log.
177 * Return 0 if ok.
178 * Return -1 with errno set on error.
179 */
180static int
181errlog(void)
182{
183	int fd[2];
184	char buff[1024];
185	FILE *f;
186	int ret = 0;
187
188	if (pipe(fd) < 0)
189		return (-1);
190	switch (fork()) {
191	case -1: /* Error */
192		return (-1);
193	case 0: /* Child */
194		if ((f = fdopen(fd[READ_END], "r")) == NULL) {
195			syslog(LOG_ERR, "fdopen: %m");
196			exit(EXIT_FAILURE);
197		}
198		(void)close(fd[WRITE_END]);
199		while (fgets(buff, sizeof(buff), f) != NULL)
200			syslog(LOG_ERR, "exec: %s", buff);
201		exit(EXIT_SUCCESS);
202		/* NOTREACHED */
203	default: /* Parent */
204		if (dup2(fd[WRITE_END], STDERR_FILENO) < 0)
205			ret = -1;
206		(void)close(fd[READ_END]);
207		(void)close(fd[WRITE_END]);
208		break;
209	}
210	return (ret);
211}
212
213/*
214 * Parse the args string as a space-separated argument vector.
215 * If argv is not NULL, split the string into its constituent
216 * components, and set argv to point to the beginning  of each
217 * string component; NULL-terminating argv.
218 * Return the number of string components.
219 */
220static int
221parse_argv(char *args, char **argv)
222{
223	int count = 0;
224	char *p;
225	enum {WORD, SPACE} state = SPACE;
226
227	for (p = args; *p; p++)
228		switch (state) {
229		case WORD:
230			if (isspace(*p)) {
231				if (argv)
232					*p = '\0';
233				state = SPACE;
234			}
235			break;
236		case SPACE:
237			if (!isspace(*p)) {
238				if (argv)
239					argv[count] = p;
240				count++;
241				state = WORD;
242			}
243		}
244	if (argv)
245		argv[count] = NULL;
246	return (count);
247}
248