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