]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mount_portalfs/pt_pipe.c
Add a new pipe sub-namespace.
[FreeBSD/FreeBSD.git] / usr.sbin / mount_portalfs / pt_pipe.c
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$");
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
48 static int  errlog(void);
49 static int  parse_argv(char *args, char **argv);
50
51 int portal_pipe(pcr, key, v, so, fdp)
52 struct portal_cred *pcr;
53 char *key;
54 char **v;
55 int so;
56 int *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
151 done:
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  */
180 static int
181 errlog(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  */
220 static int
221 parse_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 }