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