xref: /freebsd/lib/libc/gen/wordexp.c (revision c9dbb1cc52b063bbd9ab078a7afc89a8696da659)
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 <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <wordexp.h>
40 #include "un-namespace.h"
41 #include "libc_private.h"
42 
43 __FBSDID("$FreeBSD$");
44 
45 static int	we_askshell(const char *, wordexp_t *, int);
46 static int	we_check(const char *, int);
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, flags)) != 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 /*
98  * we_askshell --
99  *	Use the `wordexp' /bin/sh builtin function to do most of the work
100  *	in expanding the word string. This function is complicated by
101  *	memory management.
102  */
103 static int
104 we_askshell(const char *words, wordexp_t *we, int flags)
105 {
106 	int pdes[2];			/* Pipe to child */
107 	char buf[18];			/* Buffer for byte and word count */
108 	long nwords, nbytes;		/* Number of words, bytes from child */
109 	long i;				/* Handy integer */
110 	size_t sofs;			/* Offset into we->we_strings */
111 	size_t vofs;			/* Offset into we->we_wordv */
112 	pid_t pid;			/* Process ID of child */
113 	pid_t wpid;			/* waitpid return value */
114 	int status;			/* Child exit status */
115 	int error;			/* Our return value */
116 	int serrno;			/* errno to return */
117 	char *np, *p;			/* Handy pointers */
118 	char *nstrings;			/* Temporary for realloc() */
119 	char **nwv;			/* Temporary for realloc() */
120 	sigset_t newsigblock, oldsigblock;
121 	const char *ifs;
122 	char save;
123 
124 	serrno = errno;
125 	ifs = getenv("IFS");
126 
127 	if (pipe2(pdes, O_CLOEXEC) < 0)
128 		return (WRDE_NOSPACE);	/* XXX */
129 	(void)sigemptyset(&newsigblock);
130 	(void)sigaddset(&newsigblock, SIGCHLD);
131 	(void)__libc_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
132 	if ((pid = fork()) < 0) {
133 		serrno = errno;
134 		_close(pdes[0]);
135 		_close(pdes[1]);
136 		(void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
137 		errno = serrno;
138 		return (WRDE_NOSPACE);	/* XXX */
139 	}
140 	else if (pid == 0) {
141 		/*
142 		 * We are the child; make /bin/sh expand `words'.
143 		 */
144 		(void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
145 		if ((pdes[1] != STDOUT_FILENO ?
146 		    _dup2(pdes[1], STDOUT_FILENO) :
147 		    _fcntl(pdes[1], F_SETFD, 0)) < 0)
148 			_exit(1);
149 		execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u",
150 		    "-c", "IFS=$1;eval \"$2\";eval \"echo;set -- $3\";"
151 		    "IFS=;a=\"$*\";printf '%08x' \"$#\" \"${#a}\";"
152 		    "printf '%s\\0' \"$@\"",
153 		    "",
154 		    ifs != NULL ? ifs : " \t\n",
155 		    flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", words,
156 		    (char *)NULL);
157 		_exit(1);
158 	}
159 
160 	/*
161 	 * We are the parent; read the output of the shell wordexp function,
162 	 * which is a byte indicating that the words were parsed successfully,
163 	 * a 32-bit hexadecimal word count, a 32-bit hexadecimal byte count
164 	 * (not including terminating null bytes), followed by the expanded
165 	 * words separated by nulls.
166 	 */
167 	_close(pdes[1]);
168 	switch (we_read_fully(pdes[0], buf, 17)) {
169 	case 1:
170 		error = WRDE_BADVAL;
171 		serrno = errno;
172 		goto cleanup;
173 	case 17:
174 		break;
175 	default:
176 		error = WRDE_SYNTAX;
177 		serrno = errno;
178 		goto cleanup;
179 	}
180 	save = buf[9];
181 	buf[9] = '\0';
182 	nwords = strtol(buf + 1, NULL, 16);
183 	buf[9] = save;
184 	buf[17] = '\0';
185 	nbytes = strtol(buf + 9, NULL, 16) + nwords;
186 
187 	/*
188 	 * Allocate or reallocate (when flags & WRDE_APPEND) the word vector
189 	 * and string storage buffers for the expanded words we're about to
190 	 * read from the child.
191 	 */
192 	sofs = we->we_nbytes;
193 	vofs = we->we_wordc;
194 	if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND))
195 		vofs += we->we_offs;
196 	we->we_wordc += nwords;
197 	we->we_nbytes += nbytes;
198 	if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
199 	    (flags & WRDE_DOOFFS ?  we->we_offs : 0)) *
200 	    sizeof(char *))) == NULL) {
201 		error = WRDE_NOSPACE;
202 		goto cleanup;
203 	}
204 	we->we_wordv = nwv;
205 	if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) {
206 		error = WRDE_NOSPACE;
207 		goto cleanup;
208 	}
209 	for (i = 0; i < vofs; i++)
210 		if (we->we_wordv[i] != NULL)
211 			we->we_wordv[i] += nstrings - we->we_strings;
212 	we->we_strings = nstrings;
213 
214 	if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
215 		error = WRDE_NOSPACE; /* abort for unknown reason */
216 		serrno = errno;
217 		goto cleanup;
218 	}
219 
220 	error = 0;
221 cleanup:
222 	_close(pdes[0]);
223 	do
224 		wpid = _waitpid(pid, &status, 0);
225 	while (wpid < 0 && errno == EINTR);
226 	(void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
227 	if (error != 0) {
228 		errno = serrno;
229 		return (error);
230 	}
231 	if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
232 		return (WRDE_NOSPACE); /* abort for unknown reason */
233 
234 	/*
235 	 * Break the null-terminated expanded word strings out into
236 	 * the vector.
237 	 */
238 	if (vofs == 0 && flags & WRDE_DOOFFS)
239 		while (vofs < we->we_offs)
240 			we->we_wordv[vofs++] = NULL;
241 	p = we->we_strings + sofs;
242 	while (nwords-- != 0) {
243 		we->we_wordv[vofs++] = p;
244 		if ((np = memchr(p, '\0', nbytes)) == NULL)
245 			return (WRDE_NOSPACE);	/* XXX */
246 		nbytes -= np - p + 1;
247 		p = np + 1;
248 	}
249 	we->we_wordv[vofs] = NULL;
250 
251 	return (0);
252 }
253 
254 /*
255  * we_check --
256  *	Check that the string contains none of the following unquoted
257  *	special characters: <newline> |&;<>(){}
258  *	or command substitutions when WRDE_NOCMD is set in flags.
259  */
260 static int
261 we_check(const char *words, int flags)
262 {
263 	char c;
264 	int dquote, level, quote, squote;
265 
266 	quote = squote = dquote = 0;
267 	while ((c = *words++) != '\0') {
268 		switch (c) {
269 		case '\\':
270 			if (squote == 0)
271 				quote ^= 1;
272 			continue;
273 		case '\'':
274 			if (quote + dquote == 0)
275 				squote ^= 1;
276 			break;
277 		case '"':
278 			if (quote + squote == 0)
279 				dquote ^= 1;
280 			break;
281 		case '`':
282 			if (quote + squote == 0 && flags & WRDE_NOCMD)
283 				return (WRDE_CMDSUB);
284 			while ((c = *words++) != '\0' && c != '`')
285 				if (c == '\\' && (c = *words++) == '\0')
286 					break;
287 			if (c == '\0')
288 				return (WRDE_SYNTAX);
289 			break;
290 		case '|': case '&': case ';': case '<': case '>':
291 		case '{': case '}': case '(': case ')': case '\n':
292 			if (quote + squote + dquote == 0)
293 				return (WRDE_BADCHAR);
294 			break;
295 		case '$':
296 			if ((c = *words++) == '\0')
297 				break;
298 			else if (quote + squote == 0 && c == '(') {
299 				if (flags & WRDE_NOCMD && *words != '(')
300 					return (WRDE_CMDSUB);
301 				level = 1;
302 				while ((c = *words++) != '\0') {
303 					if (c == '\\') {
304 						if ((c = *words++) == '\0')
305 							break;
306 					} else if (c == '(')
307 						level++;
308 					else if (c == ')' && --level == 0)
309 						break;
310 				}
311 				if (c == '\0' || level != 0)
312 					return (WRDE_SYNTAX);
313 			} else if (quote + squote == 0 && c == '{') {
314 				level = 1;
315 				while ((c = *words++) != '\0') {
316 					if (c == '\\') {
317 						if ((c = *words++) == '\0')
318 							break;
319 					} else if (c == '{')
320 						level++;
321 					else if (c == '}' && --level == 0)
322 						break;
323 				}
324 				if (c == '\0' || level != 0)
325 					return (WRDE_SYNTAX);
326 			} else
327 				--words;
328 			break;
329 		default:
330 			break;
331 		}
332 		quote = 0;
333 	}
334 	if (quote + squote + dquote != 0)
335 		return (WRDE_SYNTAX);
336 
337 	return (0);
338 }
339 
340 /*
341  * wordfree --
342  *	Free the result of wordexp(). See wordexp(3).
343  *
344  *	Specified by IEEE Std. 1003.1-2001.
345  */
346 void
347 wordfree(wordexp_t *we)
348 {
349 
350 	if (we == NULL)
351 		return;
352 	free(we->we_wordv);
353 	free(we->we_strings);
354 	we->we_wordv = NULL;
355 	we->we_strings = NULL;
356 	we->we_nbytes = 0;
357 	we->we_wordc = 0;
358 }
359