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