1143389Sdds/*-
2143402Sdds * Copyright (C) 2005 Diomidis Spinellis. All rights reserved.
3143389Sdds *
4143389Sdds * Redistribution and use in source and binary forms, with or without
5143389Sdds * modification, are permitted provided that the following conditions
6143389Sdds * are met:
7143389Sdds * 1. Redistributions of source code must retain the above copyright
8143389Sdds *    notice, this list of conditions and the following disclaimer.
9143389Sdds * 2. Redistributions in binary form must reproduce the above copyright
10143389Sdds *    notice, this list of conditions and the following disclaimer in the
11143389Sdds *    documentation and/or other materials provided with the distribution.
12143389Sdds *
13143389Sdds * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14143389Sdds * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15143389Sdds * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16143389Sdds * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17143389Sdds * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18143389Sdds * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19143389Sdds * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20143389Sdds * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21143389Sdds * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22143389Sdds * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23143389Sdds * SUCH DAMAGE.
24143389Sdds *
25143389Sdds */
26143389Sdds
27143389Sdds#include <sys/cdefs.h>
28143389Sdds__FBSDID("$FreeBSD$");
29143389Sdds
30143389Sdds#include <ctype.h>
31143389Sdds#include <errno.h>
32143389Sdds#include <fcntl.h>
33143389Sdds#include <stdio.h>
34143389Sdds#include <stdlib.h>
35143389Sdds#include <string.h>
36143389Sdds#include <unistd.h>
37143389Sdds
38143389Sdds#include <sys/types.h>
39143389Sdds#include <sys/param.h>
40143389Sdds#include <sys/syslog.h>
41143389Sdds
42143389Sdds#include "portald.h"
43143389Sdds
44143389Sdds/* Usage conventions for the pipe's endpoints. */
45143389Sdds#define READ_END	0
46143389Sdds#define WRITE_END	1
47143389Sdds
48143389Sddsstatic int  errlog(void);
49143389Sddsstatic int  parse_argv(char *args, char **argv);
50143389Sdds
51166157Srodrigcint portal_pipe(struct portal_cred *pcr, char *key, char **v,
52166157Srodrigc    int kso __unused, int *fdp)
53143389Sdds{
54143389Sdds	int fd[2];		/* Pipe endpoints. */
55143389Sdds	int caller_end;		/* The pipe end we will use. */
56143389Sdds	int process_end;	/* The pipe end the spawned process will use. */
57143389Sdds	int redirect_fd;	/* The fd to redirect on the spawned process. */
58143389Sdds	char pbuf[MAXPATHLEN];
59143389Sdds	int error = 0;
60143389Sdds	int i;
61143389Sdds	char **argv;
62143389Sdds	int argc;
63143402Sdds	struct portal_cred save_area;
64143389Sdds
65143389Sdds	/* Validate open mode, and assign roles. */
66143389Sdds	if ((pcr->pcr_flag & FWRITE) && (pcr->pcr_flag & FREAD))
67143389Sdds		/* Don't allow both on a single fd. */
68143389Sdds		return (EINVAL);
69143389Sdds	else if (pcr->pcr_flag & FREAD) {
70143389Sdds		/*
71143389Sdds		 * The caller reads from the pipe,
72143389Sdds		 * the spawned process writes to it.
73143389Sdds		 */
74143389Sdds		caller_end = READ_END;
75143389Sdds		process_end = WRITE_END;
76143389Sdds		redirect_fd = STDOUT_FILENO;
77143389Sdds	} else if (pcr->pcr_flag & FWRITE) {
78143389Sdds		/*
79143389Sdds		 * The caller writes to the pipe,
80143389Sdds		 * the spawned process reads from it.
81143389Sdds		 */
82143389Sdds		caller_end = WRITE_END;
83143389Sdds		process_end = READ_END;
84143389Sdds		redirect_fd = STDIN_FILENO;
85143389Sdds	} else
86143389Sdds		return (EINVAL);
87143389Sdds
88143389Sdds	/* Get and check command line. */
89143389Sdds	pbuf[0] = '/';
90143389Sdds	strcpy(pbuf+1, key + (v[1] ? strlen(v[1]) : 0));
91143389Sdds	argc = parse_argv(pbuf, NULL);
92143389Sdds	if (argc == 0)
93143389Sdds		return (ENOENT);
94143389Sdds
95143389Sdds	/* Swap priviledges. */
96143402Sdds	if (set_user_credentials(pcr, &save_area) < 0)
97143389Sdds		return (errno);
98143389Sdds
99143389Sdds	/* Redirect and spawn the specified process. */
100143389Sdds	fd[READ_END] = fd[WRITE_END] = -1;
101143389Sdds	if (pipe(fd) < 0) {
102143389Sdds		error = errno;
103143389Sdds		goto done;
104143389Sdds	}
105143389Sdds	switch (fork()) {
106143389Sdds	case -1: /* Error */
107143389Sdds		error = errno;
108143389Sdds		break;
109143389Sdds	default: /* Parent */
110143389Sdds		(void)close(fd[process_end]);
111143389Sdds		break;
112143389Sdds	case 0: /* Child */
113143389Sdds		argv = (char **)malloc((argc + 1) * sizeof(char *));
114143389Sdds		if (argv == 0) {
115143389Sdds			syslog(LOG_ALERT,
116143389Sdds			    "malloc: failed to get space for %d pointers",
117143389Sdds			    argc + 1);
118143389Sdds			exit(EXIT_FAILURE);
119143389Sdds		}
120143389Sdds		parse_argv(pbuf, argv);
121143389Sdds
122143389Sdds		if (dup2(fd[process_end], redirect_fd) < 0) {
123143389Sdds			syslog(LOG_ERR, "dup2: %m");
124143389Sdds			exit(EXIT_FAILURE);
125143389Sdds		}
126143389Sdds		(void)close(fd[caller_end]);
127143389Sdds		(void)close(fd[process_end]);
128143389Sdds		if (errlog() < 0) {
129143389Sdds			syslog(LOG_ERR, "errlog: %m");
130143389Sdds			exit(EXIT_FAILURE);
131143389Sdds		}
132143389Sdds		if (execv(argv[0], argv) < 0) {
133143389Sdds			syslog(LOG_ERR, "execv(%s): %m", argv[0]);
134143389Sdds			exit(EXIT_FAILURE);
135143389Sdds		}
136143389Sdds		/* NOTREACHED */
137143389Sdds	}
138143389Sdds
139143389Sddsdone:
140143389Sdds	/* Re-establish our priviledges. */
141143402Sdds	if (restore_credentials(&save_area) < 0)
142143389Sdds		error = errno;
143143389Sdds
144143389Sdds	/* Set return fd value. */
145143389Sdds	if (error == 0)
146143389Sdds		*fdp = fd[caller_end];
147143389Sdds	else {
148143389Sdds		for (i = 0; i < 2; i++)
149143389Sdds			if (fd[i] >= 0)
150143389Sdds				(void)close(fd[i]);
151143389Sdds		*fdp = -1;
152143389Sdds	}
153143389Sdds
154143389Sdds	return (error);
155143389Sdds}
156143389Sdds
157143389Sdds/*
158143389Sdds * Redirect stderr to the system log.
159143389Sdds * Return 0 if ok.
160143389Sdds * Return -1 with errno set on error.
161143389Sdds */
162143389Sddsstatic int
163143389Sddserrlog(void)
164143389Sdds{
165143389Sdds	int fd[2];
166143389Sdds	char buff[1024];
167143389Sdds	FILE *f;
168143389Sdds	int ret = 0;
169143389Sdds
170143389Sdds	if (pipe(fd) < 0)
171143389Sdds		return (-1);
172143389Sdds	switch (fork()) {
173143389Sdds	case -1: /* Error */
174143389Sdds		return (-1);
175143389Sdds	case 0: /* Child */
176143389Sdds		if ((f = fdopen(fd[READ_END], "r")) == NULL) {
177143389Sdds			syslog(LOG_ERR, "fdopen: %m");
178143389Sdds			exit(EXIT_FAILURE);
179143389Sdds		}
180143389Sdds		(void)close(fd[WRITE_END]);
181143389Sdds		while (fgets(buff, sizeof(buff), f) != NULL)
182143389Sdds			syslog(LOG_ERR, "exec: %s", buff);
183143389Sdds		exit(EXIT_SUCCESS);
184143389Sdds		/* NOTREACHED */
185143389Sdds	default: /* Parent */
186143389Sdds		if (dup2(fd[WRITE_END], STDERR_FILENO) < 0)
187143389Sdds			ret = -1;
188143389Sdds		(void)close(fd[READ_END]);
189143389Sdds		(void)close(fd[WRITE_END]);
190143389Sdds		break;
191143389Sdds	}
192143389Sdds	return (ret);
193143389Sdds}
194143389Sdds
195143389Sdds/*
196143389Sdds * Parse the args string as a space-separated argument vector.
197143389Sdds * If argv is not NULL, split the string into its constituent
198143389Sdds * components, and set argv to point to the beginning  of each
199143389Sdds * string component; NULL-terminating argv.
200143389Sdds * Return the number of string components.
201143389Sdds */
202143389Sddsstatic int
203143389Sddsparse_argv(char *args, char **argv)
204143389Sdds{
205143389Sdds	int count = 0;
206143389Sdds	char *p;
207143389Sdds	enum {WORD, SPACE} state = SPACE;
208143389Sdds
209143389Sdds	for (p = args; *p; p++)
210143389Sdds		switch (state) {
211143389Sdds		case WORD:
212143389Sdds			if (isspace(*p)) {
213143389Sdds				if (argv)
214143389Sdds					*p = '\0';
215143389Sdds				state = SPACE;
216143389Sdds			}
217143389Sdds			break;
218143389Sdds		case SPACE:
219143389Sdds			if (!isspace(*p)) {
220143389Sdds				if (argv)
221143389Sdds					argv[count] = p;
222143389Sdds				count++;
223143389Sdds				state = WORD;
224143389Sdds			}
225143389Sdds		}
226143389Sdds	if (argv)
227143389Sdds		argv[count] = NULL;
228143389Sdds	return (count);
229143389Sdds}
230