1 /*
2 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4
5 /****************************************************************************
6 Copyright (c) 1999,2000,2001 WU-FTPD Development Group.
7 All rights reserved.
8
9 Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
10 The Regents of the University of California.
11 Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
12 Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
13 Portions Copyright (c) 1989 Massachusetts Institute of Technology.
14 Portions Copyright (c) 1998 Sendmail, Inc.
15 Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
16 Portions Copyright (c) 1997 by Stan Barber.
17 Portions Copyright (c) 1997 by Kent Landfield.
18 Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
19 Free Software Foundation, Inc.
20
21 Use and distribution of this software and its source code are governed
22 by the terms and conditions of the WU-FTPD Software License ("LICENSE").
23
24 If you did not receive a copy of the license, it may be obtained online
25 at http://www.wu-ftpd.org/license.html.
26
27 $Id: glob.c,v 1.14.2.2 2001/11/29 17:01:38 wuftpd Exp $
28
29 ****************************************************************************/
30 /*
31 * C-shell glob for random programs.
32 */
33
34 #include "config.h"
35
36 #include <sys/param.h>
37 #include <sys/stat.h>
38
39 #ifdef HAVE_DIRENT_H
40 #include <dirent.h>
41 #else
42 #include <sys/dir.h>
43 #endif
44
45 #include <pwd.h>
46 #include <errno.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50
51 #include "proto.h"
52
53 #define QUOTE 0200
54 #define TRIM 0177
55 #define eq(a,b) (strcmp(a, b)==0)
56 #define GAVSIZ (1024 * 8)
57 #define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR)
58
59 static char **gargv; /* Pointer to the (stack) arglist */
60 static char **agargv;
61 static size_t agargv_size;
62 static int gargc; /* Number args in gargv */
63 static size_t gnleft;
64 static short gflag;
65 static int tglob(register char);
66
67 /* Prototypes */
68
69 static char *strend(register char *);
70 static void addpath(char);
71 static void ginit(char **);
72 static void collect(register char *, boolean_t check_ncargs);
73 static void acollect(register char *, boolean_t check_ncargs);
74 static void sort(void);
75 static void expand(char *, boolean_t check_ncargs);
76 static void matchdir(char *, boolean_t check_ncargs);
77 static int execbrc(char *, char *);
78 static int match(char *, char *, boolean_t check_ncargs);
79 static int amatch(char *, char *, boolean_t check_ncargs);
80 static void Gcat(register char *, register char *, boolean_t check_ncargs);
81 static void rscan(register char **, int (*f) (register char));
82 static int tglob(register char c);
83 static int gethdir(char *);
84
85 int letter(register char);
86 int digit(register char);
87 int any(register int, register char *);
88 int blklen(register char **);
89 char **blkcpy(char **, register char **);
90
91 char *globerr;
92 char *home;
93 extern int errno;
94
95 static int globcnt;
96
97 char *globchars = "`{[*?";
98
99 static char *gpath, *gpathp, *lastgpathp;
100 static int globbed;
101 static char *entp;
102 static char **sortbas;
103
104 #ifdef OTHER_PASSWD
105 #include "getpwnam.h"
106 extern char _path_passwd[];
107 #endif
108
ftpglob(register char * v,boolean_t check_ncargs)109 char **ftpglob(register char *v, boolean_t check_ncargs)
110 {
111 char agpath[BUFSIZ];
112 char *vv[2];
113
114 if (agargv == NULL) {
115 agargv = (char **) malloc(GAVSIZ * sizeof (char *));
116 if (agargv == NULL) {
117 fatal("Out of memory");
118 }
119 agargv_size = GAVSIZ;
120 }
121 fixpath(v);
122 if (v[0] == '\0')
123 v = ".";
124 else if ((strlen(v) > 1) && (v[strlen(v) - 1] == '/'))
125 v[strlen(v) - 1] = '\0';
126
127 vv[0] = v;
128 vv[1] = NULL;
129 globerr = NULL;
130 gflag = 0;
131 rscan(vv, tglob);
132 if (gflag == 0) {
133 vv[0] = strspl(v, "");
134 return (copyblk(vv));
135 }
136
137 globerr = NULL;
138 gpath = agpath;
139 gpathp = gpath;
140 *gpathp = 0;
141 lastgpathp = &gpath[sizeof agpath - 2];
142 ginit(agargv);
143 globcnt = 0;
144 collect(v, check_ncargs);
145 if (globcnt == 0 && (gflag & 1)) {
146 blkfree(gargv), gargv = 0;
147 return (0);
148 }
149 else
150 return (gargv = copyblk(gargv));
151 }
152
ginit(char ** agargv)153 static void ginit(char **agargv)
154 {
155
156 agargv[0] = 0;
157 gargv = agargv;
158 sortbas = agargv;
159 gargc = 0;
160 gnleft = NCARGS - 4;
161 }
162
collect(register char * as,boolean_t check_ncargs)163 static void collect(register char *as, boolean_t check_ncargs)
164 {
165 if (eq(as, "{") || eq(as, "{}")) {
166 Gcat(as, "", check_ncargs);
167 sort();
168 }
169 else
170 acollect(as, check_ncargs);
171 }
172
acollect(register char * as,boolean_t check_ncargs)173 static void acollect(register char *as, boolean_t check_ncargs)
174 {
175 register int ogargc = gargc;
176
177 gpathp = gpath;
178 *gpathp = 0;
179 globbed = 0;
180 expand(as, check_ncargs);
181 if (gargc != ogargc)
182 sort();
183 }
184
185 static int
argcmp(const void * p1,const void * p2)186 argcmp(const void *p1, const void *p2)
187 {
188 char *s1 = *(char **) p1;
189 char *s2 = *(char **) p2;
190
191 return (strcmp(s1, s2));
192 }
193
sort(void)194 static void sort(void)
195 {
196 char **Gvp = &gargv[gargc];
197
198 if (!globerr)
199 qsort(sortbas, Gvp - sortbas, sizeof (*sortbas), argcmp);
200 sortbas = Gvp;
201 }
202
expand(char * as,boolean_t check_ncargs)203 static void expand(char *as, boolean_t check_ncargs)
204 {
205 register char *cs;
206 register char *sgpathp, *oldcs;
207 struct stat stb;
208
209 if (globerr)
210 return;
211 sgpathp = gpathp;
212 cs = as;
213 if (*cs == '~' && gpathp == gpath) {
214 addpath('~');
215 for (cs++; letter(*cs) || digit(*cs) || *cs == '-';)
216 addpath(*cs++);
217 if (!*cs || *cs == '/') {
218 if (gpathp != gpath + 1) {
219 *gpathp = 0;
220 if (gethdir(gpath + 1))
221 globerr = "Unknown user name after ~";
222 /* memmove used as strings overlap */
223 (void) memmove(gpath, gpath + 1, strlen(gpath + 1) + 1);
224 }
225 else
226 (void) strlcpy(gpath, home, BUFSIZ);
227 gpathp = strend(gpath);
228 }
229 }
230 while (!any(*cs, globchars)) {
231 if (*cs == 0) {
232 if (!globbed)
233 Gcat(gpath, "", check_ncargs);
234 else if (stat(gpath, &stb) >= 0) {
235 Gcat(gpath, "", check_ncargs);
236 globcnt++;
237 }
238 goto endit;
239 }
240 addpath(*cs++);
241 }
242 oldcs = cs;
243 while (cs > as && *cs != '/')
244 cs--, gpathp--;
245 if (*cs == '/')
246 cs++, gpathp++;
247 *gpathp = 0;
248 if (*oldcs == '{') {
249 (void) execbrc(cs, ((char *) 0));
250 return;
251 }
252 matchdir(cs, check_ncargs);
253 endit:
254 gpathp = sgpathp;
255 *gpathp = 0;
256 }
257
matchdir(char * pattern,boolean_t check_ncargs)258 static void matchdir(char *pattern, boolean_t check_ncargs)
259 {
260 struct stat stb;
261
262 #ifdef HAVE_DIRENT_H
263 register struct dirent *dp;
264 #else
265 register struct direct *dp;
266 #endif
267
268 DIR *dirp;
269
270 dirp = opendir(*gpath == '\0' ? "." : gpath);
271 if (dirp == NULL) {
272 if (globbed)
273 return;
274 goto patherr2;
275 }
276 #ifdef HAVE_DIRFD
277 if (fstat(dirfd(dirp), &stb) < 0)
278 #else /* HAVE_DIRFD */
279 if (fstat(dirp->dd_fd, &stb) < 0)
280 #endif /* HAVE_DIRFD */
281 goto patherr1;
282 if (!isdir(stb)) {
283 errno = ENOTDIR;
284 goto patherr1;
285 }
286 while (!globerr && ((dp = readdir(dirp)) != NULL)) {
287 if (dp->d_ino == 0)
288 continue;
289 if (match(dp->d_name, pattern, check_ncargs)) {
290 Gcat(gpath, dp->d_name, check_ncargs);
291 globcnt++;
292 }
293 }
294 closedir(dirp);
295 return;
296
297 patherr1:
298 closedir(dirp);
299 patherr2:
300 globerr = "Bad directory components";
301 }
302
execbrc(char * p,char * s)303 static int execbrc(char *p, char *s)
304 {
305 char restbuf[BUFSIZ + 2];
306 char *restbufend = &restbuf[sizeof(restbuf)];
307 register char *pe, *pm, *pl;
308 int brclev = 0;
309 char *lm, savec, *sgpathp;
310
311 for (lm = restbuf; *p != '{'; *lm++ = *p++) {
312 if (lm >= restbufend)
313 return (0);
314 }
315 for (pe = ++p; *pe; pe++) {
316 switch (*pe) {
317
318 case '{':
319 brclev++;
320 continue;
321
322 case '}':
323 if (brclev == 0)
324 goto pend;
325 brclev--;
326 continue;
327
328 case '[':
329 for (pe++; *pe && *pe != ']'; pe++)
330 continue;
331 if (!*pe) {
332 globerr = "Missing ]";
333 return (0);
334 }
335 continue;
336 }
337 }
338 pend:
339 if (brclev || !*pe) {
340 globerr = "Missing }";
341 return (0);
342 }
343 for (pl = pm = p; pm <= pe; pm++) {
344 switch (*pm & (QUOTE | TRIM)) {
345
346 case '{':
347 brclev++;
348 continue;
349
350 case '}':
351 if (brclev) {
352 brclev--;
353 continue;
354 }
355 goto doit;
356
357 case ',' | QUOTE:
358 case ',':
359 if (brclev)
360 continue;
361 doit:
362 savec = *pm;
363 *pm = 0;
364 if (lm + strlen(pl) + strlen(pe + 1) >= restbufend)
365 return (0);
366 (void) strlcpy(lm, pl, restbufend - lm);
367 (void) strlcat(restbuf, pe + 1, sizeof(restbuf));
368 *pm = savec;
369 if (s == 0) {
370 sgpathp = gpathp;
371 expand(restbuf, B_TRUE);
372 gpathp = sgpathp;
373 *gpathp = 0;
374 }
375 else if (amatch(s, restbuf, B_TRUE))
376 return (1);
377 sort();
378 pl = pm + 1;
379 continue;
380
381 case '[':
382 for (pm++; *pm && *pm != ']'; pm++)
383 continue;
384 if (!*pm) {
385 globerr = "Missing ]";
386 return (0);
387 }
388 continue;
389 }
390 }
391 return (0);
392 }
393
match(char * s,char * p,boolean_t check_ncargs)394 static int match(char *s, char *p, boolean_t check_ncargs)
395 {
396 register int c;
397 register char *sentp;
398 char sglobbed = globbed;
399
400 if (*s == '.' && *p != '.')
401 return (0);
402 sentp = entp;
403 entp = s;
404 c = amatch(s, p, check_ncargs);
405 entp = sentp;
406 globbed = sglobbed;
407 return (c);
408 }
409
amatch(char * s,char * p,boolean_t check_ncargs)410 static int amatch(char *s, char *p, boolean_t check_ncargs)
411 {
412 register int scc;
413 int ok, lc;
414 char *sgpathp;
415 struct stat stb;
416 int c, cc;
417
418 globbed = 1;
419 for (;;) {
420 scc = *s++ & TRIM;
421 switch (c = *p++) {
422
423 case '{':
424 return (execbrc(p - 1, s - 1));
425
426 case '[':
427 ok = 0;
428 lc = 077777;
429 while ((cc = *p++)) {
430 if (cc == ']') {
431 if (ok)
432 break;
433 return (0);
434 }
435 if (cc == '-') {
436 if (lc <= scc && scc <= *p++)
437 ok++;
438 }
439 else if (scc == (lc = cc))
440 ok++;
441 }
442 if (cc == 0) {
443 globerr = "Missing ]";
444 return (0);
445 }
446 continue;
447
448 case '*':
449 if (!*p)
450 return (1);
451 if (*p == '/') {
452 p++;
453 goto slash;
454 } else if (*p == '*') {
455 s--;
456 continue;
457 }
458 s--;
459 do {
460 if (amatch(s, p, check_ncargs))
461 return (1);
462 } while (*s++);
463 return (0);
464
465 case 0:
466 return (scc == 0);
467
468 default:
469 if (c != scc)
470 return (0);
471 continue;
472
473 case '?':
474 if (scc == 0)
475 return (0);
476 continue;
477
478 case '/':
479 if (scc)
480 return (0);
481 slash:
482 s = entp;
483 sgpathp = gpathp;
484 while (*s)
485 addpath(*s++);
486 addpath('/');
487 if (stat(gpath, &stb) == 0 && isdir(stb))
488 if (*p == 0) {
489 Gcat(gpath, "", check_ncargs);
490 globcnt++;
491 }
492 else
493 expand(p, check_ncargs);
494 gpathp = sgpathp;
495 *gpathp = 0;
496 return (0);
497 }
498 }
499 }
500
Gcat(register char * s1,register char * s2,boolean_t check_ncargs)501 static void Gcat(register char *s1, register char *s2, boolean_t check_ncargs)
502 {
503 register size_t len = strlen(s1) + strlen(s2) + 1;
504
505 if (globerr)
506 return;
507 if ((check_ncargs) && ((len + sizeof (char *)) >= gnleft)) {
508 globerr = "Arguments too long";
509 return;
510 }
511 if (len > MAXPATHLEN) {
512 globerr = "Pathname too long";
513 return;
514 }
515 if (gargc >= agargv_size - 1) {
516 char **tmp;
517
518 tmp = (char **)realloc(agargv,
519 (agargv_size + GAVSIZ) * sizeof (char *));
520 if (tmp == NULL) {
521 fatal("Out of memory");
522 } else {
523 agargv = tmp;
524 agargv_size += GAVSIZ;
525 }
526 gargv = agargv;
527 sortbas = agargv;
528 }
529 gargc++;
530 if (check_ncargs)
531 gnleft -= len + sizeof (char *);
532 gargv[gargc] = 0;
533 gargv[gargc - 1] = strspl(s1, s2);
534 }
535
addpath(char c)536 static void addpath(char c)
537 {
538
539 if (gpathp >= lastgpathp)
540 globerr = "Pathname too long";
541 else {
542 *gpathp++ = c;
543 *gpathp = 0;
544 }
545 }
546
rscan(register char ** t,int (* f)(register char))547 static void rscan(register char **t, int (*f) (register char))
548 {
549 register char *p, c;
550
551 while ((p = *t++)) {
552 if (*p == '~')
553 gflag |= 2;
554 else if (eq(p, "{") || eq(p, "{}"))
555 continue;
556 while ((c = *p++))
557 (*f) (c);
558 }
559 }
tglob(register char c)560 static int tglob(register char c)
561 {
562 if (any(c, globchars))
563 gflag |= c == '{' ? 2 : 1;
564 return (c);
565 }
566
letter(register char c)567 int letter(register char c)
568 {
569 return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))
570 || (c == '_'));
571 }
572
digit(register char c)573 int digit(register char c)
574 {
575 return (c >= '0' && c <= '9');
576 }
577
any(register int c,register char * s)578 int any(register int c, register char *s)
579 {
580 while (*s)
581 if (*s++ == c)
582 return (1);
583 return (0);
584 }
585
blklen(register char ** av)586 int blklen(register char **av)
587 {
588 register int i = 0;
589
590 while (*av++)
591 i++;
592 return (i);
593 }
594
blkcpy(char ** oav,register char ** bv)595 char **blkcpy(char **oav, register char **bv)
596 {
597 register char **av = oav;
598
599 while ((*av++ = *bv++))
600 continue;
601 return (oav);
602 }
603
blkfree(char ** av0)604 void blkfree(char **av0)
605 {
606 register char **av = av0;
607
608 if (av) {
609 while (*av)
610 free(*av++);
611 }
612 }
613
strspl(register char * cp,register char * dp)614 char *strspl(register char *cp, register char *dp)
615 {
616 int bufsize = strlen(cp) + strlen(dp) + 1;
617 char *ep = malloc(bufsize);
618
619 if (ep == NULL)
620 fatal("Out of memory");
621 (void) strlcpy(ep, cp, bufsize);
622 (void) strlcat(ep, dp, bufsize);
623 return (ep);
624 }
625
copyblk(register char ** v)626 char **copyblk(register char **v)
627 {
628 register char **nv = (char **) malloc((unsigned) ((blklen(v) + 1) *
629 sizeof(char **)));
630 if (nv == (char **) 0)
631 fatal("Out of memory");
632
633 return (blkcpy(nv, v));
634 }
635
strend(register char * cp)636 static char *strend(register char *cp)
637 {
638 while (*cp)
639 cp++;
640 return (cp);
641 }
642 /*
643 * Extract a home directory from the password file
644 * The argument points to a buffer where the name of the
645 * user whose home directory is sought is currently.
646 * We write the home directory of the user back there.
647 */
gethdir(char * home)648 static int gethdir(char *home)
649 {
650 #ifdef OTHER_PASSWD
651 register struct passwd *pp = bero_getpwnam(home, _path_passwd);
652 #else
653 register struct passwd *pp = getpwnam(home);
654 #endif
655 register char *root = NULL;
656 if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
657 return (1);
658 root = strstr(pp->pw_dir, "/./");
659 (void) strlcpy(home, root ? (root + 2) : pp->pw_dir, lastgpathp - home);
660
661 return (0);
662 }
663