xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/glob.c (revision 12333:f835be4545bb)
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