1108288Stjr/*- 2108288Stjr * Copyright (c) 2002 Tim J. Robbins. 3108288Stjr * All rights reserved. 4108288Stjr * 5108288Stjr * Redistribution and use in source and binary forms, with or without 6108288Stjr * modification, are permitted provided that the following conditions 7108288Stjr * are met: 8108288Stjr * 1. Redistributions of source code must retain the above copyright 9108288Stjr * notice, this list of conditions and the following disclaimer. 10108288Stjr * 2. Redistributions in binary form must reproduce the above copyright 11108288Stjr * notice, this list of conditions and the following disclaimer in the 12108288Stjr * documentation and/or other materials provided with the distribution. 13108288Stjr * 14108288Stjr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15108288Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16108288Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17108288Stjr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18108288Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19108288Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20108288Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21108288Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22108288Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23108288Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24108288Stjr * SUCH DAMAGE. 25108288Stjr */ 26108288Stjr 27108288Stjr#include "namespace.h" 28108288Stjr#include <sys/cdefs.h> 29108288Stjr#include <sys/types.h> 30108288Stjr#include <sys/wait.h> 31198406Sjilles#include <errno.h> 32108288Stjr#include <fcntl.h> 33108288Stjr#include <paths.h> 34198406Sjilles#include <signal.h> 35288430Sjilles#include <stdbool.h> 36108288Stjr#include <stdio.h> 37108288Stjr#include <stdlib.h> 38108288Stjr#include <string.h> 39108288Stjr#include <unistd.h> 40108288Stjr#include <wordexp.h> 41108288Stjr#include "un-namespace.h" 42287292Skib#include "libc_private.h" 43108288Stjr 44108288Stjr__FBSDID("$FreeBSD: releng/11.0/lib/libc/gen/wordexp.c 288430 2015-09-30 21:32:29Z jilles $"); 45108288Stjr 46108288Stjrstatic int we_askshell(const char *, wordexp_t *, int); 47288430Sjillesstatic int we_check(const char *); 48108288Stjr 49108288Stjr/* 50108288Stjr * wordexp -- 51108288Stjr * Perform shell word expansion on `words' and place the resulting list 52108288Stjr * of words in `we'. See wordexp(3). 53108288Stjr * 54108288Stjr * Specified by IEEE Std. 1003.1-2001. 55108288Stjr */ 56108288Stjrint 57108288Stjrwordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) 58108288Stjr{ 59108288Stjr int error; 60108288Stjr 61108288Stjr if (flags & WRDE_REUSE) 62108288Stjr wordfree(we); 63108288Stjr if ((flags & WRDE_APPEND) == 0) { 64108288Stjr we->we_wordc = 0; 65108288Stjr we->we_wordv = NULL; 66108288Stjr we->we_strings = NULL; 67108288Stjr we->we_nbytes = 0; 68108288Stjr } 69288430Sjilles if ((error = we_check(words)) != 0) { 70108288Stjr wordfree(we); 71108288Stjr return (error); 72108288Stjr } 73108288Stjr if ((error = we_askshell(words, we, flags)) != 0) { 74108288Stjr wordfree(we); 75108288Stjr return (error); 76108288Stjr } 77108288Stjr return (0); 78108288Stjr} 79108288Stjr 80198406Sjillesstatic size_t 81198406Sjilleswe_read_fully(int fd, char *buffer, size_t len) 82198406Sjilles{ 83198406Sjilles size_t done; 84198406Sjilles ssize_t nread; 85198406Sjilles 86198406Sjilles done = 0; 87198406Sjilles do { 88198406Sjilles nread = _read(fd, buffer + done, len - done); 89198406Sjilles if (nread == -1 && errno == EINTR) 90198406Sjilles continue; 91198406Sjilles if (nread <= 0) 92198406Sjilles break; 93198406Sjilles done += nread; 94198406Sjilles } while (done != len); 95198406Sjilles return done; 96198406Sjilles} 97198406Sjilles 98288430Sjillesstatic bool 99288430Sjilleswe_write_fully(int fd, const char *buffer, size_t len) 100288430Sjilles{ 101288430Sjilles size_t done; 102288430Sjilles ssize_t nwritten; 103288430Sjilles 104288430Sjilles done = 0; 105288430Sjilles do { 106288430Sjilles nwritten = _write(fd, buffer + done, len - done); 107288430Sjilles if (nwritten == -1 && errno == EINTR) 108288430Sjilles continue; 109288430Sjilles if (nwritten <= 0) 110288430Sjilles return (false); 111288430Sjilles done += nwritten; 112288430Sjilles } while (done != len); 113288430Sjilles return (true); 114288430Sjilles} 115288430Sjilles 116108288Stjr/* 117108288Stjr * we_askshell -- 118288430Sjilles * Use the `freebsd_wordexp' /bin/sh builtin function to do most of the 119288430Sjilles * work in expanding the word string. This function is complicated by 120108288Stjr * memory management. 121108288Stjr */ 122108288Stjrstatic int 123108288Stjrwe_askshell(const char *words, wordexp_t *we, int flags) 124108288Stjr{ 125288430Sjilles int pdesw[2]; /* Pipe for writing words */ 126288430Sjilles int pdes[2]; /* Pipe for reading output */ 127288430Sjilles char wfdstr[sizeof(int) * 3 + 1]; 128288430Sjilles char buf[35]; /* Buffer for byte and word count */ 129108288Stjr long nwords, nbytes; /* Number of words, bytes from child */ 130108288Stjr long i; /* Handy integer */ 131108288Stjr size_t sofs; /* Offset into we->we_strings */ 132108288Stjr size_t vofs; /* Offset into we->we_wordv */ 133108288Stjr pid_t pid; /* Process ID of child */ 134198406Sjilles pid_t wpid; /* waitpid return value */ 135108288Stjr int status; /* Child exit status */ 136198406Sjilles int error; /* Our return value */ 137198406Sjilles int serrno; /* errno to return */ 138108288Stjr char *np, *p; /* Handy pointers */ 139108288Stjr char *nstrings; /* Temporary for realloc() */ 140108288Stjr char **nwv; /* Temporary for realloc() */ 141198406Sjilles sigset_t newsigblock, oldsigblock; 142280919Sjilles const char *ifs; 143108288Stjr 144198406Sjilles serrno = errno; 145280919Sjilles ifs = getenv("IFS"); 146108288Stjr 147288430Sjilles if (pipe2(pdesw, O_CLOEXEC) < 0) 148108288Stjr return (WRDE_NOSPACE); /* XXX */ 149288430Sjilles snprintf(wfdstr, sizeof(wfdstr), "%d", pdesw[0]); 150288430Sjilles if (pipe2(pdes, O_CLOEXEC) < 0) { 151288430Sjilles _close(pdesw[0]); 152288430Sjilles _close(pdesw[1]); 153288430Sjilles return (WRDE_NOSPACE); /* XXX */ 154288430Sjilles } 155198406Sjilles (void)sigemptyset(&newsigblock); 156198406Sjilles (void)sigaddset(&newsigblock, SIGCHLD); 157287292Skib (void)__libc_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 158108288Stjr if ((pid = fork()) < 0) { 159198406Sjilles serrno = errno; 160288430Sjilles _close(pdesw[0]); 161288430Sjilles _close(pdesw[1]); 162108865Stjr _close(pdes[0]); 163108865Stjr _close(pdes[1]); 164287292Skib (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 165198406Sjilles errno = serrno; 166108288Stjr return (WRDE_NOSPACE); /* XXX */ 167108288Stjr } 168108288Stjr else if (pid == 0) { 169108288Stjr /* 170286830Sjilles * We are the child; make /bin/sh expand `words'. 171108288Stjr */ 172287292Skib (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 173254977Sjilles if ((pdes[1] != STDOUT_FILENO ? 174254977Sjilles _dup2(pdes[1], STDOUT_FILENO) : 175254977Sjilles _fcntl(pdes[1], F_SETFD, 0)) < 0) 176108288Stjr _exit(1); 177288430Sjilles if (_fcntl(pdesw[0], F_SETFD, 0) < 0) 178288430Sjilles _exit(1); 179108288Stjr execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 180288430Sjilles "-c", "IFS=$1;eval \"$2\";" 181288430Sjilles "freebsd_wordexp -f \"$3\" ${4:+\"$4\"}", 182286830Sjilles "", 183280919Sjilles ifs != NULL ? ifs : " \t\n", 184288430Sjilles flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", 185288430Sjilles wfdstr, 186288430Sjilles flags & WRDE_NOCMD ? "-p" : "", 187250406Sjilles (char *)NULL); 188108288Stjr _exit(1); 189108288Stjr } 190108288Stjr 191108288Stjr /* 192288430Sjilles * We are the parent; write the words. 193288430Sjilles */ 194288430Sjilles _close(pdes[1]); 195288430Sjilles _close(pdesw[0]); 196288430Sjilles if (!we_write_fully(pdesw[1], words, strlen(words))) { 197288430Sjilles _close(pdesw[1]); 198288430Sjilles error = WRDE_SYNTAX; 199288430Sjilles goto cleanup; 200288430Sjilles } 201288430Sjilles _close(pdesw[1]); 202288430Sjilles /* 203288430Sjilles * Read the output of the shell wordexp function, 204286941Sjilles * which is a byte indicating that the words were parsed successfully, 205288430Sjilles * a 64-bit hexadecimal word count, a dummy byte, a 64-bit hexadecimal 206288430Sjilles * byte count (not including terminating null bytes), followed by the 207288430Sjilles * expanded words separated by nulls. 208108288Stjr */ 209288430Sjilles switch (we_read_fully(pdes[0], buf, 34)) { 210286941Sjilles case 1: 211288430Sjilles error = buf[0] == 'C' ? WRDE_CMDSUB : WRDE_BADVAL; 212198406Sjilles serrno = errno; 213198406Sjilles goto cleanup; 214288430Sjilles case 34: 215286941Sjilles break; 216286941Sjilles default: 217286941Sjilles error = WRDE_SYNTAX; 218286941Sjilles serrno = errno; 219286941Sjilles goto cleanup; 220108288Stjr } 221288430Sjilles buf[17] = '\0'; 222286941Sjilles nwords = strtol(buf + 1, NULL, 16); 223288430Sjilles buf[34] = '\0'; 224288430Sjilles nbytes = strtol(buf + 18, NULL, 16) + nwords; 225108288Stjr 226108288Stjr /* 227108288Stjr * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 228108288Stjr * and string storage buffers for the expanded words we're about to 229108288Stjr * read from the child. 230108288Stjr */ 231108288Stjr sofs = we->we_nbytes; 232108288Stjr vofs = we->we_wordc; 233131331Stjr if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) 234108641Stjr vofs += we->we_offs; 235108288Stjr we->we_wordc += nwords; 236108288Stjr we->we_nbytes += nbytes; 237108288Stjr if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + 238131331Stjr (flags & WRDE_DOOFFS ? we->we_offs : 0)) * 239108288Stjr sizeof(char *))) == NULL) { 240198406Sjilles error = WRDE_NOSPACE; 241198406Sjilles goto cleanup; 242108288Stjr } 243108288Stjr we->we_wordv = nwv; 244108288Stjr if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 245198406Sjilles error = WRDE_NOSPACE; 246198406Sjilles goto cleanup; 247108288Stjr } 248108288Stjr for (i = 0; i < vofs; i++) 249108288Stjr if (we->we_wordv[i] != NULL) 250108288Stjr we->we_wordv[i] += nstrings - we->we_strings; 251108288Stjr we->we_strings = nstrings; 252108288Stjr 253198406Sjilles if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 254286941Sjilles error = WRDE_NOSPACE; /* abort for unknown reason */ 255198406Sjilles serrno = errno; 256198406Sjilles goto cleanup; 257108288Stjr } 258108288Stjr 259198406Sjilles error = 0; 260198406Sjillescleanup: 261198406Sjilles _close(pdes[0]); 262198406Sjilles do 263198406Sjilles wpid = _waitpid(pid, &status, 0); 264198406Sjilles while (wpid < 0 && errno == EINTR); 265287292Skib (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 266198406Sjilles if (error != 0) { 267198406Sjilles errno = serrno; 268198406Sjilles return (error); 269198406Sjilles } 270198406Sjilles if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 271286941Sjilles return (WRDE_NOSPACE); /* abort for unknown reason */ 272108288Stjr 273108288Stjr /* 274108288Stjr * Break the null-terminated expanded word strings out into 275108288Stjr * the vector. 276108288Stjr */ 277131331Stjr if (vofs == 0 && flags & WRDE_DOOFFS) 278108288Stjr while (vofs < we->we_offs) 279108288Stjr we->we_wordv[vofs++] = NULL; 280108288Stjr p = we->we_strings + sofs; 281108288Stjr while (nwords-- != 0) { 282108288Stjr we->we_wordv[vofs++] = p; 283108288Stjr if ((np = memchr(p, '\0', nbytes)) == NULL) 284108288Stjr return (WRDE_NOSPACE); /* XXX */ 285108288Stjr nbytes -= np - p + 1; 286108288Stjr p = np + 1; 287108288Stjr } 288108288Stjr we->we_wordv[vofs] = NULL; 289108288Stjr 290108288Stjr return (0); 291108288Stjr} 292108288Stjr 293108288Stjr/* 294108288Stjr * we_check -- 295108288Stjr * Check that the string contains none of the following unquoted 296108288Stjr * special characters: <newline> |&;<>(){} 297288430Sjilles * This mainly serves for {} which are normally legal in sh. 298288430Sjilles * It deliberately does not attempt to model full sh syntax. 299108288Stjr */ 300108299Stjrstatic int 301288430Sjilleswe_check(const char *words) 302108288Stjr{ 303108288Stjr char c; 304288430Sjilles /* Saw \ or $, possibly not special: */ 305288430Sjilles bool quote = false, dollar = false; 306288430Sjilles /* Saw ', ", ${, ` or $(, possibly not special: */ 307288430Sjilles bool have_sq = false, have_dq = false, have_par_begin = false; 308288430Sjilles bool have_cmd = false; 309288430Sjilles /* Definitely saw a ', ", ${, ` or $(, need a closing character: */ 310288430Sjilles bool need_sq = false, need_dq = false, need_par_end = false; 311288430Sjilles bool need_cmd_old = false, need_cmd_new = false; 312108288Stjr 313108288Stjr while ((c = *words++) != '\0') { 314108288Stjr switch (c) { 315108288Stjr case '\\': 316288430Sjilles quote = !quote; 317108641Stjr continue; 318288430Sjilles case '$': 319288430Sjilles if (quote) 320288430Sjilles quote = false; 321288430Sjilles else 322288430Sjilles dollar = !dollar; 323288430Sjilles continue; 324108288Stjr case '\'': 325288430Sjilles if (!quote && !have_sq && !have_dq) 326288430Sjilles need_sq = true; 327288430Sjilles else 328288430Sjilles need_sq = false; 329288430Sjilles have_sq = true; 330108288Stjr break; 331108288Stjr case '"': 332288430Sjilles if (!quote && !have_sq && !have_dq) 333288430Sjilles need_dq = true; 334288430Sjilles else 335288430Sjilles need_dq = false; 336288430Sjilles have_dq = true; 337108288Stjr break; 338108288Stjr case '`': 339288430Sjilles if (!quote && !have_sq && !have_cmd) 340288430Sjilles need_cmd_old = true; 341288430Sjilles else 342288430Sjilles need_cmd_old = false; 343288430Sjilles have_cmd = true; 344108288Stjr break; 345288430Sjilles case '{': 346288430Sjilles if (!quote && !dollar && !have_sq && !have_dq && 347288430Sjilles !have_cmd) 348108288Stjr return (WRDE_BADCHAR); 349288430Sjilles if (dollar) { 350288430Sjilles if (!quote && !have_sq) 351288430Sjilles need_par_end = true; 352288430Sjilles have_par_begin = true; 353288430Sjilles } 354108288Stjr break; 355288430Sjilles case '}': 356288430Sjilles if (!quote && !have_sq && !have_dq && !have_par_begin && 357288430Sjilles !have_cmd) 358288430Sjilles return (WRDE_BADCHAR); 359288430Sjilles need_par_end = false; 360108288Stjr break; 361288430Sjilles case '(': 362288430Sjilles if (!quote && !dollar && !have_sq && !have_dq && 363288430Sjilles !have_cmd) 364288430Sjilles return (WRDE_BADCHAR); 365288430Sjilles if (dollar) { 366288430Sjilles if (!quote && !have_sq) 367288430Sjilles need_cmd_new = true; 368288430Sjilles have_cmd = true; 369288430Sjilles } 370288430Sjilles break; 371288430Sjilles case ')': 372288430Sjilles if (!quote && !have_sq && !have_dq && !have_cmd) 373288430Sjilles return (WRDE_BADCHAR); 374288430Sjilles need_cmd_new = false; 375288430Sjilles break; 376288430Sjilles case '|': case '&': case ';': case '<': case '>': case '\n': 377288430Sjilles if (!quote && !have_sq && !have_dq && !have_cmd) 378288430Sjilles return (WRDE_BADCHAR); 379288430Sjilles break; 380108288Stjr default: 381108288Stjr break; 382108288Stjr } 383288430Sjilles quote = dollar = false; 384108288Stjr } 385288430Sjilles if (quote || dollar || need_sq || need_dq || need_par_end || 386288430Sjilles need_cmd_old || need_cmd_new) 387108288Stjr return (WRDE_SYNTAX); 388108288Stjr 389108288Stjr return (0); 390108288Stjr} 391108288Stjr 392108288Stjr/* 393108288Stjr * wordfree -- 394108288Stjr * Free the result of wordexp(). See wordexp(3). 395108288Stjr * 396108288Stjr * Specified by IEEE Std. 1003.1-2001. 397108288Stjr */ 398108288Stjrvoid 399108288Stjrwordfree(wordexp_t *we) 400108288Stjr{ 401108288Stjr 402108288Stjr if (we == NULL) 403108288Stjr return; 404108288Stjr free(we->we_wordv); 405108288Stjr free(we->we_strings); 406108288Stjr we->we_wordv = NULL; 407108288Stjr we->we_strings = NULL; 408108288Stjr we->we_nbytes = 0; 409108288Stjr we->we_wordc = 0; 410108288Stjr} 411