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