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 }