pt_pipe.c revision 143402
1169691Skan/*-
2169691Skan * Copyright (C) 2005 Diomidis Spinellis. All rights reserved.
3169691Skan *
4169691Skan * Redistribution and use in source and binary forms, with or without
5169691Skan * modification, are permitted provided that the following conditions
6169691Skan * are met:
7169691Skan * 1. Redistributions of source code must retain the above copyright
8169691Skan *    notice, this list of conditions and the following disclaimer.
9169691Skan * 2. Redistributions in binary form must reproduce the above copyright
10169691Skan *    notice, this list of conditions and the following disclaimer in the
11169691Skan *    documentation and/or other materials provided with the distribution.
12169691Skan *
13169691Skan * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14169691Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15169691Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16169691Skan * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17169691Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18169691Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19169691Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20169691Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21169691Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22169691Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23169691Skan * SUCH DAMAGE.
24169691Skan *
25169691Skan */
26169691Skan
27169691Skan#include <sys/cdefs.h>
28169691Skan__FBSDID("$FreeBSD: head/usr.sbin/mount_portalfs/pt_pipe.c 143402 2005-03-11 08:39:58Z dds $");
29169691Skan
30169691Skan#include <ctype.h>
31169691Skan#include <errno.h>
32169691Skan#include <fcntl.h>
33169691Skan#include <stdio.h>
34169691Skan#include <stdlib.h>
35169691Skan#include <string.h>
36169691Skan#include <unistd.h>
37169691Skan
38169691Skan#include <sys/types.h>
39169691Skan#include <sys/param.h>
40169691Skan#include <sys/syslog.h>
41169691Skan
42169691Skan#include "portald.h"
43169691Skan
44169691Skan/* Usage conventions for the pipe's endpoints. */
45169691Skan#define READ_END	0
46169691Skan#define WRITE_END	1
47169691Skan
48169691Skanstatic int  errlog(void);
49169691Skanstatic int  parse_argv(char *args, char **argv);
50169691Skan
51169691Skanint portal_pipe(pcr, key, v, so, fdp)
52169691Skanstruct portal_cred *pcr;
53169691Skanchar *key;
54169691Skanchar **v;
55169691Skanint so;
56169691Skanint *fdp;
57169691Skan{
58169691Skan	int fd[2];		/* Pipe endpoints. */
59169691Skan	int caller_end;		/* The pipe end we will use. */
60169691Skan	int process_end;	/* The pipe end the spawned process will use. */
61169691Skan	int redirect_fd;	/* The fd to redirect on the spawned process. */
62169691Skan	char pbuf[MAXPATHLEN];
63169691Skan	int error = 0;
64169691Skan	int i;
65169691Skan	char **argv;
66169691Skan	int argc;
67169691Skan	struct portal_cred save_area;
68169691Skan
69169691Skan	/* Validate open mode, and assign roles. */
70169691Skan	if ((pcr->pcr_flag & FWRITE) && (pcr->pcr_flag & FREAD))
71169691Skan		/* Don't allow both on a single fd. */
72169691Skan		return (EINVAL);
73169691Skan	else if (pcr->pcr_flag & FREAD) {
74169691Skan		/*
75169691Skan		 * The caller reads from the pipe,
76169691Skan		 * the spawned process writes to it.
77169691Skan		 */
78169691Skan		caller_end = READ_END;
79169691Skan		process_end = WRITE_END;
80169691Skan		redirect_fd = STDOUT_FILENO;
81169691Skan	} else if (pcr->pcr_flag & FWRITE) {
82169691Skan		/*
83169691Skan		 * The caller writes to the pipe,
84169691Skan		 * the spawned process reads from it.
85169691Skan		 */
86169691Skan		caller_end = WRITE_END;
87169691Skan		process_end = READ_END;
88169691Skan		redirect_fd = STDIN_FILENO;
89169691Skan	} else
90169691Skan		return (EINVAL);
91169691Skan
92169691Skan	/* Get and check command line. */
93169691Skan	pbuf[0] = '/';
94169691Skan	strcpy(pbuf+1, key + (v[1] ? strlen(v[1]) : 0));
95169691Skan	argc = parse_argv(pbuf, NULL);
96169691Skan	if (argc == 0)
97169691Skan		return (ENOENT);
98169691Skan
99169691Skan	/* Swap priviledges. */
100169691Skan	if (set_user_credentials(pcr, &save_area) < 0)
101169691Skan		return (errno);
102169691Skan
103169691Skan	/* Redirect and spawn the specified process. */
104169691Skan	fd[READ_END] = fd[WRITE_END] = -1;
105169691Skan	if (pipe(fd) < 0) {
106169691Skan		error = errno;
107169691Skan		goto done;
108169691Skan	}
109169691Skan	switch (fork()) {
110169691Skan	case -1: /* Error */
111169691Skan		error = errno;
112169691Skan		break;
113169691Skan	default: /* Parent */
114169691Skan		(void)close(fd[process_end]);
115169691Skan		break;
116169691Skan	case 0: /* Child */
117169691Skan		argv = (char **)malloc((argc + 1) * sizeof(char *));
118169691Skan		if (argv == 0) {
119169691Skan			syslog(LOG_ALERT,
120169691Skan			    "malloc: failed to get space for %d pointers",
121169691Skan			    argc + 1);
122169691Skan			exit(EXIT_FAILURE);
123169691Skan		}
124169691Skan		parse_argv(pbuf, argv);
125169691Skan
126169691Skan		if (dup2(fd[process_end], redirect_fd) < 0) {
127169691Skan			syslog(LOG_ERR, "dup2: %m");
128169691Skan			exit(EXIT_FAILURE);
129169691Skan		}
130169691Skan		(void)close(fd[caller_end]);
131169691Skan		(void)close(fd[process_end]);
132169691Skan		if (errlog() < 0) {
133169691Skan			syslog(LOG_ERR, "errlog: %m");
134169691Skan			exit(EXIT_FAILURE);
135169691Skan		}
136169691Skan		if (execv(argv[0], argv) < 0) {
137169691Skan			syslog(LOG_ERR, "execv(%s): %m", argv[0]);
138169691Skan			exit(EXIT_FAILURE);
139169691Skan		}
140169691Skan		/* NOTREACHED */
141169691Skan	}
142169691Skan
143169691Skandone:
144169691Skan	/* Re-establish our priviledges. */
145169691Skan	if (restore_credentials(&save_area) < 0)
146169691Skan		error = errno;
147169691Skan
148169691Skan	/* Set return fd value. */
149169691Skan	if (error == 0)
150169691Skan		*fdp = fd[caller_end];
151169691Skan	else {
152169691Skan		for (i = 0; i < 2; i++)
153169691Skan			if (fd[i] >= 0)
154169691Skan				(void)close(fd[i]);
155169691Skan		*fdp = -1;
156169691Skan	}
157169691Skan
158169691Skan	return (error);
159169691Skan}
160169691Skan
161169691Skan/*
162169691Skan * Redirect stderr to the system log.
163169691Skan * Return 0 if ok.
164169691Skan * Return -1 with errno set on error.
165169691Skan */
166169691Skanstatic int
167169691Skanerrlog(void)
168169691Skan{
169169691Skan	int fd[2];
170169691Skan	char buff[1024];
171169691Skan	FILE *f;
172169691Skan	int ret = 0;
173169691Skan
174169691Skan	if (pipe(fd) < 0)
175169691Skan		return (-1);
176169691Skan	switch (fork()) {
177169691Skan	case -1: /* Error */
178169691Skan		return (-1);
179169691Skan	case 0: /* Child */
180169691Skan		if ((f = fdopen(fd[READ_END], "r")) == NULL) {
181169691Skan			syslog(LOG_ERR, "fdopen: %m");
182169691Skan			exit(EXIT_FAILURE);
183169691Skan		}
184169691Skan		(void)close(fd[WRITE_END]);
185169691Skan		while (fgets(buff, sizeof(buff), f) != NULL)
186169691Skan			syslog(LOG_ERR, "exec: %s", buff);
187169691Skan		exit(EXIT_SUCCESS);
188169691Skan		/* NOTREACHED */
189169691Skan	default: /* Parent */
190169691Skan		if (dup2(fd[WRITE_END], STDERR_FILENO) < 0)
191169691Skan			ret = -1;
192169691Skan		(void)close(fd[READ_END]);
193169691Skan		(void)close(fd[WRITE_END]);
194169691Skan		break;
195169691Skan	}
196169691Skan	return (ret);
197169691Skan}
198169691Skan
199169691Skan/*
200169691Skan * Parse the args string as a space-separated argument vector.
201169691Skan * If argv is not NULL, split the string into its constituent
202169691Skan * components, and set argv to point to the beginning  of each
203169691Skan * string component; NULL-terminating argv.
204169691Skan * Return the number of string components.
205169691Skan */
206169691Skanstatic int
207169691Skanparse_argv(char *args, char **argv)
208169691Skan{
209169691Skan	int count = 0;
210169691Skan	char *p;
211169691Skan	enum {WORD, SPACE} state = SPACE;
212169691Skan
213169691Skan	for (p = args; *p; p++)
214169691Skan		switch (state) {
215169691Skan		case WORD:
216169691Skan			if (isspace(*p)) {
217169691Skan				if (argv)
218169691Skan					*p = '\0';
219169691Skan				state = SPACE;
220169691Skan			}
221169691Skan			break;
222169691Skan		case SPACE:
223169691Skan			if (!isspace(*p)) {
224169691Skan				if (argv)
225169691Skan					argv[count] = p;
226169691Skan				count++;
227169691Skan				state = WORD;
228169691Skan			}
229169691Skan		}
230169691Skan	if (argv)
231169691Skan		argv[count] = NULL;
232169691Skan	return (count);
233169691Skan}
234169691Skan