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> 35289938Sjilles#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" 42287480Skib#include "libc_private.h" 43108288Stjr 44108288Stjr__FBSDID("$FreeBSD: releng/10.3/lib/libc/gen/wordexp.c 289938 2015-10-25 17:17:50Z jilles $"); 45108288Stjr 46108288Stjrstatic int we_askshell(const char *, wordexp_t *, int); 47289938Sjillesstatic 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 } 69289938Sjilles 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 98289938Sjillesstatic bool 99289938Sjilleswe_write_fully(int fd, const char *buffer, size_t len) 100289938Sjilles{ 101289938Sjilles size_t done; 102289938Sjilles ssize_t nwritten; 103289938Sjilles 104289938Sjilles done = 0; 105289938Sjilles do { 106289938Sjilles nwritten = _write(fd, buffer + done, len - done); 107289938Sjilles if (nwritten == -1 && errno == EINTR) 108289938Sjilles continue; 109289938Sjilles if (nwritten <= 0) 110289938Sjilles return (false); 111289938Sjilles done += nwritten; 112289938Sjilles } while (done != len); 113289938Sjilles return (true); 114289938Sjilles} 115289938Sjilles 116108288Stjr/* 117108288Stjr * we_askshell -- 118289938Sjilles * Use the `freebsd_wordexp' /bin/sh builtin function to do most of the 119289938Sjilles * 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{ 125289938Sjilles int pdesw[2]; /* Pipe for writing words */ 126289938Sjilles int pdes[2]; /* Pipe for reading output */ 127289938Sjilles char wfdstr[sizeof(int) * 3 + 1]; 128289938Sjilles 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; 142281743Sjilles const char *ifs; 143108288Stjr 144198406Sjilles serrno = errno; 145281743Sjilles ifs = getenv("IFS"); 146108288Stjr 147289938Sjilles if (pipe2(pdesw, O_CLOEXEC) < 0) 148108288Stjr return (WRDE_NOSPACE); /* XXX */ 149289938Sjilles snprintf(wfdstr, sizeof(wfdstr), "%d", pdesw[0]); 150289938Sjilles if (pipe2(pdes, O_CLOEXEC) < 0) { 151289938Sjilles _close(pdesw[0]); 152289938Sjilles _close(pdesw[1]); 153289938Sjilles return (WRDE_NOSPACE); /* XXX */ 154289938Sjilles } 155198406Sjilles (void)sigemptyset(&newsigblock); 156198406Sjilles (void)sigaddset(&newsigblock, SIGCHLD); 157287480Skib (void)__libc_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 158108288Stjr if ((pid = fork()) < 0) { 159198406Sjilles serrno = errno; 160289938Sjilles _close(pdesw[0]); 161289938Sjilles _close(pdesw[1]); 162108865Stjr _close(pdes[0]); 163108865Stjr _close(pdes[1]); 164287480Skib (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 165198406Sjilles errno = serrno; 166108288Stjr return (WRDE_NOSPACE); /* XXX */ 167108288Stjr } 168108288Stjr else if (pid == 0) { 169108288Stjr /* 170289938Sjilles * We are the child; make /bin/sh expand `words'. 171108288Stjr */ 172287480Skib (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); 177289938Sjilles if (_fcntl(pdesw[0], F_SETFD, 0) < 0) 178289938Sjilles _exit(1); 179108288Stjr execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 180289938Sjilles "-c", "IFS=$1;eval \"$2\";" 181289938Sjilles "freebsd_wordexp -f \"$3\" ${4:+\"$4\"}", 182289938Sjilles "", 183281743Sjilles ifs != NULL ? ifs : " \t\n", 184289938Sjilles flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", 185289938Sjilles wfdstr, 186289938Sjilles flags & WRDE_NOCMD ? "-p" : "", 187250406Sjilles (char *)NULL); 188108288Stjr _exit(1); 189108288Stjr } 190108288Stjr 191108288Stjr /* 192289938Sjilles * We are the parent; write the words. 193108288Stjr */ 194108865Stjr _close(pdes[1]); 195289938Sjilles _close(pdesw[0]); 196289938Sjilles if (!we_write_fully(pdesw[1], words, strlen(words))) { 197289938Sjilles _close(pdesw[1]); 198289938Sjilles error = WRDE_SYNTAX; 199289938Sjilles goto cleanup; 200289938Sjilles } 201289938Sjilles _close(pdesw[1]); 202289938Sjilles /* 203289938Sjilles * Read the output of the shell wordexp function, 204289938Sjilles * which is a byte indicating that the words were parsed successfully, 205289938Sjilles * a 64-bit hexadecimal word count, a dummy byte, a 64-bit hexadecimal 206289938Sjilles * byte count (not including terminating null bytes), followed by the 207289938Sjilles * expanded words separated by nulls. 208289938Sjilles */ 209289938Sjilles switch (we_read_fully(pdes[0], buf, 34)) { 210289938Sjilles case 1: 211289938Sjilles error = buf[0] == 'C' ? WRDE_CMDSUB : 212289938Sjilles flags & WRDE_UNDEF ? WRDE_BADVAL : 213289938Sjilles WRDE_SYNTAX; 214198406Sjilles serrno = errno; 215198406Sjilles goto cleanup; 216289938Sjilles case 34: 217289938Sjilles break; 218289938Sjilles default: 219289938Sjilles error = WRDE_SYNTAX; 220289938Sjilles serrno = errno; 221289938Sjilles goto cleanup; 222108288Stjr } 223289938Sjilles buf[17] = '\0'; 224289938Sjilles nwords = strtol(buf + 1, NULL, 16); 225289938Sjilles buf[34] = '\0'; 226289938Sjilles nbytes = strtol(buf + 18, NULL, 16) + nwords; 227108288Stjr 228108288Stjr /* 229108288Stjr * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 230108288Stjr * and string storage buffers for the expanded words we're about to 231108288Stjr * read from the child. 232108288Stjr */ 233108288Stjr sofs = we->we_nbytes; 234108288Stjr vofs = we->we_wordc; 235131331Stjr if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) 236108641Stjr vofs += we->we_offs; 237108288Stjr we->we_wordc += nwords; 238108288Stjr we->we_nbytes += nbytes; 239108288Stjr if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + 240131331Stjr (flags & WRDE_DOOFFS ? we->we_offs : 0)) * 241108288Stjr sizeof(char *))) == NULL) { 242198406Sjilles error = WRDE_NOSPACE; 243198406Sjilles goto cleanup; 244108288Stjr } 245108288Stjr we->we_wordv = nwv; 246108288Stjr if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 247198406Sjilles error = WRDE_NOSPACE; 248198406Sjilles goto cleanup; 249108288Stjr } 250108288Stjr for (i = 0; i < vofs; i++) 251108288Stjr if (we->we_wordv[i] != NULL) 252108288Stjr we->we_wordv[i] += nstrings - we->we_strings; 253108288Stjr we->we_strings = nstrings; 254108288Stjr 255198406Sjilles if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 256198406Sjilles error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; 257198406Sjilles serrno = errno; 258198406Sjilles goto cleanup; 259108288Stjr } 260108288Stjr 261198406Sjilles error = 0; 262198406Sjillescleanup: 263198406Sjilles _close(pdes[0]); 264198406Sjilles do 265198406Sjilles wpid = _waitpid(pid, &status, 0); 266198406Sjilles while (wpid < 0 && errno == EINTR); 267287480Skib (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 268198406Sjilles if (error != 0) { 269198406Sjilles errno = serrno; 270198406Sjilles return (error); 271198406Sjilles } 272198406Sjilles if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 273108288Stjr return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); 274108288Stjr 275108288Stjr /* 276108288Stjr * Break the null-terminated expanded word strings out into 277108288Stjr * the vector. 278108288Stjr */ 279131331Stjr if (vofs == 0 && flags & WRDE_DOOFFS) 280108288Stjr while (vofs < we->we_offs) 281108288Stjr we->we_wordv[vofs++] = NULL; 282108288Stjr p = we->we_strings + sofs; 283108288Stjr while (nwords-- != 0) { 284108288Stjr we->we_wordv[vofs++] = p; 285108288Stjr if ((np = memchr(p, '\0', nbytes)) == NULL) 286108288Stjr return (WRDE_NOSPACE); /* XXX */ 287108288Stjr nbytes -= np - p + 1; 288108288Stjr p = np + 1; 289108288Stjr } 290108288Stjr we->we_wordv[vofs] = NULL; 291108288Stjr 292108288Stjr return (0); 293108288Stjr} 294108288Stjr 295108288Stjr/* 296108288Stjr * we_check -- 297108288Stjr * Check that the string contains none of the following unquoted 298108288Stjr * special characters: <newline> |&;<>(){} 299289938Sjilles * This mainly serves for {} which are normally legal in sh. 300289938Sjilles * It deliberately does not attempt to model full sh syntax. 301108288Stjr */ 302108299Stjrstatic int 303289938Sjilleswe_check(const char *words) 304108288Stjr{ 305108288Stjr char c; 306289938Sjilles /* Saw \ or $, possibly not special: */ 307289938Sjilles bool quote = false, dollar = false; 308289938Sjilles /* Saw ', ", ${, ` or $(, possibly not special: */ 309289938Sjilles bool have_sq = false, have_dq = false, have_par_begin = false; 310289938Sjilles bool have_cmd = false; 311289938Sjilles /* Definitely saw a ', ", ${, ` or $(, need a closing character: */ 312289938Sjilles bool need_sq = false, need_dq = false, need_par_end = false; 313289938Sjilles bool need_cmd_old = false, need_cmd_new = false; 314108288Stjr 315108288Stjr while ((c = *words++) != '\0') { 316108288Stjr switch (c) { 317108288Stjr case '\\': 318289938Sjilles quote = !quote; 319108641Stjr continue; 320289938Sjilles case '$': 321289938Sjilles if (quote) 322289938Sjilles quote = false; 323289938Sjilles else 324289938Sjilles dollar = !dollar; 325289938Sjilles continue; 326108288Stjr case '\'': 327289938Sjilles if (!quote && !have_sq && !have_dq) 328289938Sjilles need_sq = true; 329289938Sjilles else 330289938Sjilles need_sq = false; 331289938Sjilles have_sq = true; 332108288Stjr break; 333108288Stjr case '"': 334289938Sjilles if (!quote && !have_sq && !have_dq) 335289938Sjilles need_dq = true; 336289938Sjilles else 337289938Sjilles need_dq = false; 338289938Sjilles have_dq = true; 339108288Stjr break; 340108288Stjr case '`': 341289938Sjilles if (!quote && !have_sq && !have_cmd) 342289938Sjilles need_cmd_old = true; 343289938Sjilles else 344289938Sjilles need_cmd_old = false; 345289938Sjilles have_cmd = true; 346108288Stjr break; 347289938Sjilles case '{': 348289938Sjilles if (!quote && !dollar && !have_sq && !have_dq && 349289938Sjilles !have_cmd) 350108288Stjr return (WRDE_BADCHAR); 351289938Sjilles if (dollar) { 352289938Sjilles if (!quote && !have_sq) 353289938Sjilles need_par_end = true; 354289938Sjilles have_par_begin = true; 355289938Sjilles } 356108288Stjr break; 357289938Sjilles case '}': 358289938Sjilles if (!quote && !have_sq && !have_dq && !have_par_begin && 359289938Sjilles !have_cmd) 360289938Sjilles return (WRDE_BADCHAR); 361289938Sjilles need_par_end = false; 362108288Stjr break; 363289938Sjilles case '(': 364289938Sjilles if (!quote && !dollar && !have_sq && !have_dq && 365289938Sjilles !have_cmd) 366289938Sjilles return (WRDE_BADCHAR); 367289938Sjilles if (dollar) { 368289938Sjilles if (!quote && !have_sq) 369289938Sjilles need_cmd_new = true; 370289938Sjilles have_cmd = true; 371289938Sjilles } 372289938Sjilles break; 373289938Sjilles case ')': 374289938Sjilles if (!quote && !have_sq && !have_dq && !have_cmd) 375289938Sjilles return (WRDE_BADCHAR); 376289938Sjilles need_cmd_new = false; 377289938Sjilles break; 378289938Sjilles case '|': case '&': case ';': case '<': case '>': case '\n': 379289938Sjilles if (!quote && !have_sq && !have_dq && !have_cmd) 380289938Sjilles return (WRDE_BADCHAR); 381289938Sjilles break; 382108288Stjr default: 383108288Stjr break; 384108288Stjr } 385289938Sjilles quote = dollar = false; 386108288Stjr } 387289938Sjilles if (quote || dollar || need_sq || need_dq || need_par_end || 388289938Sjilles need_cmd_old || need_cmd_new) 389108288Stjr return (WRDE_SYNTAX); 390108288Stjr 391108288Stjr return (0); 392108288Stjr} 393108288Stjr 394108288Stjr/* 395108288Stjr * wordfree -- 396108288Stjr * Free the result of wordexp(). See wordexp(3). 397108288Stjr * 398108288Stjr * Specified by IEEE Std. 1003.1-2001. 399108288Stjr */ 400108288Stjrvoid 401108288Stjrwordfree(wordexp_t *we) 402108288Stjr{ 403108288Stjr 404108288Stjr if (we == NULL) 405108288Stjr return; 406108288Stjr free(we->we_wordv); 407108288Stjr free(we->we_strings); 408108288Stjr we->we_wordv = NULL; 409108288Stjr we->we_strings = NULL; 410108288Stjr we->we_nbytes = 0; 411108288Stjr we->we_wordc = 0; 412108288Stjr} 413