1 /* 2 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. 3 */ 4 5 /**************************************************************************** 6 7 Copyright (c) 1999,2000 WU-FTPD Development Group. 8 All rights reserved. 9 10 Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994 11 The Regents of the University of California. 12 Portions Copyright (c) 1993, 1994 Washington University in Saint Louis. 13 Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc. 14 Portions Copyright (c) 1989 Massachusetts Institute of Technology. 15 Portions Copyright (c) 1998 Sendmail, Inc. 16 Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman. 17 Portions Copyright (c) 1997 by Stan Barber. 18 Portions Copyright (c) 1997 by Kent Landfield. 19 Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997 20 Free Software Foundation, Inc. 21 22 Use and distribution of this software and its source code are governed 23 by the terms and conditions of the WU-FTPD Software License ("LICENSE"). 24 25 If you did not receive a copy of the license, it may be obtained online 26 at http://www.wu-ftpd.org/license.html. 27 28 $Id: extensions.c,v 1.48 2000/07/01 18:17:38 wuftpd Exp $ 29 30 ****************************************************************************/ 31 #include "config.h" 32 33 #include <stdio.h> 34 #include <errno.h> 35 #include <string.h> 36 37 #ifdef HAVE_SYS_SYSLOG_H 38 #include <sys/syslog.h> 39 #endif 40 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H)) 41 #include <syslog.h> 42 #endif 43 44 #ifdef TIME_WITH_SYS_TIME 45 #include <time.h> 46 #include <sys/time.h> 47 #else 48 #ifdef HAVE_SYS_TIME_H 49 #include <sys/time.h> 50 #else 51 #include <time.h> 52 #endif 53 #endif 54 #include <pwd.h> 55 #include <setjmp.h> 56 #include <grp.h> 57 58 #include <sys/types.h> 59 #include <sys/stat.h> 60 #include <sys/file.h> 61 #include <sys/param.h> 62 63 #ifdef HAVE_SYS_FS_UFS_QUOTA_H 64 #include <sys/fs/ufs_quota.h> 65 #elif defined(HAVE_UFS_UFS_QUOTA_H) 66 #include <ufs/ufs/quota.h> 67 #elif defined(HAVE_UFS_QUOTA_H) 68 #include <ufs/quota.h> 69 #elif defined(HAVE_SYS_MNTENT_H) 70 #include <sys/mntent.h> 71 #elif defined(HAVE_SYS_MNTTAB_H) 72 #include <sys/mnttab.h> 73 #endif 74 75 #if defined(HAVE_STATVFS) 76 #include <sys/statvfs.h> 77 #elif defined(HAVE_SYS_VFS) 78 #include <sys/vfs.h> 79 #elif defined(HAVE_SYS_MOUNT) 80 #include <sys/mount.h> 81 #endif 82 83 #include <arpa/ftp.h> 84 85 #ifdef HAVE_PATHS_H 86 #include <paths.h> 87 #endif 88 #include "pathnames.h" 89 #include "extensions.h" 90 #include "wu_fnmatch.h" 91 #include "proto.h" 92 93 #if defined(HAVE_FTW) 94 #include <ftw.h> 95 #else 96 #include "support/ftw.h" 97 #endif 98 99 #ifdef QUOTA 100 struct dqblk quota; 101 char *time_quota(long curstate, long softlimit, long timelimit, char *timeleft); 102 #endif 103 104 #ifdef HAVE_REGEX_H 105 #include <regex.h> 106 #endif 107 108 #if defined(HAVE_REGEX) && defined(SVR4) && ! (defined(NO_LIBGEN)) 109 #include <libgen.h> 110 #endif 111 112 extern int type, transflag, ftwflag, authenticated, autospout_free, data, 113 pdata, anonymous, guest; 114 extern char chroot_path[], guestpw[]; 115 116 #ifdef TRANSFER_COUNT 117 extern off_t data_count_in; 118 extern off_t data_count_out; 119 #ifdef TRANSFER_LIMIT 120 extern off_t data_limit_raw_in; 121 extern off_t data_limit_raw_out; 122 extern off_t data_limit_raw_total; 123 extern off_t data_limit_data_in; 124 extern off_t data_limit_data_out; 125 extern off_t data_limit_data_total; 126 #ifdef RATIO /* 1998/08/06 K.Wakui */ 127 #define TRUNC_KB(n) ((n)/1024+(((n)%1024)?1:0)) 128 extern time_t login_time; 129 extern time_t limit_time; 130 extern off_t total_free_dl; 131 extern int upload_download_rate; 132 #endif /* RATIO */ 133 #endif 134 #endif 135 136 #ifdef OTHER_PASSWD 137 #include "getpwnam.h" 138 extern char _path_passwd[]; 139 #endif 140 141 #ifdef LOG_FAILED 142 extern char the_user[]; 143 #endif 144 145 extern char *globerr, remotehost[]; 146 #ifdef THROUGHPUT 147 extern char remoteaddr[]; 148 #endif 149 150 #ifndef HAVE_REGEX 151 char *re_comp(const char *regex); 152 int re_exec(const char *p1); 153 #endif 154 155 char shuttime[30], denytime[30], disctime[30]; 156 157 FILE *dout; 158 159 time_t newer_time; 160 161 int show_fullinfo; 162 163 /* This always was a bug, because neither st_size nor time_t were required to 164 be compatible with int, but needs fixing properly for C9X. */ 165 166 /* Some systems use one format, some another. This takes care of the garbage */ 167 /* Do the system specific stuff only if we aren't autoconfed */ 168 #if !defined(L_FORMAT) 169 #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T) 170 #define L_FORMAT "qd" 171 #else 172 #define L_FORMAT "d" 173 #endif 174 #endif 175 #if !defined(T_FORMAT) 176 #define T_FORMAT "d" 177 #endif 178 #if !defined(PW_UID_FORMAT) 179 #define PW_UID_FORMAT "d" 180 #endif 181 #if !defined(GR_GID_FORMAT) 182 #define GR_GID_FORMAT "d" 183 #endif 184 185 int snprintf(char *str, size_t count, const char *fmt,...); 186 187 #ifdef SITE_NEWER 188 int check_newer(const char *path, const struct stat *st, int flag) 189 { 190 if (st->st_mtime > newer_time) { 191 if (show_fullinfo != 0) { 192 if (flag == FTW_F || flag == FTW_D) { 193 fprintf(dout, "%s %" L_FORMAT " %" T_FORMAT " %s\n", 194 flag == FTW_F ? "F" : "D", 195 st->st_size, st->st_mtime, path); 196 } 197 } 198 else if (flag == FTW_F) 199 fprintf(dout, "%s\n", path); 200 } 201 202 /* When an ABOR has been received (which sets ftwflag > 1) return a 203 * non-zero value which causes ftw to stop tree traversal and return. 204 */ 205 206 return (ftwflag > 1 ? 1 : 0); 207 } 208 #endif 209 210 #if defined(HAVE_STATVFS) 211 long getSize(char *s) 212 { 213 struct statvfs buf; 214 215 if (statvfs(s, &buf) != 0) 216 return (0); 217 218 return (buf.f_bavail * buf.f_frsize / 1024); 219 } 220 #elif defined(HAVE_SYS_VFS) || defined (HAVE_SYS_MOUNT) 221 long getSize(char *s) 222 { 223 struct statfs buf; 224 225 if (statfs(s, &buf) != 0) 226 return (0); 227 228 return (buf.f_bavail * buf.f_bsize / 1024); 229 } 230 #endif 231 232 /*************************************************************************/ 233 /* FUNCTION : msg_massage */ 234 /* PURPOSE : Scan a message line for magic cookies, replacing them as */ 235 /* needed. */ 236 /* ARGUMENTS : pointer input and output buffers */ 237 /*************************************************************************/ 238 239 void msg_massage(const char *inbuf, char *outbuf, size_t outlen) 240 { 241 const char *inptr = inbuf; 242 char *outptr = outbuf; 243 #ifdef QUOTA 244 char timeleft[80]; 245 #endif 246 char buffer[MAXPATHLEN]; 247 time_t curtime; 248 int limit; 249 #ifndef LOG_FAILED 250 extern struct passwd *pw; 251 #endif 252 struct aclmember *entry; 253 254 #ifdef VIRTUAL 255 extern int virtual_mode; 256 extern int virtual_ftpaccess; 257 extern char virtual_email[]; 258 #endif 259 extern char hostname[]; 260 extern char authuser[]; 261 262 (void) acl_getclass(buffer); 263 limit = acl_getlimit(buffer, NULL); 264 265 while ((outlen > 1) && (*inptr != '\0')) { 266 if (*inptr != '%') { 267 *outptr++ = *inptr; 268 outlen -= 1; 269 } 270 else { 271 entry = NULL; 272 switch (*++inptr) { 273 case 'E': 274 #ifdef VIRTUAL 275 if (virtual_mode && !virtual_ftpaccess && virtual_email[0] != '\0') 276 snprintf(outptr, outlen, "%s", virtual_email); 277 else 278 #endif 279 if ((getaclentry("email", &entry)) && ARG0) 280 snprintf(outptr, outlen, "%s", ARG0); 281 else 282 *outptr = '\0'; 283 break; 284 285 case 'N': 286 snprintf(outptr, outlen, "%d", acl_countusers(buffer)); 287 break; 288 289 case 'M': 290 if (limit == -1) 291 strncpy(outptr, "unlimited", outlen); 292 else 293 snprintf(outptr, outlen, "%d", limit); 294 break; 295 296 case 'T': 297 (void) time(&curtime); 298 strncpy(outptr, ctime(&curtime), outlen); 299 if (outlen > 24) 300 *(outptr + 24) = '\0'; 301 break; 302 303 case 'F': 304 #if defined(HAVE_STATVFS) || defined(HAVE_SYS_VFS) || defined(HAVE_SYS_MOUNT) 305 snprintf(outptr, outlen, "%lu", (long) getSize(".")); 306 #else 307 *outptr = '\0'; 308 #endif 309 break; 310 311 case 'C': 312 #ifdef HAVE_GETCWD 313 (void) getcwd(outptr, outlen); 314 #else 315 #error wu-ftpd on this platform has security deficiencies!!! 316 (void) getwd(outptr); 317 #endif 318 break; 319 320 case 'R': 321 strncpy(outptr, remotehost, outlen); 322 break; 323 324 case 'L': 325 strncpy(outptr, hostname, outlen); 326 break; 327 328 case 'U': 329 if (xferdone && anonymous) 330 strncpy(outptr, guestpw, outlen); 331 else 332 #ifdef LOG_FAILED 333 strncpy(outptr, the_user, outlen); 334 #else /* LOG_FAILED */ 335 strncpy(outptr, 336 (pw == NULL) ? "[unknown]" : pw->pw_name, outlen); 337 #endif /* LOG_FAILED */ 338 break; 339 340 case 's': 341 strncpy(outptr, shuttime, outlen); 342 if (outlen > 24) 343 *(outptr + 24) = '\0'; 344 break; 345 346 case 'd': 347 strncpy(outptr, disctime, outlen); 348 if (outlen > 24) 349 *(outptr + 24) = '\0'; 350 break; 351 352 case 'r': 353 strncpy(outptr, denytime, outlen); 354 if (outlen > 24) 355 *(outptr + 24) = '\0'; 356 break; 357 358 /* KH : cookie %u for RFC931 name */ 359 case 'u': 360 if (authenticated) 361 strncpy(outptr, authuser, outlen); 362 else { 363 if (xferdone) 364 snprintf(outptr, outlen, "%c", '*'); 365 else 366 strncpy(outptr, "[unknown]", outlen); 367 } 368 break; 369 370 #ifdef QUOTA 371 case 'B': 372 #ifdef QUOTA_BLOCKS /* 1024-blocks instead of 512-blocks */ 373 snprintf(outptr, outlen, "%ld", quota.dqb_bhardlimit % 2 ? 374 (long) (quota.dqb_bhardlimit / 2 + 1) : (long) (quota.dqb_bhardlimit / 2)); 375 #else 376 snprintf(outptr, outlen, "%ld", (long) quota.dqb_bhardlimit); 377 #endif 378 break; 379 380 case 'b': 381 #ifdef QUOTA_BLOCKS /* 1024-blocks instead of 512-blocks */ 382 snprintf(outptr, outlen, "%ld", quota.dqb_bsoftlimit % 2 ? 383 (long) (quota.dqb_bsoftlimit / 2 + 1) : (long) (quota.dqb_bsoftlimit / 2)); 384 #else 385 snprintf(outptr, outlen, "%ld", (long) quota.dqb_bsoftlimit); 386 #endif 387 break; 388 389 case 'Q': 390 #ifdef QUOTA_BLOCKS /* 1024-blocks instead of 512-blocks */ 391 snprintf(outptr, outlen, "%ld", quota.dqb_curblocks % 2 ? 392 (long) (quota.dqb_curblocks / 2 + 1) : (long) (quota.dqb_curblocks / 2)); 393 #else 394 snprintf(outptr, outlen, "%ld", quota.dqb_curblocks); 395 #endif 396 break; 397 398 case 'I': 399 #if defined(QUOTA_INODE) 400 snprintf(outptr, outlen, "%d", quota.dqb_ihardlimit); 401 #else 402 snprintf(outptr, outlen, "%ld", (long) quota.dqb_fhardlimit); 403 #endif 404 break; 405 406 case 'i': 407 #if defined(QUOTA_INODE) 408 snprintf(outptr, outlen, "%d", quota.dqb_isoftlimit); 409 #else 410 snprintf(outptr, outlen, "%ld", (long) quota.dqb_fsoftlimit); 411 #endif 412 break; 413 414 case 'q': 415 #if defined(QUOTA_INODE) 416 snprintf(outptr, outlen, "%d", quota.dqb_curinodes); 417 #else 418 snprintf(outptr, outlen, "%ld", (long) quota.dqb_curfiles); 419 #endif 420 break; 421 422 case 'H': 423 time_quota(quota.dqb_curblocks, quota.dqb_bsoftlimit, 424 #if defined(QUOTA_INODE) 425 quota.dqb_btime, timeleft); 426 #else 427 quota.dqb_btimelimit, timeleft); 428 #endif 429 strncpy(outptr, timeleft, outlen); 430 break; 431 432 case 'h': 433 #if defined(QUOTA_INODE) 434 time_quota(quota.dqb_curinodes, quota.dqb_isoftlimit, 435 quota.dqb_itime, timeleft); 436 #else 437 time_quota(quota.dqb_curfiles, quota.dqb_fsoftlimit, 438 quota.dqb_ftimelimit, timeleft); 439 #endif 440 strncpy(outptr, timeleft, outlen); 441 break; 442 #endif /* QUOTA */ 443 444 case '%': 445 *outptr++ = '%'; 446 outlen -= 1; 447 *outptr = '\0'; 448 break; 449 450 #ifdef TRANSFER_COUNT 451 #ifdef TRANSFER_LIMIT 452 #ifdef RATIO 453 case 'x': 454 switch (*++inptr) { 455 case 'u': /* upload bytes */ 456 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_count_in) ); 457 break; 458 case 'd': /* download bytes */ 459 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_count_out) ); 460 break; 461 case 'R': /* rate 1:n */ 462 if( upload_download_rate > 0 ) { 463 sprintf(outptr,"%d", upload_download_rate ); 464 } 465 else { 466 strcpy(outptr,"free"); 467 } 468 break; 469 case 'c': /* credit bytes */ 470 if( upload_download_rate > 0 ) { 471 off_t credit=( data_count_in * upload_download_rate) - (data_count_out - total_free_dl); 472 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(credit) ); 473 } 474 else { 475 strcpy(outptr,"unlimited"); 476 } 477 break; 478 case 'T': /* time limit (minutes) */ 479 if( limit_time > 0 ) { 480 sprintf(outptr,"%d", limit_time ); 481 } 482 else { 483 strcpy(outptr,"unlimited"); 484 } 485 break; 486 case 'E': /* elapsed time from loggedin (minutes) */ 487 sprintf(outptr,"%d", (time(NULL)-login_time)/60 ); 488 break; 489 case 'L': /* times left until force logout (minutes) */ 490 if( limit_time > 0 ) { 491 sprintf(outptr,"%d", limit_time-(time(NULL)-login_time)/60 ); 492 } 493 else { 494 strcpy(outptr,"unlimited"); 495 } 496 break; 497 case 'U': /* upload limit */ 498 if( data_limit_raw_in > 0 ) { 499 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_in)); 500 } 501 else if( data_limit_data_in > 0 ) { 502 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_in)); 503 } 504 else if( data_limit_raw_total > 0 ) { 505 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_total)); 506 } 507 else if( data_limit_data_total > 0 ) { 508 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_total)); 509 } 510 else { 511 strcpy(outptr, "unlimited"); 512 } 513 break; 514 case 'D': /* download limit */ 515 if( data_limit_raw_out > 0 ) { 516 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_out)); 517 } 518 else if( data_limit_data_out > 0 ) { 519 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_out)); 520 } 521 else if( data_limit_raw_total > 0 ) { 522 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_total)); 523 } 524 else if( data_limit_data_total > 0 ) { 525 sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_total)); 526 } 527 else { 528 strcpy(outptr, "unlimited"); 529 } 530 break; 531 default: 532 strcpy(outptr,"%??"); 533 break; 534 } 535 break; 536 #endif /* RATIO */ 537 #endif 538 #endif 539 /* File transfer logging (xferlog) */ 540 case 'X': 541 if (xferdone) { /* only if a transfer has just occurred */ 542 switch (*++inptr) { 543 case 't': 544 snprintf(outptr, outlen, "%d", xfervalues.transfer_time); 545 break; 546 case 's': 547 snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.filesize); 548 break; 549 case 'n': 550 snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.transfer_bytes); 551 break; 552 case 'P': /* absolute pathname */ 553 /* FALLTHROUGH */ 554 case 'p': /* chroot-relative pathname */ 555 { 556 char namebuf[MAXPATHLEN]; 557 int loop; 558 559 if (*inptr == 'P') 560 wu_realpath(xfervalues.filename, namebuf, chroot_path); 561 else 562 fb_realpath(xfervalues.filename, namebuf); 563 for (loop = 0; namebuf[loop]; loop++) { 564 if (isspace(namebuf[loop]) || iscntrl(namebuf[loop])) 565 namebuf[loop] = '_'; 566 } 567 snprintf(outptr, outlen, "%s", namebuf); 568 break; 569 } 570 case 'y': 571 snprintf(outptr, outlen, "%c", xfervalues.transfer_type); 572 break; 573 case 'f': 574 snprintf(outptr, outlen, "%s", xfervalues.special_action); 575 break; 576 case 'd': 577 snprintf(outptr, outlen, "%c", xfervalues.transfer_direction); 578 break; 579 case 'm': 580 snprintf(outptr, outlen, "%c", xfervalues.access_mode); 581 break; 582 case 'a': 583 snprintf(outptr, outlen, "%d", xfervalues.auth); 584 break; 585 case 'r': 586 snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.restart_offset); 587 break; 588 case 'c': 589 snprintf(outptr, outlen, "%c", xfervalues.completion); 590 break; 591 default: 592 snprintf(outptr, outlen, "%%X%c", *inptr); 593 break; 594 } 595 } 596 else 597 snprintf(outptr, outlen, "%%%c", *inptr); 598 break; 599 600 default: 601 *outptr++ = '%'; 602 outlen -= 1; 603 if (outlen > 1) { 604 *outptr++ = *inptr; 605 outlen -= 1; 606 } 607 *outptr = '\0'; 608 break; 609 } 610 outptr[outlen - 1] = '\0'; 611 while (*outptr) { 612 outptr++; 613 outlen -= 1; 614 } 615 } 616 inptr++; 617 } 618 if (outlen > 0) 619 *outptr = '\0'; 620 } 621 622 /*************************************************************************/ 623 /* FUNCTION : cwd_beenhere */ 624 /* PURPOSE : Return 1 if the user has already visited this directory */ 625 /* via C_WD. */ 626 /* ARGUMENTS : a power-of-two directory function code (README, MESSAGE) */ 627 /*************************************************************************/ 628 629 int cwd_beenhere(int dircode) 630 { 631 struct dirlist { 632 struct dirlist *next; 633 int dircode; 634 char dirname[1]; 635 }; 636 637 static struct dirlist *head = NULL; 638 struct dirlist *curptr; 639 char cwd[MAXPATHLEN]; 640 641 (void) fb_realpath(".", cwd); 642 643 for (curptr = head; curptr != NULL; curptr = curptr->next) 644 if (strcmp(curptr->dirname, cwd) == 0) { 645 if (!(curptr->dircode & dircode)) { 646 curptr->dircode |= dircode; 647 return (0); 648 } 649 return (1); 650 } 651 curptr = (struct dirlist *) malloc(strlen(cwd) + 1 + sizeof(struct dirlist)); 652 653 if (curptr != NULL) { 654 curptr->next = head; 655 head = curptr; 656 curptr->dircode = dircode; 657 strcpy(curptr->dirname, cwd); 658 } 659 return (0); 660 } 661 662 /*************************************************************************/ 663 /* FUNCTION : show_banner */ 664 /* PURPOSE : Display a banner on the user's terminal before login */ 665 /* ARGUMENTS : reply code to use */ 666 /*************************************************************************/ 667 668 void show_banner(int msgcode) 669 { 670 char *crptr, linebuf[1024], outbuf[1024]; 671 struct aclmember *entry = NULL; 672 FILE *infile; 673 674 #ifdef VIRTUAL 675 extern int virtual_mode; 676 extern int virtual_ftpaccess; 677 extern char virtual_banner[]; 678 679 if (virtual_mode && !virtual_ftpaccess) { 680 infile = fopen(virtual_banner, "r"); 681 if (infile) { 682 while (fgets(linebuf, sizeof(linebuf), infile) != NULL) { 683 if ((crptr = strchr(linebuf, '\n')) != NULL) 684 *crptr = '\0'; 685 msg_massage(linebuf, outbuf, sizeof(outbuf)); 686 lreply(msgcode, "%s", outbuf); 687 } 688 fclose(infile); 689 #ifndef NO_SUCKING_NEWLINES 690 lreply(msgcode, ""); 691 #endif 692 } 693 } 694 else { 695 #endif 696 /* banner <path> */ 697 while (getaclentry("banner", &entry)) { 698 if (!ARG0) 699 continue; 700 infile = fopen(ARG0, "r"); 701 if (infile) { 702 while (fgets(linebuf, sizeof(linebuf), infile) != NULL) { 703 if ((crptr = strchr(linebuf, '\n')) != NULL) 704 *crptr = '\0'; 705 msg_massage(linebuf, outbuf, sizeof(outbuf)); 706 lreply(msgcode, "%s", outbuf); 707 } 708 fclose(infile); 709 #ifndef NO_SUCKING_NEWLINES 710 lreply(msgcode, ""); 711 #endif 712 } 713 } 714 #ifdef VIRTUAL 715 } 716 #endif 717 } 718 /*************************************************************************/ 719 /* FUNCTION : show_message */ 720 /* PURPOSE : Display a message on the user's terminal if the current */ 721 /* conditions are right */ 722 /* ARGUMENTS : reply code to use, LOG_IN|CMD */ 723 /*************************************************************************/ 724 725 void show_message(int msgcode, int mode) 726 { 727 char *crptr, linebuf[1024], outbuf[1024], class[MAXPATHLEN], cwd[MAXPATHLEN]; 728 int show, which; 729 struct aclmember *entry = NULL; 730 FILE *infile; 731 732 if (mode == C_WD && cwd_beenhere(1) != 0) 733 return; 734 735 #ifdef HAVE_GETCWD 736 (void) getcwd(cwd, MAXPATHLEN - 1); 737 #else 738 (void) getwd(cwd); 739 #endif 740 (void) acl_getclass(class); 741 742 /* message <path> [<when> [<class>]] */ 743 while (getaclentry("message", &entry)) { 744 if (!ARG0) 745 continue; 746 show = 0; 747 748 if (mode == LOG_IN && (!ARG1 || !strcasecmp(ARG1, "login"))) 749 if (!ARG2) 750 show++; 751 else { 752 for (which = 2; (which < MAXARGS) && ARG[which]; which++) 753 if (strcasecmp(class, ARG[which]) == 0) 754 show++; 755 } 756 if (mode == C_WD && ARG1 && !strncasecmp(ARG1, "cwd=", 4) && 757 (!strcmp((ARG1) + 4, cwd) || *(ARG1 + 4) == '*' || 758 !wu_fnmatch((ARG1) + 4, cwd, FNM_PATHNAME))) 759 if (!ARG2) 760 show++; 761 else { 762 for (which = 2; (which < MAXARGS) && ARG[which]; which++) 763 if (strcasecmp(class, ARG[which]) == 0) 764 show++; 765 } 766 if (show && (int) strlen(ARG0) > 0) { 767 infile = fopen(ARG0, "r"); 768 if (infile) { 769 while (fgets(linebuf, sizeof(linebuf), infile) != NULL) { 770 if ((crptr = strchr(linebuf, '\n')) != NULL) 771 *crptr = '\0'; 772 msg_massage(linebuf, outbuf, sizeof(outbuf)); 773 lreply(msgcode, "%s", outbuf); 774 } 775 fclose(infile); 776 #ifndef NO_SUCKING_NEWLINES 777 lreply(msgcode, ""); 778 #endif 779 } 780 } 781 } 782 } 783 784 /*************************************************************************/ 785 /* FUNCTION : show_readme */ 786 /* PURPOSE : Display a message about a README file to the user if the */ 787 /* current conditions are right */ 788 /* ARGUMENTS : pointer to ACL buffer, reply code, LOG_IN|C_WD */ 789 /*************************************************************************/ 790 791 void show_readme(int code, int mode) 792 { 793 char **filelist, **sfilelist, class[MAXPATHLEN], cwd[MAXPATHLEN]; 794 int show, which, days; 795 time_t clock; 796 797 struct stat buf; 798 struct tm *tp; 799 struct aclmember *entry = NULL; 800 801 if (cwd_beenhere(2) != 0) 802 return; 803 804 #ifdef HAVE_GETCWD 805 (void) getcwd(cwd, MAXPATHLEN - 1); 806 #else 807 (void) getwd(cwd); 808 #endif 809 (void) acl_getclass(class); 810 811 /* readme <path> {<when>} */ 812 while (getaclentry("readme", &entry)) { 813 if (!ARG0) 814 continue; 815 show = 0; 816 817 if (mode == LOG_IN && (!ARG1 || !strcasecmp(ARG1, "login"))) 818 if (!ARG2) 819 show++; 820 else { 821 for (which = 2; (which < MAXARGS) && ARG[which]; which++) 822 if (strcasecmp(class, ARG[which]) == 0) 823 show++; 824 } 825 if (mode == C_WD && ARG1 && !strncasecmp(ARG1, "cwd=", 4) 826 && (!strcmp((ARG1) + 4, cwd) || *(ARG1 + 4) == '*' || 827 !wu_fnmatch((ARG1) + 4, cwd, FNM_PATHNAME))) 828 if (!ARG2) 829 show++; 830 else { 831 for (which = 2; (which < MAXARGS) && ARG[which]; which++) 832 if (strcasecmp(class, ARG[which]) == 0) 833 show++; 834 } 835 if (show) { 836 globerr = NULL; 837 filelist = ftpglob(ARG0, B_TRUE); 838 sfilelist = filelist; /* save to free later */ 839 if (!globerr) { 840 while (filelist && *filelist) { 841 errno = 0; 842 if (!stat(*filelist, &buf) && 843 (buf.st_mode & S_IFMT) == S_IFREG) { 844 lreply(code, "Please read the file %s", *filelist); 845 (void) time(&clock); 846 tp = localtime(&clock); 847 days = 365 * tp->tm_year + tp->tm_yday; 848 tp = localtime((time_t *) & buf.st_mtime); 849 days -= 365 * tp->tm_year + tp->tm_yday; 850 /* 851 if (days == 0) { 852 lreply(code, " it was last modified on %.24s - Today", 853 ctime((time_t *)&buf.st_mtime)); 854 } else { 855 */ 856 lreply(code, 857 " it was last modified on %.24s - %d day%s ago", 858 ctime((time_t *) & buf.st_mtime), days, days == 1 ? "" : "s"); 859 /* 860 } 861 */ 862 } 863 filelist++; 864 } 865 } 866 if (sfilelist) { 867 blkfree(sfilelist); 868 free((char *) sfilelist); 869 } 870 } 871 } 872 } 873 874 /*************************************************************************/ 875 /* FUNCTION : deny_badxfertype */ 876 /* PURPOSE : If user is in ASCII transfer mode and tries to retrieve a */ 877 /* binary file, abort transfer and display appropriate error */ 878 /* ARGUMENTS : message code to use for denial, path of file to check for */ 879 /* binary contents or NULL to assume binary file */ 880 /*************************************************************************/ 881 882 int deny_badasciixfer(int msgcode, char *filepath) 883 { 884 885 if (type == TYPE_A && !*filepath) { 886 reply(msgcode, "This is a BINARY file, using ASCII mode to transfer will corrupt it."); 887 return (1); 888 } 889 /* The hooks are here to prevent transfers of actual binary files, not 890 * just TAR or COMPRESS mode files... */ 891 return (0); 892 } 893 894 /*************************************************************************/ 895 /* FUNCTION : is_shutdown */ 896 /* PURPOSE : Check to see if the server is shutting down, if it is */ 897 /* arrange for the shutdown message to be sent in the next */ 898 /* reply to the user */ 899 /* ARGUMENTS : whether to arrange for a shutdown message to be sent, new */ 900 /* or existing connection */ 901 /* RETURNS : 1 if shutting down, 0 if not */ 902 /*************************************************************************/ 903 904 int is_shutdown(int quiet, int new) 905 { 906 static struct tm tmbuf; 907 static struct stat s_last; 908 static time_t last = 0, shut, deny, disc; 909 static int valid; 910 static char text[2048]; 911 struct stat s_cur; 912 913 extern char *autospout, Shutdown[]; 914 915 FILE *fp; 916 917 int deny_off, disc_off; 918 919 time_t curtime = time(NULL); 920 921 char buf[1024], linebuf[1024]; 922 923 if (Shutdown[0] == '\0' || stat(Shutdown, &s_cur)) 924 return (0); 925 926 if (s_last.st_mtime != s_cur.st_mtime) { 927 valid = 0; 928 929 fp = fopen(Shutdown, "r"); 930 if (fp == NULL) 931 return (0); 932 s_last = s_cur; 933 fgets(buf, sizeof(buf), fp); 934 if (sscanf(buf, "%d %d %d %d %d %ld %ld", &tmbuf.tm_year, &tmbuf.tm_mon, 935 &tmbuf.tm_mday, &tmbuf.tm_hour, &tmbuf.tm_min, &deny, &disc) != 7) { 936 (void) fclose(fp); 937 return (0); 938 } 939 valid = 1; 940 deny_off = 3600 * (deny / 100) + 60 * (deny % 100); 941 disc_off = 3600 * (disc / 100) + 60 * (disc % 100); 942 943 tmbuf.tm_year -= 1900; 944 tmbuf.tm_isdst = -1; 945 shut = mktime(&tmbuf); 946 strcpy(shuttime, ctime(&shut)); 947 948 disc = shut - disc_off; 949 strcpy(disctime, ctime(&disc)); 950 951 deny = shut - deny_off; 952 strcpy(denytime, ctime(&deny)); 953 954 text[0] = '\0'; 955 956 while (fgets(buf, sizeof(buf), fp) != NULL) { 957 msg_massage(buf, linebuf, sizeof(linebuf)); 958 if ((strlen(text) + strlen(linebuf)) < sizeof(text)) 959 strcat(text, linebuf); 960 } 961 962 (void) fclose(fp); 963 } 964 if (!valid) 965 return (0); 966 967 /* if last == 0, then is_shutdown() only called with quiet == 1 so far */ 968 if (last == 0 && !quiet) { 969 autospout = text; /* warn them for the first time */ 970 autospout_free = 0; 971 last = curtime; 972 } 973 /* if a new connection and past deny time, tell caller to drop 'em */ 974 if (new && curtime > deny) 975 return (1); 976 977 /* if past disconnect time, tell caller to drop 'em */ 978 if (curtime > disc) 979 return (1); 980 981 /* if less than 60 seconds to disconnection, warn 'em continuously */ 982 if (curtime > (disc - 60) && !quiet) { 983 autospout = text; 984 autospout_free = 0; 985 last = curtime; 986 } 987 /* if less than 15 minutes to disconnection, warn 'em every 5 mins */ 988 if (curtime > (disc - 60 * 15)) { 989 if ((curtime - last) > (60 * 5) && !quiet) { 990 autospout = text; 991 autospout_free = 0; 992 last = curtime; 993 } 994 } 995 /* if less than 24 hours to disconnection, warn 'em every 30 mins */ 996 if (curtime < (disc - 24 * 60 * 60) && !quiet) { 997 if ((curtime - last) > (60 * 30)) { 998 autospout = text; 999 autospout_free = 0; 1000 last = curtime; 1001 } 1002 } 1003 /* if more than 24 hours to disconnection, warn 'em every 60 mins */ 1004 if (curtime > (disc - 24 * 60 * 60) && !quiet) { 1005 if ((curtime - last) >= (24 * 60 * 60)) { 1006 autospout = text; 1007 autospout_free = 0; 1008 last = curtime; 1009 } 1010 } 1011 return (0); 1012 } 1013 1014 #ifdef SITE_NEWER 1015 void newer(char *date, char *path, int showlots) 1016 { 1017 struct tm tm; 1018 1019 if (sscanf(date, "%04d%02d%02d%02d%02d%02d", 1020 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 1021 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) { 1022 1023 tm.tm_year -= 1900; 1024 tm.tm_mon--; 1025 tm.tm_isdst = -1; 1026 newer_time = mktime(&tm); 1027 dout = dataconn("file list", (off_t) - 1, "w"); 1028 1029 if (dout != NULL) { 1030 /* As ftw allocates storage it needs a chance to cleanup, setting 1031 * ftwflag prevents myoob from calling longjmp, incrementing 1032 * ftwflag instead which causes check_newer to return non-zero 1033 * which makes ftw return. */ 1034 ftwflag = 1; 1035 transflag++; 1036 show_fullinfo = showlots; 1037 #if defined(HAVE_FTW) 1038 ftw(path, check_newer, -1); 1039 #else 1040 treewalk(path, check_newer, -1, NULL); 1041 #endif 1042 1043 /* don't send a reply if myoob has already replied */ 1044 if (ftwflag == 1) { 1045 if (ferror(dout) != 0) 1046 perror_reply(550, "Data connection"); 1047 else 1048 reply(226, "Transfer complete."); 1049 } 1050 1051 (void) fclose(dout); 1052 data = -1; 1053 pdata = -1; 1054 transflag = 0; 1055 ftwflag = 0; 1056 } 1057 } 1058 else 1059 reply(501, "Bad DATE format"); 1060 } 1061 #endif 1062 1063 int type_match(char *typelist) 1064 { 1065 char *start, *p; 1066 int len; 1067 1068 if (typelist == NULL) 1069 return (0); 1070 1071 for (p = start = typelist; *start != '\0'; start = p) { 1072 while (*p != '\0' && *p != ',') 1073 p++; 1074 len = p - start; 1075 if (*p != '\0') 1076 p++; 1077 if (len == 9 && anonymous && strncasecmp(start, "anonymous", 9) == 0) 1078 return (1); 1079 if (len == 5 && guest && strncasecmp(start, "guest", 5) == 0) 1080 return (1); 1081 if (len == 4 && !guest && !anonymous && 1082 strncasecmp(start, "real", 4) == 0) 1083 return (1); 1084 1085 if (len > 6 && strncasecmp(start, "class=", 6) == 0) { 1086 char class[1024]; 1087 1088 if ((acl_getclass(class) == 1) && (strlen(class) == len - 6) && 1089 (strncasecmp(start + 6, class, len - 6) == 0)) 1090 return (1); 1091 } 1092 } 1093 return (0); 1094 } 1095 1096 int path_compare(char *p1, char *p2) 1097 { 1098 if ((strcmp(p1, "*") == 0) || (wu_fnmatch(p1, p2, FNM_PATHNAME) == 0)) /* 0 means they matched */ 1099 return (strlen(p1)); 1100 else 1101 return (-2); 1102 } 1103 1104 void expand_id(void) 1105 { 1106 char class[1024]; 1107 struct aclmember *entry = NULL; 1108 (void) acl_getclass(class); 1109 while (getaclentry("upload", &entry)) { 1110 char *q; 1111 int i = 0; 1112 int options = 1; 1113 int classfound = 0; 1114 int classmatched = 0; 1115 while (options 1116 && (i < MAXARGS) 1117 && ((q = entry->arg[i]) != (char *) NULL) 1118 && (q[0] != '\0')) { 1119 if (strcasecmp(q, "absolute") == 0) 1120 i++; 1121 else if (strcasecmp(q, "relative") == 0) 1122 i++; 1123 else if (strncasecmp(q, "class=", 6) == 0) { 1124 i++; 1125 classfound = 1; 1126 if (strcasecmp(q + 6, class) == 0) 1127 classmatched = 1; 1128 } 1129 else if (strcmp(q, "-") == 0) { 1130 i++; 1131 options = 0; 1132 } 1133 else 1134 options = 0; 1135 } 1136 if (!classfound || classmatched) { 1137 char buf[BUFSIZ]; 1138 /* 1139 * UID 1140 */ 1141 if (((i + 3) < MAXARGS) 1142 && ((q = entry->arg[i + 3]) != (char *) NULL) 1143 && (q[0] != '\0') 1144 && (strcmp(q, "*") != 0)) { 1145 if (q[0] == '%') 1146 sprintf(buf, "%s", q + 1); 1147 else { 1148 struct passwd *pwent = getpwnam(q); 1149 if (pwent) 1150 sprintf(buf, "%" PW_UID_FORMAT, pwent->pw_uid); 1151 else 1152 sprintf(buf, "%d", 0); 1153 } 1154 entry->arg[i + 3] = (char *) malloc(strlen(buf) + 1); 1155 if (entry->arg[i + 3] == NULL) { 1156 syslog(LOG_ERR, "calloc error in expand_id"); 1157 dologout(1); 1158 } 1159 strcpy(entry->arg[i + 3], buf); 1160 } 1161 /* 1162 * GID 1163 */ 1164 if (((i + 4) < MAXARGS) 1165 && ((q = entry->arg[i + 4]) != (char *) NULL) 1166 && (q[0] != '\0') 1167 && (strcmp(q, "*") != 0)) { 1168 if (q[0] == '%') 1169 sprintf(buf, "%s", q + 1); 1170 else { 1171 struct group *grent = getgrnam(q); 1172 if (grent) 1173 sprintf(buf, "%" GR_GID_FORMAT, grent->gr_gid); 1174 else 1175 sprintf(buf, "%d", 0); 1176 endgrent(); 1177 } 1178 entry->arg[i + 4] = (char *) malloc(strlen(buf) + 1); 1179 if (entry->arg[i + 4] == NULL) { 1180 syslog(LOG_ERR, "calloc error in expand_id"); 1181 dologout(1); 1182 } 1183 strcpy(entry->arg[i + 4], buf); 1184 } 1185 } 1186 } 1187 } 1188 1189 int fn_check(char *name) 1190 { 1191 /* check to see if this is a valid file name... path-filter <type> 1192 * <message_file> <allowed_charset> <disallowed> */ 1193 1194 struct aclmember *entry = NULL; 1195 int j; 1196 char *path; 1197 #if ! defined(HAVE_REGEXEC) 1198 char *sp; 1199 #endif 1200 1201 #ifdef M_UNIX 1202 #ifdef HAVE_REGEX 1203 char *regp; 1204 #endif 1205 #endif 1206 1207 #ifdef HAVE_REGEXEC 1208 regex_t regexbuf; 1209 regmatch_t regmatchbuf; 1210 int rval; 1211 #endif 1212 1213 #ifdef LINUX 1214 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; 1215 #endif 1216 1217 while (getaclentry("path-filter", &entry) && ARG0 != NULL) { 1218 if (type_match(ARG0) && ARG1 && ARG2) { 1219 1220 /* 1221 * check *only* the basename 1222 */ 1223 1224 if ((path = strrchr(name, '/'))) 1225 ++path; 1226 else 1227 path = name; 1228 1229 /* is it in the allowed character set? */ 1230 #if defined(HAVE_REGEXEC) 1231 if (regcomp(®exbuf, ARG2, REG_EXTENDED) != 0) { 1232 reply(550, "HAVE_REGEX error"); 1233 #elif defined(HAVE_REGEX) 1234 if ((sp = regcmp(ARG2, (char *) 0)) == NULL) { 1235 reply(550, "HAVE_REGEX error"); 1236 #else 1237 if ((sp = re_comp(ARG2)) != 0) { 1238 perror_reply(550, sp); 1239 #endif 1240 return (0); 1241 } 1242 #if defined(HAVE_REGEXEC) 1243 rval = regexec(®exbuf, path, 1, ®matchbuf, 0); 1244 regfree(®exbuf); 1245 if (rval != 0) { 1246 #elif defined(HAVE_REGEX) 1247 #ifdef M_UNIX 1248 regp = regex(sp, path); 1249 free(sp); 1250 if (regp == NULL) { 1251 #else 1252 if ((regex(sp, path)) == NULL) { 1253 #endif 1254 #else 1255 if ((re_exec(path)) != 1) { 1256 #endif 1257 pr_mesg(550, ARG1); 1258 reply(550, "%s: Permission denied on server. (Filename (accept))", name); 1259 return (0); 1260 } 1261 /* is it in any of the disallowed regexps */ 1262 1263 for (j = 3; j < MAXARGS; ++j) { 1264 /* ARGj == entry->arg[j] */ 1265 if (entry->arg[j]) { 1266 #if defined(HAVE_REGEXEC) 1267 if (regcomp(®exbuf, entry->arg[j], REG_EXTENDED) != 0) { 1268 reply(550, "HAVE_REGEX error"); 1269 #elif defined(HAVE_REGEX) 1270 if ((sp = regcmp(entry->arg[j], (char *) 0)) == NULL) { 1271 reply(550, "HAVE_REGEX error"); 1272 #else 1273 if ((sp = re_comp(entry->arg[j])) != 0) { 1274 perror_reply(550, sp); 1275 #endif 1276 return (0); 1277 } 1278 #if defined(HAVE_REGEXEC) 1279 rval = regexec(®exbuf, path, 1, ®matchbuf, 0); 1280 regfree(®exbuf); 1281 if (rval == 0) { 1282 #elif defined(HAVE_REGEX) 1283 #ifdef M_UNIX 1284 regp = regex(sp, path); 1285 free(sp); 1286 if (regp != NULL) { 1287 #else 1288 if ((regex(sp, path)) != NULL) { 1289 #endif 1290 #else 1291 if ((re_exec(path)) == 1) { 1292 #endif 1293 pr_mesg(550, ARG1); 1294 reply(550, "%s: Permission denied on server. (Filename (deny))", name); 1295 return (0); 1296 } 1297 } 1298 } 1299 } 1300 } 1301 return (1); 1302 } 1303 1304 int dir_check(char *name, uid_t * uid, gid_t * gid, int *d_mode, int *valid) 1305 { 1306 struct aclmember *entry = NULL; 1307 int match_value = -1; 1308 char *ap2 = NULL; 1309 char *ap3 = NULL; 1310 char *ap4 = NULL; 1311 char *ap5 = NULL; 1312 char *ap6 = NULL; 1313 char *ap7 = NULL; 1314 char cwdir[MAXPATHLEN]; 1315 char *pwdir; 1316 char abspwdir[MAXPATHLEN]; 1317 char relpwdir[MAXPATHLEN]; 1318 char path[MAXPATHLEN]; 1319 char *sp; 1320 struct stat stbuf; 1321 int stat_result = -1; 1322 char class[1024]; 1323 extern char *home; 1324 1325 (void) acl_getclass(class); 1326 1327 *valid = 0; 1328 /* what's our current directory? */ 1329 1330 /* XXX We could use dynamic RAM to store this path, but I'd rather just bail 1331 out with an error. The rest of wu is so crufy that a long path might 1332 just blow up later */ 1333 1334 if ((strlen(name) + 1) > sizeof(path)) { 1335 perror_reply(550, "Path too long"); 1336 return (-1); 1337 } 1338 1339 strcpy(path, name); 1340 sp = strrchr(path, '/'); 1341 if (sp) 1342 *sp = '\0'; 1343 else 1344 strcpy(path, "."); 1345 1346 if ((fb_realpath(path, cwdir)) == NULL) { 1347 perror_reply(550, "Could not determine cwdir"); 1348 return (-1); 1349 } 1350 1351 if ((fb_realpath(home, relpwdir)) == NULL) { 1352 perror_reply(550, "Could not determine pwdir"); 1353 return (-1); 1354 } 1355 1356 if ((wu_realpath(home, abspwdir, chroot_path)) == NULL) { 1357 perror_reply(550, "Could not determine pwdir"); 1358 return (-1); 1359 } 1360 1361 while (getaclentry("upload", &entry)) { 1362 char *q; 1363 int i = 0; 1364 int options = 1; 1365 int classfound = 0; 1366 int classmatched = 0; 1367 pwdir = abspwdir; 1368 while (options 1369 && (i < MAXARGS) 1370 && ((q = entry->arg[i]) != (char *) NULL) 1371 && (q[0] != '\0')) { 1372 if (strcasecmp(q, "absolute") == 0) { 1373 i++; 1374 pwdir = abspwdir; 1375 } 1376 else if (strcasecmp(q, "relative") == 0) { 1377 i++; 1378 pwdir = relpwdir; 1379 } 1380 else if (strncasecmp(q, "class=", 6) == 0) { 1381 i++; 1382 classfound = 1; 1383 if (strcasecmp(q + 6, class) == 0) 1384 classmatched = 1; 1385 } 1386 else if (strcmp(q, "-") == 0) { 1387 i++; 1388 options = 0; 1389 } 1390 else 1391 options = 0; 1392 } 1393 if (!classfound || classmatched) { 1394 int j; 1395 if (((i + 1) < MAXARGS) 1396 && ((q = entry->arg[i]) != (char *) NULL) 1397 && (q[0] != '\0') 1398 && (0 < path_compare(q, pwdir)) 1399 && ((j = path_compare(entry->arg[i + 1], cwdir)) >= match_value)) { 1400 match_value = j; 1401 1402 ap2 = NULL; 1403 if (((i + 2) < MAXARGS) 1404 && ((q = entry->arg[i + 2]) != (char *) NULL) 1405 && (q[0] != '\0')) 1406 ap2 = q; 1407 1408 ap3 = NULL; 1409 if (((i + 3) < MAXARGS) 1410 && ((q = entry->arg[i + 3]) != (char *) NULL) 1411 && (q[0] != '\0')) 1412 ap3 = q; 1413 1414 ap4 = NULL; 1415 if (((i + 4) < MAXARGS) 1416 && ((q = entry->arg[i + 4]) != (char *) NULL) 1417 && (q[0] != '\0')) 1418 ap4 = q; 1419 1420 ap5 = NULL; 1421 if (((i + 5) < MAXARGS) 1422 && ((q = entry->arg[i + 5]) != (char *) NULL) 1423 && (q[0] != '\0')) 1424 ap5 = q; 1425 1426 ap6 = NULL; 1427 if (((i + 6) < MAXARGS) 1428 && ((q = entry->arg[i + 6]) != (char *) NULL) 1429 && (q[0] != '\0')) 1430 ap6 = q; 1431 1432 ap7 = NULL; 1433 if (((i + 7) < MAXARGS) 1434 && ((q = entry->arg[i + 7]) != (char *) NULL) 1435 && (q[0] != '\0')) 1436 ap7 = q; 1437 } 1438 } 1439 } 1440 1441 if (anonymous && (match_value < 0)) { 1442 reply(550, "%s: Permission denied on server. (Upload dirs)", name); 1443 return (0); 1444 } 1445 if ((ap2 && !strcasecmp(ap2, "no")) 1446 || (ap3 && !strcasecmp(ap3, "nodirs")) 1447 || (ap6 && !strcasecmp(ap6, "nodirs"))) { 1448 reply(550, "%s: Permission denied on server. (Upload dirs)", name); 1449 return (0); 1450 } 1451 if ((ap3 && *ap3 == '*') || (ap4 && *ap4 == '*')) 1452 stat_result = stat(path, &stbuf); 1453 if (ap3) { 1454 if ((ap3[0] != '*') || (ap3[1] != '\0')) 1455 *uid = atoi(ap3); /* the uid */ 1456 else if (stat_result == 0) 1457 *uid = stbuf.st_uid; 1458 } 1459 if (ap4) { 1460 if ((ap4[0] != '*') || (ap4[1] != '\0')) 1461 *gid = atoi(ap4); /* the gid */ 1462 else if (stat_result == 0) 1463 *gid = stbuf.st_gid; 1464 } 1465 if (ap7) { 1466 sscanf(ap7, "%o", d_mode); 1467 *valid = 1; 1468 } 1469 else if (ap5) { 1470 sscanf(ap5, "%o", d_mode); 1471 if (*d_mode & 0600) 1472 *d_mode |= 0100; 1473 if (*d_mode & 0060) 1474 *d_mode |= 0010; 1475 if (*d_mode & 0006) 1476 *d_mode |= 0001; 1477 *valid = 1; 1478 } 1479 return (1); 1480 } 1481 1482 int upl_check(char *name, uid_t * uid, gid_t * gid, int *f_mode, int *valid) 1483 { 1484 int match_value = -1; 1485 char cwdir[MAXPATHLEN]; 1486 char *pwdir; 1487 char abspwdir[MAXPATHLEN]; 1488 char relpwdir[MAXPATHLEN]; 1489 char path[MAXPATHLEN]; 1490 char *sp; 1491 struct stat stbuf; 1492 int stat_result = -1; 1493 char *ap2 = NULL; 1494 char *ap3 = NULL; 1495 char *ap4 = NULL; 1496 char *ap5 = NULL; 1497 struct aclmember *entry = NULL; 1498 char class[1024]; 1499 extern char *home; 1500 1501 *valid = 0; 1502 (void) acl_getclass(class); 1503 1504 /* what's our current directory? */ 1505 1506 /* XXX We could use dynamic RAM to store this path, but I'd rather just bail 1507 out with an error. The rest of wu is so crufy that a long path might 1508 just blow up later */ 1509 1510 if ((strlen(name) + 1) > sizeof(path)) { 1511 perror_reply(553, "Path too long"); 1512 return (-1); 1513 } 1514 1515 strcpy(path, name); 1516 sp = strrchr(path, '/'); 1517 if (sp) 1518 *sp = '\0'; 1519 else 1520 strcpy(path, "."); 1521 1522 if ((fb_realpath(path, cwdir)) == NULL) { 1523 perror_reply(553, "Could not determine cwdir"); 1524 return (-1); 1525 } 1526 1527 if ((wu_realpath(home, abspwdir, chroot_path)) == NULL) { 1528 perror_reply(553, "Could not determine pwdir"); 1529 return (-1); 1530 } 1531 1532 if ((fb_realpath(home, relpwdir)) == NULL) { 1533 perror_reply(553, "Could not determine pwdir"); 1534 return (-1); 1535 } 1536 1537 /* 1538 * we are doing a "best match"... ..so we keep track of what "match 1539 * value" we have received so far... 1540 */ 1541 while (getaclentry("upload", &entry)) { 1542 char *q; 1543 int i = 0; 1544 int options = 1; 1545 int classfound = 0; 1546 int classmatched = 0; 1547 pwdir = abspwdir; 1548 while (options 1549 && (i < MAXARGS) 1550 && ((q = entry->arg[i]) != (char *) NULL) 1551 && (q[0] != '\0')) { 1552 if (strcasecmp(q, "absolute") == 0) { 1553 i++; 1554 pwdir = abspwdir; 1555 } 1556 else if (strcasecmp(q, "relative") == 0) { 1557 i++; 1558 pwdir = relpwdir; 1559 } 1560 else if (strncasecmp(q, "class=", 6) == 0) { 1561 i++; 1562 classfound = 1; 1563 if (strcasecmp(q + 6, class) == 0) 1564 classmatched = 1; 1565 } 1566 else if (strcmp(q, "-") == 0) { 1567 i++; 1568 options = 0; 1569 } 1570 else 1571 options = 0; 1572 } 1573 if (!classfound || classmatched) { 1574 int j; 1575 if (((i + 1) < MAXARGS) 1576 && ((q = entry->arg[i]) != (char *) NULL) 1577 && (q[0] != '\0') 1578 && (0 < path_compare(q, pwdir)) 1579 && ((j = path_compare(entry->arg[i + 1], cwdir)) >= match_value)) { 1580 match_value = j; 1581 1582 ap2 = NULL; 1583 if (((i + 2) < MAXARGS) 1584 && ((q = entry->arg[i + 2]) != (char *) NULL) 1585 && (q[0] != '\0')) 1586 ap2 = q; 1587 1588 ap3 = NULL; 1589 if (((i + 3) < MAXARGS) 1590 && ((q = entry->arg[i + 3]) != (char *) NULL) 1591 && (q[0] != '\0')) 1592 ap3 = q; 1593 1594 ap4 = NULL; 1595 if (((i + 4) < MAXARGS) 1596 && ((q = entry->arg[i + 4]) != (char *) NULL) 1597 && (q[0] != '\0')) 1598 ap4 = q; 1599 1600 ap5 = NULL; 1601 if (((i + 5) < MAXARGS) 1602 && ((q = entry->arg[i + 5]) != (char *) NULL) 1603 && (q[0] != '\0')) 1604 ap5 = q; 1605 } 1606 } 1607 } 1608 1609 if (ap3 1610 && ((!strcasecmp("dirs", ap3)) 1611 || (!strcasecmp("nodirs", ap3)))) 1612 ap3 = NULL; 1613 1614 /* 1615 * if we did get matches ... else don't do any of this stuff 1616 */ 1617 if (match_value >= 0) { 1618 if (!strcasecmp(ap2, "yes")) { 1619 if ((ap3 && *ap3 == '*') || (ap4 && *ap4 == '*')) 1620 stat_result = stat(path, &stbuf); 1621 if (ap3) { 1622 if ((ap3[0] != '*') || (ap3[1] != '\0')) 1623 *uid = atoi(ap3); /* the uid */ 1624 else if (stat_result == 0) 1625 *uid = stbuf.st_uid; 1626 } 1627 if (ap4) { 1628 if ((ap4[0] != '*') || (ap4[1] != '\0')) 1629 *gid = atoi(ap4); /* the gid */ 1630 else if (stat_result == 0) 1631 *gid = stbuf.st_gid; 1632 *valid = 1; 1633 } 1634 if (ap5) 1635 sscanf(ap5, "%o", f_mode); /* the mode */ 1636 } 1637 else { 1638 reply(553, "%s: Permission denied on server. (Upload)", name); 1639 return (-1); 1640 } 1641 } 1642 else { 1643 /* 1644 * upload defaults to "permitted" 1645 */ 1646 /* Not if anonymous */ 1647 if (anonymous) { 1648 reply(553, "%s: Permission denied on server. (Upload)", name); 1649 return (-1); 1650 } 1651 return (1); 1652 } 1653 1654 return (match_value); 1655 } 1656 1657 int del_check(char *name) 1658 { 1659 int pdelete = (anonymous ? 0 : 1); 1660 struct aclmember *entry = NULL; 1661 1662 while (getaclentry("delete", &entry) && ARG0 && ARG1 != NULL) { 1663 if (type_match(ARG1)) 1664 if (anonymous) { 1665 if (*ARG0 == 'y') 1666 pdelete = 1; 1667 } 1668 else if (*ARG0 == 'n') 1669 pdelete = 0; 1670 } 1671 1672 /* H* fix: no deletion, period. You put a file here, I get to look at it. */ 1673 #ifdef PARANOID 1674 pdelete = 0; 1675 #endif 1676 1677 if (!pdelete) { 1678 reply(553, "%s: Permission denied on server. (Delete)", name); 1679 return (0); 1680 } 1681 else { 1682 return (1); 1683 } 1684 } 1685 1686 /* The following is from the Debian add-ons. */ 1687 1688 #define lbasename(x) (strrchr(x,'/')?1+strrchr(x,'/'):x) 1689 1690 int regexmatch(char *name, char *rgexp) 1691 { 1692 1693 #ifdef M_UNIX 1694 #ifdef HAVE_REGEX 1695 char *regp; 1696 #endif 1697 #endif 1698 1699 #ifdef HAVE_REGEXEC 1700 regex_t regexbuf; 1701 regmatch_t regmatchbuf; 1702 int rval; 1703 #else 1704 char *sp; 1705 #endif 1706 1707 #if defined(HAVE_REGEXEC) 1708 if (regcomp(®exbuf, rgexp, REG_EXTENDED) != 0) { 1709 reply(553, "HAVE_REGEX error"); 1710 #elif defined(HAVE_REGEX) 1711 if ((sp = regcmp(rgexp, (char *) 0)) == NULL) { 1712 reply(553, "HAVE_REGEX error"); 1713 #else 1714 if ((sp = re_comp(rgexp)) != 0) { 1715 perror_reply(553, sp); 1716 #endif 1717 return (0); 1718 } 1719 1720 #if defined(HAVE_REGEXEC) 1721 rval = regexec(®exbuf, name, 1, ®matchbuf, 0); 1722 regfree(®exbuf); 1723 if (rval != 0) { 1724 #elif defined(HAVE_REGEX) 1725 #ifdef M_UNIX 1726 regp = regex(sp, name); 1727 free(sp); 1728 if (regp == NULL) { 1729 #else 1730 if ((regex(sp, name)) == NULL) { 1731 #endif 1732 #else 1733 if ((re_exec(name)) != 1) { 1734 #endif 1735 return (0); 1736 } 1737 return (1); 1738 } 1739 1740 static int allow_retrieve(char *name) 1741 { 1742 char realname[MAXPATHLEN + 1]; 1743 char localname[MAXPATHLEN + 1]; 1744 char *whichname; 1745 int i; 1746 struct aclmember *entry = NULL; 1747 char *p, *q; 1748 int options; 1749 int classfound; 1750 int classmatched; 1751 char class[1024]; 1752 1753 (void) acl_getclass(class); 1754 if ((name == (char *) NULL) 1755 || (*name == '\0')) 1756 return 0; 1757 fb_realpath(name, localname); 1758 wu_realpath(name, realname, chroot_path); 1759 while (getaclentry("allow-retrieve", &entry)) { 1760 whichname = realname; 1761 i = 0; 1762 options = 1; 1763 classfound = 0; 1764 classmatched = 0; 1765 while (options 1766 && (i < MAXARGS) 1767 && ((q = entry->arg[i]) != (char *) NULL) 1768 && (q[0] != '\0')) { 1769 if (strcasecmp(q, "absolute") == 0) { 1770 i++; 1771 whichname = realname; 1772 } 1773 else if (strcasecmp(q, "relative") == 0) { 1774 i++; 1775 whichname = localname; 1776 } 1777 else if (strncasecmp(q, "class=", 6) == 0) { 1778 i++; 1779 classfound = 1; 1780 if (strcasecmp(q + 6, class) == 0) 1781 classmatched = 1; 1782 } 1783 else if (strcmp(q, "-") == 0) { 1784 i++; 1785 options = 0; 1786 } 1787 else 1788 options = 0; 1789 } 1790 if (!classfound || classmatched) { 1791 for (; (i < MAXARGS) && ((q = entry->arg[i]) != (char *) NULL) && (q[0] != '\0'); i++) { 1792 p = (q[0] == '/') ? whichname : lbasename(whichname); 1793 if (!wu_fnmatch(q, p, FNM_PATHNAME | FNM_LEADING_DIR)) { 1794 return 1; 1795 } 1796 } 1797 } 1798 } 1799 return 0; 1800 } 1801 1802 int checknoretrieve(char *name) 1803 { 1804 char realname[MAXPATHLEN + 1]; 1805 char localname[MAXPATHLEN + 1]; 1806 char *whichname; 1807 int i; 1808 struct aclmember *entry = NULL; 1809 char *p, *q; 1810 int options; 1811 int classfound; 1812 int classmatched; 1813 char class[1024]; 1814 1815 extern struct passwd *pw; 1816 extern char *remoteident; 1817 1818 (void) acl_getclass(class); 1819 if ((name == (char *) NULL) 1820 || (*name == '\0')) 1821 return 0; 1822 fb_realpath(name, localname); 1823 wu_realpath(name, realname, chroot_path); 1824 while (getaclentry("noretrieve", &entry)) { 1825 whichname = realname; 1826 i = 0; 1827 options = 1; 1828 classfound = 0; 1829 classmatched = 0; 1830 while (options 1831 && (i < MAXARGS) 1832 && ((q = entry->arg[i]) != (char *) NULL) 1833 && (q[0] != '\0')) { 1834 if (strcasecmp(q, "absolute") == 0) { 1835 i++; 1836 whichname = realname; 1837 } 1838 else if (strcasecmp(q, "relative") == 0) { 1839 i++; 1840 whichname = localname; 1841 } 1842 else if (strncasecmp(q, "class=", 6) == 0) { 1843 i++; 1844 classfound = 1; 1845 if (strcasecmp(q + 6, class) == 0) 1846 classmatched = 1; 1847 } 1848 else if (strcmp(q, "-") == 0) { 1849 i++; 1850 options = 0; 1851 } 1852 else 1853 options = 0; 1854 } 1855 if (!classfound || classmatched) { 1856 for (; (i < MAXARGS) && ((q = entry->arg[i]) != (char *) NULL) && (q[0] != '\0'); i++) { 1857 p = (q[0] == '/') ? whichname : lbasename(whichname); 1858 if (!wu_fnmatch(q, p, FNM_PATHNAME | FNM_LEADING_DIR)) { 1859 if (!allow_retrieve(name)) { 1860 reply(550, "%s is marked unretrievable", localname); 1861 return 1; 1862 } 1863 } 1864 } 1865 } 1866 } 1867 return 0; 1868 } 1869 1870 #ifdef QUOTA 1871 1872 #ifndef MNTMAXSTR 1873 #define MNTMAXSTR 2048 /* And hope it's enough */ 1874 #endif 1875 1876 #ifdef QUOTA_DEVICE 1877 1878 int path_to_device(char *pathname, char *result) 1879 { 1880 FILE *fp; 1881 #ifdef HAS_OLDSTYLE_GETMNTENT 1882 struct mnttab static_mp; 1883 struct mnttab *mp = &static_mp; 1884 #else 1885 struct mntent *mp; 1886 #endif 1887 struct mount_ent { 1888 char mnt_fsname[MNTMAXSTR], mnt_dir[MNTMAXSTR]; 1889 struct mount_ent *next; 1890 } mountent; 1891 struct mount_ent *current, *start, *new; 1892 char path[1024], mnt_dir[1024], *pos; 1893 int flag = 1; 1894 1895 start = current = NULL; 1896 #ifdef HAS_OLDSTYLE_GETMNTENT 1897 fp = fopen(MNTTAB, "r"); 1898 #else 1899 fp = setmntent(MNTTAB, "r"); 1900 #endif 1901 if (fp == NULL) 1902 return 0; 1903 #ifdef HAS_OLDSTYLE_GETMNTENT 1904 while (getmntent(fp, &static_mp) == 0) 1905 #else 1906 while (mp = getmntent(fp)) 1907 #endif 1908 { 1909 if (!(new = (struct mount_ent *) malloc(sizeof(mountent)))) { 1910 perror("malloc"); 1911 flag = 0; 1912 break; 1913 } 1914 1915 if (!start) 1916 start = current = new; 1917 else 1918 current = current->next = new; 1919 1920 #ifdef HAS_OLDSTYLE_GETMNTENT 1921 strncpy(current->mnt_fsname, mp->mnt_special, strlen(mp->mnt_special) + 1); 1922 strncpy(current->mnt_dir, mp->mnt_mountp, strlen(mp->mnt_mountp) + 1); 1923 #else 1924 strncpy(current->mnt_fsname, mp->mnt_fsname, strlen(mp->mnt_fsname) + 1); 1925 strncpy(current->mnt_dir, mp->mnt_dir, strlen(mp->mnt_dir) + 1); 1926 #endif 1927 } 1928 #ifdef HAS_OLDSTYLE_GETMNTENT 1929 fclose(fp); 1930 #else 1931 endmntent(fp); 1932 #endif 1933 current->next = NULL; 1934 1935 wu_realpath(pathname, path, chroot_path); 1936 1937 while (*path && flag) { 1938 current = start; 1939 while (current && flag) { 1940 if (strcmp(current->mnt_dir, "swap")) { 1941 wu_realpath(current->mnt_dir, mnt_dir, chroot_path); 1942 if (!strcmp(mnt_dir, path)) { 1943 flag = 0; 1944 /* no support for remote quota yet */ 1945 if (!strchr(current->mnt_fsname, ':')) 1946 strcpy(result, current->mnt_fsname); 1947 } 1948 } 1949 current = current->next; 1950 } 1951 if (!((pos = strrchr(path, '/')) - path) && strlen(path) > 1) 1952 strcpy(path, "/"); 1953 else 1954 path[pos - path] = '\0'; 1955 } 1956 while (current) { 1957 new = current->next; 1958 free(current); 1959 current = new; 1960 } 1961 return 1; 1962 } 1963 #endif 1964 1965 void get_quota(char *fs, int uid) 1966 { 1967 char mnt_fsname[MNTMAXSTR]; 1968 #ifdef HAS_NO_QUOTACTL 1969 int dirfd; 1970 struct quotctl qp; 1971 #endif 1972 1973 /* 1974 * Getting file system quota information can take a noticeable amount 1975 * of time, so only get quota information for specified users. 1976 * quota-info <uid-range> [<uid-range> ...] 1977 */ 1978 if (!uid_match("quota-info", uid)) 1979 return; 1980 1981 #ifdef HAS_NO_QUOTACTL 1982 if (path_to_device(fs, mnt_fsname)) { 1983 dirfd = open(fs, O_RDONLY); 1984 qp.op = Q_GETQUOTA; 1985 qp.uid = uid; 1986 qp.addr = (char *) "a; 1987 ioctl(dirfd, Q_QUOTACTL, &qp); 1988 close(dirfd); 1989 } 1990 #else 1991 #ifdef QUOTA_DEVICE 1992 1993 if (path_to_device(fs, mnt_fsname)) 1994 #ifdef QCMD 1995 quotactl(QCMD(Q_GETQUOTA, USRQUOTA), mnt_fsname, uid, (char *) "a); 1996 #else 1997 quotactl(Q_GETQUOTA, mnt_fsname, uid, (char *) "a); 1998 #endif 1999 #else 2000 quotactl(fs, QCMD(Q_GETQUOTA, USRQUOTA), uid, (char *) "a); 2001 #endif 2002 #endif /* HAS_NO_QUOTACTL */ 2003 } 2004 2005 char *time_quota(long curstate, long softlimit, long timelimit, char *timeleft) 2006 { 2007 struct timeval tv; 2008 2009 gettimeofday(&tv, NULL); 2010 if (softlimit && curstate >= softlimit) { 2011 if (timelimit == 0) { 2012 strcpy(timeleft, "NOT STARTED"); 2013 } 2014 else if (timelimit > tv.tv_sec) { 2015 fmttime(timeleft, timelimit - tv.tv_sec); 2016 } 2017 else { 2018 strcpy(timeleft, "EXPIRED"); 2019 } 2020 } 2021 else { 2022 timeleft[0] = '\0'; 2023 } 2024 return (timeleft); 2025 } 2026 2027 void fmttime(char *buf, register long time) 2028 { 2029 int i; 2030 static struct { 2031 int c_secs; /* conversion units in secs */ 2032 char *c_str; /* unit string */ 2033 } cunits[] = { 2034 { 2035 60 *60 * 24 * 28, "months" 2036 } , 2037 { 2038 60 *60 * 24 * 7, "weeks" 2039 } , 2040 { 2041 60 *60 * 24, "days" 2042 } , 2043 { 2044 60 *60, "hours" 2045 } , 2046 { 2047 60, "mins" 2048 } , 2049 { 2050 1, "secs" 2051 } 2052 }; 2053 2054 if (time <= 0) { 2055 strcpy(buf, "EXPIRED"); 2056 return; 2057 } 2058 for (i = 0; i < sizeof(cunits) / sizeof(cunits[0]); i++) { 2059 if (time >= cunits[i].c_secs) 2060 break; 2061 } 2062 sprintf(buf, "%.1f %s", (double) time / cunits[i].c_secs, cunits[i].c_str); 2063 } 2064 2065 #endif 2066 2067 #ifdef THROUGHPUT 2068 2069 int file_compare(char *patterns, char *file) 2070 { 2071 char buf[MAXPATHLEN+1]; 2072 char *cp; 2073 char *cp2; 2074 int i; 2075 int matches = 0; 2076 2077 strncpy(buf, patterns, sizeof(buf) - 1); 2078 buf[sizeof(buf) - 2] = '\0'; 2079 i = strlen(buf); 2080 buf[i++] = ','; 2081 buf[i++] = '\0'; 2082 2083 cp = buf; 2084 while ((cp2 = strchr(cp, ',')) != NULL) { 2085 *cp2++ = '\0'; 2086 if (wu_fnmatch(cp, file, FNM_PATHNAME) == 0) { 2087 matches = 1; 2088 break; 2089 } 2090 cp = cp2; 2091 } 2092 return matches; 2093 } 2094 2095 int remote_compare(char *patterns) 2096 { 2097 char buf[MAXPATHLEN+1]; 2098 char *cp; 2099 char *cp2; 2100 int i; 2101 int matches = 0; 2102 2103 strncpy(buf, patterns, sizeof(buf) - 1); 2104 buf[sizeof(buf) - 2] = '\0'; 2105 i = strlen(buf); 2106 buf[i++] = ','; 2107 buf[i++] = '\0'; 2108 2109 cp = buf; 2110 while ((cp2 = strchr(cp, ',')) != NULL) { 2111 *cp2++ = '\0'; 2112 if (hostmatch(cp, remoteaddr, remotehost)) { 2113 matches = 1; 2114 break; 2115 } 2116 cp = cp2; 2117 } 2118 return matches; 2119 } 2120 2121 void throughput_calc(char *name, int *bps, double *bpsmult) 2122 { 2123 int match_value = -1; 2124 char cwdir[MAXPATHLEN]; 2125 char pwdir[MAXPATHLEN]; 2126 char path[MAXPATHLEN]; 2127 char file[MAXPATHLEN]; 2128 char *ap3 = NULL, *ap4 = NULL; 2129 struct aclmember *entry = NULL; 2130 extern char *home; 2131 char *sp; 2132 int i; 2133 2134 /* default is maximum throughput */ 2135 *bps = -1; 2136 *bpsmult = 1.0; 2137 2138 /* XXX We could use dynamic RAM to store this path, but I'd rather just bail 2139 out with an error. The rest of wu is so crufy that a long path might 2140 just blow up later */ 2141 2142 if ((strlen(name) + 1) > sizeof(path)) { 2143 return; 2144 } 2145 2146 /* what's our current directory? */ 2147 strcpy(path, name); 2148 if ((sp = strrchr(path, '/'))) 2149 *sp = '\0'; 2150 else 2151 strcpy(path, "."); 2152 if ((sp = strrchr(name, '/'))) 2153 strcpy(file, sp + 1); 2154 else 2155 strcpy(file, name); 2156 if ((fb_realpath(path, cwdir)) == NULL) { 2157 return; 2158 } 2159 2160 wu_realpath(home, pwdir, chroot_path); 2161 2162 /* find best matching entry */ 2163 while (getaclentry("throughput", &entry) && ARG0 && ARG1 && ARG2 && ARG3 && ARG4 && ARG5 != NULL) { 2164 if ((0 < path_compare(ARG0, pwdir)) 2165 && ((i = path_compare(ARG1, cwdir)) >= match_value) 2166 ) { 2167 if (file_compare(ARG2, file)) { 2168 if (remote_compare(ARG5)) { 2169 match_value = i; 2170 ap3 = ARG3; 2171 ap4 = ARG4; 2172 } 2173 } 2174 } 2175 } 2176 2177 /* if we did get matches */ 2178 if (match_value >= 0) { 2179 if (strcasecmp(ap3, "oo") == 0) 2180 *bps = -1; 2181 else 2182 *bps = atoi(ap3); 2183 if (strcmp(ap4, "-") == 0) 2184 *bpsmult = 1.0; 2185 else 2186 *bpsmult = atof(ap4); 2187 } 2188 return; 2189 } 2190 2191 void throughput_adjust(char *name) 2192 { 2193 int match_value = -1; 2194 char pwdir[MAXPATHLEN]; 2195 char cwdir[MAXPATHLEN]; 2196 char path[MAXPATHLEN]; 2197 char file[MAXPATHLEN]; 2198 char buf[MAXPATHLEN]; 2199 char *ap3 = NULL, *ap4 = NULL; 2200 char **pap; 2201 struct aclmember *entry = NULL; 2202 extern char *home; 2203 char *sp; 2204 int i; 2205 2206 /* XXX We could use dynamic RAM to store this path, but I'd rather just bail 2207 out with an error. The rest of wu is so crufy that a long path might 2208 just blow up later */ 2209 2210 if ((strlen(name) + 1) > sizeof(path)) { 2211 return; 2212 } 2213 2214 /* what's our current directory? */ 2215 strcpy(path, name); 2216 if ((sp = strrchr(path, '/'))) 2217 *sp = '\0'; 2218 else 2219 strcpy(path, "."); 2220 if ((sp = strrchr(name, '/'))) 2221 strcpy(file, sp + 1); 2222 else 2223 strcpy(file, name); 2224 if ((fb_realpath(path, cwdir)) == NULL) { 2225 return; 2226 } 2227 2228 wu_realpath(home, pwdir, chroot_path); 2229 2230 /* find best matching entry */ 2231 while (getaclentry("throughput", &entry) && ARG0 && ARG1 && ARG2 && ARG3 && ARG4 && ARG5 != NULL) { 2232 if ((0 < path_compare(ARG0, pwdir)) 2233 && ((i = path_compare(ARG1, cwdir)) >= match_value) 2234 ) { 2235 if (file_compare(ARG2, file)) { 2236 if (remote_compare(ARG5)) { 2237 match_value = i; 2238 ap3 = ARG3; 2239 pap = ARG; 2240 ap4 = ARG4; 2241 } 2242 } 2243 } 2244 } 2245 2246 /* if we did get matches */ 2247 if (match_value >= 0) { 2248 if (strcasecmp(ap3, "oo") != 0) { 2249 if (strcmp(ap4, "-") != 0) { 2250 sprintf(buf, "%.0f", atoi(ap3) * atof(ap4)); 2251 pap[3] = (char *) malloc(strlen(buf) + 1); 2252 if (pap[3] == NULL) { 2253 syslog(LOG_ERR, "malloc error in throughput_adjust"); 2254 dologout(1); 2255 } 2256 /* Use ARG6 to keep track of malloced memory */ 2257 if (pap[6]) 2258 free(pap[6]); 2259 pap[6] = pap[3]; 2260 strcpy(pap[3], buf); 2261 } 2262 } 2263 } 2264 return; 2265 } 2266 2267 #endif 2268 2269 #ifdef SOLARIS_2 2270 static int CheckMethod = 1; 2271 #else 2272 static int CheckMethod = 0; 2273 #endif 2274 2275 void SetCheckMethod(const char *method) 2276 { 2277 if ((strcasecmp(method, "md5") == 0) 2278 || (strcasecmp(method, "rfc1321") == 0)) 2279 CheckMethod = 0; 2280 else if ((strcasecmp(method, "crc") == 0) 2281 || (strcasecmp(method, "posix") == 0)) 2282 CheckMethod = 1; 2283 else { 2284 reply(500, "Unrecognized checksum method"); 2285 return; 2286 } 2287 switch (CheckMethod) { 2288 default: 2289 reply(200, "Checksum method is now: MD5 (RFC1321)"); 2290 break; 2291 case 1: 2292 reply(200, "Checksum method is now: CRC (POSIX)"); 2293 break; 2294 } 2295 } 2296 2297 void ShowCheckMethod(void) 2298 { 2299 switch (CheckMethod) { 2300 default: 2301 reply(200, "Current checksum method: MD5 (RFC1321)"); 2302 break; 2303 case 1: 2304 reply(200, "Current checksum method: CRC (POSIX)"); 2305 break; 2306 } 2307 } 2308 2309 void CheckSum(char *pathname) 2310 { 2311 char *cmd; 2312 char buf[MAXPATHLEN]; 2313 FILE *cmdf; 2314 struct stat st; 2315 2316 if (stat(pathname, &st) == 0) { 2317 if ((st.st_mode & S_IFMT) != S_IFREG) { 2318 reply(500, "%s: not a plain file.", pathname); 2319 return; 2320 } 2321 } 2322 else { 2323 perror_reply(550, pathname); 2324 return; 2325 } 2326 2327 switch (CheckMethod) { 2328 default: 2329 cmd = "/bin/md5sum"; 2330 break; 2331 case 1: 2332 cmd = "/bin/cksum"; 2333 break; 2334 } 2335 2336 if (strlen(cmd) + 1 + strlen(pathname) + 1 > sizeof(buf)) { 2337 reply(500, "Pathname too long"); 2338 return; 2339 } 2340 sprintf(buf, "%s %s", cmd, pathname); 2341 2342 cmdf = ftpd_popen(buf, "r", 0); 2343 if (!cmdf) { 2344 perror_reply(550, cmd); 2345 } 2346 else { 2347 if (fgets(buf, sizeof buf, cmdf)) { 2348 char *crptr = strchr(buf, '\n'); 2349 if (crptr != NULL) 2350 *crptr = '\0'; 2351 reply(200, "%s", buf); 2352 } 2353 ftpd_pclose(cmdf); 2354 } 2355 } 2356 2357 void CheckSumLastFile(void) 2358 { 2359 extern char LastFileTransferred[]; 2360 2361 if (LastFileTransferred[0] == '\0') 2362 reply(500, "Nothing transferred yet"); 2363 else 2364 CheckSum(LastFileTransferred); 2365 }