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