1 /* 2 * Copyright 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: ftpshut.c,v 1.12 2000/07/01 18:17:39 wuftpd Exp $ 32 33 ****************************************************************************/ 34 /* ftpshut 35 * ======= 36 * creates the ftpd shutdown file. 37 */ 38 39 #include "config.h" 40 41 #include <errno.h> 42 #include <pwd.h> 43 #include <stdio.h> 44 #include <string.h> 45 #include <ctype.h> 46 #ifdef TIME_WITH_SYS_TIME 47 #include <time.h> 48 #include <sys/time.h> 49 #else 50 #ifdef HAVE_SYS_TIME_H 51 #include <sys/time.h> 52 #else 53 #include <time.h> 54 #endif 55 #endif 56 57 #include <sys/types.h> 58 #include <sys/stat.h> 59 #include <sys/file.h> 60 #include <sys/param.h> 61 #if defined(VIRTUAL) && defined(INET6) 62 #include <netinet/in.h> 63 #endif 64 65 #include "pathnames.h" 66 67 #define WIDTH 70 68 69 int verbose = 0; 70 int denyoffset = 10; /* default deny time */ 71 int discoffset = 5; /* default disc time */ 72 char *message = "System shutdown at %s"; /* default message */ 73 74 struct tm *tp; 75 76 #define MAXVIRTUALS 512 77 78 char *progname; 79 char *msgfiles[MAXVIRTUALS]; 80 int numfiles = 0; 81 82 #ifdef VIRTUAL 83 extern int read_servers_line(FILE *, char *, size_t, char *, size_t); 84 #endif 85 void print_copyright(void); 86 87 static int newfile(char *fpath) 88 { 89 int i; 90 int fnd; 91 92 /* 93 ** Check to see if the message file path has already been 94 ** seen. If so then there is no need to create it again. 95 */ 96 97 fnd = 0; 98 for (i = 0; i < numfiles; i++) { 99 if (strcmp(msgfiles[i], fpath) == 0) { 100 fnd = 1; 101 break; 102 } 103 } 104 if (!fnd) { 105 msgfiles[numfiles++] = strdup(fpath); 106 return (1); 107 } 108 return (0); 109 } 110 111 static int shutdown_msgfile(char *filename, char *buffer) 112 { 113 FILE *fp; 114 mode_t oldmask; 115 116 oldmask = umask(022); 117 fp = fopen(filename, "w"); 118 (void) umask(oldmask); 119 if (fp == NULL) { 120 fprintf(stderr, "%s: could not open shutdown file %s: %s\n", 121 progname, filename, strerror(errno)); 122 return (1); 123 } 124 125 fprintf(fp, "%.4d %.2d %.2d %.2d %.2d %.4d %.4d\n", 126 (tp->tm_year) + 1900, 127 tp->tm_mon, 128 tp->tm_mday, 129 tp->tm_hour, 130 tp->tm_min, 131 denyoffset, 132 discoffset); 133 fprintf(fp, "%s\n", buffer); 134 fclose(fp); 135 if (verbose) 136 printf("%s: %s created\n", progname, filename); 137 return (0); 138 } 139 140 static void massage(char *buf) 141 { 142 char *sp = NULL; 143 char *ptr; 144 int i = 0; 145 int j = 0; 146 147 ptr = buf; 148 149 while (*ptr++ != '\0') { 150 ++i; 151 152 /* if we have a space, keep track of where and at what "count" */ 153 154 if (*ptr == ' ') { 155 sp = ptr; 156 j = i; 157 } 158 /* magic cookies... */ 159 160 if (*ptr == '%') { 161 ++ptr; 162 switch (*ptr) { 163 case 'r': 164 case 's': 165 case 'd': 166 case 'T': 167 i = i + 24; 168 break; 169 case '\n': 170 i = 0; 171 break; 172 case 'C': 173 case 'R': 174 case 'L': 175 case 'U': 176 i = i + 10; 177 break; 178 case 'M': 179 case 'N': 180 i = i + 3; 181 break; 182 case '\0': 183 return; 184 /* break; */ 185 default: 186 i = i + 1; 187 break; 188 } 189 } 190 /* break up the long lines... */ 191 192 if ((i >= WIDTH) && (sp != NULL)) { 193 *sp = '\n'; 194 sp = NULL; 195 i = i - j; 196 } 197 } 198 } 199 200 static void usage(int exitval) 201 { 202 fprintf(stderr, 203 "Usage: %s [-d min] [-l min] now [\"message\"]\n", progname); 204 fprintf(stderr, 205 " %s [-d min] [-l min] +dd [\"message\"]\n", progname); 206 fprintf(stderr, 207 " %s [-d min] [-l min] HHMM [\"message\"]\n", progname); 208 exit(exitval); 209 } 210 211 int main(int argc, char **argv) 212 { 213 time_t c_time = 0; 214 215 char buf[BUFSIZ]; 216 217 int c; 218 extern int optind; 219 extern char *optarg; 220 221 FILE *accessfile; 222 char *aclbuf, *myaclbuf, *crptr; 223 char *sp = NULL; 224 char linebuf[1024]; 225 char shutmsg[BUFSIZ]; 226 char anonpath[MAXPATHLEN]; 227 struct stat finfo; 228 struct passwd *pwent; 229 230 #ifdef VIRTUAL 231 char *cp = NULL; 232 FILE *svrfp; 233 #ifdef INET6 234 char hostaddress[INET6_ADDRSTRLEN]; 235 #else 236 char hostaddress[32]; 237 #endif 238 char root[MAXPATHLEN]; 239 char accesspath[MAXPATHLEN]; 240 char configdir[MAXPATHLEN]; 241 char altmsgpath[MAXPATHLEN]; 242 #endif 243 244 if ((progname = strrchr(argv[0], '/'))) 245 ++progname; 246 else 247 progname = argv[0]; 248 249 while ((c = getopt(argc, argv, "vVl:d:")) != EOF) { 250 switch (c) { 251 case 'v': 252 verbose++; 253 break; 254 case 'l': 255 denyoffset = atoi(optarg); 256 break; 257 case 'd': 258 discoffset = atoi(optarg); 259 break; 260 case 'V': 261 print_copyright(); 262 exit(0); 263 default: 264 usage(-1); 265 } 266 } 267 268 if ((accessfile = fopen(_PATH_FTPACCESS, "r")) == NULL) { 269 if (errno != ENOENT) 270 fprintf(stderr, "%s: could not open access file %s: %s\n", 271 progname, _PATH_FTPACCESS, strerror(errno)); 272 exit(1); 273 } 274 if (fstat(fileno(accessfile), &finfo) != 0) { 275 fprintf(stderr, "%s: could not fstat() access file %s: %s\n", 276 progname, _PATH_FTPACCESS, strerror(errno)); 277 exit(1); 278 } 279 if (finfo.st_size == 0) { 280 fprintf(stderr, "%s: no shutdown path defined in ftpaccess file %s.\n", 281 progname, _PATH_FTPACCESS); 282 exit(1); 283 } 284 else { 285 if (!(aclbuf = (char *) malloc(finfo.st_size + 1))) { 286 fprintf(stderr, "%s: could not malloc aclbuf: %s\n", 287 progname, strerror(errno)); 288 exit(1); 289 } 290 fread(aclbuf, finfo.st_size, 1, accessfile); 291 *(aclbuf + finfo.st_size) = '\0'; 292 } 293 294 myaclbuf = aclbuf; 295 while (*myaclbuf != '\0') { 296 if (strncasecmp(myaclbuf, "shutdown", 8) == 0) { 297 for (crptr = myaclbuf; *crptr++ != '\n';); 298 *--crptr = '\0'; 299 (void) strlcpy(linebuf, myaclbuf, sizeof(linebuf)); 300 *crptr = '\n'; 301 (void) strtok(linebuf, " \t"); /* returns "shutdown" */ 302 sp = strtok(NULL, " \t"); /* returns shutdown path */ 303 /* save for future use */ 304 (void) strlcpy(shutmsg, sp, sizeof(shutmsg)); 305 } 306 while (*myaclbuf && *myaclbuf++ != '\n'); 307 } 308 309 /* three cases 310 * -- now 311 * -- +ddd 312 * -- HHMM 313 */ 314 315 c = -1; 316 317 if (optind < argc) { 318 if (!strcasecmp(argv[optind], "now")) { 319 c_time = time(0); 320 tp = localtime(&c_time); 321 } 322 else if ((*(argv[optind])) == '+') { 323 c_time = time(0); 324 c_time += 60 * atoi(++(argv[optind])); 325 tp = localtime(&c_time); 326 } 327 else if ((c = atoi(argv[optind])) >= 0) { 328 c_time = time(0); 329 tp = localtime(&c_time); 330 tp->tm_hour = c / 100; 331 tp->tm_min = c % 100; 332 333 if ((tp->tm_hour > 23) || (tp->tm_min > 59)) { 334 fprintf(stderr, "%s: illegal time format.\n", progname); 335 exit(1); 336 } 337 } 338 } 339 if (c_time <= 0) { 340 usage(1); 341 } 342 343 if (sp == NULL) { 344 fprintf(stderr, "%s: no shutdown path defined in ftpaccess file %s.\n", 345 progname, _PATH_FTPACCESS); 346 exit(1); 347 } 348 349 /* do we have a shutdown message? */ 350 if (++optind < argc) 351 (void) strlcpy(buf, argv[optind++], sizeof(buf)); 352 else 353 (void) strlcpy(buf, message, sizeof(buf)); 354 355 massage(buf); 356 357 /* 358 ** Create the system shutdown message file at the location 359 ** specified in the ftpaccess 'shutdown' directive. This 360 ** is for support of real system users. 361 */ 362 c = shutdown_msgfile(shutmsg, buf); 363 msgfiles[numfiles++] = shutmsg; 364 365 /* 366 ** Determine if the site supports anonymous ftp and if so, create 367 ** the shutdown message file in the anonymous ftp area as well 368 ** so that shutdown works appropriately for both real and guest 369 ** accounts. Save in msgfiles array for later comparison. 370 */ 371 372 if ((pwent = getpwnam("ftp")) != NULL) { 373 (void) snprintf(anonpath, sizeof(anonpath), "%s%s", pwent->pw_dir, 374 shutmsg); 375 if (newfile(anonpath)) 376 c += shutdown_msgfile(anonpath, buf); 377 } 378 379 #ifdef VIRTUAL 380 /* 381 ** Search the Master access file for virtual ftp servers. 382 ** If found, construct a path to the shutdown message file 383 ** under the virtual server's root. Don't duplicate what 384 ** is specified in the "ftp" account directory information. 385 */ 386 387 rewind(accessfile); 388 389 while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) { 390 if (strncasecmp(linebuf, "virtual", 7) == 0) { 391 392 if ((sp = strstr(linebuf, "root")) != NULL) { 393 if ((cp = strchr(sp, '\n')) != NULL) 394 *cp = '\0'; /* strip newline */ 395 396 sp += 4; /* skip past "root" keyword */ 397 398 while (*sp && isspace(*sp)) /* skip whitespace to root path */ 399 sp++; 400 cp = sp; 401 while (*sp && !isspace(*sp)) 402 sp++; 403 *sp = '\0'; /* truncate blanks, comments etc. */ 404 405 (void) snprintf(altmsgpath, sizeof(altmsgpath), "%s%s", cp, 406 shutmsg); 407 408 if (newfile(altmsgpath)) 409 c += shutdown_msgfile(altmsgpath, buf); 410 } 411 } 412 } 413 414 /* 415 ** Need to deal with the access files at the virtual domain directory 416 ** locations specified in the ftpservers file. 417 */ 418 419 if ((svrfp = fopen(_PATH_FTPSERVERS, "r")) != NULL) { 420 while (read_servers_line(svrfp, hostaddress, sizeof(hostaddress), 421 configdir, sizeof(configdir)) == 1) { 422 /* get rid of any trailing slash */ 423 sp = configdir + (strlen(configdir) - 1); 424 if (*sp == '/') 425 *sp = '\0'; 426 427 /* 428 ** check to see that a valid directory value was 429 ** supplied and not something such as "INTERNAL" 430 ** 431 ** It is valid to have a string such as "INTERNAL" in the 432 ** ftpservers entry. This is not an error. Silently ignore it. 433 */ 434 435 if ((stat(configdir, &finfo) < 0) || 436 ((finfo.st_mode & S_IFMT) != S_IFDIR)) 437 continue; 438 439 (void) snprintf(accesspath, sizeof(accesspath), "%s/ftpaccess", 440 configdir); 441 442 (void) fclose(accessfile); 443 444 if ((accessfile = fopen(accesspath, "r")) == NULL) { 445 if (errno != ENOENT) { 446 fprintf(stderr, "%s: could not open access file %s: %s\n", 447 progname, accesspath, strerror(errno)); 448 continue; 449 } 450 } 451 452 /* need to find the root path */ 453 454 while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) { 455 if ((sp = strstr(linebuf, "root")) != NULL) { 456 if ((cp = strchr(sp, '\n')) != NULL) 457 *cp = '\0'; /* strip newline */ 458 sp += 4; /* skip past "root" keyword */ 459 460 while (*sp && isspace(*sp)) /* skip whitespace to path */ 461 sp++; 462 cp = sp; 463 while (*sp && !isspace(*sp)) 464 sp++; 465 *sp = '\0'; /* truncate blanks, comments etc. */ 466 (void) strlcpy(root, cp, sizeof(root)); 467 break; 468 } 469 } 470 /* need to find the shutdown message file path */ 471 472 rewind(accessfile); 473 474 while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) { 475 if ((sp = strstr(linebuf, "shutdown")) != NULL) { 476 if ((cp = strchr(sp, '\n')) != NULL) 477 *cp = '\0'; /* strip newline */ 478 sp += 8; /* skip past "root" keyword */ 479 480 while (*sp && isspace(*sp)) /* skip whitespace to path */ 481 sp++; 482 cp = sp; 483 while (*sp && !isspace(*sp)) 484 sp++; 485 *sp = '\0'; /* truncate blanks, comments etc. */ 486 break; 487 } 488 } 489 490 /* 491 ** check to make sure the admin hasn't specified 492 ** a complete path in the 'shutdown' directive. 493 */ 494 if ((sp = strstr(cp, root)) == NULL) 495 (void) snprintf(altmsgpath, sizeof(altmsgpath), "%s%s", root, 496 cp); 497 498 /* 499 ** Check to see if the message file has been created elsewhere. 500 */ 501 if (newfile(altmsgpath)) 502 c += shutdown_msgfile(altmsgpath, buf); 503 } 504 fclose(svrfp); 505 } 506 #endif /* VIRTUAL */ 507 508 fclose(accessfile); 509 free(aclbuf); 510 exit(c > 0 ? 1 : 0); 511 }