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