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