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