1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2002 Tim J. Robbins. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "namespace.h" 30 #include <sys/cdefs.h> 31 #include <sys/types.h> 32 #include <sys/wait.h> 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <paths.h> 36 #include <signal.h> 37 #include <stdbool.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <wordexp.h> 43 #include "un-namespace.h" 44 #include "libc_private.h" 45 static int we_askshell(const char *, wordexp_t *, int); 46 static int we_check(const char *); 47 48 /* 49 * wordexp -- 50 * Perform shell word expansion on `words' and place the resulting list 51 * of words in `we'. See wordexp(3). 52 * 53 * Specified by IEEE Std. 1003.1-2001. 54 */ 55 int 56 wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) 57 { 58 int error; 59 60 if (flags & WRDE_REUSE) 61 wordfree(we); 62 if ((flags & WRDE_APPEND) == 0) { 63 we->we_wordc = 0; 64 we->we_wordv = NULL; 65 we->we_strings = NULL; 66 we->we_nbytes = 0; 67 } 68 if ((error = we_check(words)) != 0) { 69 wordfree(we); 70 return (error); 71 } 72 if ((error = we_askshell(words, we, flags)) != 0) { 73 wordfree(we); 74 return (error); 75 } 76 return (0); 77 } 78 79 static size_t 80 we_read_fully(int fd, char *buffer, size_t len) 81 { 82 size_t done; 83 ssize_t nread; 84 85 done = 0; 86 do { 87 nread = _read(fd, buffer + done, len - done); 88 if (nread == -1 && errno == EINTR) 89 continue; 90 if (nread <= 0) 91 break; 92 done += nread; 93 } while (done != len); 94 return done; 95 } 96 97 static bool 98 we_write_fully(int fd, const char *buffer, size_t len) 99 { 100 size_t done; 101 ssize_t nwritten; 102 103 done = 0; 104 do { 105 nwritten = _write(fd, buffer + done, len - done); 106 if (nwritten == -1 && errno == EINTR) 107 continue; 108 if (nwritten <= 0) 109 return (false); 110 done += nwritten; 111 } while (done != len); 112 return (true); 113 } 114 115 /* 116 * we_askshell -- 117 * Use the `freebsd_wordexp' /bin/sh builtin function to do most of the 118 * work in expanding the word string. This function is complicated by 119 * memory management. 120 */ 121 static int 122 we_askshell(const char *words, wordexp_t *we, int flags) 123 { 124 int pdesw[2]; /* Pipe for writing words */ 125 int pdes[2]; /* Pipe for reading output */ 126 char wfdstr[sizeof(int) * 3 + 1]; 127 char buf[35]; /* Buffer for byte and word count */ 128 long nwords, nbytes; /* Number of words, bytes from child */ 129 long i; /* Handy integer */ 130 size_t sofs; /* Offset into we->we_strings */ 131 size_t vofs; /* Offset into we->we_wordv */ 132 pid_t pid; /* Process ID of child */ 133 pid_t wpid; /* waitpid return value */ 134 int status; /* Child exit status */ 135 int error; /* Our return value */ 136 int serrno; /* errno to return */ 137 char *np, *p; /* Handy pointers */ 138 char *nstrings; /* Temporary for realloc() */ 139 char **nwv; /* Temporary for realloc() */ 140 sigset_t newsigblock, oldsigblock; 141 const char *ifs; 142 143 serrno = errno; 144 ifs = getenv("IFS"); 145 146 if (pipe2(pdesw, O_CLOEXEC) < 0) 147 return (WRDE_NOSPACE); /* XXX */ 148 snprintf(wfdstr, sizeof(wfdstr), "%d", pdesw[0]); 149 if (pipe2(pdes, O_CLOEXEC) < 0) { 150 _close(pdesw[0]); 151 _close(pdesw[1]); 152 return (WRDE_NOSPACE); /* XXX */ 153 } 154 (void)sigemptyset(&newsigblock); 155 (void)sigaddset(&newsigblock, SIGCHLD); 156 (void)__libc_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 157 if ((pid = fork()) < 0) { 158 serrno = errno; 159 _close(pdesw[0]); 160 _close(pdesw[1]); 161 _close(pdes[0]); 162 _close(pdes[1]); 163 (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 164 errno = serrno; 165 return (WRDE_NOSPACE); /* XXX */ 166 } 167 else if (pid == 0) { 168 /* 169 * We are the child; make /bin/sh expand `words'. 170 */ 171 (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 172 if ((pdes[1] != STDOUT_FILENO ? 173 _dup2(pdes[1], STDOUT_FILENO) : 174 _fcntl(pdes[1], F_SETFD, 0)) < 0) 175 _exit(1); 176 if (_fcntl(pdesw[0], F_SETFD, 0) < 0) 177 _exit(1); 178 execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 179 "-c", "IFS=$1;eval \"$2\";" 180 "freebsd_wordexp -f \"$3\" ${4:+\"$4\"}", 181 "", 182 ifs != NULL ? ifs : " \t\n", 183 flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", 184 wfdstr, 185 flags & WRDE_NOCMD ? "-p" : "", 186 (char *)NULL); 187 _exit(1); 188 } 189 190 /* 191 * We are the parent; write the words. 192 */ 193 _close(pdes[1]); 194 _close(pdesw[0]); 195 if (!we_write_fully(pdesw[1], words, strlen(words))) { 196 _close(pdesw[1]); 197 error = WRDE_SYNTAX; 198 goto cleanup; 199 } 200 _close(pdesw[1]); 201 /* 202 * Read the output of the shell wordexp function, 203 * which is a byte indicating that the words were parsed successfully, 204 * a 64-bit hexadecimal word count, a dummy byte, a 64-bit hexadecimal 205 * byte count (not including terminating null bytes), followed by the 206 * expanded words separated by nulls. 207 */ 208 switch (we_read_fully(pdes[0], buf, 34)) { 209 case 1: 210 error = buf[0] == 'C' ? WRDE_CMDSUB : WRDE_BADVAL; 211 serrno = errno; 212 goto cleanup; 213 case 34: 214 break; 215 default: 216 error = WRDE_SYNTAX; 217 serrno = errno; 218 goto cleanup; 219 } 220 buf[17] = '\0'; 221 nwords = strtol(buf + 1, NULL, 16); 222 buf[34] = '\0'; 223 nbytes = strtol(buf + 18, NULL, 16) + nwords; 224 225 /* 226 * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 227 * and string storage buffers for the expanded words we're about to 228 * read from the child. 229 */ 230 sofs = we->we_nbytes; 231 vofs = we->we_wordc; 232 if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) 233 vofs += we->we_offs; 234 we->we_wordc += nwords; 235 we->we_nbytes += nbytes; 236 if ((nwv = reallocarray(we->we_wordv, (we->we_wordc + 1 + 237 (flags & WRDE_DOOFFS ? we->we_offs : 0)), 238 sizeof(char *))) == NULL) { 239 error = WRDE_NOSPACE; 240 goto cleanup; 241 } 242 we->we_wordv = nwv; 243 if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 244 error = WRDE_NOSPACE; 245 goto cleanup; 246 } 247 for (i = 0; i < vofs; i++) 248 if (we->we_wordv[i] != NULL) 249 we->we_wordv[i] += nstrings - we->we_strings; 250 we->we_strings = nstrings; 251 252 if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 253 error = WRDE_NOSPACE; /* abort for unknown reason */ 254 serrno = errno; 255 goto cleanup; 256 } 257 258 error = 0; 259 cleanup: 260 _close(pdes[0]); 261 do 262 wpid = _waitpid(pid, &status, 0); 263 while (wpid < 0 && errno == EINTR); 264 (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 265 if (error != 0) { 266 errno = serrno; 267 return (error); 268 } 269 if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 270 return (WRDE_NOSPACE); /* abort for unknown reason */ 271 272 /* 273 * Break the null-terminated expanded word strings out into 274 * the vector. 275 */ 276 if (vofs == 0 && flags & WRDE_DOOFFS) 277 while (vofs < we->we_offs) 278 we->we_wordv[vofs++] = NULL; 279 p = we->we_strings + sofs; 280 while (nwords-- != 0) { 281 we->we_wordv[vofs++] = p; 282 if ((np = memchr(p, '\0', nbytes)) == NULL) 283 return (WRDE_NOSPACE); /* XXX */ 284 nbytes -= np - p + 1; 285 p = np + 1; 286 } 287 we->we_wordv[vofs] = NULL; 288 289 return (0); 290 } 291 292 /* 293 * we_check -- 294 * Check that the string contains none of the following unquoted 295 * special characters: <newline> |&;<>(){} 296 * This mainly serves for {} which are normally legal in sh. 297 * It deliberately does not attempt to model full sh syntax. 298 */ 299 static int 300 we_check(const char *words) 301 { 302 char c; 303 /* Saw \ or $, possibly not special: */ 304 bool quote = false, dollar = false; 305 /* Saw ', ", ${, ` or $(, possibly not special: */ 306 bool have_sq = false, have_dq = false, have_par_begin = false; 307 bool have_cmd = false; 308 /* Definitely saw a ', ", ${, ` or $(, need a closing character: */ 309 bool need_sq = false, need_dq = false, need_par_end = false; 310 bool need_cmd_old = false, need_cmd_new = false; 311 312 while ((c = *words++) != '\0') { 313 switch (c) { 314 case '\\': 315 quote = !quote; 316 continue; 317 case '$': 318 if (quote) 319 quote = false; 320 else 321 dollar = !dollar; 322 continue; 323 case '\'': 324 if (!quote && !have_sq && !have_dq) 325 need_sq = true; 326 else 327 need_sq = false; 328 have_sq = true; 329 break; 330 case '"': 331 if (!quote && !have_sq && !have_dq) 332 need_dq = true; 333 else 334 need_dq = false; 335 have_dq = true; 336 break; 337 case '`': 338 if (!quote && !have_sq && !have_cmd) 339 need_cmd_old = true; 340 else 341 need_cmd_old = false; 342 have_cmd = true; 343 break; 344 case '{': 345 if (!quote && !dollar && !have_sq && !have_dq && 346 !have_cmd) 347 return (WRDE_BADCHAR); 348 if (dollar) { 349 if (!quote && !have_sq) 350 need_par_end = true; 351 have_par_begin = true; 352 } 353 break; 354 case '}': 355 if (!quote && !have_sq && !have_dq && !have_par_begin && 356 !have_cmd) 357 return (WRDE_BADCHAR); 358 need_par_end = false; 359 break; 360 case '(': 361 if (!quote && !dollar && !have_sq && !have_dq && 362 !have_cmd) 363 return (WRDE_BADCHAR); 364 if (dollar) { 365 if (!quote && !have_sq) 366 need_cmd_new = true; 367 have_cmd = true; 368 } 369 break; 370 case ')': 371 if (!quote && !have_sq && !have_dq && !have_cmd) 372 return (WRDE_BADCHAR); 373 need_cmd_new = false; 374 break; 375 case '|': case '&': case ';': case '<': case '>': case '\n': 376 if (!quote && !have_sq && !have_dq && !have_cmd) 377 return (WRDE_BADCHAR); 378 break; 379 default: 380 break; 381 } 382 quote = dollar = false; 383 } 384 if (quote || dollar || need_sq || need_dq || need_par_end || 385 need_cmd_old || need_cmd_new) 386 return (WRDE_SYNTAX); 387 388 return (0); 389 } 390 391 /* 392 * wordfree -- 393 * Free the result of wordexp(). See wordexp(3). 394 * 395 * Specified by IEEE Std. 1003.1-2001. 396 */ 397 void 398 wordfree(wordexp_t *we) 399 { 400 401 if (we == NULL) 402 return; 403 free(we->we_wordv); 404 free(we->we_strings); 405 we->we_wordv = NULL; 406 we->we_strings = NULL; 407 we->we_nbytes = 0; 408 we->we_wordc = 0; 409 } 410