]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - lib/libc/gen/wordexp.c
Fix regression in pw(8) when creating numeric users or groups.
[FreeBSD/releng/10.2.git] / lib / libc / gen / wordexp.c
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 bbuf[9];                   /* Buffer for byte count */
108         char wbuf[9];                   /* Buffer for word count */
109         long nwords, nbytes;            /* Number of words, bytes from child */
110         long i;                         /* Handy integer */
111         size_t sofs;                    /* Offset into we->we_strings */
112         size_t vofs;                    /* Offset into we->we_wordv */
113         pid_t pid;                      /* Process ID of child */
114         pid_t wpid;                     /* waitpid return value */
115         int status;                     /* Child exit status */
116         int error;                      /* Our return value */
117         int serrno;                     /* errno to return */
118         char *np, *p;                   /* Handy pointers */
119         char *nstrings;                 /* Temporary for realloc() */
120         char **nwv;                     /* Temporary for realloc() */
121         sigset_t newsigblock, oldsigblock;
122         const char *ifs;
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; just get /bin/sh to run the wordexp
143                  * builtin on `words'.
144                  */
145                 (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
146                 if ((pdes[1] != STDOUT_FILENO ?
147                     _dup2(pdes[1], STDOUT_FILENO) :
148                     _fcntl(pdes[1], F_SETFD, 0)) < 0)
149                         _exit(1);
150                 execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u",
151                     "-c", "IFS=$1;eval \"$2\";eval \"wordexp $3\"", "",
152                     ifs != NULL ? ifs : " \t\n",
153                     flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", words,
154                     (char *)NULL);
155                 _exit(1);
156         }
157
158         /*
159          * We are the parent; read the output of the shell wordexp function,
160          * which is a 32-bit hexadecimal word count, a 32-bit hexadecimal
161          * byte count (not including terminating null bytes), followed by
162          * the expanded words separated by nulls.
163          */
164         _close(pdes[1]);
165         if (we_read_fully(pdes[0], wbuf, 8) != 8 ||
166                         we_read_fully(pdes[0], bbuf, 8) != 8) {
167                 error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
168                 serrno = errno;
169                 goto cleanup;
170         }
171         wbuf[8] = bbuf[8] = '\0';
172         nwords = strtol(wbuf, NULL, 16);
173         nbytes = strtol(bbuf, NULL, 16) + nwords;
174
175         /*
176          * Allocate or reallocate (when flags & WRDE_APPEND) the word vector
177          * and string storage buffers for the expanded words we're about to
178          * read from the child.
179          */
180         sofs = we->we_nbytes;
181         vofs = we->we_wordc;
182         if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND))
183                 vofs += we->we_offs;
184         we->we_wordc += nwords;
185         we->we_nbytes += nbytes;
186         if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
187             (flags & WRDE_DOOFFS ?  we->we_offs : 0)) *
188             sizeof(char *))) == NULL) {
189                 error = WRDE_NOSPACE;
190                 goto cleanup;
191         }
192         we->we_wordv = nwv;
193         if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) {
194                 error = WRDE_NOSPACE;
195                 goto cleanup;
196         }
197         for (i = 0; i < vofs; i++)
198                 if (we->we_wordv[i] != NULL)
199                         we->we_wordv[i] += nstrings - we->we_strings;
200         we->we_strings = nstrings;
201
202         if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
203                 error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
204                 serrno = errno;
205                 goto cleanup;
206         }
207
208         error = 0;
209 cleanup:
210         _close(pdes[0]);
211         do
212                 wpid = _waitpid(pid, &status, 0);
213         while (wpid < 0 && errno == EINTR);
214         (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
215         if (error != 0) {
216                 errno = serrno;
217                 return (error);
218         }
219         if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
220                 return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
221
222         /*
223          * Break the null-terminated expanded word strings out into
224          * the vector.
225          */
226         if (vofs == 0 && flags & WRDE_DOOFFS)
227                 while (vofs < we->we_offs)
228                         we->we_wordv[vofs++] = NULL;
229         p = we->we_strings + sofs;
230         while (nwords-- != 0) {
231                 we->we_wordv[vofs++] = p;
232                 if ((np = memchr(p, '\0', nbytes)) == NULL)
233                         return (WRDE_NOSPACE);  /* XXX */
234                 nbytes -= np - p + 1;
235                 p = np + 1;
236         }
237         we->we_wordv[vofs] = NULL;
238
239         return (0);
240 }
241
242 /*
243  * we_check --
244  *      Check that the string contains none of the following unquoted
245  *      special characters: <newline> |&;<>(){}
246  *      or command substitutions when WRDE_NOCMD is set in flags.
247  */
248 static int
249 we_check(const char *words, int flags)
250 {
251         char c;
252         int dquote, level, quote, squote;
253
254         quote = squote = dquote = 0;
255         while ((c = *words++) != '\0') {
256                 switch (c) {
257                 case '\\':
258                         if (squote == 0)
259                                 quote ^= 1;
260                         continue;
261                 case '\'':
262                         if (quote + dquote == 0)
263                                 squote ^= 1;
264                         break;
265                 case '"':
266                         if (quote + squote == 0)
267                                 dquote ^= 1;
268                         break;
269                 case '`':
270                         if (quote + squote == 0 && flags & WRDE_NOCMD)
271                                 return (WRDE_CMDSUB);
272                         while ((c = *words++) != '\0' && c != '`')
273                                 if (c == '\\' && (c = *words++) == '\0')
274                                         break;
275                         if (c == '\0')
276                                 return (WRDE_SYNTAX);
277                         break;
278                 case '|': case '&': case ';': case '<': case '>':
279                 case '{': case '}': case '(': case ')': case '\n':
280                         if (quote + squote + dquote == 0)
281                                 return (WRDE_BADCHAR);
282                         break;
283                 case '$':
284                         if ((c = *words++) == '\0')
285                                 break;
286                         else if (quote + squote == 0 && c == '(') {
287                                 if (flags & WRDE_NOCMD && *words != '(')
288                                         return (WRDE_CMDSUB);
289                                 level = 1;
290                                 while ((c = *words++) != '\0') {
291                                         if (c == '\\') {
292                                                 if ((c = *words++) == '\0')
293                                                         break;
294                                         } else if (c == '(')
295                                                 level++;
296                                         else if (c == ')' && --level == 0)
297                                                 break;
298                                 }
299                                 if (c == '\0' || level != 0)
300                                         return (WRDE_SYNTAX);
301                         } else if (quote + squote == 0 && c == '{') {
302                                 level = 1;
303                                 while ((c = *words++) != '\0') {
304                                         if (c == '\\') {
305                                                 if ((c = *words++) == '\0')
306                                                         break;
307                                         } else if (c == '{')
308                                                 level++;
309                                         else if (c == '}' && --level == 0)
310                                                 break;
311                                 }
312                                 if (c == '\0' || level != 0)
313                                         return (WRDE_SYNTAX);
314                         } else
315                                 --words;
316                         break;
317                 default:
318                         break;
319                 }
320                 quote = 0;
321         }
322         if (quote + squote + dquote != 0)
323                 return (WRDE_SYNTAX);
324
325         return (0);
326 }
327
328 /*
329  * wordfree --
330  *      Free the result of wordexp(). See wordexp(3).
331  *
332  *      Specified by IEEE Std. 1003.1-2001.
333  */
334 void
335 wordfree(wordexp_t *we)
336 {
337
338         if (we == NULL)
339                 return;
340         free(we->we_wordv);
341         free(we->we_strings);
342         we->we_wordv = NULL;
343         we->we_strings = NULL;
344         we->we_nbytes = 0;
345         we->we_wordc = 0;
346 }