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 }