1/*- 2 * Copyright (C) 2005 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 48static int errlog(void); 49static int parse_argv(char *args, char **argv); 50 51int portal_pipe(struct portal_cred *pcr, char *key, char **v, 52 int kso __unused, int *fdp) 53{ 54 int fd[2]; /* Pipe endpoints. */ 55 int caller_end; /* The pipe end we will use. */ 56 int process_end; /* The pipe end the spawned process will use. */ 57 int redirect_fd; /* The fd to redirect on the spawned process. */ 58 char pbuf[MAXPATHLEN]; 59 int error = 0; 60 int i; 61 char **argv; 62 int argc; 63 struct portal_cred save_area; 64 65 /* Validate open mode, and assign roles. */ 66 if ((pcr->pcr_flag & FWRITE) && (pcr->pcr_flag & FREAD)) 67 /* Don't allow both on a single fd. */ 68 return (EINVAL); 69 else if (pcr->pcr_flag & FREAD) { 70 /* 71 * The caller reads from the pipe, 72 * the spawned process writes to it. 73 */ 74 caller_end = READ_END; 75 process_end = WRITE_END; 76 redirect_fd = STDOUT_FILENO; 77 } else if (pcr->pcr_flag & FWRITE) { 78 /* 79 * The caller writes to the pipe, 80 * the spawned process reads from it. 81 */ 82 caller_end = WRITE_END; 83 process_end = READ_END; 84 redirect_fd = STDIN_FILENO; 85 } else 86 return (EINVAL); 87 88 /* Get and check command line. */ 89 pbuf[0] = '/'; 90 strcpy(pbuf+1, key + (v[1] ? strlen(v[1]) : 0)); 91 argc = parse_argv(pbuf, NULL); 92 if (argc == 0) 93 return (ENOENT); 94 95 /* Swap priviledges. */ 96 if (set_user_credentials(pcr, &save_area) < 0) 97 return (errno); 98 99 /* Redirect and spawn the specified process. */ 100 fd[READ_END] = fd[WRITE_END] = -1; 101 if (pipe(fd) < 0) { 102 error = errno; 103 goto done; 104 } 105 switch (fork()) { 106 case -1: /* Error */ 107 error = errno; 108 break; 109 default: /* Parent */ 110 (void)close(fd[process_end]); 111 break; 112 case 0: /* Child */ 113 argv = (char **)malloc((argc + 1) * sizeof(char *)); 114 if (argv == 0) { 115 syslog(LOG_ALERT, 116 "malloc: failed to get space for %d pointers", 117 argc + 1); 118 exit(EXIT_FAILURE); 119 } 120 parse_argv(pbuf, argv); 121 122 if (dup2(fd[process_end], redirect_fd) < 0) { 123 syslog(LOG_ERR, "dup2: %m"); 124 exit(EXIT_FAILURE); 125 } 126 (void)close(fd[caller_end]); 127 (void)close(fd[process_end]); 128 if (errlog() < 0) { 129 syslog(LOG_ERR, "errlog: %m"); 130 exit(EXIT_FAILURE); 131 } 132 if (execv(argv[0], argv) < 0) { 133 syslog(LOG_ERR, "execv(%s): %m", argv[0]); 134 exit(EXIT_FAILURE); 135 } 136 /* NOTREACHED */ 137 } 138 139done: 140 /* Re-establish our priviledges. */ 141 if (restore_credentials(&save_area) < 0) 142 error = errno; 143 144 /* Set return fd value. */ 145 if (error == 0) 146 *fdp = fd[caller_end]; 147 else { 148 for (i = 0; i < 2; i++) 149 if (fd[i] >= 0) 150 (void)close(fd[i]); 151 *fdp = -1; 152 } 153 154 return (error); 155} 156 157/* 158 * Redirect stderr to the system log. 159 * Return 0 if ok. 160 * Return -1 with errno set on error. 161 */ 162static int 163errlog(void) 164{ 165 int fd[2]; 166 char buff[1024]; 167 FILE *f; 168 int ret = 0; 169 170 if (pipe(fd) < 0) 171 return (-1); 172 switch (fork()) { 173 case -1: /* Error */ 174 return (-1); 175 case 0: /* Child */ 176 if ((f = fdopen(fd[READ_END], "r")) == NULL) { 177 syslog(LOG_ERR, "fdopen: %m"); 178 exit(EXIT_FAILURE); 179 } 180 (void)close(fd[WRITE_END]); 181 while (fgets(buff, sizeof(buff), f) != NULL) 182 syslog(LOG_ERR, "exec: %s", buff); 183 exit(EXIT_SUCCESS); 184 /* NOTREACHED */ 185 default: /* Parent */ 186 if (dup2(fd[WRITE_END], STDERR_FILENO) < 0) 187 ret = -1; 188 (void)close(fd[READ_END]); 189 (void)close(fd[WRITE_END]); 190 break; 191 } 192 return (ret); 193} 194 195/* 196 * Parse the args string as a space-separated argument vector. 197 * If argv is not NULL, split the string into its constituent 198 * components, and set argv to point to the beginning of each 199 * string component; NULL-terminating argv. 200 * Return the number of string components. 201 */ 202static int 203parse_argv(char *args, char **argv) 204{ 205 int count = 0; 206 char *p; 207 enum {WORD, SPACE} state = SPACE; 208 209 for (p = args; *p; p++) 210 switch (state) { 211 case WORD: 212 if (isspace(*p)) { 213 if (argv) 214 *p = '\0'; 215 state = SPACE; 216 } 217 break; 218 case SPACE: 219 if (!isspace(*p)) { 220 if (argv) 221 argv[count] = p; 222 count++; 223 state = WORD; 224 } 225 } 226 if (argv) 227 argv[count] = NULL; 228 return (count); 229} 230