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