Home | History | Annotate | Download | only in in.ftpd
      1 /*
      2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /****************************************************************************
      7   Copyright (c) 1999,2000,2001 WU-FTPD Development Group.
      8   All rights reserved.
      9 
     10   Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
     11     The Regents of the University of California.
     12   Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
     13   Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
     14   Portions Copyright (c) 1989 Massachusetts Institute of Technology.
     15   Portions Copyright (c) 1998 Sendmail, Inc.
     16   Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P.  Allman.
     17   Portions Copyright (c) 1997 by Stan Barber.
     18   Portions Copyright (c) 1997 by Kent Landfield.
     19   Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
     20     Free Software Foundation, Inc.
     21 
     22   Use and distribution of this software and its source code are governed
     23   by the terms and conditions of the WU-FTPD Software License ("LICENSE").
     24 
     25   If you did not receive a copy of the license, it may be obtained online
     26   at http://www.wu-ftpd.org/license.html.
     27 
     28   $Id: ftpcmd.y,v 1.27.2.2 2001/11/29 17:01:38 wuftpd Exp $
     29 
     30 ****************************************************************************/
     31 /*
     32  * Grammar for FTP commands.
     33  * See RFC 959.
     34  */
     35 
     36 %{
     37 #include "config.h"
     38 #include <sys/param.h>
     39 #include <sys/types.h>
     40 #include <sys/socket.h>
     41 #include <sys/stat.h>
     42 #include <netinet/in.h>
     43 #include <arpa/inet.h>
     44 #include <arpa/ftp.h>
     45 #include <stdio.h>
     46 #include <signal.h>
     47 #include <errno.h>
     48 #include <ctype.h>
     49 #include <pwd.h>
     50 #include <setjmp.h>
     51 #ifdef HAVE_SYS_SYSLOG_H
     52 #include <sys/syslog.h>
     53 #endif
     54 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
     55 #include <syslog.h>
     56 #endif
     57 #include <time.h>
     58 #include <stdlib.h>
     59 #include <string.h>
     60 #include <limits.h>
     61 #include <alloca.h>
     62 #include "extensions.h"
     63 #include "pathnames.h"
     64 #include "proto.h"
     65 
     66 #if defined(USE_TLS) || defined(USE_GSS)
     67 static int pbsz_command_issued = 0;
     68 char *cur_auth_type = NULL;
     69 extern char *protnames[];
     70 #endif /* defined(USE_TLS) || defined(USE_GSS) */
     71 
     72 #if defined(USE_GSS)
     73 #include "gssutil.h"
     74 
     75 extern gss_info_t gss_info;
     76 #endif /* defined(USE_GSS) */
     77 
     78 extern int dolreplies;
     79 #ifndef INTERNAL_LS
     80 extern char ls_long[];
     81 extern char ls_short[];
     82 #endif
     83 extern struct SOCKSTORAGE data_dest;
     84 extern struct SOCKSTORAGE his_addr;
     85 extern int logged_in;
     86 extern struct passwd *pw;
     87 extern int anonymous;
     88 extern int logging;
     89 extern int log_commands;
     90 extern int log_security;
     91 extern int type;
     92 extern int form;
     93 extern int debug;
     94 extern unsigned int timeout_idle;
     95 extern unsigned int timeout_maxidle;
     96 extern int pdata;
     97 extern char hostname[], remotehost[], *remoteident;
     98 extern char remoteaddr[];
     99 extern char chroot_path[];
    100 extern char guestpw[], authuser[];	/* added.  _H */
    101 extern char proctitle[];
    102 extern char *globerr;
    103 extern int usedefault;
    104 extern int transflag;
    105 extern char tmpline[];
    106 extern int data;
    107 extern int errno;
    108 extern char *home;
    109 
    110 off_t restart_point;
    111 int yyerrorcalled;
    112 
    113 extern char *strunames[];
    114 extern char *typenames[];
    115 extern char *modenames[];
    116 extern char *formnames[];
    117 extern int restricted_user;	/* global flag indicating if user is restricted to home directory */
    118 
    119 #ifdef TRANSFER_COUNT
    120 extern off_t data_count_total;
    121 extern off_t byte_count_total;
    122 extern off_t byte_count_in;
    123 extern int file_count_total;
    124 extern int xfer_count_total;
    125 #endif
    126 
    127 extern int retrieve_is_data;
    128 
    129 #ifdef VIRTUAL
    130 extern int virtual_mode;
    131 extern int virtual_ftpaccess;
    132 extern char virtual_email[];
    133 #endif
    134 
    135 #ifdef IGNORE_NOOP
    136 static int alarm_running = 0;
    137 #endif
    138 
    139 static unsigned short cliport = 0;
    140 static struct in_addr cliaddr;
    141 static int cmd_type;
    142 static int cmd_form;
    143 static int cmd_bytesz;
    144 char cbuf[16 * BUFSIZ];
    145 char *fromname;
    146 
    147 #ifndef L_FORMAT		/* Autoconf detects this... */
    148 #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
    149 #define L_FORMAT "qd"
    150 #else
    151 #ifdef _AIX42
    152 #define L_FORMAT "lld"
    153 #else
    154 #ifdef SOLARIS_2
    155 #define L_FORMAT "ld"
    156 #else
    157 #define L_FORMAT "d"
    158 #endif
    159 #endif
    160 #endif
    161 #endif
    162 
    163 #ifdef INET6
    164 extern int epsv_all;
    165 int lport_error;
    166 #endif
    167 
    168 /* Debian linux bison fix: moved this up, added forward decls */
    169 
    170 struct tab {
    171     char *name;
    172     short token;
    173     short state;
    174     short implemented;		/* 1 if command is implemented */
    175     char *help;
    176 };
    177 
    178 extern struct tab cmdtab[];
    179 extern struct tab sitetab[];
    180 
    181 static void toolong(int);
    182 void help(struct tab *ctab, char *s);
    183 struct tab *lookup(register struct tab *p, char *cmd);
    184 int yylex(void);
    185 
    186 static char *nullstr = "(null)";
    187 #define CHECKNULL(p) ((p) ? (p) : nullstr)
    188 
    189 extern int pasv_allowed(const char *remoteaddr);
    190 extern int port_allowed(const char *remoteaddr);
    191 %}
    192 
    193 %token
    194     A   B   C   E   F   I
    195     L   N   P   R   S   T
    196 
    197     SP  CRLF    COMMA   STRING  NUMBER
    198 
    199     USER    PASS    ACCT    REIN    QUIT    PORT
    200     PASV    TYPE    STRU    MODE    RETR    STOR
    201     APPE    MLFL    MAIL    MSND    MSOM    MSAM
    202     MRSQ    MRCP    ALLO    REST    RNFR    RNTO
    203     ABOR    DELE    CWD     LIST    NLST    SITE
    204     STAT    HELP    NOOP    MKD     RMD     PWD
    205     CDUP    STOU    SMNT    SYST    SIZE    MDTM
    206     EPRT    EPSV    LPRT    LPSV
    207     PROT    PBSZ    AUTH    ADAT    CCC
    208 
    209     UMASK   IDLE    CHMOD   GROUP   GPASS   NEWER
    210     MINFO   INDEX   EXEC    ALIAS   CDPATH  GROUPS
    211     CHECKMETHOD     CHECKSUM
    212 
    213     LEXERR
    214 
    215 %union {
    216     char *String;
    217     int Number;
    218 }
    219 
    220 %type <String>  STRING password pathname pathstring username method
    221 %type <Number>  NUMBER byte_size check_login form_code
    222 %type <Number>  struct_code mode_code octal_number
    223 %type <Number>  prot_code
    224 
    225 %start  cmd_list
    226 
    227 %%
    228 
    229 cmd_list:	/* empty */
    230     | cmd_list cmd
    231 	=	{
    232 	    if (fromname) {
    233 		free(fromname);
    234 		fromname = NULL;
    235 	    }
    236 	    restart_point = 0;
    237 	}
    238     | cmd_list rcmd
    239     ;
    240 
    241 cmd: USER SP username CRLF
    242 	=	{
    243 	    user($3);
    244 	    if (log_commands)
    245 		syslog(LOG_INFO, "USER %s", $3);
    246 	    free($3);
    247 	}
    248     | PASS SP password CRLF
    249 	=	{
    250 	    if (log_commands)
    251 		if (anonymous)
    252 		    syslog(LOG_INFO, "PASS %s", $3);
    253 		else
    254 		    syslog(LOG_INFO, "PASS password");
    255 
    256 	    pass($3);
    257 	    free($3);
    258 	}
    259     | PORT check_login SP host_port CRLF
    260 	=	{
    261 	    if (log_commands)
    262 		syslog(LOG_INFO, "PORT");
    263 /* H* port fix, part B: admonish the twit.
    264    Also require login before PORT works */
    265 	    if ($2) {
    266 #ifndef DISABLE_PORT
    267 #ifdef INET6
    268 		if (epsv_all) {
    269 		    reply(501, "PORT not allowed after EPSV ALL");
    270 		    goto prt_done;
    271 		}
    272 #endif
    273 		if (((sock_cmp_inaddr(&his_addr, cliaddr) == 0)
    274 		     || port_allowed(inet_ntoa(cliaddr)))
    275 		    && (ntohs(cliport) >= IPPORT_RESERVED)) {
    276 		    usedefault = 0;
    277 		    if (pdata >= 0) {
    278 			(void) close(pdata);
    279 			pdata = -1;
    280 		    }
    281 		    SET_SOCK_FAMILY(data_dest, SOCK_FAMILY(his_addr));
    282 		    SET_SOCK_PORT(data_dest, cliport);
    283 		    SET_SOCK_ADDR4(data_dest, cliaddr);
    284 		    reply(200, "PORT command successful.");
    285 		}
    286 		else {
    287 #endif /* DISABLE_PORT */
    288 		    reply(502, "Illegal PORT Command");
    289 prt_done:
    290 		    usedefault = 1;
    291 		    syslog(LOG_WARNING, "refused PORT %s,%d from %s",
    292 			   inet_ntoa(cliaddr), ntohs(cliport), remoteident);
    293 #ifndef DISABLE_PORT
    294 		}
    295 #endif
    296 	    }
    297 	}
    298     | EPRT check_login SP STRING CRLF
    299 	=	{
    300 #ifdef INET6
    301 	    if (log_commands)
    302 		syslog(LOG_INFO, "EPRT");
    303 	    if ($2 && $4 != NULL) {
    304 #ifndef DISABLE_PORT
    305 		char d, fmt[32], addr[INET6_ADDRSTRLEN + 1];
    306 		int proto;
    307 		unsigned short port;
    308 
    309 		if (epsv_all) {
    310 		    reply(501, "EPRT not allowed after EPSV ALL");
    311 		    goto eprt_done;
    312 		}
    313 		d = *((char *)$4);
    314 		if ((d < 33) || (d > 126)) {
    315 		    reply(501, "Bad delimiter '%c' (%d).", d, d);
    316 		    goto eprt_done;
    317 		}
    318 		if (d == '%')
    319 		    (void) snprintf(fmt, sizeof(fmt),
    320 			    "%%%1$c%%d%%%1$c%%%2$d[^%%%1$c]%%%1$c%%hu%%%1$c",
    321 			    d, INET6_ADDRSTRLEN);
    322 		else
    323 		    (void) snprintf(fmt, sizeof(fmt),
    324 			    "%1$c%%d%1$c%%%2$d[^%1$c]%1$c%%hu%1$c",
    325 			    d, INET6_ADDRSTRLEN);
    326 
    327 		if (sscanf((const char *)$4, fmt, &proto, addr, &port) != 3) {
    328 		    reply(501, "EPRT bad format.");
    329 		    goto eprt_done;
    330 		}
    331 		port = htons(port);
    332 
    333 		switch (proto) {
    334 		case 1:
    335 		    SET_SOCK_FAMILY(data_dest, AF_INET);
    336 		    break;
    337 		case 2:
    338 		    memset(&data_dest, 0, sizeof(struct sockaddr_in6));
    339 		    SET_SOCK_FAMILY(data_dest, AF_INET6);
    340 		    break;
    341 		default:
    342 		    reply(522, "Network protocol not supported, use (1,2)");
    343 		    goto eprt_done;
    344 		}
    345 		if (inet_pton(SOCK_FAMILY(data_dest), addr, SOCK_ADDR(data_dest))
    346 		    != 1) {
    347 		    reply(501, "Bad address %s.", addr);
    348 		    goto eprt_done;
    349 		}
    350 
    351 		if (((sock_cmp_addr(&his_addr, &data_dest) == 0)
    352 		     || port_allowed(inet_stop(&data_dest)))
    353 		    && (ntohs(port) >= IPPORT_RESERVED)) {
    354 		    usedefault = 0;
    355 		    if (pdata >= 0) {
    356 			(void) close(pdata);
    357 			pdata = -1;
    358 		    }
    359 		    SET_SOCK_PORT(data_dest, port);
    360 		    SET_SOCK_SCOPE(data_dest, his_addr);
    361 		    reply(200, "EPRT command successful.");
    362 		}
    363 		else {
    364 #endif /* DISABLE_PORT */
    365 		    reply(502, "Illegal EPRT Command");
    366 eprt_done:
    367 		    usedefault = 1;
    368 		    syslog(LOG_WARNING, "refused EPRT %s from %s",
    369 			   $4, remoteident);
    370 #ifndef DISABLE_PORT
    371 		}
    372 #endif
    373 	    }
    374 	    if ($4 != NULL)
    375 		free($4);
    376 #endif /* INET6 */
    377 	}
    378     | LPRT check_login SP host_lport CRLF
    379 	=	{
    380 #ifdef INET6
    381 	    if (log_commands)
    382 		syslog(LOG_INFO, "LPRT");
    383 	    if ($2) {
    384 #ifndef DISABLE_PORT
    385 		if (lport_error)
    386 		    goto lprt_done;
    387 		if (((sock_cmp_addr(&his_addr, &data_dest) == 0)
    388 		     || port_allowed(inet_stop(&data_dest)))
    389 		    && (SOCK_PORT(data_dest) >= IPPORT_RESERVED)) {
    390 		    usedefault = 0;
    391 		    if (pdata >= 0) {
    392 			(void) close(pdata);
    393 			pdata = -1;
    394 		    }
    395 		    SET_SOCK_SCOPE(data_dest, his_addr);
    396 		    reply(200, "LPRT command successful.");
    397 		}
    398 		else {
    399 #endif /* DISABLE_PORT */
    400 		    reply(502, "Illegal LPRT Command");
    401 lprt_done:
    402 		    usedefault = 1;
    403 		    syslog(LOG_WARNING, "refused LPRT from %s", remoteident);
    404 #ifndef DISABLE_PORT
    405 		}
    406 #endif
    407 	    }
    408 #endif /* INET6 */
    409 	}
    410     | PASV check_login CRLF
    411 	=	{
    412 /* Require login for PASV, too.  This actually fixes a bug -- telnet to an
    413    unfixed wu-ftpd and type PASV first off, and it crashes! */
    414 	    if (log_commands)
    415 		syslog(LOG_INFO, "PASV");
    416 	    if ($2)
    417 #if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
    418 #ifdef INET6
    419 		if (epsv_all)
    420 		    reply(501, "PASV not allowed after EPSV ALL");
    421 		else
    422 #endif
    423 		    passive(TYPE_PASV, 0);
    424 #else
    425 		reply(502, "Illegal PASV Command");
    426 #endif
    427 	}
    428     | EPSV check_login CRLF
    429 	=	{
    430 #ifdef INET6
    431 	    if (log_commands)
    432 		syslog(LOG_INFO, "EPSV");
    433 	    if ($2)
    434 #if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
    435 		passive(TYPE_EPSV, 0);
    436 #else
    437 		reply(502, "Illegal EPSV Command");
    438 #endif
    439 #endif /* INET6 */
    440 	}
    441     | EPSV check_login SP STRING CRLF
    442 	=	{
    443 #ifdef INET6
    444 	    if (log_commands)
    445 		syslog(LOG_INFO, "EPSV");
    446 	    if ($2 && $4 != NULL)
    447 #if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
    448 		if (strcasecmp((const char *)$4, "ALL") == 0) {
    449 		    epsv_all = 1;
    450 		    reply(200, "EPSV ALL command successful.");
    451 		}
    452 		else {
    453 		    int af;
    454 		    char *endp;
    455 
    456 		    af = strtoul((char *)$4, &endp, 0);
    457 		    if (*endp)
    458 			reply(501, "'EPSV %s':" "command not understood.", $4);
    459 		    else {
    460 			/* Not allowed to specify address family 0 */
    461 			if (af == 0)
    462 			    af = -1;
    463 			passive(TYPE_EPSV, af);
    464 		    }
    465 		}
    466 #else
    467 		reply(502, "Illegal EPSV Command");
    468 #endif
    469 	    if ($4 != NULL)
    470 		free($4);
    471 #endif /* INET6 */
    472 	}
    473     | LPSV check_login CRLF
    474 	=	{
    475 #ifdef INET6
    476 	    if (log_commands)
    477 		syslog(LOG_INFO, "LPSV");
    478 	    if ($2)
    479 #if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
    480 		if (epsv_all)
    481 		    reply(501, "LPSV not allowed after EPSV ALL");
    482 		else
    483 		    passive(TYPE_LPSV, 0);
    484 #else
    485 		reply(502, "Illegal LPSV Command");
    486 #endif
    487 #endif /* INET6 */
    488 	}
    489     | TYPE check_login SP type_code CRLF
    490 	=	{
    491 	    if (log_commands)
    492 		syslog(LOG_INFO, "TYPE %s", typenames[cmd_type]);
    493 	    if ($2)
    494 		switch (cmd_type) {
    495 
    496 		case TYPE_A:
    497 		    if (cmd_form == FORM_N) {
    498 			reply(200, "Type set to A.");
    499 			type = cmd_type;
    500 			form = cmd_form;
    501 		    }
    502 		    else
    503 			reply(504, "Form must be N.");
    504 		    break;
    505 
    506 		case TYPE_E:
    507 		    reply(504, "Type E not implemented.");
    508 		    break;
    509 
    510 		case TYPE_I:
    511 		    reply(200, "Type set to I.");
    512 		    type = cmd_type;
    513 		    break;
    514 
    515 		case TYPE_L:
    516 #if NBBY == 8
    517 		    if (cmd_bytesz == 8) {
    518 			reply(200,
    519 			      "Type set to L (byte size 8).");
    520 			type = cmd_type;
    521 		    }
    522 		    else
    523 			reply(504, "Byte size must be 8.");
    524 #else /* NBBY == 8 */
    525 #error UNIMPLEMENTED for NBBY != 8
    526 #endif /* NBBY == 8 */
    527 		}
    528 	}
    529     | STRU check_login SP struct_code CRLF
    530 	=	{
    531 	    if (log_commands)
    532 		syslog(LOG_INFO, "STRU %s", strunames[$4]);
    533 	    if ($2)
    534 		switch ($4) {
    535 
    536 		case STRU_F:
    537 		    reply(200, "STRU F ok.");
    538 		    break;
    539 
    540 		default:
    541 		    reply(504, "Unimplemented STRU type.");
    542 		}
    543 	}
    544     | MODE check_login SP mode_code CRLF
    545 	=	{
    546 	    if (log_commands)
    547 		syslog(LOG_INFO, "MODE %s", modenames[$4]);
    548 	    if ($2)
    549 		switch ($4) {
    550 
    551 		case MODE_S:
    552 		    reply(200, "MODE S ok.");
    553 		    break;
    554 
    555 		default:
    556 		    reply(502, "Unimplemented MODE type.");
    557 		}
    558 	}
    559     | ALLO check_login SP NUMBER CRLF
    560 	=	{
    561 	    if (log_commands)
    562 		syslog(LOG_INFO, "ALLO %d", $4);
    563 	    if ($2)
    564 		reply(202, "ALLO command ignored.");
    565 	}
    566     | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
    567 	=	{
    568 	    if (log_commands)
    569 		syslog(LOG_INFO, "ALLO %d R %d", $4, $8);
    570 	    if ($2)
    571 		reply(202, "ALLO command ignored.");
    572 	}
    573     | RETR check_login SP pathname CRLF
    574 	=	{
    575 	    if (log_commands)
    576 		syslog(LOG_INFO, "RETR %s", CHECKNULL($4));
    577 	    if ($2 && $4 != NULL && !restrict_check($4)) {
    578 		retrieve_is_data = 1;
    579 		retrieve((char *) NULL, $4);
    580 	    }
    581 	    if ($4 != NULL)
    582 		free($4);
    583 	}
    584     | STOR check_login SP pathname CRLF
    585 	=	{
    586 	    if (log_commands)
    587 		syslog(LOG_INFO, "STOR %s", CHECKNULL($4));
    588 	    if ($2 && $4 != NULL && !restrict_check($4))
    589 		store($4, "w", 0);
    590 	    if ($4 != NULL)
    591 		free($4);
    592 	}
    593     | APPE check_login SP pathname CRLF
    594 	=	{
    595 	    if (log_commands)
    596 		syslog(LOG_INFO, "APPE %s", CHECKNULL($4));
    597 	    if ($2 && $4 != NULL && !restrict_check($4))
    598 		store($4, "a", 0);
    599 	    if ($4 != NULL)
    600 		free($4);
    601 	}
    602     | NLST check_login CRLF
    603 	=	{
    604 	    if (log_commands)
    605 		syslog(LOG_INFO, "NLST");
    606 	    if ($2 && !restrict_check("."))
    607 		send_file_list("");
    608 	}
    609     | NLST check_login SP STRING CRLF
    610 	=	{
    611 	    if (log_commands)
    612 		syslog(LOG_INFO, "NLST %s", $4);
    613 	    if ($2 && $4 && !restrict_check($4))
    614 		send_file_list($4);
    615 	    if ($4 != NULL)
    616 		free($4);
    617 	}
    618     | LIST check_login CRLF
    619 	=	{
    620 	    if (log_commands)
    621 		syslog(LOG_INFO, "LIST");
    622 	    if ($2 && !restrict_check(".")) {
    623 		retrieve_is_data = 0;
    624 #ifndef INTERNAL_LS
    625 		if (anonymous && dolreplies)
    626 		    retrieve(ls_long, "");
    627 		else
    628 		    retrieve(ls_short, "");
    629 #else
    630 		ls(NULL, 0);
    631 #endif
    632 	    }
    633 	}
    634     | LIST check_login SP pathname CRLF
    635 	=	{
    636 	    if (log_commands)
    637 		syslog(LOG_INFO, "LIST %s", CHECKNULL($4));
    638 	    if ($2 && $4 != NULL && !restrict_list_check($4)) {
    639 		retrieve_is_data = 0;
    640 #ifndef INTERNAL_LS
    641 		if (anonymous && dolreplies)
    642 		    retrieve(ls_long, $4);
    643 		else
    644 		    retrieve(ls_short, $4);
    645 #else
    646 		ls($4, 0);
    647 #endif
    648 	    }
    649 	    if ($4 != NULL)
    650 		free($4);
    651 	}
    652     | STAT check_login SP pathname CRLF
    653 	=	{
    654 	    if (log_commands)
    655 		syslog(LOG_INFO, "STAT %s", CHECKNULL($4));
    656 	    if ($2 && $4 != NULL && !restrict_check($4))
    657 		statfilecmd($4);
    658 	    if ($4 != NULL)
    659 		free($4);
    660 	}
    661     | STAT check_login CRLF
    662 	=	{
    663 	    if (log_commands)
    664 		syslog(LOG_INFO, "STAT");
    665 	    if ($2)
    666 		statcmd();
    667 	}
    668     | DELE check_login SP pathname CRLF
    669 	=	{
    670 	    if (log_commands)
    671 		syslog(LOG_INFO, "DELE %s", CHECKNULL($4));
    672 	    if ($2 && $4 != NULL && !restrict_check($4))
    673 		delete($4);
    674 	    if ($4 != NULL)
    675 		free($4);
    676 	}
    677     | RNTO check_login SP pathname CRLF
    678 	=	{
    679 	    if (log_commands)
    680 		syslog(LOG_INFO, "RNTO %s", CHECKNULL($4));
    681 	    if ($2 && $4 && !restrict_check($4)) {
    682 		if (fromname) {
    683 		    renamecmd(fromname, $4);
    684 		    free(fromname);
    685 		    fromname = NULL;
    686 		}
    687 		else {
    688 		    reply(503, "Bad sequence of commands.");
    689 		}
    690 	    }
    691 	    if ($4)
    692 		free($4);
    693 	}
    694     | ABOR check_login CRLF
    695 	=	{
    696 	    if (log_commands)
    697 		syslog(LOG_INFO, "ABOR");
    698 	    if ($2)
    699 		reply(225, "ABOR command successful.");
    700 	}
    701     | CWD check_login CRLF
    702 	=	{
    703 	    if (log_commands)
    704 		syslog(LOG_INFO, "CWD");
    705 	    if ($2 && !restrict_check(home))
    706 		cwd(home);
    707 	}
    708     | CWD check_login SP pathname CRLF
    709 	=	{
    710 	    if (log_commands)
    711 		syslog(LOG_INFO, "CWD %s", CHECKNULL($4));
    712 	    if ($2 && $4 != NULL && !restrict_check($4))
    713 		cwd($4);
    714 	    if ($4 != NULL)
    715 		free($4);
    716 	}
    717     | HELP check_login CRLF
    718 	=	{
    719 	    if (log_commands)
    720 		syslog(LOG_INFO, "HELP");
    721 	    if ($2)
    722 		help(cmdtab, (char *) NULL);
    723 	}
    724     | HELP check_login SP STRING CRLF
    725 	=	{
    726 	    register char *cp = (char *) $4;
    727 
    728 	    if (log_commands)
    729 		syslog(LOG_INFO, "HELP %s", $4);
    730 	    if ($2)
    731 		if (strncasecmp(cp, "SITE", 4) == 0) {
    732 		    cp = (char *) $4 + 4;
    733 		    if (*cp == ' ')
    734 			cp++;
    735 		    if (*cp)
    736 			help(sitetab, cp);
    737 		    else
    738 			help(sitetab, (char *) NULL);
    739 		}
    740 		else
    741 		    help(cmdtab, $4);
    742 	    if ($4 != NULL)
    743 		free($4);
    744 	}
    745     | NOOP check_login CRLF
    746 	=	{
    747 	    if (log_commands)
    748 		syslog(LOG_INFO, "NOOP");
    749 	    if ($2)
    750 		reply(200, "NOOP command successful.");
    751 	}
    752     | MKD check_login SP pathname CRLF
    753 	=	{
    754 	    if (log_commands)
    755 		syslog(LOG_INFO, "MKD %s", CHECKNULL($4));
    756 	    if ($2 && $4 != NULL && !restrict_check($4))
    757 		makedir($4);
    758 	    if ($4 != NULL)
    759 		free($4);
    760 	}
    761     | RMD check_login SP pathname CRLF
    762 	=	{
    763 	    if (log_commands)
    764 		syslog(LOG_INFO, "RMD %s", CHECKNULL($4));
    765 	    if ($2 && $4 != NULL && !restrict_check($4))
    766 		removedir($4);
    767 	    if ($4 != NULL)
    768 		free($4);
    769 	}
    770     | PWD check_login CRLF
    771 	=	{
    772 	    if (log_commands)
    773 		syslog(LOG_INFO, "PWD");
    774 	    if ($2)
    775 		pwd();
    776 	}
    777     | CDUP check_login CRLF
    778 	=	{
    779 	    if (log_commands)
    780 		syslog(LOG_INFO, "CDUP");
    781 	    if ($2)
    782 		if (!test_restriction(".."))
    783 		    cwd("..");
    784 		else
    785 		    ack("CWD");
    786 	}
    787 
    788     | SITE check_login SP HELP CRLF
    789 	=	{
    790 	    if (log_commands)
    791 		syslog(LOG_INFO, "SITE HELP");
    792 	    if ($2)
    793 		help(sitetab, (char *) NULL);
    794 	}
    795     | SITE check_login SP HELP SP STRING CRLF
    796 	=	{
    797 	    if (log_commands)
    798 		syslog(LOG_INFO, "SITE HELP %s", $6);
    799 	    if ($2)
    800 		help(sitetab, $6);
    801 	    if ($6 != NULL)
    802 		free($6);
    803 	}
    804     | SITE check_login SP UMASK CRLF
    805 	=	{
    806 	    mode_t oldmask;
    807 
    808 	    if (log_commands)
    809 		syslog(LOG_INFO, "SITE UMASK");
    810 	    if ($2) {
    811 		oldmask = umask(0);
    812 		(void) umask(oldmask);
    813 		reply(200, "Current UMASK is %03o", oldmask);
    814 	    }
    815 	}
    816     | SITE check_login SP UMASK SP octal_number CRLF
    817 	=	{
    818 	    mode_t oldmask;
    819 	    struct aclmember *entry = NULL;
    820 	    int ok = 1;
    821 
    822 	    if (log_commands)
    823 		syslog(LOG_INFO, "SITE UMASK %03o", $6);
    824 	    if ($2) {
    825 		/* check for umask permission */
    826 		while (getaclentry("umask", &entry) && ARG0 && ARG1 != NULL) {
    827 		    if (type_match(ARG1))
    828 			if (*ARG0 == 'n')
    829 			    ok = 0;
    830 		}
    831 		if (ok && !restricted_user) {
    832 		    if (($6 < 0) || ($6 > 0777)) {
    833 			reply(501, "Bad UMASK value");
    834 		    }
    835 		    else {
    836 			oldmask = umask((mode_t) $6);
    837 			reply(200, "UMASK set to %03o (was %03o)", $6, oldmask);
    838 		    }
    839 		}
    840 		else
    841 		    reply(553, "Permission denied on server. (umask)");
    842 	    }
    843 	}
    844     | SITE check_login SP CHMOD SP octal_number SP pathname CRLF
    845 	=	{
    846 	    struct aclmember *entry = NULL;
    847 	    int ok = (anonymous ? 0 : 1);
    848 
    849 	    if (log_commands)
    850 		syslog(LOG_INFO, "SITE CHMOD %03o %s", $6, CHECKNULL($8));
    851 	    if ($2 && $8) {
    852 		/* check for chmod permission */
    853 		while (getaclentry("chmod", &entry) && ARG0 && ARG1 != NULL) {
    854 		    if (type_match(ARG1))
    855 			if (anonymous) {
    856 			    if (*ARG0 == 'y')
    857 				ok = 1;
    858 			}
    859 			else if (*ARG0 == 'n')
    860 			    ok = 0;
    861 		}
    862 		if (ok) {
    863 #ifdef UNRESTRICTED_CHMOD
    864 		    if (chmod($8, (mode_t) $6) < 0)
    865 #else
    866 		    if (($6 < 0) || ($6 > 0777))
    867 			reply(501,
    868 			    "CHMOD: Mode value must be between 0 and 0777");
    869 		    else if (chmod($8, (mode_t) $6) < 0)
    870 #endif
    871 			perror_reply(550, $8);
    872 		    else {
    873 			char path[MAXPATHLEN];
    874 
    875 			wu_realpath($8, path, chroot_path);
    876 
    877 			if (log_security)
    878 			    if (anonymous) {
    879 				syslog(LOG_NOTICE, "%s of %s changed permissions for %s", guestpw, remoteident, path);
    880 			    }
    881 			    else {
    882 				syslog(LOG_NOTICE, "%s of %s changed permissions for %s", pw->pw_name,
    883 				       remoteident, path);
    884 			    }
    885 			reply(200, "CHMOD command successful.");
    886 		    }
    887 		}
    888 		else
    889 		    reply(553, "Permission denied on server. (chmod)");
    890 	    }
    891 	    if ($8 != NULL)
    892 		free($8);
    893 	}
    894     | SITE check_login SP IDLE CRLF
    895 	=	{
    896 	    if (log_commands)
    897 		syslog(LOG_INFO, "SITE IDLE");
    898 	    if ($2)
    899 		reply(200,
    900 		      "Current IDLE time limit is %d seconds; max %d",
    901 		      timeout_idle, timeout_maxidle);
    902 	}
    903     | SITE check_login SP IDLE SP NUMBER CRLF
    904 	=	{
    905 	    if (log_commands)
    906 		syslog(LOG_INFO, "SITE IDLE %d", $6);
    907 	    if ($2)
    908 		if ($6 < 30 || $6 > timeout_maxidle) {
    909 		    reply(501,
    910 		      "Maximum IDLE time must be between 30 and %d seconds",
    911 			  timeout_maxidle);
    912 		}
    913 		else {
    914 		    timeout_idle = $6;
    915 		    reply(200, "Maximum IDLE time set to %d seconds", timeout_idle);
    916 		}
    917 	}
    918     | SITE check_login SP GROUP SP username CRLF
    919 	=	{
    920 #ifndef NO_PRIVATE
    921 	    if (log_commands)
    922 		syslog(LOG_INFO, "SITE GROUP %s", $6);
    923 	    if (!restricted_user && $2 && $6)
    924 		priv_group($6);
    925 	    free($6);
    926 #endif /* !NO_PRIVATE */
    927 	}
    928     | SITE check_login SP GPASS SP password CRLF
    929 	=	{
    930 #ifndef NO_PRIVATE
    931 	    if (log_commands)
    932 		syslog(LOG_INFO, "SITE GPASS password");
    933 	    if (!restricted_user && $2 && $6)
    934 		priv_gpass($6);
    935 	    free($6);
    936 #endif /* !NO_PRIVATE */
    937 	}
    938     | SITE check_login SP GPASS CRLF
    939 	=	{
    940 #ifndef NO_PRIVATE
    941 	    if (log_commands)
    942 		syslog(LOG_INFO, "SITE GPASS");
    943 	    if (!restricted_user && $2)
    944 		priv_gpass(NULL);
    945 #endif /* !NO_PRIVATE */
    946 	}
    947     | SITE check_login SP NEWER SP STRING CRLF
    948 	=	{
    949 	    if (log_commands)
    950 		syslog(LOG_INFO, "SITE NEWER %s", $6);
    951 #ifdef SITE_NEWER
    952 	    if ($2 && $6 && !restrict_check("."))
    953 		newer($6, ".", 0);
    954 #else
    955 	    reply(502, "Command no longer honored by this server");
    956 #endif
    957 	    free($6);
    958 	}
    959     | SITE check_login SP NEWER SP STRING SP pathname CRLF
    960 	=	{
    961 	    if (log_commands)
    962 		syslog(LOG_INFO, "SITE NEWER %s %s", $6,
    963 		       CHECKNULL($8));
    964 #ifdef SITE_NEWER
    965 	    if ($2 && $6 && $8 && !restrict_check($8))
    966 		newer($6, $8, 0);
    967 #else
    968 	    reply(502, "Command no longer honored by this server");
    969 #endif
    970 	    free($6);
    971 	    if ($8)
    972 		free($8);
    973 	}
    974     | SITE check_login SP MINFO SP STRING CRLF
    975 	=	{
    976 	    if (log_commands)
    977 		syslog(LOG_INFO, "SITE MINFO %s", $6);
    978 #ifdef SITE_NEWER
    979 	    if ($2 && $6 && !restrict_check("."))
    980 		newer($6, ".", 1);
    981 #else
    982 	    reply(502, "Command no longer honored by this server");
    983 #endif
    984 	    free($6);
    985 	}
    986     | SITE check_login SP MINFO SP STRING SP pathname CRLF
    987 	=	{
    988 	    if (log_commands)
    989 		syslog(LOG_INFO, "SITE MINFO %s %s", $6,
    990 		       CHECKNULL($8));
    991 #ifdef SITE_NEWER
    992 	    if ($2 && $6 && $8 && !restrict_check($8))
    993 		newer($6, $8, 1);
    994 #else
    995 	    reply(502, "Command no longer honored by this server");
    996 #endif
    997 	    free($6);
    998 	    if ($8)
    999 		free($8);
   1000 	}
   1001     | SITE check_login SP INDEX SP STRING CRLF
   1002 	=	{
   1003 	    /* this is just for backward compatibility since we
   1004 	     * thought of INDEX before we thought of EXEC
   1005 	     */
   1006 	    if (!restricted_user && $2 != 0 && $6 != NULL) {
   1007 		char buf[MAXPATHLEN];
   1008 		if (strlen($6) + 7 <= sizeof(buf)) {
   1009 		    (void) snprintf(buf, sizeof(buf), "index %s", (char *) $6);
   1010 		    (void) site_exec(buf);
   1011 		}
   1012 	    }
   1013 	    if ($6 != NULL)
   1014 		free($6);
   1015 	}
   1016     | SITE check_login SP EXEC SP STRING CRLF
   1017 	=	{
   1018 	    if (!restricted_user && $2 != 0 && $6 != NULL) {
   1019 		(void) site_exec((char *) $6);
   1020 	    }
   1021 	    if ($6 != NULL)
   1022 		free($6);
   1023 	}
   1024 
   1025     | STOU check_login
   1026 	= 	{
   1027 	    char *default_filename = "ftp";
   1028 	    if (log_commands)
   1029 		syslog(LOG_INFO, "STOU");
   1030 	    if ($2 && !restrict_check(default_filename))
   1031 		store(default_filename, "w", 1);
   1032 	}
   1033     | STOU check_login SP pathname CRLF
   1034 	=	{
   1035 	    if (log_commands)
   1036 		syslog(LOG_INFO, "STOU %s", CHECKNULL($4));
   1037 	    if ($2 && $4 && !restrict_check($4))
   1038 		store($4, "w", 1);
   1039 	    if ($4 != NULL)
   1040 		free($4);
   1041 	}
   1042     | SYST check_login CRLF
   1043 	=	{
   1044 	    if (log_commands)
   1045 		syslog(LOG_INFO, "SYST");
   1046 	    if ($2)
   1047 #ifdef BSD
   1048 		reply(215, "UNIX Type: L%d Version: BSD-%d", NBBY, BSD);
   1049 #elif defined(SOLARIS_2)
   1050 		reply(215, "UNIX Type: L%d Version: SUNOS", NBBY);
   1051 #elif defined(unix) || defined(__unix__)
   1052 		reply(215, "UNIX Type: L%d", NBBY);
   1053 #else
   1054 		reply(215, "UNKNOWN Type: L%d", NBBY);
   1055 #endif /* BSD */
   1056 	}
   1057 
   1058 	/*
   1059 	 * SIZE is not in RFC959, but Postel has blessed it and
   1060 	 * it will be in the updated RFC.
   1061 	 *
   1062 	 * Return size of file in a format suitable for
   1063 	 * using with RESTART (we just count bytes).
   1064 	 */
   1065     | SIZE check_login SP pathname CRLF
   1066 	=	{
   1067 	    if (log_commands)
   1068 		syslog(LOG_INFO, "SIZE %s", CHECKNULL($4));
   1069 	    if ($2 && $4 && !restrict_check($4)) {
   1070 		sizecmd($4);
   1071 	    }
   1072 	    if ($4 != NULL)
   1073 		free($4);
   1074 	}
   1075 
   1076 	/*
   1077 	 * MDTM is not in RFC959, but Postel has blessed it and
   1078 	 * it will be in the updated RFC.
   1079 	 *
   1080 	 * Return modification time of file as an ISO 3307
   1081 	 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
   1082 	 * where xxx is the fractional second (of any precision,
   1083 	 * not necessarily 3 digits)
   1084 	 */
   1085     | MDTM check_login SP pathname CRLF
   1086 	=	{
   1087 	    if (log_commands)
   1088 		syslog(LOG_INFO, "MDTM %s", CHECKNULL($4));
   1089 	    if ($2 && $4 && !restrict_check($4)) {
   1090 		struct stat stbuf;
   1091 
   1092 		if (stat($4, &stbuf) < 0)
   1093 		    perror_reply(550, $4);
   1094 		else if ((stbuf.st_mode & S_IFMT) != S_IFREG) {
   1095 		    reply(550, "%s: not a plain file.",
   1096 			  $4);
   1097 		}
   1098 		else {
   1099 		    register struct tm *t;
   1100 		    t = gmtime(&stbuf.st_mtime);
   1101 		    reply(213,
   1102 			  "%04d%02d%02d%02d%02d%02d",
   1103 			  t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
   1104 			  t->tm_hour, t->tm_min, t->tm_sec);
   1105 		}
   1106 	    }
   1107 	    if ($4 != NULL)
   1108 		free($4);
   1109 	}
   1110     | QUIT CRLF
   1111 	=	{
   1112 	    if (log_commands)
   1113 		syslog(LOG_INFO, "QUIT");
   1114 #ifdef TRANSFER_COUNT
   1115 	    if (logged_in) {
   1116 		lreply(221, "You have transferred %" L_FORMAT " bytes in %d files.", data_count_total, file_count_total);
   1117 		lreply(221, "Total traffic for this session was %" L_FORMAT " bytes in %d transfers.", byte_count_total, xfer_count_total);
   1118 		lreply(221, "Thank you for using the FTP service on %s.", hostname);
   1119 	    }
   1120 #endif /* TRANSFER_COUNT */
   1121 	    reply(221, "Goodbye.");
   1122 	    dologout(0);
   1123 	}
   1124     | error CRLF
   1125 	=	{
   1126 	    yyerrok;
   1127 	}
   1128     ;
   1129 
   1130 rcmd: RNFR check_login SP pathname CRLF
   1131 	=	{
   1132 
   1133 	    if (log_commands)
   1134 		syslog(LOG_INFO, "RNFR %s", CHECKNULL($4));
   1135 	    if ($2)
   1136 		restart_point = 0;
   1137 	    if (fromname) {
   1138 		free(fromname);
   1139 		fromname = NULL;
   1140 	    }
   1141 	    if ($2 && $4 && !restrict_check($4)) {
   1142 		fromname = renamefrom($4);
   1143 	    }
   1144 	    if (fromname == NULL && $4)
   1145 		free($4);
   1146 	}
   1147     | REST check_login SP STRING CRLF
   1148 	=	{
   1149 	    if (log_commands)
   1150 		syslog(LOG_INFO, "REST %s", CHECKNULL($4));
   1151 	    if ($2 && $4 != NULL) {
   1152 		char *endp;
   1153 
   1154 		if (fromname) {
   1155 		    free(fromname);
   1156 		    fromname = NULL;
   1157 		}
   1158 		errno = 0;
   1159 #if _FILE_OFFSET_BITS == 64
   1160 		restart_point = strtoll($4, &endp, 10);
   1161 #else
   1162 		restart_point = strtol($4, &endp, 10);
   1163 #endif
   1164 		if ((errno == 0) && (restart_point >= 0) && (*endp == '\0')) {
   1165 		    reply(350, "Restarting at %" L_FORMAT
   1166 			  ". Send STORE or RETRIEVE to initiate transfer.",
   1167 			  restart_point);
   1168 		}
   1169 		else {
   1170 		    restart_point = 0;
   1171 		    reply(501, "Bad value for REST: %s", $4);
   1172 		}
   1173 	    }
   1174 	    if ($4 != NULL)
   1175 		free($4);
   1176 	}
   1177 
   1178     | SITE check_login SP ALIAS CRLF
   1179 	=	{
   1180 	    if (log_commands)
   1181 		syslog(LOG_INFO, "SITE ALIAS");
   1182 	    if ($2)
   1183 		alias((char *) NULL);
   1184 	}
   1185     | SITE check_login SP ALIAS SP STRING CRLF
   1186 	=	{
   1187 	    if (log_commands)
   1188 		syslog(LOG_INFO, "SITE ALIAS %s", $6);
   1189 	    if ($2)
   1190 		alias($6);
   1191 	    if ($6 != NULL)
   1192 		free($6);
   1193 	}
   1194     | SITE check_login SP GROUPS CRLF
   1195 	=	{
   1196 	    if (log_commands)
   1197 		syslog(LOG_INFO, "SITE GROUPS");
   1198 	    if ($2)
   1199 		print_groups();
   1200 	}
   1201     | SITE check_login SP CDPATH CRLF
   1202 	=	{
   1203 	    if (log_commands)
   1204 		syslog(LOG_INFO, "SITE CDPATH");
   1205 	    if ($2)
   1206 		cdpath();
   1207 	}
   1208     | SITE check_login SP CHECKMETHOD SP method CRLF
   1209 	=	{
   1210 	    if (log_commands)
   1211 		syslog(LOG_INFO, "SITE CHECKMETHOD %s", CHECKNULL($6));
   1212 	    if (($2) && ($6 != NULL))
   1213 		SetCheckMethod($6);
   1214 	    if ($6 != NULL)
   1215 		free($6);
   1216 	}
   1217     | SITE check_login SP CHECKMETHOD CRLF
   1218 	=	{
   1219 	    if (log_commands)
   1220 		syslog(LOG_INFO, "SITE CHECKMETHOD");
   1221 	    if ($2)
   1222 		ShowCheckMethod();
   1223 	}
   1224     | SITE check_login SP CHECKSUM SP pathname CRLF
   1225 	=	{
   1226 	    if (log_commands)
   1227 		syslog(LOG_INFO, "SITE CHECKSUM %s", CHECKNULL($6));
   1228 	    if (($2) && ($6 != NULL) && (!restrict_check($6)))
   1229 		CheckSum($6);
   1230 	    if ($6 != NULL)
   1231 		free($6);
   1232 	}
   1233     | SITE check_login SP CHECKSUM CRLF
   1234 	=	{
   1235 	    if (log_commands)
   1236 		syslog(LOG_INFO, "SITE CHECKSUM");
   1237 	    if ($2)
   1238 		CheckSumLastFile();
   1239 	}
   1240     | PBSZ SP STRING CRLF
   1241 	=	{
   1242 #if defined(USE_TLS) || defined(USE_GSS)
   1243 	    if (log_commands)
   1244 		syslog(LOG_INFO, "PBSZ %s", $3);
   1245 	    {
   1246 		int sz = 0;
   1247 #if defined(USE_GSS)
   1248 		sz = gss_setpbsz((char *)$3);
   1249 #else
   1250 		reply(200, "PBSZ=%d", sz);
   1251 #endif /* defined(USE_GSS) */
   1252 		pbsz_command_issued = 1;
   1253 	    }
   1254 #endif /* defined(USE_TLS) || defined(USE_GSS) */
   1255 	    if ($3 != NULL)
   1256 		free((char *)$3);
   1257 	}
   1258     | AUTH SP STRING CRLF
   1259 	=	{
   1260 #if defined(USE_TLS) || defined(USE_GSS)
   1261 	    register char *cp = (char *) $3;
   1262 	    if (log_commands)
   1263 		syslog(LOG_INFO, "AUTH %s", $3);
   1264 	    /* convert to UPPER case as per RFC 2228 */
   1265 	    while (*cp) {
   1266 		*cp = toupper(*cp);
   1267 		cp++;
   1268 	    }
   1269 #if defined(USE_GSS)
   1270 	    if (!strcmp((char *) $3, "GSSAPI")) {
   1271 		if (cur_auth_type != NULL) {
   1272 		    reply(534, "Authentication type already set to %s",
   1273 			cur_auth_type);
   1274 		    syslog(LOG_ERR, "Rejecting duplicate AUTH command");
   1275 		} else {
   1276 		    cur_auth_type = strdup((char *)$3);
   1277 		    reply(334, "Using AUTH type %s; ADAT must follow",
   1278 			cur_auth_type);
   1279 		}
   1280 	    } else
   1281 #endif /* defined(USE_GSS) */
   1282 	    {
   1283 		/*
   1284 		 * Previous auth_type did not work, clear the string.
   1285 		 */
   1286 		if (cur_auth_type != NULL) {
   1287 		    free(cur_auth_type);
   1288 		    cur_auth_type = NULL;
   1289 		}
   1290 		reply(504,"AUTH %s not supported.", $3);
   1291 	    }
   1292 #endif /* !(defined(USE_TLS)) && !defined(USE_GSS) */
   1293 	    if ($3 != NULL)
   1294 		free((char *)$3);
   1295 	}
   1296     |   PROT SP prot_code CRLF
   1297 	=	{
   1298 #if defined(USE_TLS) || defined(USE_GSS)
   1299 	    if (log_commands)
   1300 		syslog(LOG_INFO, "PROT %s", protnames[$3]);
   1301 	    {
   1302 		if (!pbsz_command_issued) {
   1303 		    reply(503, "PROT command not valid before PBSZ.");
   1304 		} else {
   1305 		    switch ($3) {
   1306 		    case PROT_P:
   1307 			reply(200, "PROT P ok.");
   1308 #if defined(USE_GSS)
   1309 			gss_info.data_prot = PROT_P;
   1310 #endif /* defined(USE_GSS) */
   1311 			break;
   1312 		    case PROT_C:
   1313 			reply(200, "PROT C ok.");
   1314 #if defined(USE_GSS)
   1315 			gss_info.data_prot = PROT_C;
   1316 #endif /* defined(USE_GSS) */
   1317 			break;
   1318 		    case PROT_E:
   1319 			reply(536, "PROT E unsupported");
   1320 			break;
   1321 		    case PROT_S:
   1322 #if defined(USE_GSS)
   1323 			reply(200, "PROT S ok.");
   1324 			gss_info.data_prot = PROT_S;
   1325 #endif /* defined(USE_GSS) */
   1326 			break;
   1327 		    default:
   1328 			reply(504, "Invalid PROT type.");
   1329 		    }
   1330 #if defined(USE_GSS)
   1331 		    gss_adjust_buflen();
   1332 #endif /* defined(USE_GSS) */
   1333 		}
   1334 	    }
   1335 #endif /* !(defined(USE_TLS) && !defined(USE_GSS)) */
   1336 	}
   1337     |   ADAT SP STRING CRLF
   1338 	=	{
   1339 #if defined(USE_GSS)
   1340 	    if (log_commands)
   1341 		syslog(LOG_INFO, "ADAT %s", $3);
   1342 	    if (cur_auth_type == NULL || strcmp(cur_auth_type, "GSSAPI")) {
   1343 		reply(503, "Must identify AUTH GSSAPI before sending ADAT");
   1344 	    } else
   1345 		    (void) gss_adat((char *)$3);
   1346 #endif
   1347 	    if ($3 != NULL)
   1348 		free((char *)$3);
   1349 	}
   1350     |   CCC CRLF
   1351 	=	{
   1352 #if defined(USE_GSS)
   1353 	    if (log_commands)
   1354 		syslog(LOG_INFO, "CCC");
   1355 	    ccc();
   1356 #endif /* defined(USE_GSS) */
   1357 	}
   1358     ;
   1359 
   1360 username: STRING
   1361     ;
   1362 
   1363 password: /* empty */
   1364 	=	{
   1365 	    $$ = (char *) malloc(1);
   1366 	    $$[0] = '\0';
   1367 	}
   1368     | STRING
   1369     ;
   1370 
   1371 byte_size: NUMBER
   1372     ;
   1373 
   1374 host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER
   1375 	=	{
   1376 	    register char *a, *p;
   1377 
   1378 	    a = (char *) &cliaddr;
   1379 	    a[0] = $1;
   1380 	    a[1] = $3;
   1381 	    a[2] = $5;
   1382 	    a[3] = $7;
   1383 	    p = (char *) &cliport;
   1384 	    p[0] = $9;
   1385 	    p[1] = $11;
   1386 	}
   1387     ;
   1388 
   1389 host_lport: NUMBER COMMA NUMBER COMMA
   1390 	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1391 	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1392 	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1393 	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1394 	NUMBER COMMA NUMBER COMMA NUMBER
   1395 	=	{
   1396 #ifdef INET6
   1397 	    char *a, *p;
   1398 	    struct sockaddr_in6 *data_dest_sin6;
   1399 
   1400 	    lport_error = 0;
   1401 	    if (epsv_all) {
   1402 		reply(501, "LPRT not allowed after EPSV ALL");
   1403 		lport_error = 1;
   1404 		goto lport_done6;
   1405 	    }
   1406 	    if ($1 != 6) {
   1407 		reply(521, "Supported address families are (4, 6)");
   1408 		lport_error = 1;
   1409 		goto lport_done6;
   1410 	    }
   1411 	    if (($3 != 16) || ($37 != 2)) {
   1412 		reply(501, "Bad length.");
   1413 		lport_error = 1;
   1414 		goto lport_done6;
   1415 	    }
   1416 	    memset(&data_dest, 0, sizeof(struct sockaddr_in6));
   1417 	    data_dest_sin6 = (struct sockaddr_in6 *) &data_dest;
   1418 	    data_dest_sin6->sin6_family = AF_INET6;
   1419 	    a = (char *)&data_dest_sin6->sin6_addr;
   1420 	    a[0]  = $5;  a[1]  = $7;  a[2]  = $9;   a[3] = $11;
   1421 	    a[4]  = $13; a[5]  = $15; a[6]  = $17;  a[7] = $19;
   1422 	    a[8]  = $21; a[9]  = $23; a[10] = $25; a[11] = $27;
   1423 	    a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
   1424 	    p = (char *)&data_dest_sin6->sin6_port;
   1425 	    p[0] = $39; p[1] = $41;
   1426 lport_done6:;
   1427 #endif /* INET6 */
   1428 	}
   1429     | NUMBER COMMA NUMBER COMMA
   1430 	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
   1431 	NUMBER COMMA NUMBER COMMA NUMBER
   1432 	=	{
   1433 #ifdef INET6
   1434 	    char *a, *p;
   1435 	    struct sockaddr_in *data_dest_sin;
   1436 
   1437 	    lport_error = 0;
   1438 	    if (epsv_all) {
   1439 		reply(501, "LPRT not allowed after EPSV ALL");
   1440 		lport_error = 1;
   1441 		goto lport_done4;
   1442 	    }
   1443 	    if ($1 != 4) {
   1444 		reply(521, "Supported address families are (4, 6)");
   1445 		lport_error = 1;
   1446 		goto lport_done4;
   1447 	    }
   1448 	    if (($3 != 4) || ($13 != 2)) {
   1449 		reply(501, "Bad length.");
   1450 		lport_error = 1;
   1451 		goto lport_done4;
   1452 	    }
   1453 	    data_dest_sin = (struct sockaddr_in *) &data_dest;
   1454 	    data_dest_sin->sin_family = AF_INET;
   1455 	    a = (char *)&data_dest_sin->sin_addr;
   1456 	    a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
   1457 	    p = (char *)&data_dest_sin->sin_port;
   1458 	    p[0] = $15; p[1] = $17;
   1459 lport_done4:;
   1460 #endif /* INET6 */
   1461 	}
   1462     ;
   1463 
   1464 form_code: N
   1465 	=	{
   1466 	    $$ = FORM_N;
   1467 	}
   1468     | T
   1469 	=	{
   1470 	    $$ = FORM_T;
   1471 	}
   1472     | C
   1473 	=	{
   1474 	    $$ = FORM_C;
   1475 	}
   1476     ;
   1477 
   1478 type_code: A
   1479 	=	{
   1480 	    cmd_type = TYPE_A;
   1481 	    cmd_form = FORM_N;
   1482 	}
   1483     | A SP form_code
   1484 	=	{
   1485 	    cmd_type = TYPE_A;
   1486 	    cmd_form = $3;
   1487 	}
   1488     | E
   1489 	=	{
   1490 	    cmd_type = TYPE_E;
   1491 	    cmd_form = FORM_N;
   1492 	}
   1493     | E SP form_code
   1494 	=	{
   1495 	    cmd_type = TYPE_E;
   1496 	    cmd_form = $3;
   1497 	}
   1498     | I
   1499 	=	{
   1500 	    cmd_type = TYPE_I;
   1501 	}
   1502     | L
   1503 	=	{
   1504 	    cmd_type = TYPE_L;
   1505 	    cmd_bytesz = NBBY;
   1506 	}
   1507     | L SP byte_size
   1508 	=	{
   1509 	    cmd_type = TYPE_L;
   1510 	    cmd_bytesz = $3;
   1511 	}
   1512     /* this is for a bug in the BBN ftp */
   1513     | L byte_size
   1514 	=	{
   1515 	    cmd_type = TYPE_L;
   1516 	    cmd_bytesz = $2;
   1517 	}
   1518     ;
   1519 
   1520 prot_code: C
   1521 	=	{
   1522 #if defined(USE_GSS)
   1523 	    $$ = PROT_C;
   1524 #endif
   1525 	}
   1526     | P
   1527 	=	{
   1528 #if defined(USE_GSS)
   1529 	    $$ = PROT_P;
   1530 #endif
   1531 	}
   1532     | S
   1533 	=	{
   1534 #if defined(USE_GSS)
   1535 	    $$ = PROT_S;
   1536 #endif
   1537 	}
   1538     | E
   1539 	=	{
   1540 #if defined(USE_GSS)
   1541 	    $$ = PROT_E;
   1542 #endif
   1543 	}
   1544     ;
   1545 
   1546 struct_code: F
   1547 	=	{
   1548 	    $$ = STRU_F;
   1549 	}
   1550     | R
   1551 	=	{
   1552 	    $$ = STRU_R;
   1553 	}
   1554     | P
   1555 	=	{
   1556 	    $$ = STRU_P;
   1557 	}
   1558     ;
   1559 
   1560 mode_code:  S
   1561 	=	{
   1562 	    $$ = MODE_S;
   1563 	}
   1564     | B
   1565 	=	{
   1566 	    $$ = MODE_B;
   1567 	}
   1568     | C
   1569 	=	{
   1570 	    $$ = MODE_C;
   1571 	}
   1572     ;
   1573 
   1574 pathname: pathstring
   1575 	=	{
   1576 	    /*
   1577 	     * Problem: this production is used for all pathname
   1578 	     * processing, but only gives a 550 error reply.
   1579 	     * This is a valid reply in some cases but not in others.
   1580 	     */
   1581 	    if (restricted_user && logged_in && $1 && strncmp($1, "/", 1) == 0) {
   1582 		/*
   1583 		 * This remaps the root so it is appearently at the user's home
   1584 		 * rather than the real root/chroot.
   1585 		 */
   1586 		size_t len = strlen($1) + 2;
   1587 		char **globlist;
   1588 		char *t = calloc(len, sizeof(char));
   1589 		if (t == NULL) {
   1590 		    errno = EAGAIN;
   1591 		    perror_reply(550, $1);
   1592 		    $$ = NULL;
   1593 		}
   1594 		else {
   1595 		    t[0] = '~';
   1596 		    t[1] = '\0';
   1597 		    if (strncmp($1, "/../", 4) == 0)
   1598 			(void) strlcat(t, $1 + 3, len);
   1599 		    else if (strcmp($1, "/..") != 0)
   1600 			(void) strlcat(t, $1, len);
   1601 		    globlist = ftpglob(t);
   1602 		    if (globerr) {
   1603 			reply(550, "%s", globerr);
   1604 			$$ = NULL;
   1605 			if (globlist) {
   1606 			    blkfree(globlist);
   1607 			    free((char *) globlist);
   1608 			}
   1609 		    }
   1610 		    else if (globlist && *globlist) {
   1611 			$$ = *globlist;
   1612 			blkfree(&globlist[1]);
   1613 			free((char *) globlist);
   1614 		    }
   1615 		    else {
   1616 			if (globlist) {
   1617 			    blkfree(globlist);
   1618 			    free((char *) globlist);
   1619 			}
   1620 			errno = ENOENT;
   1621 			perror_reply(550, $1);
   1622 			$$ = NULL;
   1623 		    }
   1624 		    free(t);
   1625 		}
   1626 		free($1);
   1627 	    }
   1628 	    else if (logged_in && $1 && strncmp($1, "~", 1) == 0) {
   1629 		char **globlist;
   1630 
   1631 		globlist = ftpglob($1);
   1632 		if (globerr) {
   1633 		    reply(550, "%s", globerr);
   1634 		    $$ = NULL;
   1635 		    if (globlist) {
   1636 			blkfree(globlist);
   1637 			free((char *) globlist);
   1638 		    }
   1639 		}
   1640 		else if (globlist && *globlist) {
   1641 		    $$ = *globlist;
   1642 		    blkfree(&globlist[1]);
   1643 		    free((char *) globlist);
   1644 		}
   1645 		else {
   1646 		    if (globlist) {
   1647 			blkfree(globlist);
   1648 			free((char *) globlist);
   1649 		    }
   1650 		    errno = ENOENT;
   1651 		    perror_reply(550, $1);
   1652 		    $$ = NULL;
   1653 		}
   1654 		free($1);
   1655 	    }
   1656 	    else
   1657 		$$ = $1;
   1658 	}
   1659     ;
   1660 
   1661 pathstring: STRING
   1662     ;
   1663 
   1664 method: STRING
   1665     ;
   1666 
   1667 octal_number: NUMBER
   1668 	=	{
   1669 	    register int ret, dec, multby, digit;
   1670 
   1671 	    /*
   1672 	     * Convert a number that was read as decimal number
   1673 	     * to what it would be if it had been read as octal.
   1674 	     */
   1675 	    dec = $1;
   1676 	    multby = 1;
   1677 	    ret = 0;
   1678 	    while (dec) {
   1679 		digit = dec % 10;
   1680 		if (digit > 7) {
   1681 		    ret = -1;
   1682 		    break;
   1683 		}
   1684 		ret += digit * multby;
   1685 		multby *= 8;
   1686 		dec /= 10;
   1687 	    }
   1688 	    $$ = ret;
   1689 	}
   1690     ;
   1691 
   1692 check_login: /* empty */
   1693 	=	{
   1694 	    if (logged_in)
   1695 		$$ = 1;
   1696 	    else {
   1697 		if (log_commands)
   1698 		    syslog(LOG_INFO, "cmd failure - not logged in");
   1699 		reply(530, "Please login with USER and PASS.");
   1700 		$$ = 0;
   1701 		yyerrorcalled = 1;
   1702 	    }
   1703 	}
   1704     ;
   1705 
   1706 %%
   1707 
   1708 extern jmp_buf errcatch;
   1709 
   1710 #define CMD 0			/* beginning of command */
   1711 #define ARGS    1		/* expect miscellaneous arguments */
   1712 #define STR1    2		/* expect SP followed by STRING */
   1713 #define STR2    3		/* expect STRING */
   1714 #define OSTR    4		/* optional SP then STRING */
   1715 #define ZSTR1   5		/* SP then optional STRING */
   1716 #define ZSTR2   6		/* optional STRING after SP */
   1717 #define SITECMD 7		/* SITE command */
   1718 #define NSTR    8		/* Number followed by a string */
   1719 #define STR3    9		/* expect STRING followed by optional SP then STRING */
   1720 
   1721 struct tab cmdtab[] =
   1722 {				/* In order defined in RFC 765 */
   1723     {"USER", USER, STR1, 1, "<sp> username"},
   1724     {"PASS", PASS, ZSTR1, 1, "<sp> password"},
   1725     {"ACCT", ACCT, STR1, 0, "(specify account)"},
   1726     {"SMNT", SMNT, ARGS, 0, "(structure mount)"},
   1727     {"REIN", REIN, ARGS, 0, "(reinitialize server state)"},
   1728     {"QUIT", QUIT, ARGS, 1, "(terminate service)",},
   1729     {"PORT", PORT, ARGS, 1, "<sp> h1, h2, h3, h4, p1, p2"},
   1730     {"PASV", PASV, ARGS, 1, "(set server in passive mode)"},
   1731 #ifdef INET6
   1732     {"EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|"},
   1733     {"EPSV", EPSV, OSTR, 1, "[<sp> af|ALL]"},
   1734     {"LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, ..., pal, p1, p2, ..."},
   1735     {"LPSV", LPSV, ARGS, 1, "(set server in long passive mode)"},
   1736 #endif
   1737     {"TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]"},
   1738     {"STRU", STRU, ARGS, 1, "(specify file structure)"},
   1739     {"MODE", MODE, ARGS, 1, "(specify transfer mode)"},
   1740     {"RETR", RETR, STR1, 1, "<sp> file-name"},
   1741     {"STOR", STOR, STR1, 1, "<sp> file-name"},
   1742     {"APPE", APPE, STR1, 1, "<sp> file-name"},
   1743     {"MLFL", MLFL, OSTR, 0, "(mail file)"},
   1744     {"MAIL", MAIL, OSTR, 0, "(mail to user)"},
   1745     {"MSND", MSND, OSTR, 0, "(mail send to terminal)"},
   1746     {"MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)"},
   1747     {"MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)"},
   1748     {"MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)"},
   1749     {"MRCP", MRCP, STR1, 0, "(mail recipient)"},
   1750     {"ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)"},
   1751     {"REST", REST, STR1, 1, "(restart command)"},
   1752     {"RNFR", RNFR, STR1, 1, "<sp> file-name"},
   1753     {"RNTO", RNTO, STR1, 1, "<sp> file-name"},
   1754     {"ABOR", ABOR, ARGS, 1, "(abort operation)"},
   1755     {"DELE", DELE, STR1, 1, "<sp> file-name"},
   1756     {"CWD", CWD, OSTR, 1, "[ <sp> directory-name ]"},
   1757     {"XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]"},
   1758     {"LIST", LIST, OSTR, 1, "[ <sp> path-name ]"},
   1759     {"NLST", NLST, OSTR, 1, "[ <sp> path-name ]"},
   1760     {"SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]"},
   1761     {"SYST", SYST, ARGS, 1, "(get type of operating system)"},
   1762     {"STAT", STAT, OSTR, 1, "[ <sp> path-name ]"},
   1763     {"HELP", HELP, OSTR, 1, "[ <sp> <string> ]"},
   1764     {"NOOP", NOOP, ARGS, 1, ""},
   1765     {"MKD", MKD, STR1, 1, "<sp> path-name"},
   1766     {"XMKD", MKD, STR1, 1, "<sp> path-name"},
   1767     {"RMD", RMD, STR1, 1, "<sp> path-name"},
   1768     {"XRMD", RMD, STR1, 1, "<sp> path-name"},
   1769     {"PWD", PWD, ARGS, 1, "(return current directory)"},
   1770     {"XPWD", PWD, ARGS, 1, "(return current directory)"},
   1771     {"CDUP", CDUP, ARGS, 1, "(change to parent directory)"},
   1772     {"XCUP", CDUP, ARGS, 1, "(change to parent directory)"},
   1773     {"STOU", STOU, OSTR, 1, "[ <sp> file-name ]"},
   1774     {"SIZE", SIZE, OSTR, 1, "<sp> path-name"},
   1775     {"MDTM", MDTM, OSTR, 1, "<sp> path-name"},
   1776 #if defined(USE_TLS) || defined(USE_GSS)
   1777     {"PROT", PROT, ARGS, 1, "<sp> protection-level"},
   1778     {"PBSZ", PBSZ, STR1, 1, "<sp> protection-buffer-size"},
   1779     {"AUTH", AUTH, STR1, 1, "<sp> authentication-mechanism"},
   1780     {"ADAT", ADAT, STR1, 1, "<sp> authentication-data"},
   1781 #if defined(USE_GSS)
   1782     {"CCC",  CCC,  ARGS, 1, "(clear command channel)"},
   1783 #endif
   1784 #endif /* defined(USE_TLS) || defined(USE_GSS) */
   1785     {NULL, 0, 0, 0, 0}
   1786 };
   1787 
   1788 struct tab sitetab[] =
   1789 {
   1790     {"UMASK", UMASK, ARGS, 1, "[ <sp> umask ]"},
   1791     {"IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]"},
   1792     {"CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name"},
   1793     {"HELP", HELP, OSTR, 1, "[ <sp> <string> ]"},
   1794     {"GROUP", GROUP, STR1, 1, "<sp> access-group"},
   1795     {"GPASS", GPASS, OSTR, 1, "<sp> access-password"},
   1796     {"NEWER", NEWER, STR3, 1, "<sp> YYYYMMDDHHMMSS [ <sp> path-name ]"},
   1797     {"MINFO", MINFO, STR3, 1, "<sp> YYYYMMDDHHMMSS [ <sp> path-name ]"},
   1798     {"INDEX", INDEX, STR1, 1, "<sp> pattern"},
   1799     {"EXEC", EXEC, STR1, 1, "<sp> command [ <sp> arguments ]"},
   1800     {"ALIAS", ALIAS, OSTR, 1, "[ <sp> alias ] "},
   1801     {"CDPATH", CDPATH, OSTR, 1, "[ <sp> ] "},
   1802     {"GROUPS", GROUPS, OSTR, 1, "[ <sp> ] "},
   1803     {"CHECKMETHOD", CHECKMETHOD, OSTR, 1, "[ <sp> crc|md5 ]"},
   1804     {"CHECKSUM", CHECKSUM, OSTR, 1, "[ <sp> file-name ]"},
   1805     {NULL, 0, 0, 0, 0}
   1806 };
   1807 
   1808 struct tab *lookup(register struct tab *p, char *cmd)
   1809 {
   1810     for (; p->name != NULL; p++)
   1811 	if (strcmp(cmd, p->name) == 0)
   1812 	    return (p);
   1813     return (0);
   1814 }
   1815 
   1816 #include <arpa/telnet.h>
   1817 
   1818 /*
   1819  * getline - a hacked up version of fgets to ignore TELNET escape codes.
   1820  */
   1821 char *wu_getline(char *s, int n, register FILE *iop)
   1822 {
   1823     register int c;
   1824     register char *cs;
   1825     char *passtxt = "PASS password\r\n";
   1826 
   1827     cs = s;
   1828 /* tmpline may contain saved command from urgent mode interruption */
   1829     for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
   1830 	*cs++ = tmpline[c];
   1831 	if (tmpline[c] == '\n') {
   1832 	    *cs++ = '\0';
   1833 	    if (debug) {
   1834 		if (strncasecmp(passtxt, s, 5) == 0)
   1835 		    syslog(LOG_DEBUG, "command: %s", passtxt);
   1836 		else
   1837 		    syslog(LOG_DEBUG, "command: %s", s);
   1838 	    }
   1839 	    tmpline[0] = '\0';
   1840 	    return (s);
   1841 	}
   1842 	if (c == 0)
   1843 	    tmpline[0] = '\0';
   1844     }
   1845   retry:
   1846     while ((c = getc(iop)) != EOF) {
   1847 #ifdef TRANSFER_COUNT
   1848 	byte_count_total++;
   1849 	byte_count_in++;
   1850 #endif
   1851 	c &= 0377;
   1852 	if (c == IAC) {
   1853 	    if ((c = getc(iop)) != EOF) {
   1854 #ifdef TRANSFER_COUNT
   1855 		byte_count_total++;
   1856 		byte_count_in++;
   1857 #endif
   1858 		c &= 0377;
   1859 		switch (c) {
   1860 		case WILL:
   1861 		case WONT:
   1862 		    c = getc(iop);
   1863 #ifdef TRANSFER_COUNT
   1864 		    byte_count_total++;
   1865 		    byte_count_in++;
   1866 #endif
   1867 		    printf("%c%c%c", IAC, DONT, 0377 & c);
   1868 		    (void) fflush(stdout);
   1869 		    continue;
   1870 		case DO:
   1871 		case DONT:
   1872 		    c = getc(iop);
   1873 #ifdef TRANSFER_COUNT
   1874 		    byte_count_total++;
   1875 		    byte_count_in++;
   1876 #endif
   1877 		    printf("%c%c%c", IAC, WONT, 0377 & c);
   1878 		    (void) fflush(stdout);
   1879 		    continue;
   1880 		case IAC:
   1881 		    break;
   1882 		default:
   1883 		    continue;	/* ignore command */
   1884 		}
   1885 	    }
   1886 	}
   1887 	*cs++ = c;
   1888 	if (--n <= 0 || c == '\n')
   1889 	    break;
   1890     }
   1891 
   1892     if (c == EOF && cs == s) {
   1893 	if (ferror(iop) && (errno == EINTR))
   1894 	    goto retry;
   1895 	return (NULL);
   1896     }
   1897 
   1898     *cs++ = '\0';
   1899 
   1900 #if defined(USE_GSS)
   1901     if (IS_GSSAUTH(cur_auth_type) &&
   1902 	(gss_info.authstate & GSS_ADAT_DONE) &&
   1903 	gss_info.context != GSS_C_NO_CONTEXT) {
   1904 	s = sec_decode_command(s);
   1905     } else if (IS_GSSAUTH(cur_auth_type) &&
   1906 	(!strncmp(s, "ENC", 3) || !strncmp(s, "MIC", 3) ||
   1907 	!strncmp(s, "CONF", 4)) &&
   1908 	!(gss_info.authstate & GSS_ADAT_DONE)) {
   1909 	if (debug)
   1910 	    syslog(LOG_DEBUG, "command: %s", s);
   1911 	reply(503, "Must perform authentication before sending protected commands");
   1912 	*s = '\0';
   1913 	return(s);
   1914     }
   1915 #endif /* USE_GSS */
   1916     if (debug) {
   1917 	if (strncasecmp(passtxt, s, 5) == 0)
   1918 	    syslog(LOG_DEBUG, "command: %s", passtxt);
   1919 	else
   1920 	    syslog(LOG_DEBUG, "command: %s", s);
   1921     }
   1922     return (s);
   1923 }
   1924 
   1925 static void toolong(int a) /* signal that caused this function to be called */
   1926 {
   1927     time_t now;
   1928 
   1929     reply(421,
   1930 	  "Timeout (%d seconds): closing control connection.", timeout_idle);
   1931     (void) time(&now);
   1932     if (logging) {
   1933 	syslog(LOG_INFO,
   1934 	       "User %s timed out after %d seconds at %.24s",
   1935 	       (pw ? pw->pw_name : "unknown"), timeout_idle, ctime(&now));
   1936     }
   1937     dologout(1);
   1938 }
   1939 
   1940 int yylex(void)
   1941 {
   1942     static int cpos, state;
   1943     register char *cp, *cp2;
   1944     register struct tab *p;
   1945     int n;
   1946     time_t now;
   1947     char c = '\0';
   1948     extern time_t limit_time;
   1949     extern time_t login_time;
   1950 
   1951     for (;;) {
   1952 	switch (state) {
   1953 
   1954 	case CMD:
   1955 	    yyerrorcalled = 0;
   1956 
   1957 	    setproctitle("%s: IDLE", proctitle);
   1958 
   1959 	    if (is_shutdown(!logged_in, 0) != 0) {
   1960 		reply(221, "Server shutting down.  Goodbye.");
   1961 		dologout(0);
   1962 	    }
   1963 
   1964 	    time(&now);
   1965 	    if ((limit_time > 0) && (((now - login_time) / 60) >= limit_time)) {
   1966 		reply(221, "Time limit reached.  Goodbye.");
   1967 		dologout(0);
   1968 	    }
   1969 
   1970 #ifdef IGNORE_NOOP
   1971 	    if (!alarm_running) {
   1972 		(void) signal(SIGALRM, toolong);
   1973 		(void) alarm((unsigned) timeout_idle);
   1974 		alarm_running = 1;
   1975 	    }
   1976 #else
   1977 	    (void) signal(SIGALRM, toolong);
   1978 	    (void) alarm((unsigned) timeout_idle);
   1979 #endif
   1980 	    if (wu_getline(cbuf, sizeof(cbuf) - 1, stdin) == NULL) {
   1981 		(void) alarm(0);
   1982 		reply(221, "You could at least say goodbye.");
   1983 		dologout(0);
   1984 	    }
   1985 #ifndef IGNORE_NOOP
   1986 	    (void) alarm(0);
   1987 #endif
   1988 	    if ((cp = strchr(cbuf, '\r'))) {
   1989 		*cp++ = '\n';
   1990 		*cp = '\0';
   1991 	    }
   1992 	    if ((cp = strpbrk(cbuf, " \n")))
   1993 		cpos = cp - cbuf;
   1994 	    if (cpos == 0)
   1995 		cpos = 4;
   1996 	    c = cbuf[cpos];
   1997 	    cbuf[cpos] = '\0';
   1998 	    upper(cbuf);
   1999 #ifdef IGNORE_NOOP
   2000 	    if (strncasecmp(cbuf, "NOOP", 4) != 0) {
   2001 		(void) alarm(0);
   2002 		alarm_running = 0;
   2003 	    }
   2004 #endif
   2005 	    p = lookup(cmdtab, cbuf);
   2006 	    cbuf[cpos] = c;
   2007 	    if (strncasecmp(cbuf, "PASS", 4) != 0 &&
   2008 		strncasecmp(cbuf, "SITE GPASS", 10) != 0) {
   2009 		if ((cp = strchr(cbuf, '\n')))
   2010 		    *cp = '\0';
   2011 		setproctitle("%s: %s", proctitle, cbuf);
   2012 		if (cp)
   2013 		    *cp = '\n';
   2014 	    }
   2015 	    if (p != 0) {
   2016 		if (p->implemented == 0) {
   2017 		    nack(p->name);
   2018 		    longjmp(errcatch, 0);
   2019 		    /* NOTREACHED */
   2020 		}
   2021 		state = p->state;
   2022 		yylval.String = p->name;
   2023 		return (p->token);
   2024 	    }
   2025 	    break;
   2026 
   2027 	case SITECMD:
   2028 	    if (cbuf[cpos] == ' ') {
   2029 		cpos++;
   2030 		return (SP);
   2031 	    }
   2032 	    cp = &cbuf[cpos];
   2033 	    if ((cp2 = strpbrk(cp, " \n")))
   2034 		cpos = cp2 - cbuf;
   2035 	    c = cbuf[cpos];
   2036 	    cbuf[cpos] = '\0';
   2037 	    upper(cp);
   2038 	    p = lookup(sitetab, cp);
   2039 	    cbuf[cpos] = c;
   2040 	    if (p != 0) {
   2041 #ifndef PARANOID		/* what GOOD is SITE *, anyways?!  _H */
   2042 		if (p->implemented == 0) {
   2043 #else
   2044 		if (1) {
   2045 		    syslog(LOG_WARNING, "refused SITE %s %s from %s of %s",
   2046 			   p->name, &cbuf[cpos],
   2047 			   anonymous ? guestpw : authuser, remoteident);
   2048 #endif /* PARANOID */
   2049 		    state = CMD;
   2050 		    nack(p->name);
   2051 		    longjmp(errcatch, 0);
   2052 		    /* NOTREACHED */
   2053 		}
   2054 		state = p->state;
   2055 		yylval.String = p->name;
   2056 		return (p->token);
   2057 	    }
   2058 	    state = CMD;
   2059 	    break;
   2060 
   2061 	case OSTR:
   2062 	    if (cbuf[cpos] == '\n') {
   2063 		state = CMD;
   2064 		return (CRLF);
   2065 	    }
   2066 	    /* FALLTHROUGH */
   2067 
   2068 	case STR1:
   2069 	case ZSTR1:
   2070 	  dostr1:
   2071 	    if (cbuf[cpos] == ' ') {
   2072 		cpos++;
   2073 		if (state == OSTR)
   2074 		    state = STR2;
   2075 		else
   2076 		    ++state;
   2077 		return (SP);
   2078 	    }
   2079 	    break;
   2080 
   2081 	case ZSTR2:
   2082 	    if (cbuf[cpos] == '\n') {
   2083 		state = CMD;
   2084 		return (CRLF);
   2085 	    }
   2086 	    /* FALLTHROUGH */
   2087 
   2088 	case STR2:
   2089 	    cp = &cbuf[cpos];
   2090 	    n = strlen(cp);
   2091 	    cpos += n - 1;
   2092 	    /*
   2093 	     * Make sure the string is nonempty and \n terminated.
   2094 	     */
   2095 	    if (n > 1 && cbuf[cpos] == '\n') {
   2096 		cbuf[cpos] = '\0';
   2097 		yylval.String = copy(cp);
   2098 		cbuf[cpos] = '\n';
   2099 		state = ARGS;
   2100 		return (STRING);
   2101 	    }
   2102 	    break;
   2103 
   2104 	case NSTR:
   2105 	    if (cbuf[cpos] == ' ') {
   2106 		cpos++;
   2107 		return (SP);
   2108 	    }
   2109 	    if (isdigit(cbuf[cpos])) {
   2110 		cp = &cbuf[cpos];
   2111 		while (isdigit(cbuf[++cpos]));
   2112 		c = cbuf[cpos];
   2113 		cbuf[cpos] = '\0';
   2114 		yylval.Number = atoi(cp);
   2115 		cbuf[cpos] = c;
   2116 		state = STR1;
   2117 		return (NUMBER);
   2118 	    }
   2119 	    state = STR1;
   2120 	    goto dostr1;
   2121 
   2122 	case STR3:
   2123 	    if (cbuf[cpos] == ' ') {
   2124 		cpos++;
   2125 		return (SP);
   2126 	    }
   2127 
   2128 	    cp = &cbuf[cpos];
   2129 	    cp2 = strpbrk(cp, " \n");
   2130 	    if (cp2 != NULL) {
   2131 		c = *cp2;
   2132 		*cp2 = '\0';
   2133 	    }
   2134 	    n = strlen(cp);
   2135 	    cpos += n;
   2136 	    /*
   2137 	     * Make sure the string is nonempty and SP terminated.
   2138 	     */
   2139 	    if ((cp2 - cp) > 1) {
   2140 		yylval.String = copy(cp);
   2141 		cbuf[cpos] = c;
   2142 		state = OSTR;
   2143 		return (STRING);
   2144 	    }
   2145 	    break;
   2146 
   2147 	case ARGS:
   2148 	    if (isdigit(cbuf[cpos])) {
   2149 		cp = &cbuf[cpos];
   2150 		while (isdigit(cbuf[++cpos]));
   2151 		c = cbuf[cpos];
   2152 		cbuf[cpos] = '\0';
   2153 		yylval.Number = atoi(cp);
   2154 		cbuf[cpos] = c;
   2155 		return (NUMBER);
   2156 	    }
   2157 	    switch (cbuf[cpos++]) {
   2158 
   2159 	    case '\n':
   2160 		state = CMD;
   2161 		return (CRLF);
   2162 
   2163 	    case ' ':
   2164 		return (SP);
   2165 
   2166 	    case ',':
   2167 		return (COMMA);
   2168 
   2169 	    case 'A':
   2170 	    case 'a':
   2171 		return (A);
   2172 
   2173 	    case 'B':
   2174 	    case 'b':
   2175 		return (B);
   2176 
   2177 	    case 'C':
   2178 	    case 'c':
   2179 		return (C);
   2180 
   2181 	    case 'E':
   2182 	    case 'e':
   2183 		return (E);
   2184 
   2185 	    case 'F':
   2186 	    case 'f':
   2187 		return (F);
   2188 
   2189 	    case 'I':
   2190 	    case 'i':
   2191 		return (I);
   2192 
   2193 	    case 'L':
   2194 	    case 'l':
   2195 		return (L);
   2196 
   2197 	    case 'N':
   2198 	    case 'n':
   2199 		return (N);
   2200 
   2201 	    case 'P':
   2202 	    case 'p':
   2203 		return (P);
   2204 
   2205 	    case 'R':
   2206 	    case 'r':
   2207 		return (R);
   2208 
   2209 	    case 'S':
   2210 	    case 's':
   2211 		return (S);
   2212 
   2213 	    case 'T':
   2214 	    case 't':
   2215 		return (T);
   2216 
   2217 	    }
   2218 	    break;
   2219 
   2220 	default:
   2221 	    fatal("Unknown state in scanner.");
   2222 	}
   2223 	if (yyerrorcalled == 0) {
   2224 	    if ((cp = strchr(cbuf, '\n')) != NULL)
   2225 		*cp = '\0';
   2226 	    if (logged_in)
   2227 		reply(500, "'%s': command not understood.", cbuf);
   2228 	    else
   2229 		reply(530, "Please login with USER and PASS.");
   2230 	}
   2231 	state = CMD;
   2232 	longjmp(errcatch, 0);
   2233     }
   2234 }
   2235 
   2236 void upper(char *s)
   2237 {
   2238     while (*s != '\0') {
   2239 	if (islower(*s))
   2240 	    *s = toupper(*s);
   2241 	s++;
   2242     }
   2243 }
   2244 
   2245 char *copy(char *s)
   2246 {
   2247     char *p;
   2248 
   2249     p = strdup(s);
   2250     if (p == NULL)
   2251 	fatal("Ran out of memory.");
   2252     return (p);
   2253 }
   2254 
   2255 void help(struct tab *ctab, char *s)
   2256 {
   2257     struct aclmember *entry = NULL;
   2258     struct tab *c;
   2259     size_t width, NCMDS;
   2260     char *type;
   2261 
   2262     if (ctab == sitetab)
   2263 	type = "SITE ";
   2264     else
   2265 	type = "";
   2266     width = 0, NCMDS = 0;
   2267     for (c = ctab; c->name != NULL; c++) {
   2268 	size_t len = strlen(c->name);
   2269 
   2270 	if (len > width)
   2271 	    width = len;
   2272 	NCMDS++;
   2273     }
   2274     width = (width + 8) & ~7;
   2275     if (s == 0) {
   2276 	register size_t i, j, w;
   2277 	size_t columns, lines;
   2278 
   2279 	lreply(214, "The following %scommands are recognized %s.",
   2280 	       type, "(* =>'s unimplemented)");
   2281 	columns = 76 / width;
   2282 	if (columns == 0)
   2283 	    columns = 1;
   2284 	lines = (NCMDS + columns - 1) / columns;
   2285 	for (i = 0; i < lines; i++) {
   2286 	    char line[BUFSIZ], *ptr = line;
   2287 	    ptr += strlcpy(line, "   ", sizeof(line));
   2288 	    for (j = 0; j < columns; j++) {
   2289 		c = ctab + j * lines + i;
   2290 		(void) snprintf(ptr, line + sizeof(line) - ptr, "%s%c",
   2291 				c->name, c->implemented ? ' ' : '*');
   2292 		w = strlen(c->name) + 1;
   2293 		ptr += w;
   2294 		if (c + lines >= &ctab[NCMDS])
   2295 		    break;
   2296 		while (w < width) {
   2297 		    *(ptr++) = ' ';
   2298 		    w++;
   2299 		}
   2300 	    }
   2301 	    *ptr = '\0';
   2302 	    lreply(0, "%s", line);
   2303 	}
   2304 	(void) fflush(stdout);
   2305 #ifdef VIRTUAL
   2306 	if (virtual_mode && !virtual_ftpaccess && virtual_email[0] != '\0')
   2307 	    reply(214, "Direct comments to %s.", virtual_email);
   2308 	else
   2309 #endif
   2310 	if ((getaclentry("email", &entry)) && ARG0)
   2311 	    reply(214, "Direct comments to %s.", ARG0);
   2312 	else
   2313 	    reply(214, "Direct comments to ftp-bugs@%s.", hostname);
   2314 	return;
   2315     }
   2316     upper(s);
   2317     c = lookup(ctab, s);
   2318     if (c == (struct tab *) NULL) {
   2319 	reply(502, "Unknown command %s.", s);
   2320 	return;
   2321     }
   2322     if (c->implemented)
   2323 	reply(214, "Syntax: %s%s %s", type, c->name, c->help);
   2324     else
   2325 	reply(214, "%s%-*s\t%s; unimplemented.", type, width,
   2326 	      c->name, c->help);
   2327 }
   2328 
   2329 void sizecmd(char *filename)
   2330 {
   2331     switch (type) {
   2332     case TYPE_L:
   2333     case TYPE_I:{
   2334 	    struct stat stbuf;
   2335 	    if (stat(filename, &stbuf) < 0 ||
   2336 		(stbuf.st_mode & S_IFMT) != S_IFREG)
   2337 		reply(550, "%s: not a plain file.", filename);
   2338 	    else
   2339 		reply(213, "%" L_FORMAT, stbuf.st_size);
   2340 	    break;
   2341 	}
   2342     case TYPE_A:{
   2343 	    FILE *fin;
   2344 	    register int c;
   2345 	    register off_t count;
   2346 	    struct stat stbuf;
   2347 	    fin = fopen(filename, "r");
   2348 	    if (fin == NULL) {
   2349 		perror_reply(550, filename);
   2350 		return;
   2351 	    }
   2352 	    if (fstat(fileno(fin), &stbuf) < 0 ||
   2353 		(stbuf.st_mode & S_IFMT) != S_IFREG) {
   2354 		reply(550, "%s: not a plain file.", filename);
   2355 		(void) fclose(fin);
   2356 		return;
   2357 	    }
   2358 
   2359 	    count = 0;
   2360 	    while ((c = getc(fin)) != EOF) {
   2361 		if (c == '\n')	/* will get expanded to \r\n */
   2362 		    count++;
   2363 		count++;
   2364 	    }
   2365 	    (void) fclose(fin);
   2366 
   2367 	    reply(213, "%" L_FORMAT, count);
   2368 	    break;
   2369 	}
   2370     default:
   2371 	reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
   2372     }
   2373 }
   2374 
   2375 void site_exec(char *cmd)
   2376 {
   2377 #ifdef PARANOID
   2378     syslog(LOG_CRIT, "REFUSED SITE_EXEC (slipped through!!): %s", cmd);
   2379 #else
   2380     char buf[MAXPATHLEN];
   2381     char *sp = (char *) strchr(cmd, ' '), *slash, *t;
   2382     FILE *cmdf;
   2383 
   2384 
   2385     /* sanitize the command-string */
   2386 
   2387     if (sp == 0) {
   2388 	while ((slash = strchr(cmd, '/')) != 0)
   2389 	    cmd = slash + 1;
   2390     }
   2391     else {
   2392 	while (sp && (slash = (char *) strchr(cmd, '/'))
   2393 	       && (slash < sp))
   2394 	    cmd = slash + 1;
   2395     }
   2396 
   2397     for (t = cmd; *t && !isspace(*t); t++) {
   2398 	if (isupper(*t)) {
   2399 	    *t = tolower(*t);
   2400 	}
   2401     }
   2402 
   2403     /* build the command */
   2404     if (strlen(_PATH_EXECPATH) + strlen(cmd) + 2 > sizeof(buf))
   2405 	return;
   2406     (void) snprintf(buf, sizeof(buf), "%s/%s", _PATH_EXECPATH, cmd);
   2407 
   2408     cmdf = ftpd_popen(buf, "r", 0);
   2409     if (!cmdf) {
   2410 	perror_reply(550, cmd);
   2411 	if (log_commands)
   2412 	    syslog(LOG_INFO, "SITE EXEC (FAIL: %m): %s", cmd);
   2413     }
   2414     else {
   2415 	int lines = 0;
   2416 	int maxlines = 0;
   2417 	struct aclmember *entry = NULL;
   2418 	char class[BUFSIZ];
   2419 	int maxfound = 0;
   2420 	int defmaxlines = 20;
   2421 	int which;
   2422 
   2423 	(void) acl_getclass(class);
   2424 	while ((getaclentry("site-exec-max-lines", &entry)) && ARG0) {
   2425 	    if (ARG1)
   2426 		for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
   2427 		    if (!strcasecmp(ARG[which], class)) {
   2428 			maxlines = atoi(ARG0);
   2429 			maxfound = 1;
   2430 		    }
   2431 		    if (!strcmp(ARG[which], "*"))
   2432 			defmaxlines = atoi(ARG0);
   2433 		}
   2434 	    else
   2435 		defmaxlines = atoi(ARG0);
   2436 	}
   2437 	if (!maxfound)
   2438 	    maxlines = defmaxlines;
   2439 	lreply(200, "%s", cmd);
   2440 	while (fgets(buf, sizeof buf, cmdf)) {
   2441 	    size_t len = strlen(buf);
   2442 
   2443 	    if (len > 0 && buf[len - 1] == '\n')
   2444 		buf[--len] = '\0';
   2445 	    lreply(200, "%s", buf);
   2446 	    if (maxlines <= 0)
   2447 		++lines;
   2448 	    else if (++lines >= maxlines) {
   2449 		lreply(200, "*** Truncated ***");
   2450 		break;
   2451 	    }
   2452 	}
   2453 	reply(200, " (end of '%s')", cmd);
   2454 	if (log_commands)
   2455 	    syslog(LOG_INFO, "SITE EXEC (lines: %d): %s", lines, cmd);
   2456 	ftpd_pclose(cmdf);
   2457     }
   2458 #endif /* PARANOID */
   2459 }
   2460 
   2461 void alias(char *s)
   2462 {
   2463     struct aclmember *entry = NULL;
   2464 
   2465     if (s != (char *) NULL) {
   2466 	while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL)
   2467 	    if (!strcmp(ARG0, s)) {
   2468 		reply(214, "%s is an alias for %s.", ARG0, ARG1);
   2469 		return;
   2470 	    }
   2471 	reply(502, "Unknown alias %s.", s);
   2472 	return;
   2473     }
   2474 
   2475     lreply(214, "The following aliases are available.");
   2476 
   2477     while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL)
   2478 	lreply(0, "   %-8s %s", ARG0, ARG1);
   2479     (void) fflush(stdout);
   2480 
   2481     reply(214, "");
   2482 }
   2483 
   2484 void cdpath(void)
   2485 {
   2486     struct aclmember *entry = NULL;
   2487 
   2488     lreply(214, "The cdpath is:");
   2489     while (getaclentry("cdpath", &entry) && ARG0 != NULL)
   2490 	lreply(0, "  %s", ARG0);
   2491     (void) fflush(stdout);
   2492     reply(214, "");
   2493 }
   2494 
   2495 void print_groups(void)
   2496 {
   2497     gid_t *groups;
   2498     int ngroups;
   2499     int maxgrp;
   2500 
   2501     maxgrp = getgroups(0, NULL);
   2502 
   2503     groups = alloca(maxgrp * sizeof (gid_t));
   2504 
   2505     if ((ngroups = getgroups(maxgrp, groups)) < 0) {
   2506 	return;
   2507     }
   2508 
   2509     lreply(214, "Group membership is:");
   2510     ngroups--;
   2511 
   2512     for (; ngroups >= 0; ngroups--)
   2513 	lreply(214, "  %d", groups[ngroups]);
   2514 
   2515     (void) fflush(stdout);
   2516     reply(214, "");
   2517 }
   2518