xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcmd.y (revision 12522:ad77f2eb6216)
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 
lookup(register struct tab * p,char * cmd)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  */
wu_getline(char * s,int n,register FILE * iop)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 
toolong(int a)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 
yylex(void)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 }
2524