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