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