Home | History | Annotate | Download | only in in.ftpd
      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 }
    512