xref: /freebsd/crypto/heimdal/appl/ftp/ftpd/ftpcmd.y (revision d6b92ffa)
1 /*	$NetBSD: ftpcmd.y,v 1.6 1995/06/03 22:46:45 mycroft Exp $	*/
2 
3 /*
4  * Copyright (c) 1985, 1988, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
36  */
37 
38 /*
39  * Grammar for FTP commands.
40  * See RFC 959.
41  */
42 
43 %{
44 
45 #include "ftpd_locl.h"
46 RCSID("$Id$");
47 
48 off_t	restart_point;
49 
50 static	int hasyyerrored;
51 
52 
53 static	int cmd_type;
54 static	int cmd_form;
55 static	int cmd_bytesz;
56 char	cbuf[64*1024];
57 char	*fromname;
58 
59 struct tab {
60 	char	*name;
61 	short	token;
62 	short	state;
63 	short	implemented;	/* 1 if command is implemented */
64 	char	*help;
65 };
66 
67 extern struct tab cmdtab[];
68 extern struct tab sitetab[];
69 
70 static char		*copy (char *);
71 static void		 help (struct tab *, char *);
72 static struct tab *
73 			 lookup (struct tab *, char *);
74 static void		 sizecmd (char *);
75 static RETSIGTYPE	 toolong (int);
76 static int		 yylex (void);
77 
78 /* This is for bison */
79 
80 #if !defined(alloca) && !defined(HAVE_ALLOCA)
81 #define alloca(x) malloc(x)
82 #endif
83 
84 %}
85 
86 %union {
87 	int	i;
88 	char   *s;
89 }
90 
91 %token
92 	A	B	C	E	F	I
93 	L	N	P	R	S	T
94 
95 	SP	CRLF	COMMA
96 
97 	USER	PASS	ACCT	REIN	QUIT	PORT
98 	PASV	TYPE	STRU	MODE	RETR	STOR
99 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
100 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
101 	ABOR	DELE	CWD	LIST	NLST	SITE
102 	sTAT	HELP	NOOP	MKD	RMD	PWD
103 	CDUP	STOU	SMNT	SYST	SIZE	MDTM
104 	EPRT	EPSV
105 
106 	UMASK	IDLE	CHMOD
107 
108 	AUTH	ADAT	PROT	PBSZ	CCC	MIC
109 	CONF	ENC
110 
111 	KAUTH	KLIST	KDESTROY KRBTKFILE AFSLOG
112 	LOCATE	URL
113 
114 	FEAT	OPTS
115 
116 	LEXERR
117 
118 %token	<s> STRING
119 %token	<i> NUMBER
120 
121 %type	<i> check_login check_login_no_guest check_secure octal_number byte_size
122 %type	<i> struct_code mode_code type_code form_code
123 %type	<s> pathstring pathname password username
124 
125 %start	cmd_list
126 
127 %%
128 
129 cmd_list
130 	: /* empty */
131 	| cmd_list cmd
132 		{
133 			fromname = (char *) 0;
134 			restart_point = (off_t) 0;
135 		}
136 	| cmd_list rcmd
137 	;
138 
139 cmd
140 	: USER SP username CRLF check_secure
141 		{
142 		    if ($5)
143 			user($3);
144 		    free($3);
145 		}
146 	| PASS SP password CRLF check_secure
147 		{
148 		    if ($5)
149 			pass($3);
150 		    memset ($3, 0, strlen($3));
151 		    free($3);
152 		}
153 
154 	| PORT SP host_port CRLF check_secure
155 		{
156 		    if ($5) {
157 			if (paranoid &&
158 			    (data_dest->sa_family != his_addr->sa_family ||
159 			     (socket_get_port(data_dest) < IPPORT_RESERVED) ||
160 			     memcmp(socket_get_address(data_dest),
161 				    socket_get_address(his_addr),
162 				    socket_addr_size(his_addr)) != 0)) {
163 			    usedefault = 1;
164 			    reply(500, "Illegal PORT range rejected.");
165 			} else {
166 			    usedefault = 0;
167 			    if (pdata >= 0) {
168 				close(pdata);
169 				pdata = -1;
170 			    }
171 			    reply(200, "PORT command successful.");
172 			}
173 		    }
174 		}
175 	| EPRT SP STRING CRLF check_secure
176 		{
177 		    if ($5)
178 			eprt ($3);
179 		    free ($3);
180 		}
181 	| PASV CRLF check_login
182 		{
183 		    if($3)
184 			pasv ();
185 		}
186 	| EPSV CRLF check_login
187 		{
188 		    if($3)
189 			epsv (NULL);
190 		}
191 	| EPSV SP STRING CRLF check_login
192 		{
193 		    if($5)
194 			epsv ($3);
195 		    free ($3);
196 		}
197 	| TYPE SP type_code CRLF check_secure
198 		{
199 		    if ($5) {
200 			switch (cmd_type) {
201 
202 			case TYPE_A:
203 				if (cmd_form == FORM_N) {
204 					reply(200, "Type set to A.");
205 					type = cmd_type;
206 					form = cmd_form;
207 				} else
208 					reply(504, "Form must be N.");
209 				break;
210 
211 			case TYPE_E:
212 				reply(504, "Type E not implemented.");
213 				break;
214 
215 			case TYPE_I:
216 				reply(200, "Type set to I.");
217 				type = cmd_type;
218 				break;
219 
220 			case TYPE_L:
221 #if NBBY == 8
222 				if (cmd_bytesz == 8) {
223 					reply(200,
224 					    "Type set to L (byte size 8).");
225 					type = cmd_type;
226 				} else
227 					reply(504, "Byte size must be 8.");
228 #else /* NBBY == 8 */
229 				UNIMPLEMENTED for NBBY != 8
230 #endif /* NBBY == 8 */
231 			}
232 		    }
233 		}
234 	| STRU SP struct_code CRLF check_secure
235 		{
236 		    if ($5) {
237 			switch ($3) {
238 
239 			case STRU_F:
240 				reply(200, "STRU F ok.");
241 				break;
242 
243 			default:
244 				reply(504, "Unimplemented STRU type.");
245 			}
246 		    }
247 		}
248 	| MODE SP mode_code CRLF check_secure
249 		{
250 		    if ($5) {
251 			switch ($3) {
252 
253 			case MODE_S:
254 				reply(200, "MODE S ok.");
255 				break;
256 
257 			default:
258 				reply(502, "Unimplemented MODE type.");
259 			}
260 		    }
261 		}
262 	| ALLO SP NUMBER CRLF check_secure
263 		{
264 		    if ($5) {
265 			reply(202, "ALLO command ignored.");
266 		    }
267 		}
268 	| ALLO SP NUMBER SP R SP NUMBER CRLF check_secure
269 		{
270 		    if ($9) {
271 			reply(202, "ALLO command ignored.");
272 		    }
273 		}
274 	| RETR SP pathname CRLF check_login
275 		{
276 			char *name = $3;
277 
278 			if ($5 && name != NULL)
279 				retrieve(0, name);
280 			if (name != NULL)
281 				free(name);
282 		}
283 	| STOR SP pathname CRLF check_login
284 		{
285 			char *name = $3;
286 
287 			if ($5 && name != NULL)
288 				do_store(name, "w", 0);
289 			if (name != NULL)
290 				free(name);
291 		}
292 	| APPE SP pathname CRLF check_login
293 		{
294 			char *name = $3;
295 
296 			if ($5 && name != NULL)
297 				do_store(name, "a", 0);
298 			if (name != NULL)
299 				free(name);
300 		}
301 	| NLST CRLF check_login
302 		{
303 			if ($3)
304 				send_file_list(".");
305 		}
306 	| NLST SP STRING CRLF check_login
307 		{
308 			char *name = $3;
309 
310 			if ($5 && name != NULL)
311 				send_file_list(name);
312 			if (name != NULL)
313 				free(name);
314 		}
315 	| LIST CRLF check_login
316 		{
317 		    if($3)
318 			list_file(".");
319 		}
320 	| LIST SP pathname CRLF check_login
321 		{
322 		    if($5)
323 			list_file($3);
324 		    free($3);
325 		}
326 	| sTAT SP pathname CRLF check_login
327 		{
328 			if ($5 && $3 != NULL)
329 				statfilecmd($3);
330 			if ($3 != NULL)
331 				free($3);
332 		}
333 	| sTAT CRLF check_secure
334 		{
335 		    if ($3)
336 			statcmd();
337 		}
338 	| DELE SP pathname CRLF check_login_no_guest
339 		{
340 			if ($5 && $3 != NULL)
341 				do_delete($3);
342 			if ($3 != NULL)
343 				free($3);
344 		}
345 	| RNTO SP pathname CRLF check_login_no_guest
346 		{
347 			if($5){
348 				if (fromname) {
349 					renamecmd(fromname, $3);
350 					free(fromname);
351 					fromname = (char *) 0;
352 				} else {
353 					reply(503, "Bad sequence of commands.");
354 				}
355 			}
356 			if ($3 != NULL)
357 				free($3);
358 		}
359 	| ABOR CRLF check_secure
360 		{
361 		    if ($3)
362 			reply(225, "ABOR command successful.");
363 		}
364 	| CWD CRLF check_login
365 		{
366 			if ($3) {
367 				const char *path = pw->pw_dir;
368 				if (dochroot || guest)
369 					path = "/";
370 				cwd(path);
371 			}
372 		}
373 	| CWD SP pathname CRLF check_login
374 		{
375 			if ($5 && $3 != NULL)
376 				cwd($3);
377 			if ($3 != NULL)
378 				free($3);
379 		}
380 	| HELP CRLF check_secure
381 		{
382 		    if ($3)
383 			help(cmdtab, (char *) 0);
384 		}
385 	| HELP SP STRING CRLF check_secure
386 		{
387 		    if ($5) {
388 			char *cp = $3;
389 
390 			if (strncasecmp(cp, "SITE", 4) == 0) {
391 				cp = $3 + 4;
392 				if (*cp == ' ')
393 					cp++;
394 				if (*cp)
395 					help(sitetab, cp);
396 				else
397 					help(sitetab, (char *) 0);
398 			} else
399 				help(cmdtab, $3);
400 		    }
401 		}
402 	| NOOP CRLF check_secure
403 		{
404 		    if ($3)
405 			reply(200, "NOOP command successful.");
406 		}
407 	| MKD SP pathname CRLF check_login
408 		{
409 			if ($5 && $3 != NULL)
410 				makedir($3);
411 			if ($3 != NULL)
412 				free($3);
413 		}
414 	| RMD SP pathname CRLF check_login_no_guest
415 		{
416 			if ($5 && $3 != NULL)
417 				removedir($3);
418 			if ($3 != NULL)
419 				free($3);
420 		}
421 	| PWD CRLF check_login
422 		{
423 			if ($3)
424 				pwd();
425 		}
426 	| CDUP CRLF check_login
427 		{
428 			if ($3)
429 				cwd("..");
430 		}
431 	| FEAT CRLF check_secure
432 		{
433 		    if ($3) {
434 			lreply(211, "Supported features:");
435 			lreply(0, " MDTM");
436 			lreply(0, " REST STREAM");
437 			lreply(0, " SIZE");
438 			reply(211, "End");
439 		    }
440 		}
441 	| OPTS SP STRING CRLF check_secure
442 		{
443 		    if ($5)
444 			reply(501, "Bad options");
445 		    free ($3);
446 		}
447 
448 	| SITE SP HELP CRLF check_secure
449 		{
450 		    if ($5)
451 			help(sitetab, (char *) 0);
452 		}
453 	| SITE SP HELP SP STRING CRLF check_secure
454 		{
455 		    if ($7)
456 			help(sitetab, $5);
457 		}
458 	| SITE SP UMASK CRLF check_login
459 		{
460 			if ($5) {
461 				int oldmask = umask(0);
462 				umask(oldmask);
463 				reply(200, "Current UMASK is %03o", oldmask);
464 			}
465 		}
466 	| SITE SP UMASK SP octal_number CRLF check_login_no_guest
467 		{
468 			if ($7) {
469 				if (($5 == -1) || ($5 > 0777)) {
470 					reply(501, "Bad UMASK value");
471 				} else {
472 					int oldmask = umask($5);
473 					reply(200,
474 					      "UMASK set to %03o (was %03o)",
475 					      $5, oldmask);
476 				}
477 			}
478 		}
479 	| SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest
480 		{
481 			if ($9 && $7 != NULL) {
482 				if ($5 > 0777)
483 					reply(501,
484 				"CHMOD: Mode value must be between 0 and 0777");
485 				else if (chmod($7, $5) < 0)
486 					perror_reply(550, $7);
487 				else
488 					reply(200, "CHMOD command successful.");
489 			}
490 			if ($7 != NULL)
491 				free($7);
492 		}
493 	| SITE SP IDLE CRLF check_secure
494 		{
495 		    if ($5)
496 			reply(200,
497 			    "Current IDLE time limit is %d seconds; max %d",
498 				ftpd_timeout, maxtimeout);
499 		}
500 	| SITE SP IDLE SP NUMBER CRLF check_secure
501 		{
502 		    if ($7) {
503 			if ($5 < 30 || $5 > maxtimeout) {
504 				reply(501,
505 			"Maximum IDLE time must be between 30 and %d seconds",
506 				    maxtimeout);
507 			} else {
508 				ftpd_timeout = $5;
509 				alarm((unsigned) ftpd_timeout);
510 				reply(200,
511 				    "Maximum IDLE time set to %d seconds",
512 				    ftpd_timeout);
513 			}
514 		    }
515 		}
516 
517 	| SITE SP KAUTH SP STRING CRLF check_login
518 		{
519 			reply(500, "Command not implemented.");
520 		}
521 	| SITE SP KLIST CRLF check_login
522 		{
523 		    if($5)
524 			klist();
525 		}
526 	| SITE SP KDESTROY CRLF check_login
527 		{
528 		    reply(500, "Command not implemented.");
529 		}
530 	| SITE SP KRBTKFILE SP STRING CRLF check_login
531 		{
532 		    reply(500, "Command not implemented.");
533 		}
534 	| SITE SP AFSLOG CRLF check_login
535 		{
536 #if defined(KRB5)
537 		    if(guest)
538 			reply(500, "Can't be done as guest.");
539 		    else if($5)
540 			afslog(NULL, 0);
541 #else
542 		    reply(500, "Command not implemented.");
543 #endif
544 		}
545 	| SITE SP AFSLOG SP STRING CRLF check_login
546 		{
547 #if defined(KRB5)
548 		    if(guest)
549 			reply(500, "Can't be done as guest.");
550 		    else if($7)
551 			afslog($5, 0);
552 		    if($5)
553 			free($5);
554 #else
555 		    reply(500, "Command not implemented.");
556 #endif
557 		}
558 	| SITE SP LOCATE SP STRING CRLF check_login
559 		{
560 		    if($7 && $5 != NULL)
561 			find($5);
562 		    if($5 != NULL)
563 			free($5);
564 		}
565 	| SITE SP URL CRLF check_secure
566 		{
567 		    if ($5)
568 			reply(200, "http://www.pdc.kth.se/heimdal/");
569 		}
570 	| STOU SP pathname CRLF check_login
571 		{
572 			if ($5 && $3 != NULL)
573 				do_store($3, "w", 1);
574 			if ($3 != NULL)
575 				free($3);
576 		}
577 	| SYST CRLF check_secure
578 		{
579 		    if ($3) {
580 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
581 			reply(215, "UNIX Type: L%d", NBBY);
582 #else
583 			reply(215, "UNKNOWN Type: L%d", NBBY);
584 #endif
585 		    }
586 		}
587 
588 		/*
589 		 * SIZE is not in RFC959, but Postel has blessed it and
590 		 * it will be in the updated RFC.
591 		 *
592 		 * Return size of file in a format suitable for
593 		 * using with RESTART (we just count bytes).
594 		 */
595 	| SIZE SP pathname CRLF check_login
596 		{
597 			if ($5 && $3 != NULL)
598 				sizecmd($3);
599 			if ($3 != NULL)
600 				free($3);
601 		}
602 
603 		/*
604 		 * MDTM is not in RFC959, but Postel has blessed it and
605 		 * it will be in the updated RFC.
606 		 *
607 		 * Return modification time of file as an ISO 3307
608 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
609 		 * where xxx is the fractional second (of any precision,
610 		 * not necessarily 3 digits)
611 		 */
612 	| MDTM SP pathname CRLF check_login
613 		{
614 			if ($5 && $3 != NULL) {
615 				struct stat stbuf;
616 				if (stat($3, &stbuf) < 0)
617 					reply(550, "%s: %s",
618 					    $3, strerror(errno));
619 				else if (!S_ISREG(stbuf.st_mode)) {
620 					reply(550,
621 					      "%s: not a plain file.", $3);
622 				} else {
623 					struct tm *t;
624 					time_t mtime = stbuf.st_mtime;
625 
626 					t = gmtime(&mtime);
627 					reply(213,
628 					      "%04d%02d%02d%02d%02d%02d",
629 					      t->tm_year + 1900,
630 					      t->tm_mon + 1,
631 					      t->tm_mday,
632 					      t->tm_hour,
633 					      t->tm_min,
634 					      t->tm_sec);
635 				}
636 			}
637 			if ($3 != NULL)
638 				free($3);
639 		}
640 	| QUIT CRLF check_secure
641 		{
642 		    if ($3) {
643 			reply(221, "Goodbye.");
644 			dologout(0);
645 		    }
646 		}
647 	| error CRLF
648 		{
649 			yyerrok;
650 		}
651 	;
652 rcmd
653 	: RNFR SP pathname CRLF check_login_no_guest
654 		{
655 			restart_point = (off_t) 0;
656 			if ($5 && $3) {
657 				fromname = renamefrom($3);
658 				if (fromname == (char *) 0 && $3) {
659 					free($3);
660 				}
661 			}
662 		}
663 	| REST SP byte_size CRLF check_secure
664 		{
665 		    if ($5) {
666 			fromname = (char *) 0;
667 			restart_point = $3;	/* XXX $3 is only "int" */
668 			reply(350, "Restarting at %ld. %s",
669 			      (long)restart_point,
670 			      "Send STORE or RETRIEVE to initiate transfer.");
671 		    }
672 		}
673 	| AUTH SP STRING CRLF
674 		{
675 			auth($3);
676 			free($3);
677 		}
678 	| ADAT SP STRING CRLF
679 		{
680 			adat($3);
681 			free($3);
682 		}
683 	| PBSZ SP NUMBER CRLF check_secure
684 		{
685 		    if ($5)
686 			pbsz($3);
687 		}
688 	| PROT SP STRING CRLF check_secure
689 		{
690 		    if ($5)
691 			prot($3);
692 		}
693 	| CCC CRLF check_secure
694 		{
695 		    if ($3)
696 			ccc();
697 		}
698 	| MIC SP STRING CRLF
699 		{
700 			mec($3, prot_safe);
701 			free($3);
702 		}
703 	| CONF SP STRING CRLF
704 		{
705 			mec($3, prot_confidential);
706 			free($3);
707 		}
708 	| ENC SP STRING CRLF
709 		{
710 			mec($3, prot_private);
711 			free($3);
712 		}
713 	;
714 
715 username
716 	: STRING
717 	;
718 
719 password
720 	: /* empty */
721 		{
722 			$$ = (char *)calloc(1, sizeof(char));
723 		}
724 	| STRING
725 	;
726 
727 byte_size
728 	: NUMBER
729 	;
730 
731 host_port
732 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
733 		NUMBER COMMA NUMBER
734 		{
735 			struct sockaddr_in *sin4 = (struct sockaddr_in *)data_dest;
736 
737 			sin4->sin_family = AF_INET;
738 			sin4->sin_port = htons($9 * 256 + $11);
739 			sin4->sin_addr.s_addr =
740 			    htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
741 		}
742 	;
743 
744 form_code
745 	: N
746 		{
747 			$$ = FORM_N;
748 		}
749 	| T
750 		{
751 			$$ = FORM_T;
752 		}
753 	| C
754 		{
755 			$$ = FORM_C;
756 		}
757 	;
758 
759 type_code
760 	: A
761 		{
762 			cmd_type = TYPE_A;
763 			cmd_form = FORM_N;
764 		}
765 	| A SP form_code
766 		{
767 			cmd_type = TYPE_A;
768 			cmd_form = $3;
769 		}
770 	| E
771 		{
772 			cmd_type = TYPE_E;
773 			cmd_form = FORM_N;
774 		}
775 	| E SP form_code
776 		{
777 			cmd_type = TYPE_E;
778 			cmd_form = $3;
779 		}
780 	| I
781 		{
782 			cmd_type = TYPE_I;
783 		}
784 	| L
785 		{
786 			cmd_type = TYPE_L;
787 			cmd_bytesz = NBBY;
788 		}
789 	| L SP byte_size
790 		{
791 			cmd_type = TYPE_L;
792 			cmd_bytesz = $3;
793 		}
794 		/* this is for a bug in the BBN ftp */
795 	| L byte_size
796 		{
797 			cmd_type = TYPE_L;
798 			cmd_bytesz = $2;
799 		}
800 	;
801 
802 struct_code
803 	: F
804 		{
805 			$$ = STRU_F;
806 		}
807 	| R
808 		{
809 			$$ = STRU_R;
810 		}
811 	| P
812 		{
813 			$$ = STRU_P;
814 		}
815 	;
816 
817 mode_code
818 	: S
819 		{
820 			$$ = MODE_S;
821 		}
822 	| B
823 		{
824 			$$ = MODE_B;
825 		}
826 	| C
827 		{
828 			$$ = MODE_C;
829 		}
830 	;
831 
832 pathname
833 	: pathstring
834 		{
835 			/*
836 			 * Problem: this production is used for all pathname
837 			 * processing, but only gives a 550 error reply.
838 			 * This is a valid reply in some cases but not in others.
839 			 */
840 			if (logged_in && $1 && *$1 == '~') {
841 				glob_t gl;
842 				int flags =
843 				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
844 
845 				memset(&gl, 0, sizeof(gl));
846 				if (glob($1, flags, NULL, &gl) ||
847 				    gl.gl_pathc == 0) {
848 					reply(550, "not found");
849 					$$ = NULL;
850 				} else {
851 					$$ = strdup(gl.gl_pathv[0]);
852 				}
853 				globfree(&gl);
854 				free($1);
855 			} else
856 				$$ = $1;
857 		}
858 	;
859 
860 pathstring
861 	: STRING
862 	;
863 
864 octal_number
865 	: NUMBER
866 		{
867 			int ret, dec, multby, digit;
868 
869 			/*
870 			 * Convert a number that was read as decimal number
871 			 * to what it would be if it had been read as octal.
872 			 */
873 			dec = $1;
874 			multby = 1;
875 			ret = 0;
876 			while (dec) {
877 				digit = dec%10;
878 				if (digit > 7) {
879 					ret = -1;
880 					break;
881 				}
882 				ret += digit * multby;
883 				multby *= 8;
884 				dec /= 10;
885 			}
886 			$$ = ret;
887 		}
888 	;
889 
890 
891 check_login_no_guest : check_login
892 		{
893 			$$ = $1 && !guest;
894 			if($1 && !$$)
895 				reply(550, "Permission denied");
896 		}
897 	;
898 
899 check_login : check_secure
900 		{
901 		    if($1) {
902 			if(($$ = logged_in) == 0)
903 			    reply(530, "Please login with USER and PASS.");
904 		    } else
905 			$$ = 0;
906 		}
907 	;
908 
909 check_secure : /* empty */
910 		{
911 		    $$ = 1;
912 		    if(sec_complete && !ccc_passed && !secure_command()) {
913 			$$ = 0;
914 			reply(533, "Command protection level denied "
915 			      "for paranoid reasons.");
916 		    }
917 		}
918 	;
919 
920 %%
921 
922 #define	CMD	0	/* beginning of command */
923 #define	ARGS	1	/* expect miscellaneous arguments */
924 #define	STR1	2	/* expect SP followed by STRING */
925 #define	STR2	3	/* expect STRING */
926 #define	OSTR	4	/* optional SP then STRING */
927 #define	ZSTR1	5	/* SP then optional STRING */
928 #define	ZSTR2	6	/* optional STRING after SP */
929 #define	SITECMD	7	/* SITE command */
930 #define	NSTR	8	/* Number followed by a string */
931 
932 struct tab cmdtab[] = {		/* In order defined in RFC 765 */
933 	{ "USER", USER, STR1, 1,	"<sp> username" },
934 	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
935 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
936 	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
937 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
938 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
939 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
940 	{ "EPRT", EPRT, STR1, 1,	"<sp> string" },
941 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
942 	{ "EPSV", EPSV, OSTR, 1,	"[<sp> foo]" },
943 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
944 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
945 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
946 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
947 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
948 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
949 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
950 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
951 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
952 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
953 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
954 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
955 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
956 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
957 	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
958 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
959 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
960 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
961 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
962 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
963 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
964 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
965 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
966 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
967 	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
968 	{ "STAT", sTAT, OSTR, 1,	"[ <sp> path-name ]" },
969 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
970 	{ "NOOP", NOOP, ARGS, 1,	"" },
971 	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
972 	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
973 	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
974 	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
975 	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
976 	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
977 	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
978 	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
979 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
980 	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
981 	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
982 
983 	/* extensions from RFC2228 */
984 	{ "AUTH", AUTH,	STR1, 1,	"<sp> auth-type" },
985 	{ "ADAT", ADAT,	STR1, 1,	"<sp> auth-data" },
986 	{ "PBSZ", PBSZ,	ARGS, 1,	"<sp> buffer-size" },
987 	{ "PROT", PROT,	STR1, 1,	"<sp> prot-level" },
988 	{ "CCC",  CCC,	ARGS, 1,	"" },
989 	{ "MIC",  MIC,	STR1, 1,	"<sp> integrity command" },
990 	{ "CONF", CONF,	STR1, 1,	"<sp> confidentiality command" },
991 	{ "ENC",  ENC,	STR1, 1,	"<sp> privacy command" },
992 
993 	/* RFC2389 */
994 	{ "FEAT", FEAT, ARGS, 1,	"" },
995 	{ "OPTS", OPTS, ARGS, 1,	"<sp> command [<sp> options]" },
996 
997 	{ NULL,   0,    0,    0,	0 }
998 };
999 
1000 struct tab sitetab[] = {
1001 	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
1002 	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
1003 	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
1004 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1005 
1006 	{ "KAUTH", KAUTH, STR1, 1,	"<sp> principal [ <sp> ticket ]" },
1007 	{ "KLIST", KLIST, ARGS, 1,	"(show ticket file)" },
1008 	{ "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" },
1009 	{ "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" },
1010 	{ "AFSLOG", AFSLOG, OSTR, 1,	"[<sp> cell]" },
1011 
1012 	{ "LOCATE", LOCATE, STR1, 1,	"<sp> globexpr" },
1013 	{ "FIND", LOCATE, STR1, 1,	"<sp> globexpr" },
1014 
1015 	{ "URL",  URL,  ARGS, 1,	"?" },
1016 
1017 	{ NULL,   0,    0,    0,	0 }
1018 };
1019 
1020 static struct tab *
1021 lookup(struct tab *p, char *cmd)
1022 {
1023 
1024 	for (; p->name != NULL; p++)
1025 		if (strcmp(cmd, p->name) == 0)
1026 			return (p);
1027 	return (0);
1028 }
1029 
1030 /*
1031  * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1032  */
1033 char *
1034 ftpd_getline(char *s, int n)
1035 {
1036 	int c;
1037 	char *cs;
1038 
1039 	cs = s;
1040 
1041 	/* might still be data within the security MIC/CONF/ENC */
1042 	if(ftp_command){
1043 	    strlcpy(s, ftp_command, n);
1044 	    if (debug)
1045 		syslog(LOG_DEBUG, "command: %s", s);
1046 	    return s;
1047 	}
1048 	while ((c = getc(stdin)) != EOF) {
1049 		c &= 0377;
1050 		if (c == IAC) {
1051 		    if ((c = getc(stdin)) != EOF) {
1052 			c &= 0377;
1053 			switch (c) {
1054 			case WILL:
1055 			case WONT:
1056 				c = getc(stdin);
1057 				printf("%c%c%c", IAC, DONT, 0377&c);
1058 				fflush(stdout);
1059 				continue;
1060 			case DO:
1061 			case DONT:
1062 				c = getc(stdin);
1063 				printf("%c%c%c", IAC, WONT, 0377&c);
1064 				fflush(stdout);
1065 				continue;
1066 			case IAC:
1067 				break;
1068 			default:
1069 				continue;	/* ignore command */
1070 			}
1071 		    }
1072 		}
1073 		*cs++ = c;
1074 		if (--n <= 0 || c == '\n')
1075 			break;
1076 	}
1077 	if (c == EOF && cs == s)
1078 		return (NULL);
1079 	*cs++ = '\0';
1080 	if (debug) {
1081 		if (!guest && strncasecmp("pass ", s, 5) == 0) {
1082 			/* Don't syslog passwords */
1083 			syslog(LOG_DEBUG, "command: %.5s ???", s);
1084 		} else {
1085 			char *cp;
1086 			int len;
1087 
1088 			/* Don't syslog trailing CR-LF */
1089 			len = strlen(s);
1090 			cp = s + len - 1;
1091 			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1092 				--cp;
1093 				--len;
1094 			}
1095 			syslog(LOG_DEBUG, "command: %.*s", len, s);
1096 		}
1097 	}
1098 #ifdef XXX
1099 	fprintf(stderr, "%s\n", s);
1100 #endif
1101 	return (s);
1102 }
1103 
1104 static RETSIGTYPE
1105 toolong(int signo)
1106 {
1107 
1108 	reply(421,
1109 	    "Timeout (%d seconds): closing control connection.",
1110 	      ftpd_timeout);
1111 	if (logging)
1112 		syslog(LOG_INFO, "User %s timed out after %d seconds",
1113 		    (pw ? pw -> pw_name : "unknown"), ftpd_timeout);
1114 	dologout(1);
1115 	SIGRETURN(0);
1116 }
1117 
1118 static int
1119 yylex(void)
1120 {
1121 	static int cpos, state;
1122 	char *cp, *cp2;
1123 	struct tab *p;
1124 	int n;
1125 	char c;
1126 
1127 	for (;;) {
1128 		switch (state) {
1129 
1130 		case CMD:
1131 			hasyyerrored = 0;
1132 
1133 			signal(SIGALRM, toolong);
1134 			alarm((unsigned) ftpd_timeout);
1135 			if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
1136 				reply(221, "You could at least say goodbye.");
1137 				dologout(0);
1138 			}
1139 			alarm(0);
1140 #ifdef HAVE_SETPROCTITLE
1141 			if (strncasecmp(cbuf, "PASS", 4) != 0)
1142 				setproctitle("%s: %s", proctitle, cbuf);
1143 #endif /* HAVE_SETPROCTITLE */
1144 			if ((cp = strchr(cbuf, '\r'))) {
1145 				*cp++ = '\n';
1146 				*cp = '\0';
1147 			}
1148 			if ((cp = strpbrk(cbuf, " \n")))
1149 				cpos = cp - cbuf;
1150 			if (cpos == 0)
1151 				cpos = 4;
1152 			c = cbuf[cpos];
1153 			cbuf[cpos] = '\0';
1154 			strupr(cbuf);
1155 			p = lookup(cmdtab, cbuf);
1156 			cbuf[cpos] = c;
1157 			if (p != 0) {
1158 				if (p->implemented == 0) {
1159 					nack(p->name);
1160 					hasyyerrored = 1;
1161 					break;
1162 				}
1163 				state = p->state;
1164 				yylval.s = p->name;
1165 				return (p->token);
1166 			}
1167 			break;
1168 
1169 		case SITECMD:
1170 			if (cbuf[cpos] == ' ') {
1171 				cpos++;
1172 				return (SP);
1173 			}
1174 			cp = &cbuf[cpos];
1175 			if ((cp2 = strpbrk(cp, " \n")))
1176 				cpos = cp2 - cbuf;
1177 			c = cbuf[cpos];
1178 			cbuf[cpos] = '\0';
1179 			strupr(cp);
1180 			p = lookup(sitetab, cp);
1181 			cbuf[cpos] = c;
1182 			if (p != 0) {
1183 				if (p->implemented == 0) {
1184 					state = CMD;
1185 					nack(p->name);
1186 					hasyyerrored = 1;
1187 					break;
1188 				}
1189 				state = p->state;
1190 				yylval.s = p->name;
1191 				return (p->token);
1192 			}
1193 			state = CMD;
1194 			break;
1195 
1196 		case OSTR:
1197 			if (cbuf[cpos] == '\n') {
1198 				state = CMD;
1199 				return (CRLF);
1200 			}
1201 			/* FALLTHROUGH */
1202 
1203 		case STR1:
1204 		case ZSTR1:
1205 		dostr1:
1206 			if (cbuf[cpos] == ' ') {
1207 				cpos++;
1208 				if(state == OSTR)
1209 				    state = STR2;
1210 				else
1211 				    state++;
1212 				return (SP);
1213 			}
1214 			break;
1215 
1216 		case ZSTR2:
1217 			if (cbuf[cpos] == '\n') {
1218 				state = CMD;
1219 				return (CRLF);
1220 			}
1221 			/* FALLTHROUGH */
1222 
1223 		case STR2:
1224 			cp = &cbuf[cpos];
1225 			n = strlen(cp);
1226 			cpos += n - 1;
1227 			/*
1228 			 * Make sure the string is nonempty and \n terminated.
1229 			 */
1230 			if (n > 1 && cbuf[cpos] == '\n') {
1231 				cbuf[cpos] = '\0';
1232 				yylval.s = copy(cp);
1233 				cbuf[cpos] = '\n';
1234 				state = ARGS;
1235 				return (STRING);
1236 			}
1237 			break;
1238 
1239 		case NSTR:
1240 			if (cbuf[cpos] == ' ') {
1241 				cpos++;
1242 				return (SP);
1243 			}
1244 			if (isdigit((unsigned char)cbuf[cpos])) {
1245 				cp = &cbuf[cpos];
1246 				while (isdigit((unsigned char)cbuf[++cpos]))
1247 					;
1248 				c = cbuf[cpos];
1249 				cbuf[cpos] = '\0';
1250 				yylval.i = atoi(cp);
1251 				cbuf[cpos] = c;
1252 				state = STR1;
1253 				return (NUMBER);
1254 			}
1255 			state = STR1;
1256 			goto dostr1;
1257 
1258 		case ARGS:
1259 			if (isdigit((unsigned char)cbuf[cpos])) {
1260 				cp = &cbuf[cpos];
1261 				while (isdigit((unsigned char)cbuf[++cpos]))
1262 					;
1263 				c = cbuf[cpos];
1264 				cbuf[cpos] = '\0';
1265 				yylval.i = atoi(cp);
1266 				cbuf[cpos] = c;
1267 				return (NUMBER);
1268 			}
1269 			switch (cbuf[cpos++]) {
1270 
1271 			case '\n':
1272 				state = CMD;
1273 				return (CRLF);
1274 
1275 			case ' ':
1276 				return (SP);
1277 
1278 			case ',':
1279 				return (COMMA);
1280 
1281 			case 'A':
1282 			case 'a':
1283 				return (A);
1284 
1285 			case 'B':
1286 			case 'b':
1287 				return (B);
1288 
1289 			case 'C':
1290 			case 'c':
1291 				return (C);
1292 
1293 			case 'E':
1294 			case 'e':
1295 				return (E);
1296 
1297 			case 'F':
1298 			case 'f':
1299 				return (F);
1300 
1301 			case 'I':
1302 			case 'i':
1303 				return (I);
1304 
1305 			case 'L':
1306 			case 'l':
1307 				return (L);
1308 
1309 			case 'N':
1310 			case 'n':
1311 				return (N);
1312 
1313 			case 'P':
1314 			case 'p':
1315 				return (P);
1316 
1317 			case 'R':
1318 			case 'r':
1319 				return (R);
1320 
1321 			case 'S':
1322 			case 's':
1323 				return (S);
1324 
1325 			case 'T':
1326 			case 't':
1327 				return (T);
1328 
1329 			}
1330 			break;
1331 
1332 		default:
1333 			fatal("Unknown state in scanner.");
1334 		}
1335 		yyerror(NULL);
1336 		state = CMD;
1337 		return (0);
1338 	}
1339 }
1340 
1341 /* ARGSUSED */
1342 void
1343 yyerror(char *s)
1344 {
1345 	char *cp;
1346 
1347 	if (hasyyerrored)
1348 	    return;
1349 
1350 	if ((cp = strchr(cbuf,'\n')))
1351 		*cp = '\0';
1352 	reply(500, "'%s': command not understood.", cbuf);
1353 	hasyyerrored = 1;
1354 }
1355 
1356 static char *
1357 copy(char *s)
1358 {
1359 	char *p;
1360 
1361 	p = strdup(s);
1362 	if (p == NULL)
1363 		fatal("Ran out of memory.");
1364 	return p;
1365 }
1366 
1367 static void
1368 help(struct tab *ctab, char *s)
1369 {
1370 	struct tab *c;
1371 	int width, NCMDS;
1372 	char *t;
1373 	char buf[1024];
1374 
1375 	if (ctab == sitetab)
1376 		t = "SITE ";
1377 	else
1378 		t = "";
1379 	width = 0, NCMDS = 0;
1380 	for (c = ctab; c->name != NULL; c++) {
1381 		int len = strlen(c->name);
1382 
1383 		if (len > width)
1384 			width = len;
1385 		NCMDS++;
1386 	}
1387 	width = (width + 8) &~ 7;
1388 	if (s == 0) {
1389 		int i, j, w;
1390 		int columns, lines;
1391 
1392 		lreply(214, "The following %scommands are recognized %s.",
1393 		    t, "(* =>'s unimplemented)");
1394 		columns = 76 / width;
1395 		if (columns == 0)
1396 			columns = 1;
1397 		lines = (NCMDS + columns - 1) / columns;
1398 		for (i = 0; i < lines; i++) {
1399 		    strlcpy (buf, "   ", sizeof(buf));
1400 		    for (j = 0; j < columns; j++) {
1401 			c = ctab + j * lines + i;
1402 			snprintf (buf + strlen(buf),
1403 				  sizeof(buf) - strlen(buf),
1404 				  "%s%c",
1405 				  c->name,
1406 				  c->implemented ? ' ' : '*');
1407 			if (c + lines >= &ctab[NCMDS])
1408 			    break;
1409 			w = strlen(c->name) + 1;
1410 			while (w < width) {
1411 			    strlcat (buf,
1412 					     " ",
1413 					     sizeof(buf));
1414 			    w++;
1415 			}
1416 		    }
1417 		    lreply(214, "%s", buf);
1418 		}
1419 		reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1420 		return;
1421 	}
1422 	strupr(s);
1423 	c = lookup(ctab, s);
1424 	if (c == (struct tab *)0) {
1425 		reply(502, "Unknown command %s.", s);
1426 		return;
1427 	}
1428 	if (c->implemented)
1429 		reply(214, "Syntax: %s%s %s", t, c->name, c->help);
1430 	else
1431 		reply(214, "%s%-*s\t%s; unimplemented.", t, width,
1432 		    c->name, c->help);
1433 }
1434 
1435 static void
1436 sizecmd(char *filename)
1437 {
1438 	switch (type) {
1439 	case TYPE_L:
1440 	case TYPE_I: {
1441 		struct stat stbuf;
1442 		if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1443 			reply(550, "%s: not a plain file.", filename);
1444 		else
1445 			reply(213, "%lu", (unsigned long)stbuf.st_size);
1446 		break;
1447 	}
1448 	case TYPE_A: {
1449 		FILE *fin;
1450 		int c;
1451 		size_t count;
1452 		struct stat stbuf;
1453 		fin = fopen(filename, "r");
1454 		if (fin == NULL) {
1455 			perror_reply(550, filename);
1456 			return;
1457 		}
1458 		if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1459 			reply(550, "%s: not a plain file.", filename);
1460 			fclose(fin);
1461 			return;
1462 		}
1463 
1464 		count = 0;
1465 		while((c=getc(fin)) != EOF) {
1466 			if (c == '\n')	/* will get expanded to \r\n */
1467 				count++;
1468 			count++;
1469 		}
1470 		fclose(fin);
1471 
1472 		reply(213, "%lu", (unsigned long)count);
1473 		break;
1474 	}
1475 	default:
1476 		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1477 	}
1478 }
1479