1/* $NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $ */
2
3/*-
4 * Copyright (c) 1997-2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39/*
40 * Copyright (c) 1985, 1988, 1993, 1994
41 * The Regents of the University of California. All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 *
67 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
68 */
69
70/*
71 * Grammar for FTP commands.
72 * See RFC 959.
73 */
74
75%{
76#include <sys/cdefs.h>
77
78#ifndef lint
79#if 0
80static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
81#else
82__RCSID("$NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $");
83#endif
84#endif /* not lint */
85
86#include <sys/param.h>
87#include <sys/socket.h>
88#include <sys/stat.h>
89
90#include <netinet/in.h>
91#include <arpa/ftp.h>
92#include <arpa/inet.h>
93
94#include <ctype.h>
95#include <errno.h>
96#include <pwd.h>
97#include <stdio.h>
98#include <stdlib.h>
99#include <string.h>
100#include <syslog.h>
101#include <time.h>
102#include <tzfile.h>
103#include <unistd.h>
104#include <netdb.h>
105
106#ifdef KERBEROS5
107#include <krb5/krb5.h>
108#endif
109
110#include "extern.h"
111#include "version.h"
112
113static int cmd_type;
114static int cmd_form;
115static int cmd_bytesz;
116
117char cbuf[FTP_BUFLEN];
118char *cmdp;
119char *fromname;
120
121extern int epsvall;
122struct tab sitetab[];
123
124static int check_write(const char *, int);
125static void help(struct tab *, const char *);
126static void port_check(const char *, int);
127 int yylex(void);
128
129%}
130
131%union {
132 struct {
133 LLT ll;
134 int i;
135 } u;
136 char *s;
137}
138
139%token
140 A B C E F I
141 L N P R S T
142
143 SP CRLF COMMA ALL
144
145 USER PASS ACCT CWD CDUP SMNT
146 QUIT REIN PORT PASV TYPE STRU
147 MODE RETR STOR STOU APPE ALLO
148 REST RNFR RNTO ABOR DELE RMD
149 MKD PWD LIST NLST SITE SYST
150 STAT HELP NOOP
151
152 AUTH ADAT PROT PBSZ CCC MIC
153 CONF ENC
154
155 FEAT OPTS
156
157 SIZE MDTM MLST MLSD
158
159 LPRT LPSV EPRT EPSV
160
161 MAIL MLFL MRCP MRSQ MSAM MSND
162 MSOM
163
164 CHMOD IDLE RATEGET RATEPUT UMASK
165
166 LEXERR
167
168%token <s> STRING
169%token <u> NUMBER
170
171%type <u.i> check_login octal_number byte_size
172%type <u.i> struct_code mode_code type_code form_code decimal_integer
173%type <s> pathstring pathname password username
174%type <s> mechanism_name base64data prot_code
175
176%start cmd_sel
177
178%%
179
180cmd_sel
181 : cmd
182 {
183 REASSIGN(fromname, NULL);
184 restart_point = (off_t) 0;
185 }
186
187 | rcmd
188
189 ;
190
191cmd
192 /* RFC 959 */
193 : USER SP username CRLF
194 {
195 user($3);
196 free($3);
197 }
198
199 | PASS SP password CRLF
200 {
201 pass($3);
202 memset($3, 0, strlen($3));
203 free($3);
204 }
205
206 | CWD check_login CRLF
207 {
208 if ($2)
209 cwd(homedir);
210 }
211
212 | CWD check_login SP pathname CRLF
213 {
214 if ($2 && $4 != NULL)
215 cwd($4);
216 if ($4 != NULL)
217 free($4);
218 }
219
220 | CDUP check_login CRLF
221 {
222 if ($2)
223 cwd("..");
224 }
225
226 | QUIT CRLF
227 {
228 if (logged_in) {
229 reply(-221, "%s", "");
230 reply(0,
231 "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
232 (LLT)total_data, PLURAL(total_data),
233 (LLT)total_files, PLURAL(total_files));
234 reply(0,
235 "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
236 (LLT)total_bytes, PLURAL(total_bytes),
237 (LLT)total_xfers, PLURAL(total_xfers));
238 }
239 reply(221,
240 "Thank you for using the FTP service on %s.",
241 hostname);
242 if (logged_in && logging) {
243 syslog(LOG_INFO,
244 "Data traffic: " LLF " byte%s in " LLF " file%s",
245 (LLT)total_data, PLURAL(total_data),
246 (LLT)total_files, PLURAL(total_files));
247 syslog(LOG_INFO,
248 "Total traffic: " LLF " byte%s in " LLF " transfer%s",
249 (LLT)total_bytes, PLURAL(total_bytes),
250 (LLT)total_xfers, PLURAL(total_xfers));
251 }
252
253 dologout(0);
254 }
255
256 | PORT check_login SP host_port CRLF
257 {
258 if ($2)
259 port_check("PORT", AF_INET);
260 }
261
262 | LPRT check_login SP host_long_port4 CRLF
263 {
264 if ($2)
265 port_check("LPRT", AF_INET);
266 }
267
268 | LPRT check_login SP host_long_port6 CRLF
269 {
270#ifdef INET6
271 if ($2)
272 port_check("LPRT", AF_INET6);
273#else
274 reply(500, "IPv6 support not available.");
275#endif
276 }
277
278 | EPRT check_login SP STRING CRLF
279 {
280 if ($2) {
281 if (extended_port($4) == 0)
282 port_check("EPRT", -1);
283 }
284 free($4);
285 }
286
287 | PASV check_login CRLF
288 {
289 if ($2) {
290 if (CURCLASS_FLAGS_ISSET(passive))
291 passive();
292 else
293 reply(500, "PASV mode not available.");
294 }
295 }
296
297 | LPSV check_login CRLF
298 {
299 if ($2) {
300 if (CURCLASS_FLAGS_ISSET(passive)) {
301 if (epsvall)
302 reply(501,
303 "LPSV disallowed after EPSV ALL");
304 else
305 long_passive("LPSV", PF_UNSPEC);
306 } else
307 reply(500, "LPSV mode not available.");
308 }
309 }
310
311 | EPSV check_login SP NUMBER CRLF
312 {
313 if ($2) {
314 if (CURCLASS_FLAGS_ISSET(passive))
315 long_passive("EPSV",
316 epsvproto2af($4.i));
317 else
318 reply(500, "EPSV mode not available.");
319 }
320 }
321
322 | EPSV check_login SP ALL CRLF
323 {
324 if ($2) {
325 if (CURCLASS_FLAGS_ISSET(passive)) {
326 reply(200,
327 "EPSV ALL command successful.");
328 epsvall++;
329 } else
330 reply(500, "EPSV mode not available.");
331 }
332 }
333
334 | EPSV check_login CRLF
335 {
336 if ($2) {
337 if (CURCLASS_FLAGS_ISSET(passive))
338 long_passive("EPSV", PF_UNSPEC);
339 else
340 reply(500, "EPSV mode not available.");
341 }
342 }
343
344 | TYPE check_login SP type_code CRLF
345 {
346 if ($2) {
347
348 switch (cmd_type) {
349
350 case TYPE_A:
351 if (cmd_form == FORM_N) {
352 reply(200, "Type set to A.");
353 type = cmd_type;
354 form = cmd_form;
355 } else
356 reply(504, "Form must be N.");
357 break;
358
359 case TYPE_E:
360 reply(504, "Type E not implemented.");
361 break;
362
363 case TYPE_I:
364 reply(200, "Type set to I.");
365 type = cmd_type;
366 break;
367
368 case TYPE_L:
369#if NBBY == 8
370 if (cmd_bytesz == 8) {
371 reply(200,
372 "Type set to L (byte size 8).");
373 type = cmd_type;
374 } else
375 reply(504, "Byte size must be 8.");
376#else /* NBBY == 8 */
377 UNIMPLEMENTED for NBBY != 8
378#endif /* NBBY == 8 */
379 }
380
381 }
382 }
383
384 | STRU check_login SP struct_code CRLF
385 {
386 if ($2) {
387 switch ($4) {
388
389 case STRU_F:
390 reply(200, "STRU F ok.");
391 break;
392
393 default:
394 reply(504, "Unimplemented STRU type.");
395 }
396 }
397 }
398
399 | MODE check_login SP mode_code CRLF
400 {
401 if ($2) {
402 switch ($4) {
403
404 case MODE_S:
405 reply(200, "MODE S ok.");
406 break;
407
408 default:
409 reply(502, "Unimplemented MODE type.");
410 }
411 }
412 }
413
414 | RETR check_login SP pathname CRLF
415 {
416 if ($2 && $4 != NULL)
417 retrieve(NULL, $4);
418 if ($4 != NULL)
419 free($4);
420 }
421
422 | STOR SP pathname CRLF
423 {
424 if (check_write($3, 1))
425 store($3, "w", 0);
426 if ($3 != NULL)
427 free($3);
428 }
429
430 | STOU SP pathname CRLF
431 {
432 if (check_write($3, 1))
433 store($3, "w", 1);
434 if ($3 != NULL)
435 free($3);
436 }
437
438 | APPE SP pathname CRLF
439 {
440 if (check_write($3, 1))
441 store($3, "a", 0);
442 if ($3 != NULL)
443 free($3);
444 }
445
446 | ALLO check_login SP NUMBER CRLF
447 {
448 if ($2)
449 reply(202, "ALLO command ignored.");
450 }
451
452 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
453 {
454 if ($2)
455 reply(202, "ALLO command ignored.");
456 }
457
458 | RNTO SP pathname CRLF
459 {
460 if (check_write($3, 0)) {
461 if (fromname) {
462 renamecmd(fromname, $3);
463 REASSIGN(fromname, NULL);
464 } else {
465 reply(503, "Bad sequence of commands.");
466 }
467 }
468 if ($3 != NULL)
469 free($3);
470 }
471
472 | ABOR check_login CRLF
473 {
474 if (is_oob)
475 abor();
476 else if ($2)
477 reply(225, "ABOR command successful.");
478 }
479
480 | DELE SP pathname CRLF
481 {
482 if (check_write($3, 0))
483 delete($3);
484 if ($3 != NULL)
485 free($3);
486 }
487
488 | RMD SP pathname CRLF
489 {
490 if (check_write($3, 0))
491 removedir($3);
492 if ($3 != NULL)
493 free($3);
494 }
495
496 | MKD SP pathname CRLF
497 {
498 if (check_write($3, 0))
499 makedir($3);
500 if ($3 != NULL)
501 free($3);
502 }
503
504 | PWD check_login CRLF
505 {
506 if ($2)
507 pwd();
508 }
509
510 | LIST check_login CRLF
511 {
512 char *argv[] = { INTERNAL_LS, "-lgA", NULL };
513
514 if (CURCLASS_FLAGS_ISSET(hidesymlinks))
515 argv[1] = "-LlgA";
516 if ($2)
517 retrieve(argv, "");
518 }
519
520 | LIST check_login SP pathname CRLF
521 {
522 char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
523
524 if (CURCLASS_FLAGS_ISSET(hidesymlinks))
525 argv[1] = "-LlgA";
526 if ($2 && $4 != NULL) {
527 argv[2] = $4;
528 retrieve(argv, $4);
529 }
530 if ($4 != NULL)
531 free($4);
532 }
533
534 | NLST check_login CRLF
535 {
536 if ($2)
537 send_file_list(".");
538 }
539
540 | NLST check_login SP pathname CRLF
541 {
542 if ($2)
543 send_file_list($4);
544 free($4);
545 }
546
547 | SITE SP HELP CRLF
548 {
549 help(sitetab, NULL);
550 }
551
552 | SITE SP CHMOD SP octal_number SP pathname CRLF
553 {
554 if (check_write($7, 0)) {
555 if (($5 == -1) || ($5 > 0777))
556 reply(501,
557 "CHMOD: Mode value must be between 0 and 0777");
558 else if (chmod($7, $5) < 0)
559 perror_reply(550, $7);
560 else
561 reply(200, "CHMOD command successful.");
562 }
563 if ($7 != NULL)
564 free($7);
565 }
566
567 | SITE SP HELP SP STRING CRLF
568 {
569 help(sitetab, $5);
570 free($5);
571 }
572
573 | SITE SP IDLE check_login CRLF
574 {
575 if ($4) {
576 reply(200,
577 "Current IDLE time limit is " LLF
578 " seconds; max " LLF,
579 (LLT)curclass.timeout,
580 (LLT)curclass.maxtimeout);
581 }
582 }
583
584 | SITE SP IDLE check_login SP NUMBER CRLF
585 {
586 if ($4) {
587 if ($6.i < 30 || $6.i > curclass.maxtimeout) {
588 reply(501,
589 "IDLE time limit must be between 30 and "
590 LLF " seconds",
591 (LLT)curclass.maxtimeout);
592 } else {
593 curclass.timeout = $6.i;
594 (void) alarm(curclass.timeout);
595 reply(200,
596 "IDLE time limit set to "
597 LLF " seconds",
598 (LLT)curclass.timeout);
599 }
600 }
601 }
602
603 | SITE SP RATEGET check_login CRLF
604 {
605 if ($4) {
606 reply(200,
607 "Current RATEGET is " LLF " bytes/sec",
608 (LLT)curclass.rateget);
609 }
610 }
611
612 | SITE SP RATEGET check_login SP STRING CRLF
613 {
614 char errbuf[100];
615 char *p = $6;
616 LLT rate;
617
618 if ($4) {
619 rate = strsuftollx("RATEGET", p, 0,
620 curclass.maxrateget
621 ? curclass.maxrateget
622 : LLTMAX, errbuf, sizeof(errbuf));
623 if (errbuf[0])
624 reply(501, "%s", errbuf);
625 else {
626 curclass.rateget = rate;
627 reply(200,
628 "RATEGET set to " LLF " bytes/sec",
629 (LLT)curclass.rateget);
630 }
631 }
632 free($6);
633 }
634
635 | SITE SP RATEPUT check_login CRLF
636 {
637 if ($4) {
638 reply(200,
639 "Current RATEPUT is " LLF " bytes/sec",
640 (LLT)curclass.rateput);
641 }
642 }
643
644 | SITE SP RATEPUT check_login SP STRING CRLF
645 {
646 char errbuf[100];
647 char *p = $6;
648 LLT rate;
649
650 if ($4) {
651 rate = strsuftollx("RATEPUT", p, 0,
652 curclass.maxrateput
653 ? curclass.maxrateput
654 : LLTMAX, errbuf, sizeof(errbuf));
655 if (errbuf[0])
656 reply(501, "%s", errbuf);
657 else {
658 curclass.rateput = rate;
659 reply(200,
660 "RATEPUT set to " LLF " bytes/sec",
661 (LLT)curclass.rateput);
662 }
663 }
664 free($6);
665 }
666
667 | SITE SP UMASK check_login CRLF
668 {
669 int oldmask;
670
671 if ($4) {
672 oldmask = umask(0);
673 (void) umask(oldmask);
674 reply(200, "Current UMASK is %03o", oldmask);
675 }
676 }
677
678 | SITE SP UMASK check_login SP octal_number CRLF
679 {
680 int oldmask;
681
682 if ($4 && check_write("", 0)) {
683 if (($6 == -1) || ($6 > 0777)) {
684 reply(501, "Bad UMASK value");
685 } else {
686 oldmask = umask($6);
687 reply(200,
688 "UMASK set to %03o (was %03o)",
689 $6, oldmask);
690 }
691 }
692 }
693
694 | SYST CRLF
695 {
696 if (EMPTYSTR(version))
697 reply(215, "UNIX Type: L%d", NBBY);
698 else
699 reply(215, "UNIX Type: L%d Version: %s", NBBY,
700 version);
701 }
702
703 | STAT check_login SP pathname CRLF
704 {
705 if ($2 && $4 != NULL)
706 statfilecmd($4);
707 if ($4 != NULL)
708 free($4);
709 }
710
711 | STAT CRLF
712 {
713 if (is_oob)
714 statxfer();
715 else
716 statcmd();
717 }
718
719 | HELP CRLF
720 {
721 help(cmdtab, NULL);
722 }
723
724 | HELP SP STRING CRLF
725 {
726 char *cp = $3;
727
728 if (strncasecmp(cp, "SITE", 4) == 0) {
729 cp = $3 + 4;
730 if (*cp == ' ')
731 cp++;
732 if (*cp)
733 help(sitetab, cp);
734 else
735 help(sitetab, NULL);
736 } else
737 help(cmdtab, $3);
738 free($3);
739 }
740
741 | NOOP CRLF
742 {
743 reply(200, "NOOP command successful.");
744 }
745
746 /* RFC 2228 */
747 | AUTH SP mechanism_name CRLF
748 {
749 reply(502, "RFC 2228 authentication not implemented.");
750 free($3);
751 }
752
753 | ADAT SP base64data CRLF
754 {
755 reply(503,
756 "Please set authentication state with AUTH.");
757 free($3);
758 }
759
760 | PROT SP prot_code CRLF
761 {
762 reply(503,
763 "Please set protection buffer size with PBSZ.");
764 free($3);
765 }
766
767 | PBSZ SP decimal_integer CRLF
768 {
769 reply(503,
770 "Please set authentication state with AUTH.");
771 }
772
773 | CCC CRLF
774 {
775 reply(533, "No protection enabled.");
776 }
777
778 | MIC SP base64data CRLF
779 {
780 reply(502, "RFC 2228 authentication not implemented.");
781 free($3);
782 }
783
784 | CONF SP base64data CRLF
785 {
786 reply(502, "RFC 2228 authentication not implemented.");
787 free($3);
788 }
789
790 | ENC SP base64data CRLF
791 {
792 reply(502, "RFC 2228 authentication not implemented.");
793 free($3);
794 }
795
796 /* RFC 2389 */
797 | FEAT CRLF
798 {
799
800 feat();
801 }
802
803 | OPTS SP STRING CRLF
804 {
805
806 opts($3);
807 free($3);
808 }
809
810
811 /* extensions from draft-ietf-ftpext-mlst-11 */
812
813 /*
814 * Return size of file in a format suitable for
815 * using with RESTART (we just count bytes).
816 */
817 | SIZE check_login SP pathname CRLF
818 {
819 if ($2 && $4 != NULL)
820 sizecmd($4);
821 if ($4 != NULL)
822 free($4);
823 }
824
825 /*
826 * Return modification time of file as an ISO 3307
827 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
828 * where xxx is the fractional second (of any precision,
829 * not necessarily 3 digits)
830 */
831 | MDTM check_login SP pathname CRLF
832 {
833 if ($2 && $4 != NULL) {
834 struct stat stbuf;
835 if (stat($4, &stbuf) < 0)
836 perror_reply(550, $4);
837 else if (!S_ISREG(stbuf.st_mode)) {
838 reply(550, "%s: not a plain file.", $4);
839 } else {
840 struct tm *t;
841
842 t = gmtime(&stbuf.st_mtime);
843 reply(213,
844 "%04d%02d%02d%02d%02d%02d",
845 TM_YEAR_BASE + t->tm_year,
846 t->tm_mon+1, t->tm_mday,
847 t->tm_hour, t->tm_min, t->tm_sec);
848 }
849 }
850 if ($4 != NULL)
851 free($4);
852 }
853
854 | MLST check_login SP pathname CRLF
855 {
856 if ($2 && $4 != NULL)
857 mlst($4);
858 if ($4 != NULL)
859 free($4);
860 }
861
862 | MLST check_login CRLF
863 {
864 mlst(NULL);
865 }
866
867 | MLSD check_login SP pathname CRLF
868 {
869 if ($2 && $4 != NULL)
870 mlsd($4);
871 if ($4 != NULL)
872 free($4);
873 }
874
875 | MLSD check_login CRLF
876 {
877 mlsd(NULL);
878 }
879
880 | error CRLF
881 {
882 yyerrok;
883 }
884 ;
885
886rcmd
887 : REST check_login SP NUMBER CRLF
888 {
889 if ($2) {
890 REASSIGN(fromname, NULL);
891 restart_point = (off_t)$4.ll;
892 reply(350,
893 "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
894 (LLT)restart_point);
895 }
896 }
897
898 | RNFR SP pathname CRLF
899 {
900 restart_point = (off_t) 0;
901 if (check_write($3, 0)) {
902 REASSIGN(fromname, NULL);
903 fromname = renamefrom($3);
904 }
905 if ($3 != NULL)
906 free($3);
907 }
908 ;
909
910username
911 : STRING
912 ;
913
914password
915 : /* empty */
916 {
917 $$ = (char *)calloc(1, sizeof(char));
918 }
919
920 | STRING
921 ;
922
923byte_size
924 : NUMBER
925 {
926 $$ = $1.i;
927 }
928 ;
929
930host_port
931 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
932 NUMBER COMMA NUMBER
933 {
934 char *a, *p;
935
936 memset(&data_dest, 0, sizeof(data_dest));
937 data_dest.su_len = sizeof(struct sockaddr_in);
938 data_dest.su_family = AF_INET;
939 p = (char *)&data_dest.su_port;
940 p[0] = $9.i; p[1] = $11.i;
941 a = (char *)&data_dest.su_addr;
942 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
943 }
944 ;
945
946host_long_port4
947 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
948 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
949 NUMBER
950 {
951 char *a, *p;
952
953 memset(&data_dest, 0, sizeof(data_dest));
954 data_dest.su_len = sizeof(struct sockaddr_in);
955 data_dest.su_family = AF_INET;
956 p = (char *)&data_dest.su_port;
957 p[0] = $15.i; p[1] = $17.i;
958 a = (char *)&data_dest.su_addr;
959 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
960
961 /* reject invalid LPRT command */
962 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
963 memset(&data_dest, 0, sizeof(data_dest));
964 }
965 ;
966
967host_long_port6
968 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
969 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
970 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
971 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
972 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
973 NUMBER
974 {
975#ifdef INET6
976 char *a, *p;
977
978 memset(&data_dest, 0, sizeof(data_dest));
979 data_dest.su_len = sizeof(struct sockaddr_in6);
980 data_dest.su_family = AF_INET6;
981 p = (char *)&data_dest.su_port;
982 p[0] = $39.i; p[1] = $41.i;
983 a = (char *)&data_dest.si_su.su_sin6.sin6_addr;
984 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
985 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
986 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
987 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
988 if (his_addr.su_family == AF_INET6) {
989 /* XXX: more sanity checks! */
990 data_dest.su_scope_id = his_addr.su_scope_id;
991 }
992#else
993 memset(&data_dest, 0, sizeof(data_dest));
994#endif /* INET6 */
995 /* reject invalid LPRT command */
996 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
997 memset(&data_dest, 0, sizeof(data_dest));
998 }
999 ;
1000
1001form_code
1002 : N
1003 {
1004 $$ = FORM_N;
1005 }
1006
1007 | T
1008 {
1009 $$ = FORM_T;
1010 }
1011
1012 | C
1013 {
1014 $$ = FORM_C;
1015 }
1016 ;
1017
1018type_code
1019 : A
1020 {
1021 cmd_type = TYPE_A;
1022 cmd_form = FORM_N;
1023 }
1024
1025 | A SP form_code
1026 {
1027 cmd_type = TYPE_A;
1028 cmd_form = $3;
1029 }
1030
1031 | E
1032 {
1033 cmd_type = TYPE_E;
1034 cmd_form = FORM_N;
1035 }
1036
1037 | E SP form_code
1038 {
1039 cmd_type = TYPE_E;
1040 cmd_form = $3;
1041 }
1042
1043 | I
1044 {
1045 cmd_type = TYPE_I;
1046 }
1047
1048 | L
1049 {
1050 cmd_type = TYPE_L;
1051 cmd_bytesz = NBBY;
1052 }
1053
1054 | L SP byte_size
1055 {
1056 cmd_type = TYPE_L;
1057 cmd_bytesz = $3;
1058 }
1059
1060 /* this is for a bug in the BBN ftp */
1061 | L byte_size
1062 {
1063 cmd_type = TYPE_L;
1064 cmd_bytesz = $2;
1065 }
1066 ;
1067
1068struct_code
1069 : F
1070 {
1071 $$ = STRU_F;
1072 }
1073
1074 | R
1075 {
1076 $$ = STRU_R;
1077 }
1078
1079 | P
1080 {
1081 $$ = STRU_P;
1082 }
1083 ;
1084
1085mode_code
1086 : S
1087 {
1088 $$ = MODE_S;
1089 }
1090
1091 | B
1092 {
1093 $$ = MODE_B;
1094 }
1095
1096 | C
1097 {
1098 $$ = MODE_C;
1099 }
1100 ;
1101
1102pathname
1103 : pathstring
1104 {
1105 /*
1106 * Problem: this production is used for all pathname
1107 * processing, but only gives a 550 error reply.
1108 * This is a valid reply in some cases but not in
1109 * others.
1110 */
1111 if (logged_in && $1 && *$1 == '~') {
1112 char *path, *home, *result;
1113 size_t len;
1114
1115 path = strchr($1 + 1, '/');
1116 if (path != NULL)
1117 *path++ = '\0';
1118 if ($1[1] == '\0')
1119 home = homedir;
1120 else {
1121 struct passwd *hpw;
1122
1123 if ((hpw = getpwnam($1 + 1)) != NULL)
1124 home = hpw->pw_dir;
1125 else
1126 home = $1;
1127 }
1128 len = strlen(home) + 1;
1129 if (path != NULL)
1130 len += strlen(path) + 1;
1131 if ((result = malloc(len)) == NULL)
1132 fatal("Local resource failure: malloc");
1133 strlcpy(result, home, len);
1134 if (path != NULL) {
1135 strlcat(result, "/", len);
1136 strlcat(result, path, len);
1137 }
1138 $$ = result;
1139 free($1);
1140 } else
1141 $$ = $1;
1142 }
1143 ;
1144
1145pathstring
1146 : STRING
1147 ;
1148
1149octal_number
1150 : NUMBER
1151 {
1152 int ret, dec, multby, digit;
1153
1154 /*
1155 * Convert a number that was read as decimal number
1156 * to what it would be if it had been read as octal.
1157 */
1158 dec = $1.i;
1159 multby = 1;
1160 ret = 0;
1161 while (dec) {
1162 digit = dec%10;
1163 if (digit > 7) {
1164 ret = -1;
1165 break;
1166 }
1167 ret += digit * multby;
1168 multby *= 8;
1169 dec /= 10;
1170 }
1171 $$ = ret;
1172 }
1173 ;
1174
1175mechanism_name
1176 : STRING
1177 ;
1178
1179base64data
1180 : STRING
1181 ;
1182
1183prot_code
1184 : STRING
1185 ;
1186
1187decimal_integer
1188 : NUMBER
1189 {
1190 $$ = $1.i;
1191 }
1192 ;
1193
1194check_login
1195 : /* empty */
1196 {
1197 if (logged_in)
1198 $$ = 1;
1199 else {
1200 reply(530, "Please login with USER and PASS.");
1201 $$ = 0;
1202 hasyyerrored = 1;
1203 }
1204 }
1205 ;
1206
1207%%
1208
1209#define CMD 0 /* beginning of command */
1210#define ARGS 1 /* expect miscellaneous arguments */
1211#define STR1 2 /* expect SP followed by STRING */
1212#define STR2 3 /* expect STRING */
1213#define OSTR 4 /* optional SP then STRING */
1214#define ZSTR1 5 /* SP then optional STRING */
1215#define ZSTR2 6 /* optional STRING after SP */
1216#define SITECMD 7 /* SITE command */
1217#define NSTR 8 /* Number followed by a string */
1218#define NOARGS 9 /* No arguments allowed */
1219#define EOLN 10 /* End of line */
1220
1221struct tab cmdtab[] = {
1222 /* From RFC 959, in order defined (5.3.1) */
1223 { "USER", USER, STR1, 1, "<sp> username" },
1224 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
1225 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1226 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1227 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1228 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1229 { "QUIT", QUIT, NOARGS, 1, "(terminate service)" },
1230 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" },
1231 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1232 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1233 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1234 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" },
1235 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1236 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1237 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1238 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1239 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1240 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1241 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1242 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1243 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1244 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1245 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1246 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1247 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1248 { "ABOR", ABOR, NOARGS, 4, "(abort operation)" },
1249 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1250 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1251 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1252 { "PWD", PWD, NOARGS, 1, "(return current directory)" },
1253 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1254 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1255 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1256 { "SYST", SYST, NOARGS, 1, "(get type of operating system)" },
1257 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" },
1258 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1259 { "NOOP", NOOP, NOARGS, 2, "" },
1260
1261 /* From RFC 2228, in order defined */
1262 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" },
1263 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" },
1264 { "PROT", PROT, STR1, 1, "<sp> prot-code" },
1265 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" },
1266 { "CCC", CCC, NOARGS, 1, "(Disable data protection)" },
1267 { "MIC", MIC, STR1, 4, "<sp> base64data" },
1268 { "CONF", CONF, STR1, 4, "<sp> base64data" },
1269 { "ENC", ENC, STR1, 4, "<sp> base64data" },
1270
1271 /* From RFC 2389, in order defined */
1272 { "FEAT", FEAT, NOARGS, 1, "(display extended features)" },
1273 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" },
1274
1275 /* from draft-ietf-ftpext-mlst-11 */
1276 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1277 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1278 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" },
1279 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" },
1280
1281 /* obsolete commands */
1282 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1283 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1284 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1285 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1286 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1287 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1288 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1289 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1290 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1291 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1292 { "XPWD", PWD, NOARGS, 1, "(return current directory)" },
1293 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1294
1295 { NULL, 0, 0, 0, 0 }
1296};
1297
1298struct tab sitetab[] = {
1299 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1300 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1301 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1302 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" },
1303 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" },
1304 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1305 { NULL, 0, 0, 0, NULL }
1306};
1307
1308/*
1309 * Check if a filename is allowed to be modified (isupload == 0) or
1310 * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
1311 * If the filename is NULL, fail.
1312 * If the filename is "", don't do the sane name check.
1313 */
1314static int
1315check_write(const char *file, int isupload)
1316{
1317 if (file == NULL)
1318 return (0);
1319 if (! logged_in) {
1320 reply(530, "Please login with USER and PASS.");
1321 return (0);
1322 }
1323 /* checking modify */
1324 if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
1325 reply(502, "No permission to use this command.");
1326 return (0);
1327 }
1328 /* checking upload */
1329 if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
1330 reply(502, "No permission to use this command.");
1331 return (0);
1332 }
1333
1334 /* checking sanenames */
1335 if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) {
1336 const char *p;
1337
1338 if (file[0] == '.')
1339 goto insane_name;
1340 for (p = file; *p; p++) {
1341 if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' ||
1342 *p == ',' || *p == '.' || *p == '_')
1343 continue;
1344 insane_name:
1345 reply(553, "File name `%s' not allowed.", file);
1346 return (0);
1347 }
1348 }
1349 return (1);
1350}
1351
1352struct tab *
1353lookup(struct tab *p, const char *cmd)
1354{
1355
1356 for (; p->name != NULL; p++)
1357 if (strcasecmp(cmd, p->name) == 0)
1358 return (p);
1359 return (0);
1360}
1361
1362#include <arpa/telnet.h>
1363
1364/*
1365 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1366 */
1367char *
1368getline(char *s, int n, FILE *iop)
1369{
1370 int c;
1371 char *cs;
1372
1373 cs = s;
1374/* tmpline may contain saved command from urgent mode interruption */
1375 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1376 *cs++ = tmpline[c];
1377 if (tmpline[c] == '\n') {
1378 *cs++ = '\0';
1379 if (ftpd_debug)
1380 syslog(LOG_DEBUG, "command: %s", s);
1381 tmpline[0] = '\0';
1382 return(s);
1383 }
1384 if (c == 0)
1385 tmpline[0] = '\0';
1386 }
1387 while ((c = getc(iop)) != EOF) {
1388 total_bytes++;
1389 total_bytes_in++;
1390 c &= 0377;
1391 if (c == IAC) {
1392 if ((c = getc(iop)) != EOF) {
1393 total_bytes++;
1394 total_bytes_in++;
1395 c &= 0377;
1396 switch (c) {
1397 case WILL:
1398 case WONT:
1399 c = getc(iop);
1400 total_bytes++;
1401 total_bytes_in++;
1402 cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
1403 (void) fflush(stdout);
1404 continue;
1405 case DO:
1406 case DONT:
1407 c = getc(iop);
1408 total_bytes++;
1409 total_bytes_in++;
1410 cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
1411 (void) fflush(stdout);
1412 continue;
1413 case IAC:
1414 break;
1415 default:
1416 continue; /* ignore command */
1417 }
1418 }
1419 }
1420 *cs++ = c;
1421 if (--n <= 0 || c == '\n')
1422 break;
1423 }
1424 if (c == EOF && cs == s)
1425 return (NULL);
1426 *cs++ = '\0';
1427 if (ftpd_debug) {
1428 if ((curclass.type != CLASS_GUEST &&
1429 strncasecmp(s, "PASS ", 5) == 0) ||
1430 strncasecmp(s, "ACCT ", 5) == 0) {
1431 /* Don't syslog passwords */
1432 syslog(LOG_DEBUG, "command: %.4s ???", s);
1433 } else {
1434 char *cp;
1435 int len;
1436
1437 /* Don't syslog trailing CR-LF */
1438 len = strlen(s);
1439 cp = s + len - 1;
1440 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1441 --cp;
1442 --len;
1443 }
1444 syslog(LOG_DEBUG, "command: %.*s", len, s);
1445 }
1446 }
1447 return (s);
1448}
1449
1450void
1451ftp_handle_line(char *cp)
1452{
1453
1454 cmdp = cp;
1455 yyparse();
1456}
1457
1458void
1459ftp_loop(void)
1460{
1461
1462 while (1) {
1463 (void) alarm(curclass.timeout);
1464 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1465 reply(221, "You could at least say goodbye.");
1466 dologout(0);
1467 }
1468 (void) alarm(0);
1469 ftp_handle_line(cbuf);
1470 }
1471 /*NOTREACHED*/
1472}
1473
1474int
1475yylex(void)
1476{
1477 static int cpos, state;
1478 char *cp, *cp2;
1479 struct tab *p;
1480 int n;
1481 char c;
1482
1483 switch (state) {
1484
1485 case CMD:
1486 hasyyerrored = 0;
1487 if ((cp = strchr(cmdp, '\r'))) {
1488 *cp = '\0';
1489#if HAVE_SETPROCTITLE
1490 if (strncasecmp(cmdp, "PASS", 4) != 0 &&
1491 strncasecmp(cmdp, "ACCT", 4) != 0)
1492 setproctitle("%s: %s", proctitle, cmdp);
1493#endif /* HAVE_SETPROCTITLE */
1494 *cp++ = '\n';
1495 *cp = '\0';
1496 }
1497 if ((cp = strpbrk(cmdp, " \n")))
1498 cpos = cp - cmdp;
1499 if (cpos == 0)
1500 cpos = 4;
1501 c = cmdp[cpos];
1502 cmdp[cpos] = '\0';
1503 p = lookup(cmdtab, cmdp);
1504 cmdp[cpos] = c;
1505 if (p != NULL) {
1506 if (is_oob && ! CMD_OOB(p)) {
1507 /* command will be handled in-band */
1508 return (0);
1509 } else if (! CMD_IMPLEMENTED(p)) {
1510 reply(502, "%s command not implemented.",
1511 p->name);
1512 hasyyerrored = 1;
1513 break;
1514 }
1515 state = p->state;
1516 yylval.s = p->name;
1517 return (p->token);
1518 }
1519 break;
1520
1521 case SITECMD:
1522 if (cmdp[cpos] == ' ') {
1523 cpos++;
1524 return (SP);
1525 }
1526 cp = &cmdp[cpos];
1527 if ((cp2 = strpbrk(cp, " \n")))
1528 cpos = cp2 - cmdp;
1529 c = cmdp[cpos];
1530 cmdp[cpos] = '\0';
1531 p = lookup(sitetab, cp);
1532 cmdp[cpos] = c;
1533 if (p != NULL) {
1534 if (!CMD_IMPLEMENTED(p)) {
1535 reply(502, "SITE %s command not implemented.",
1536 p->name);
1537 hasyyerrored = 1;
1538 break;
1539 }
1540 state = p->state;
1541 yylval.s = p->name;
1542 return (p->token);
1543 }
1544 break;
1545
1546 case OSTR:
1547 if (cmdp[cpos] == '\n') {
1548 state = EOLN;
1549 return (CRLF);
1550 }
1551 /* FALLTHROUGH */
1552
1553 case STR1:
1554 case ZSTR1:
1555 dostr1:
1556 if (cmdp[cpos] == ' ') {
1557 cpos++;
1558 state = state == OSTR ? STR2 : state+1;
1559 return (SP);
1560 }
1561 break;
1562
1563 case ZSTR2:
1564 if (cmdp[cpos] == '\n') {
1565 state = EOLN;
1566 return (CRLF);
1567 }
1568 /* FALLTHROUGH */
1569
1570 case STR2:
1571 cp = &cmdp[cpos];
1572 n = strlen(cp);
1573 cpos += n - 1;
1574 /*
1575 * Make sure the string is nonempty and \n terminated.
1576 */
1577 if (n > 1 && cmdp[cpos] == '\n') {
1578 cmdp[cpos] = '\0';
1579 yylval.s = ftpd_strdup(cp);
1580 cmdp[cpos] = '\n';
1581 state = ARGS;
1582 return (STRING);
1583 }
1584 break;
1585
1586 case NSTR:
1587 if (cmdp[cpos] == ' ') {
1588 cpos++;
1589 return (SP);
1590 }
1591 if (isdigit((unsigned char)cmdp[cpos])) {
1592 cp = &cmdp[cpos];
1593 while (isdigit((unsigned char)cmdp[++cpos]))
1594 ;
1595 c = cmdp[cpos];
1596 cmdp[cpos] = '\0';
1597 yylval.u.i = atoi(cp);
1598 cmdp[cpos] = c;
1599 state = STR1;
1600 return (NUMBER);
1601 }
1602 state = STR1;
1603 goto dostr1;
1604
1605 case ARGS:
1606 if (isdigit((unsigned char)cmdp[cpos])) {
1607 cp = &cmdp[cpos];
1608 while (isdigit((unsigned char)cmdp[++cpos]))
1609 ;
1610 c = cmdp[cpos];
1611 cmdp[cpos] = '\0';
1612 yylval.u.i = atoi(cp);
1613 yylval.u.ll = STRTOLL(cp, (char **)NULL, 10);
1614 cmdp[cpos] = c;
1615 return (NUMBER);
1616 }
1617 if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
1618 && !isalnum((unsigned char)cmdp[cpos + 3])) {
1619 cpos += 3;
1620 return (ALL);
1621 }
1622 switch (cmdp[cpos++]) {
1623
1624 case '\n':
1625 state = EOLN;
1626 return (CRLF);
1627
1628 case ' ':
1629 return (SP);
1630
1631 case ',':
1632 return (COMMA);
1633
1634 case 'A':
1635 case 'a':
1636 return (A);
1637
1638 case 'B':
1639 case 'b':
1640 return (B);
1641
1642 case 'C':
1643 case 'c':
1644 return (C);
1645
1646 case 'E':
1647 case 'e':
1648 return (E);
1649
1650 case 'F':
1651 case 'f':
1652 return (F);
1653
1654 case 'I':
1655 case 'i':
1656 return (I);
1657
1658 case 'L':
1659 case 'l':
1660 return (L);
1661
1662 case 'N':
1663 case 'n':
1664 return (N);
1665
1666 case 'P':
1667 case 'p':
1668 return (P);
1669
1670 case 'R':
1671 case 'r':
1672 return (R);
1673
1674 case 'S':
1675 case 's':
1676 return (S);
1677
1678 case 'T':
1679 case 't':
1680 return (T);
1681
1682 }
1683 break;
1684
1685 case NOARGS:
1686 if (cmdp[cpos] == '\n') {
1687 state = EOLN;
1688 return (CRLF);
1689 }
1690 c = cmdp[cpos];
1691 cmdp[cpos] = '\0';
1692 reply(501, "'%s' command does not take any arguments.", cmdp);
1693 hasyyerrored = 1;
1694 cmdp[cpos] = c;
1695 break;
1696
1697 case EOLN:
1698 state = CMD;
1699 return (0);
1700
1701 default:
1702 fatal("Unknown state in scanner.");
1703 }
1704 yyerror(NULL);
1705 state = CMD;
1706 return (0);
1707}
1708
1709/* ARGSUSED */
1710void
1711yyerror(char *s)
1712{
1713 char *cp;
1714
1715 if (hasyyerrored || is_oob)
1716 return;
1717 if ((cp = strchr(cmdp,'\n')) != NULL)
1718 *cp = '\0';
1719 reply(500, "'%s': command not understood.", cmdp);
1720 hasyyerrored = 1;
1721}
1722
1723static void
1724help(struct tab *ctab, const char *s)
1725{
1726 struct tab *c;
1727 int width, NCMDS;
1728 char *htype;
1729
1730 if (ctab == sitetab)
1731 htype = "SITE ";
1732 else
1733 htype = "";
1734 width = 0, NCMDS = 0;
1735 for (c = ctab; c->name != NULL; c++) {
1736 int len = strlen(c->name);
1737
1738 if (len > width)
1739 width = len;
1740 NCMDS++;
1741 }
1742 width = (width + 8) &~ 7;
1743 if (s == 0) {
1744 int i, j, w;
1745 int columns, lines;
1746
1747 reply(-214, "%s", "");
1748 reply(0, "The following %scommands are recognized.", htype);
1749 reply(0, "(`-' = not implemented, `+' = supports options)");
1750 columns = 76 / width;
1751 if (columns == 0)
1752 columns = 1;
1753 lines = (NCMDS + columns - 1) / columns;
1754 for (i = 0; i < lines; i++) {
1755 cprintf(stdout, " ");
1756 for (j = 0; j < columns; j++) {
1757 c = ctab + j * lines + i;
1758 cprintf(stdout, "%s", c->name);
1759 w = strlen(c->name);
1760 if (! CMD_IMPLEMENTED(c)) {
1761 CPUTC('-', stdout);
1762 w++;
1763 }
1764 if (CMD_HAS_OPTIONS(c)) {
1765 CPUTC('+', stdout);
1766 w++;
1767 }
1768 if (c + lines >= &ctab[NCMDS])
1769 break;
1770 while (w < width) {
1771 CPUTC(' ', stdout);
1772 w++;
1773 }
1774 }
1775 cprintf(stdout, "\r\n");
1776 }
1777 (void) fflush(stdout);
1778 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1779 return;
1780 }
1781 c = lookup(ctab, s);
1782 if (c == (struct tab *)0) {
1783 reply(502, "Unknown command '%s'.", s);
1784 return;
1785 }
1786 if (CMD_IMPLEMENTED(c))
1787 reply(214, "Syntax: %s%s %s", htype, c->name, c->help);
1788 else
1789 reply(504, "%s%-*s\t%s; not implemented.", htype, width,
1790 c->name, c->help);
1791}
1792
1793/*
1794 * Check that the structures used for a PORT, LPRT or EPRT command are
1795 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
1796 * If family != -1 check that his_addr.su_family == family.
1797 */
1798static void
1799port_check(const char *cmd, int family)
1800{
1801 char h1[NI_MAXHOST], h2[NI_MAXHOST];
1802 char s1[NI_MAXHOST], s2[NI_MAXHOST];
1803#ifdef NI_WITHSCOPEID
1804 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1805#else
1806 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1807#endif
1808
1809 if (epsvall) {
1810 reply(501, "%s disallowed after EPSV ALL", cmd);
1811 return;
1812 }
1813
1814 if (family != -1 && his_addr.su_family != family) {
1815 port_check_fail:
1816 reply(500, "Illegal %s command rejected", cmd);
1817 return;
1818 }
1819
1820 if (data_dest.su_family != his_addr.su_family)
1821 goto port_check_fail;
1822
1823 /* be paranoid, if told so */
1824 if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
1825#ifdef INET6
1826 /*
1827 * be paranoid, there are getnameinfo implementation that does
1828 * not present scopeid portion
1829 */
1830 if (data_dest.su_family == AF_INET6 &&
1831 data_dest.su_scope_id != his_addr.su_scope_id)
1832 goto port_check_fail;
1833#endif
1834
1835 if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
1836 h1, sizeof(h1), s1, sizeof(s1), niflags))
1837 goto port_check_fail;
1838 if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1839 h2, sizeof(h2), s2, sizeof(s2), niflags))
1840 goto port_check_fail;
1841
1842 if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
1843 goto port_check_fail;
1844 }
1845
1846 usedefault = 0;
1847 if (pdata >= 0) {
1848 (void) close(pdata);
1849 pdata = -1;
1850 }
1851 reply(200, "%s command successful.", cmd);
1852}
2
3/*-
4 * Copyright (c) 1997-2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39/*
40 * Copyright (c) 1985, 1988, 1993, 1994
41 * The Regents of the University of California. All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 *
67 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
68 */
69
70/*
71 * Grammar for FTP commands.
72 * See RFC 959.
73 */
74
75%{
76#include <sys/cdefs.h>
77
78#ifndef lint
79#if 0
80static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
81#else
82__RCSID("$NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $");
83#endif
84#endif /* not lint */
85
86#include <sys/param.h>
87#include <sys/socket.h>
88#include <sys/stat.h>
89
90#include <netinet/in.h>
91#include <arpa/ftp.h>
92#include <arpa/inet.h>
93
94#include <ctype.h>
95#include <errno.h>
96#include <pwd.h>
97#include <stdio.h>
98#include <stdlib.h>
99#include <string.h>
100#include <syslog.h>
101#include <time.h>
102#include <tzfile.h>
103#include <unistd.h>
104#include <netdb.h>
105
106#ifdef KERBEROS5
107#include <krb5/krb5.h>
108#endif
109
110#include "extern.h"
111#include "version.h"
112
113static int cmd_type;
114static int cmd_form;
115static int cmd_bytesz;
116
117char cbuf[FTP_BUFLEN];
118char *cmdp;
119char *fromname;
120
121extern int epsvall;
122struct tab sitetab[];
123
124static int check_write(const char *, int);
125static void help(struct tab *, const char *);
126static void port_check(const char *, int);
127 int yylex(void);
128
129%}
130
131%union {
132 struct {
133 LLT ll;
134 int i;
135 } u;
136 char *s;
137}
138
139%token
140 A B C E F I
141 L N P R S T
142
143 SP CRLF COMMA ALL
144
145 USER PASS ACCT CWD CDUP SMNT
146 QUIT REIN PORT PASV TYPE STRU
147 MODE RETR STOR STOU APPE ALLO
148 REST RNFR RNTO ABOR DELE RMD
149 MKD PWD LIST NLST SITE SYST
150 STAT HELP NOOP
151
152 AUTH ADAT PROT PBSZ CCC MIC
153 CONF ENC
154
155 FEAT OPTS
156
157 SIZE MDTM MLST MLSD
158
159 LPRT LPSV EPRT EPSV
160
161 MAIL MLFL MRCP MRSQ MSAM MSND
162 MSOM
163
164 CHMOD IDLE RATEGET RATEPUT UMASK
165
166 LEXERR
167
168%token <s> STRING
169%token <u> NUMBER
170
171%type <u.i> check_login octal_number byte_size
172%type <u.i> struct_code mode_code type_code form_code decimal_integer
173%type <s> pathstring pathname password username
174%type <s> mechanism_name base64data prot_code
175
176%start cmd_sel
177
178%%
179
180cmd_sel
181 : cmd
182 {
183 REASSIGN(fromname, NULL);
184 restart_point = (off_t) 0;
185 }
186
187 | rcmd
188
189 ;
190
191cmd
192 /* RFC 959 */
193 : USER SP username CRLF
194 {
195 user($3);
196 free($3);
197 }
198
199 | PASS SP password CRLF
200 {
201 pass($3);
202 memset($3, 0, strlen($3));
203 free($3);
204 }
205
206 | CWD check_login CRLF
207 {
208 if ($2)
209 cwd(homedir);
210 }
211
212 | CWD check_login SP pathname CRLF
213 {
214 if ($2 && $4 != NULL)
215 cwd($4);
216 if ($4 != NULL)
217 free($4);
218 }
219
220 | CDUP check_login CRLF
221 {
222 if ($2)
223 cwd("..");
224 }
225
226 | QUIT CRLF
227 {
228 if (logged_in) {
229 reply(-221, "%s", "");
230 reply(0,
231 "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
232 (LLT)total_data, PLURAL(total_data),
233 (LLT)total_files, PLURAL(total_files));
234 reply(0,
235 "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
236 (LLT)total_bytes, PLURAL(total_bytes),
237 (LLT)total_xfers, PLURAL(total_xfers));
238 }
239 reply(221,
240 "Thank you for using the FTP service on %s.",
241 hostname);
242 if (logged_in && logging) {
243 syslog(LOG_INFO,
244 "Data traffic: " LLF " byte%s in " LLF " file%s",
245 (LLT)total_data, PLURAL(total_data),
246 (LLT)total_files, PLURAL(total_files));
247 syslog(LOG_INFO,
248 "Total traffic: " LLF " byte%s in " LLF " transfer%s",
249 (LLT)total_bytes, PLURAL(total_bytes),
250 (LLT)total_xfers, PLURAL(total_xfers));
251 }
252
253 dologout(0);
254 }
255
256 | PORT check_login SP host_port CRLF
257 {
258 if ($2)
259 port_check("PORT", AF_INET);
260 }
261
262 | LPRT check_login SP host_long_port4 CRLF
263 {
264 if ($2)
265 port_check("LPRT", AF_INET);
266 }
267
268 | LPRT check_login SP host_long_port6 CRLF
269 {
270#ifdef INET6
271 if ($2)
272 port_check("LPRT", AF_INET6);
273#else
274 reply(500, "IPv6 support not available.");
275#endif
276 }
277
278 | EPRT check_login SP STRING CRLF
279 {
280 if ($2) {
281 if (extended_port($4) == 0)
282 port_check("EPRT", -1);
283 }
284 free($4);
285 }
286
287 | PASV check_login CRLF
288 {
289 if ($2) {
290 if (CURCLASS_FLAGS_ISSET(passive))
291 passive();
292 else
293 reply(500, "PASV mode not available.");
294 }
295 }
296
297 | LPSV check_login CRLF
298 {
299 if ($2) {
300 if (CURCLASS_FLAGS_ISSET(passive)) {
301 if (epsvall)
302 reply(501,
303 "LPSV disallowed after EPSV ALL");
304 else
305 long_passive("LPSV", PF_UNSPEC);
306 } else
307 reply(500, "LPSV mode not available.");
308 }
309 }
310
311 | EPSV check_login SP NUMBER CRLF
312 {
313 if ($2) {
314 if (CURCLASS_FLAGS_ISSET(passive))
315 long_passive("EPSV",
316 epsvproto2af($4.i));
317 else
318 reply(500, "EPSV mode not available.");
319 }
320 }
321
322 | EPSV check_login SP ALL CRLF
323 {
324 if ($2) {
325 if (CURCLASS_FLAGS_ISSET(passive)) {
326 reply(200,
327 "EPSV ALL command successful.");
328 epsvall++;
329 } else
330 reply(500, "EPSV mode not available.");
331 }
332 }
333
334 | EPSV check_login CRLF
335 {
336 if ($2) {
337 if (CURCLASS_FLAGS_ISSET(passive))
338 long_passive("EPSV", PF_UNSPEC);
339 else
340 reply(500, "EPSV mode not available.");
341 }
342 }
343
344 | TYPE check_login SP type_code CRLF
345 {
346 if ($2) {
347
348 switch (cmd_type) {
349
350 case TYPE_A:
351 if (cmd_form == FORM_N) {
352 reply(200, "Type set to A.");
353 type = cmd_type;
354 form = cmd_form;
355 } else
356 reply(504, "Form must be N.");
357 break;
358
359 case TYPE_E:
360 reply(504, "Type E not implemented.");
361 break;
362
363 case TYPE_I:
364 reply(200, "Type set to I.");
365 type = cmd_type;
366 break;
367
368 case TYPE_L:
369#if NBBY == 8
370 if (cmd_bytesz == 8) {
371 reply(200,
372 "Type set to L (byte size 8).");
373 type = cmd_type;
374 } else
375 reply(504, "Byte size must be 8.");
376#else /* NBBY == 8 */
377 UNIMPLEMENTED for NBBY != 8
378#endif /* NBBY == 8 */
379 }
380
381 }
382 }
383
384 | STRU check_login SP struct_code CRLF
385 {
386 if ($2) {
387 switch ($4) {
388
389 case STRU_F:
390 reply(200, "STRU F ok.");
391 break;
392
393 default:
394 reply(504, "Unimplemented STRU type.");
395 }
396 }
397 }
398
399 | MODE check_login SP mode_code CRLF
400 {
401 if ($2) {
402 switch ($4) {
403
404 case MODE_S:
405 reply(200, "MODE S ok.");
406 break;
407
408 default:
409 reply(502, "Unimplemented MODE type.");
410 }
411 }
412 }
413
414 | RETR check_login SP pathname CRLF
415 {
416 if ($2 && $4 != NULL)
417 retrieve(NULL, $4);
418 if ($4 != NULL)
419 free($4);
420 }
421
422 | STOR SP pathname CRLF
423 {
424 if (check_write($3, 1))
425 store($3, "w", 0);
426 if ($3 != NULL)
427 free($3);
428 }
429
430 | STOU SP pathname CRLF
431 {
432 if (check_write($3, 1))
433 store($3, "w", 1);
434 if ($3 != NULL)
435 free($3);
436 }
437
438 | APPE SP pathname CRLF
439 {
440 if (check_write($3, 1))
441 store($3, "a", 0);
442 if ($3 != NULL)
443 free($3);
444 }
445
446 | ALLO check_login SP NUMBER CRLF
447 {
448 if ($2)
449 reply(202, "ALLO command ignored.");
450 }
451
452 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
453 {
454 if ($2)
455 reply(202, "ALLO command ignored.");
456 }
457
458 | RNTO SP pathname CRLF
459 {
460 if (check_write($3, 0)) {
461 if (fromname) {
462 renamecmd(fromname, $3);
463 REASSIGN(fromname, NULL);
464 } else {
465 reply(503, "Bad sequence of commands.");
466 }
467 }
468 if ($3 != NULL)
469 free($3);
470 }
471
472 | ABOR check_login CRLF
473 {
474 if (is_oob)
475 abor();
476 else if ($2)
477 reply(225, "ABOR command successful.");
478 }
479
480 | DELE SP pathname CRLF
481 {
482 if (check_write($3, 0))
483 delete($3);
484 if ($3 != NULL)
485 free($3);
486 }
487
488 | RMD SP pathname CRLF
489 {
490 if (check_write($3, 0))
491 removedir($3);
492 if ($3 != NULL)
493 free($3);
494 }
495
496 | MKD SP pathname CRLF
497 {
498 if (check_write($3, 0))
499 makedir($3);
500 if ($3 != NULL)
501 free($3);
502 }
503
504 | PWD check_login CRLF
505 {
506 if ($2)
507 pwd();
508 }
509
510 | LIST check_login CRLF
511 {
512 char *argv[] = { INTERNAL_LS, "-lgA", NULL };
513
514 if (CURCLASS_FLAGS_ISSET(hidesymlinks))
515 argv[1] = "-LlgA";
516 if ($2)
517 retrieve(argv, "");
518 }
519
520 | LIST check_login SP pathname CRLF
521 {
522 char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
523
524 if (CURCLASS_FLAGS_ISSET(hidesymlinks))
525 argv[1] = "-LlgA";
526 if ($2 && $4 != NULL) {
527 argv[2] = $4;
528 retrieve(argv, $4);
529 }
530 if ($4 != NULL)
531 free($4);
532 }
533
534 | NLST check_login CRLF
535 {
536 if ($2)
537 send_file_list(".");
538 }
539
540 | NLST check_login SP pathname CRLF
541 {
542 if ($2)
543 send_file_list($4);
544 free($4);
545 }
546
547 | SITE SP HELP CRLF
548 {
549 help(sitetab, NULL);
550 }
551
552 | SITE SP CHMOD SP octal_number SP pathname CRLF
553 {
554 if (check_write($7, 0)) {
555 if (($5 == -1) || ($5 > 0777))
556 reply(501,
557 "CHMOD: Mode value must be between 0 and 0777");
558 else if (chmod($7, $5) < 0)
559 perror_reply(550, $7);
560 else
561 reply(200, "CHMOD command successful.");
562 }
563 if ($7 != NULL)
564 free($7);
565 }
566
567 | SITE SP HELP SP STRING CRLF
568 {
569 help(sitetab, $5);
570 free($5);
571 }
572
573 | SITE SP IDLE check_login CRLF
574 {
575 if ($4) {
576 reply(200,
577 "Current IDLE time limit is " LLF
578 " seconds; max " LLF,
579 (LLT)curclass.timeout,
580 (LLT)curclass.maxtimeout);
581 }
582 }
583
584 | SITE SP IDLE check_login SP NUMBER CRLF
585 {
586 if ($4) {
587 if ($6.i < 30 || $6.i > curclass.maxtimeout) {
588 reply(501,
589 "IDLE time limit must be between 30 and "
590 LLF " seconds",
591 (LLT)curclass.maxtimeout);
592 } else {
593 curclass.timeout = $6.i;
594 (void) alarm(curclass.timeout);
595 reply(200,
596 "IDLE time limit set to "
597 LLF " seconds",
598 (LLT)curclass.timeout);
599 }
600 }
601 }
602
603 | SITE SP RATEGET check_login CRLF
604 {
605 if ($4) {
606 reply(200,
607 "Current RATEGET is " LLF " bytes/sec",
608 (LLT)curclass.rateget);
609 }
610 }
611
612 | SITE SP RATEGET check_login SP STRING CRLF
613 {
614 char errbuf[100];
615 char *p = $6;
616 LLT rate;
617
618 if ($4) {
619 rate = strsuftollx("RATEGET", p, 0,
620 curclass.maxrateget
621 ? curclass.maxrateget
622 : LLTMAX, errbuf, sizeof(errbuf));
623 if (errbuf[0])
624 reply(501, "%s", errbuf);
625 else {
626 curclass.rateget = rate;
627 reply(200,
628 "RATEGET set to " LLF " bytes/sec",
629 (LLT)curclass.rateget);
630 }
631 }
632 free($6);
633 }
634
635 | SITE SP RATEPUT check_login CRLF
636 {
637 if ($4) {
638 reply(200,
639 "Current RATEPUT is " LLF " bytes/sec",
640 (LLT)curclass.rateput);
641 }
642 }
643
644 | SITE SP RATEPUT check_login SP STRING CRLF
645 {
646 char errbuf[100];
647 char *p = $6;
648 LLT rate;
649
650 if ($4) {
651 rate = strsuftollx("RATEPUT", p, 0,
652 curclass.maxrateput
653 ? curclass.maxrateput
654 : LLTMAX, errbuf, sizeof(errbuf));
655 if (errbuf[0])
656 reply(501, "%s", errbuf);
657 else {
658 curclass.rateput = rate;
659 reply(200,
660 "RATEPUT set to " LLF " bytes/sec",
661 (LLT)curclass.rateput);
662 }
663 }
664 free($6);
665 }
666
667 | SITE SP UMASK check_login CRLF
668 {
669 int oldmask;
670
671 if ($4) {
672 oldmask = umask(0);
673 (void) umask(oldmask);
674 reply(200, "Current UMASK is %03o", oldmask);
675 }
676 }
677
678 | SITE SP UMASK check_login SP octal_number CRLF
679 {
680 int oldmask;
681
682 if ($4 && check_write("", 0)) {
683 if (($6 == -1) || ($6 > 0777)) {
684 reply(501, "Bad UMASK value");
685 } else {
686 oldmask = umask($6);
687 reply(200,
688 "UMASK set to %03o (was %03o)",
689 $6, oldmask);
690 }
691 }
692 }
693
694 | SYST CRLF
695 {
696 if (EMPTYSTR(version))
697 reply(215, "UNIX Type: L%d", NBBY);
698 else
699 reply(215, "UNIX Type: L%d Version: %s", NBBY,
700 version);
701 }
702
703 | STAT check_login SP pathname CRLF
704 {
705 if ($2 && $4 != NULL)
706 statfilecmd($4);
707 if ($4 != NULL)
708 free($4);
709 }
710
711 | STAT CRLF
712 {
713 if (is_oob)
714 statxfer();
715 else
716 statcmd();
717 }
718
719 | HELP CRLF
720 {
721 help(cmdtab, NULL);
722 }
723
724 | HELP SP STRING CRLF
725 {
726 char *cp = $3;
727
728 if (strncasecmp(cp, "SITE", 4) == 0) {
729 cp = $3 + 4;
730 if (*cp == ' ')
731 cp++;
732 if (*cp)
733 help(sitetab, cp);
734 else
735 help(sitetab, NULL);
736 } else
737 help(cmdtab, $3);
738 free($3);
739 }
740
741 | NOOP CRLF
742 {
743 reply(200, "NOOP command successful.");
744 }
745
746 /* RFC 2228 */
747 | AUTH SP mechanism_name CRLF
748 {
749 reply(502, "RFC 2228 authentication not implemented.");
750 free($3);
751 }
752
753 | ADAT SP base64data CRLF
754 {
755 reply(503,
756 "Please set authentication state with AUTH.");
757 free($3);
758 }
759
760 | PROT SP prot_code CRLF
761 {
762 reply(503,
763 "Please set protection buffer size with PBSZ.");
764 free($3);
765 }
766
767 | PBSZ SP decimal_integer CRLF
768 {
769 reply(503,
770 "Please set authentication state with AUTH.");
771 }
772
773 | CCC CRLF
774 {
775 reply(533, "No protection enabled.");
776 }
777
778 | MIC SP base64data CRLF
779 {
780 reply(502, "RFC 2228 authentication not implemented.");
781 free($3);
782 }
783
784 | CONF SP base64data CRLF
785 {
786 reply(502, "RFC 2228 authentication not implemented.");
787 free($3);
788 }
789
790 | ENC SP base64data CRLF
791 {
792 reply(502, "RFC 2228 authentication not implemented.");
793 free($3);
794 }
795
796 /* RFC 2389 */
797 | FEAT CRLF
798 {
799
800 feat();
801 }
802
803 | OPTS SP STRING CRLF
804 {
805
806 opts($3);
807 free($3);
808 }
809
810
811 /* extensions from draft-ietf-ftpext-mlst-11 */
812
813 /*
814 * Return size of file in a format suitable for
815 * using with RESTART (we just count bytes).
816 */
817 | SIZE check_login SP pathname CRLF
818 {
819 if ($2 && $4 != NULL)
820 sizecmd($4);
821 if ($4 != NULL)
822 free($4);
823 }
824
825 /*
826 * Return modification time of file as an ISO 3307
827 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
828 * where xxx is the fractional second (of any precision,
829 * not necessarily 3 digits)
830 */
831 | MDTM check_login SP pathname CRLF
832 {
833 if ($2 && $4 != NULL) {
834 struct stat stbuf;
835 if (stat($4, &stbuf) < 0)
836 perror_reply(550, $4);
837 else if (!S_ISREG(stbuf.st_mode)) {
838 reply(550, "%s: not a plain file.", $4);
839 } else {
840 struct tm *t;
841
842 t = gmtime(&stbuf.st_mtime);
843 reply(213,
844 "%04d%02d%02d%02d%02d%02d",
845 TM_YEAR_BASE + t->tm_year,
846 t->tm_mon+1, t->tm_mday,
847 t->tm_hour, t->tm_min, t->tm_sec);
848 }
849 }
850 if ($4 != NULL)
851 free($4);
852 }
853
854 | MLST check_login SP pathname CRLF
855 {
856 if ($2 && $4 != NULL)
857 mlst($4);
858 if ($4 != NULL)
859 free($4);
860 }
861
862 | MLST check_login CRLF
863 {
864 mlst(NULL);
865 }
866
867 | MLSD check_login SP pathname CRLF
868 {
869 if ($2 && $4 != NULL)
870 mlsd($4);
871 if ($4 != NULL)
872 free($4);
873 }
874
875 | MLSD check_login CRLF
876 {
877 mlsd(NULL);
878 }
879
880 | error CRLF
881 {
882 yyerrok;
883 }
884 ;
885
886rcmd
887 : REST check_login SP NUMBER CRLF
888 {
889 if ($2) {
890 REASSIGN(fromname, NULL);
891 restart_point = (off_t)$4.ll;
892 reply(350,
893 "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
894 (LLT)restart_point);
895 }
896 }
897
898 | RNFR SP pathname CRLF
899 {
900 restart_point = (off_t) 0;
901 if (check_write($3, 0)) {
902 REASSIGN(fromname, NULL);
903 fromname = renamefrom($3);
904 }
905 if ($3 != NULL)
906 free($3);
907 }
908 ;
909
910username
911 : STRING
912 ;
913
914password
915 : /* empty */
916 {
917 $$ = (char *)calloc(1, sizeof(char));
918 }
919
920 | STRING
921 ;
922
923byte_size
924 : NUMBER
925 {
926 $$ = $1.i;
927 }
928 ;
929
930host_port
931 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
932 NUMBER COMMA NUMBER
933 {
934 char *a, *p;
935
936 memset(&data_dest, 0, sizeof(data_dest));
937 data_dest.su_len = sizeof(struct sockaddr_in);
938 data_dest.su_family = AF_INET;
939 p = (char *)&data_dest.su_port;
940 p[0] = $9.i; p[1] = $11.i;
941 a = (char *)&data_dest.su_addr;
942 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
943 }
944 ;
945
946host_long_port4
947 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
948 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
949 NUMBER
950 {
951 char *a, *p;
952
953 memset(&data_dest, 0, sizeof(data_dest));
954 data_dest.su_len = sizeof(struct sockaddr_in);
955 data_dest.su_family = AF_INET;
956 p = (char *)&data_dest.su_port;
957 p[0] = $15.i; p[1] = $17.i;
958 a = (char *)&data_dest.su_addr;
959 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
960
961 /* reject invalid LPRT command */
962 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
963 memset(&data_dest, 0, sizeof(data_dest));
964 }
965 ;
966
967host_long_port6
968 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
969 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
970 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
971 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
972 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
973 NUMBER
974 {
975#ifdef INET6
976 char *a, *p;
977
978 memset(&data_dest, 0, sizeof(data_dest));
979 data_dest.su_len = sizeof(struct sockaddr_in6);
980 data_dest.su_family = AF_INET6;
981 p = (char *)&data_dest.su_port;
982 p[0] = $39.i; p[1] = $41.i;
983 a = (char *)&data_dest.si_su.su_sin6.sin6_addr;
984 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
985 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
986 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
987 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
988 if (his_addr.su_family == AF_INET6) {
989 /* XXX: more sanity checks! */
990 data_dest.su_scope_id = his_addr.su_scope_id;
991 }
992#else
993 memset(&data_dest, 0, sizeof(data_dest));
994#endif /* INET6 */
995 /* reject invalid LPRT command */
996 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
997 memset(&data_dest, 0, sizeof(data_dest));
998 }
999 ;
1000
1001form_code
1002 : N
1003 {
1004 $$ = FORM_N;
1005 }
1006
1007 | T
1008 {
1009 $$ = FORM_T;
1010 }
1011
1012 | C
1013 {
1014 $$ = FORM_C;
1015 }
1016 ;
1017
1018type_code
1019 : A
1020 {
1021 cmd_type = TYPE_A;
1022 cmd_form = FORM_N;
1023 }
1024
1025 | A SP form_code
1026 {
1027 cmd_type = TYPE_A;
1028 cmd_form = $3;
1029 }
1030
1031 | E
1032 {
1033 cmd_type = TYPE_E;
1034 cmd_form = FORM_N;
1035 }
1036
1037 | E SP form_code
1038 {
1039 cmd_type = TYPE_E;
1040 cmd_form = $3;
1041 }
1042
1043 | I
1044 {
1045 cmd_type = TYPE_I;
1046 }
1047
1048 | L
1049 {
1050 cmd_type = TYPE_L;
1051 cmd_bytesz = NBBY;
1052 }
1053
1054 | L SP byte_size
1055 {
1056 cmd_type = TYPE_L;
1057 cmd_bytesz = $3;
1058 }
1059
1060 /* this is for a bug in the BBN ftp */
1061 | L byte_size
1062 {
1063 cmd_type = TYPE_L;
1064 cmd_bytesz = $2;
1065 }
1066 ;
1067
1068struct_code
1069 : F
1070 {
1071 $$ = STRU_F;
1072 }
1073
1074 | R
1075 {
1076 $$ = STRU_R;
1077 }
1078
1079 | P
1080 {
1081 $$ = STRU_P;
1082 }
1083 ;
1084
1085mode_code
1086 : S
1087 {
1088 $$ = MODE_S;
1089 }
1090
1091 | B
1092 {
1093 $$ = MODE_B;
1094 }
1095
1096 | C
1097 {
1098 $$ = MODE_C;
1099 }
1100 ;
1101
1102pathname
1103 : pathstring
1104 {
1105 /*
1106 * Problem: this production is used for all pathname
1107 * processing, but only gives a 550 error reply.
1108 * This is a valid reply in some cases but not in
1109 * others.
1110 */
1111 if (logged_in && $1 && *$1 == '~') {
1112 char *path, *home, *result;
1113 size_t len;
1114
1115 path = strchr($1 + 1, '/');
1116 if (path != NULL)
1117 *path++ = '\0';
1118 if ($1[1] == '\0')
1119 home = homedir;
1120 else {
1121 struct passwd *hpw;
1122
1123 if ((hpw = getpwnam($1 + 1)) != NULL)
1124 home = hpw->pw_dir;
1125 else
1126 home = $1;
1127 }
1128 len = strlen(home) + 1;
1129 if (path != NULL)
1130 len += strlen(path) + 1;
1131 if ((result = malloc(len)) == NULL)
1132 fatal("Local resource failure: malloc");
1133 strlcpy(result, home, len);
1134 if (path != NULL) {
1135 strlcat(result, "/", len);
1136 strlcat(result, path, len);
1137 }
1138 $$ = result;
1139 free($1);
1140 } else
1141 $$ = $1;
1142 }
1143 ;
1144
1145pathstring
1146 : STRING
1147 ;
1148
1149octal_number
1150 : NUMBER
1151 {
1152 int ret, dec, multby, digit;
1153
1154 /*
1155 * Convert a number that was read as decimal number
1156 * to what it would be if it had been read as octal.
1157 */
1158 dec = $1.i;
1159 multby = 1;
1160 ret = 0;
1161 while (dec) {
1162 digit = dec%10;
1163 if (digit > 7) {
1164 ret = -1;
1165 break;
1166 }
1167 ret += digit * multby;
1168 multby *= 8;
1169 dec /= 10;
1170 }
1171 $$ = ret;
1172 }
1173 ;
1174
1175mechanism_name
1176 : STRING
1177 ;
1178
1179base64data
1180 : STRING
1181 ;
1182
1183prot_code
1184 : STRING
1185 ;
1186
1187decimal_integer
1188 : NUMBER
1189 {
1190 $$ = $1.i;
1191 }
1192 ;
1193
1194check_login
1195 : /* empty */
1196 {
1197 if (logged_in)
1198 $$ = 1;
1199 else {
1200 reply(530, "Please login with USER and PASS.");
1201 $$ = 0;
1202 hasyyerrored = 1;
1203 }
1204 }
1205 ;
1206
1207%%
1208
1209#define CMD 0 /* beginning of command */
1210#define ARGS 1 /* expect miscellaneous arguments */
1211#define STR1 2 /* expect SP followed by STRING */
1212#define STR2 3 /* expect STRING */
1213#define OSTR 4 /* optional SP then STRING */
1214#define ZSTR1 5 /* SP then optional STRING */
1215#define ZSTR2 6 /* optional STRING after SP */
1216#define SITECMD 7 /* SITE command */
1217#define NSTR 8 /* Number followed by a string */
1218#define NOARGS 9 /* No arguments allowed */
1219#define EOLN 10 /* End of line */
1220
1221struct tab cmdtab[] = {
1222 /* From RFC 959, in order defined (5.3.1) */
1223 { "USER", USER, STR1, 1, "<sp> username" },
1224 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
1225 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1226 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1227 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1228 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1229 { "QUIT", QUIT, NOARGS, 1, "(terminate service)" },
1230 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" },
1231 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1232 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1233 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1234 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" },
1235 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1236 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1237 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1238 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1239 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1240 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1241 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1242 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1243 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1244 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1245 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1246 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1247 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1248 { "ABOR", ABOR, NOARGS, 4, "(abort operation)" },
1249 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1250 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1251 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1252 { "PWD", PWD, NOARGS, 1, "(return current directory)" },
1253 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1254 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1255 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1256 { "SYST", SYST, NOARGS, 1, "(get type of operating system)" },
1257 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" },
1258 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1259 { "NOOP", NOOP, NOARGS, 2, "" },
1260
1261 /* From RFC 2228, in order defined */
1262 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" },
1263 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" },
1264 { "PROT", PROT, STR1, 1, "<sp> prot-code" },
1265 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" },
1266 { "CCC", CCC, NOARGS, 1, "(Disable data protection)" },
1267 { "MIC", MIC, STR1, 4, "<sp> base64data" },
1268 { "CONF", CONF, STR1, 4, "<sp> base64data" },
1269 { "ENC", ENC, STR1, 4, "<sp> base64data" },
1270
1271 /* From RFC 2389, in order defined */
1272 { "FEAT", FEAT, NOARGS, 1, "(display extended features)" },
1273 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" },
1274
1275 /* from draft-ietf-ftpext-mlst-11 */
1276 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1277 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1278 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" },
1279 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" },
1280
1281 /* obsolete commands */
1282 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1283 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1284 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1285 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1286 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1287 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1288 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1289 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1290 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1291 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1292 { "XPWD", PWD, NOARGS, 1, "(return current directory)" },
1293 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1294
1295 { NULL, 0, 0, 0, 0 }
1296};
1297
1298struct tab sitetab[] = {
1299 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1300 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1301 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1302 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" },
1303 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" },
1304 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1305 { NULL, 0, 0, 0, NULL }
1306};
1307
1308/*
1309 * Check if a filename is allowed to be modified (isupload == 0) or
1310 * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
1311 * If the filename is NULL, fail.
1312 * If the filename is "", don't do the sane name check.
1313 */
1314static int
1315check_write(const char *file, int isupload)
1316{
1317 if (file == NULL)
1318 return (0);
1319 if (! logged_in) {
1320 reply(530, "Please login with USER and PASS.");
1321 return (0);
1322 }
1323 /* checking modify */
1324 if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
1325 reply(502, "No permission to use this command.");
1326 return (0);
1327 }
1328 /* checking upload */
1329 if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
1330 reply(502, "No permission to use this command.");
1331 return (0);
1332 }
1333
1334 /* checking sanenames */
1335 if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) {
1336 const char *p;
1337
1338 if (file[0] == '.')
1339 goto insane_name;
1340 for (p = file; *p; p++) {
1341 if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' ||
1342 *p == ',' || *p == '.' || *p == '_')
1343 continue;
1344 insane_name:
1345 reply(553, "File name `%s' not allowed.", file);
1346 return (0);
1347 }
1348 }
1349 return (1);
1350}
1351
1352struct tab *
1353lookup(struct tab *p, const char *cmd)
1354{
1355
1356 for (; p->name != NULL; p++)
1357 if (strcasecmp(cmd, p->name) == 0)
1358 return (p);
1359 return (0);
1360}
1361
1362#include <arpa/telnet.h>
1363
1364/*
1365 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1366 */
1367char *
1368getline(char *s, int n, FILE *iop)
1369{
1370 int c;
1371 char *cs;
1372
1373 cs = s;
1374/* tmpline may contain saved command from urgent mode interruption */
1375 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1376 *cs++ = tmpline[c];
1377 if (tmpline[c] == '\n') {
1378 *cs++ = '\0';
1379 if (ftpd_debug)
1380 syslog(LOG_DEBUG, "command: %s", s);
1381 tmpline[0] = '\0';
1382 return(s);
1383 }
1384 if (c == 0)
1385 tmpline[0] = '\0';
1386 }
1387 while ((c = getc(iop)) != EOF) {
1388 total_bytes++;
1389 total_bytes_in++;
1390 c &= 0377;
1391 if (c == IAC) {
1392 if ((c = getc(iop)) != EOF) {
1393 total_bytes++;
1394 total_bytes_in++;
1395 c &= 0377;
1396 switch (c) {
1397 case WILL:
1398 case WONT:
1399 c = getc(iop);
1400 total_bytes++;
1401 total_bytes_in++;
1402 cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
1403 (void) fflush(stdout);
1404 continue;
1405 case DO:
1406 case DONT:
1407 c = getc(iop);
1408 total_bytes++;
1409 total_bytes_in++;
1410 cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
1411 (void) fflush(stdout);
1412 continue;
1413 case IAC:
1414 break;
1415 default:
1416 continue; /* ignore command */
1417 }
1418 }
1419 }
1420 *cs++ = c;
1421 if (--n <= 0 || c == '\n')
1422 break;
1423 }
1424 if (c == EOF && cs == s)
1425 return (NULL);
1426 *cs++ = '\0';
1427 if (ftpd_debug) {
1428 if ((curclass.type != CLASS_GUEST &&
1429 strncasecmp(s, "PASS ", 5) == 0) ||
1430 strncasecmp(s, "ACCT ", 5) == 0) {
1431 /* Don't syslog passwords */
1432 syslog(LOG_DEBUG, "command: %.4s ???", s);
1433 } else {
1434 char *cp;
1435 int len;
1436
1437 /* Don't syslog trailing CR-LF */
1438 len = strlen(s);
1439 cp = s + len - 1;
1440 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1441 --cp;
1442 --len;
1443 }
1444 syslog(LOG_DEBUG, "command: %.*s", len, s);
1445 }
1446 }
1447 return (s);
1448}
1449
1450void
1451ftp_handle_line(char *cp)
1452{
1453
1454 cmdp = cp;
1455 yyparse();
1456}
1457
1458void
1459ftp_loop(void)
1460{
1461
1462 while (1) {
1463 (void) alarm(curclass.timeout);
1464 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1465 reply(221, "You could at least say goodbye.");
1466 dologout(0);
1467 }
1468 (void) alarm(0);
1469 ftp_handle_line(cbuf);
1470 }
1471 /*NOTREACHED*/
1472}
1473
1474int
1475yylex(void)
1476{
1477 static int cpos, state;
1478 char *cp, *cp2;
1479 struct tab *p;
1480 int n;
1481 char c;
1482
1483 switch (state) {
1484
1485 case CMD:
1486 hasyyerrored = 0;
1487 if ((cp = strchr(cmdp, '\r'))) {
1488 *cp = '\0';
1489#if HAVE_SETPROCTITLE
1490 if (strncasecmp(cmdp, "PASS", 4) != 0 &&
1491 strncasecmp(cmdp, "ACCT", 4) != 0)
1492 setproctitle("%s: %s", proctitle, cmdp);
1493#endif /* HAVE_SETPROCTITLE */
1494 *cp++ = '\n';
1495 *cp = '\0';
1496 }
1497 if ((cp = strpbrk(cmdp, " \n")))
1498 cpos = cp - cmdp;
1499 if (cpos == 0)
1500 cpos = 4;
1501 c = cmdp[cpos];
1502 cmdp[cpos] = '\0';
1503 p = lookup(cmdtab, cmdp);
1504 cmdp[cpos] = c;
1505 if (p != NULL) {
1506 if (is_oob && ! CMD_OOB(p)) {
1507 /* command will be handled in-band */
1508 return (0);
1509 } else if (! CMD_IMPLEMENTED(p)) {
1510 reply(502, "%s command not implemented.",
1511 p->name);
1512 hasyyerrored = 1;
1513 break;
1514 }
1515 state = p->state;
1516 yylval.s = p->name;
1517 return (p->token);
1518 }
1519 break;
1520
1521 case SITECMD:
1522 if (cmdp[cpos] == ' ') {
1523 cpos++;
1524 return (SP);
1525 }
1526 cp = &cmdp[cpos];
1527 if ((cp2 = strpbrk(cp, " \n")))
1528 cpos = cp2 - cmdp;
1529 c = cmdp[cpos];
1530 cmdp[cpos] = '\0';
1531 p = lookup(sitetab, cp);
1532 cmdp[cpos] = c;
1533 if (p != NULL) {
1534 if (!CMD_IMPLEMENTED(p)) {
1535 reply(502, "SITE %s command not implemented.",
1536 p->name);
1537 hasyyerrored = 1;
1538 break;
1539 }
1540 state = p->state;
1541 yylval.s = p->name;
1542 return (p->token);
1543 }
1544 break;
1545
1546 case OSTR:
1547 if (cmdp[cpos] == '\n') {
1548 state = EOLN;
1549 return (CRLF);
1550 }
1551 /* FALLTHROUGH */
1552
1553 case STR1:
1554 case ZSTR1:
1555 dostr1:
1556 if (cmdp[cpos] == ' ') {
1557 cpos++;
1558 state = state == OSTR ? STR2 : state+1;
1559 return (SP);
1560 }
1561 break;
1562
1563 case ZSTR2:
1564 if (cmdp[cpos] == '\n') {
1565 state = EOLN;
1566 return (CRLF);
1567 }
1568 /* FALLTHROUGH */
1569
1570 case STR2:
1571 cp = &cmdp[cpos];
1572 n = strlen(cp);
1573 cpos += n - 1;
1574 /*
1575 * Make sure the string is nonempty and \n terminated.
1576 */
1577 if (n > 1 && cmdp[cpos] == '\n') {
1578 cmdp[cpos] = '\0';
1579 yylval.s = ftpd_strdup(cp);
1580 cmdp[cpos] = '\n';
1581 state = ARGS;
1582 return (STRING);
1583 }
1584 break;
1585
1586 case NSTR:
1587 if (cmdp[cpos] == ' ') {
1588 cpos++;
1589 return (SP);
1590 }
1591 if (isdigit((unsigned char)cmdp[cpos])) {
1592 cp = &cmdp[cpos];
1593 while (isdigit((unsigned char)cmdp[++cpos]))
1594 ;
1595 c = cmdp[cpos];
1596 cmdp[cpos] = '\0';
1597 yylval.u.i = atoi(cp);
1598 cmdp[cpos] = c;
1599 state = STR1;
1600 return (NUMBER);
1601 }
1602 state = STR1;
1603 goto dostr1;
1604
1605 case ARGS:
1606 if (isdigit((unsigned char)cmdp[cpos])) {
1607 cp = &cmdp[cpos];
1608 while (isdigit((unsigned char)cmdp[++cpos]))
1609 ;
1610 c = cmdp[cpos];
1611 cmdp[cpos] = '\0';
1612 yylval.u.i = atoi(cp);
1613 yylval.u.ll = STRTOLL(cp, (char **)NULL, 10);
1614 cmdp[cpos] = c;
1615 return (NUMBER);
1616 }
1617 if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
1618 && !isalnum((unsigned char)cmdp[cpos + 3])) {
1619 cpos += 3;
1620 return (ALL);
1621 }
1622 switch (cmdp[cpos++]) {
1623
1624 case '\n':
1625 state = EOLN;
1626 return (CRLF);
1627
1628 case ' ':
1629 return (SP);
1630
1631 case ',':
1632 return (COMMA);
1633
1634 case 'A':
1635 case 'a':
1636 return (A);
1637
1638 case 'B':
1639 case 'b':
1640 return (B);
1641
1642 case 'C':
1643 case 'c':
1644 return (C);
1645
1646 case 'E':
1647 case 'e':
1648 return (E);
1649
1650 case 'F':
1651 case 'f':
1652 return (F);
1653
1654 case 'I':
1655 case 'i':
1656 return (I);
1657
1658 case 'L':
1659 case 'l':
1660 return (L);
1661
1662 case 'N':
1663 case 'n':
1664 return (N);
1665
1666 case 'P':
1667 case 'p':
1668 return (P);
1669
1670 case 'R':
1671 case 'r':
1672 return (R);
1673
1674 case 'S':
1675 case 's':
1676 return (S);
1677
1678 case 'T':
1679 case 't':
1680 return (T);
1681
1682 }
1683 break;
1684
1685 case NOARGS:
1686 if (cmdp[cpos] == '\n') {
1687 state = EOLN;
1688 return (CRLF);
1689 }
1690 c = cmdp[cpos];
1691 cmdp[cpos] = '\0';
1692 reply(501, "'%s' command does not take any arguments.", cmdp);
1693 hasyyerrored = 1;
1694 cmdp[cpos] = c;
1695 break;
1696
1697 case EOLN:
1698 state = CMD;
1699 return (0);
1700
1701 default:
1702 fatal("Unknown state in scanner.");
1703 }
1704 yyerror(NULL);
1705 state = CMD;
1706 return (0);
1707}
1708
1709/* ARGSUSED */
1710void
1711yyerror(char *s)
1712{
1713 char *cp;
1714
1715 if (hasyyerrored || is_oob)
1716 return;
1717 if ((cp = strchr(cmdp,'\n')) != NULL)
1718 *cp = '\0';
1719 reply(500, "'%s': command not understood.", cmdp);
1720 hasyyerrored = 1;
1721}
1722
1723static void
1724help(struct tab *ctab, const char *s)
1725{
1726 struct tab *c;
1727 int width, NCMDS;
1728 char *htype;
1729
1730 if (ctab == sitetab)
1731 htype = "SITE ";
1732 else
1733 htype = "";
1734 width = 0, NCMDS = 0;
1735 for (c = ctab; c->name != NULL; c++) {
1736 int len = strlen(c->name);
1737
1738 if (len > width)
1739 width = len;
1740 NCMDS++;
1741 }
1742 width = (width + 8) &~ 7;
1743 if (s == 0) {
1744 int i, j, w;
1745 int columns, lines;
1746
1747 reply(-214, "%s", "");
1748 reply(0, "The following %scommands are recognized.", htype);
1749 reply(0, "(`-' = not implemented, `+' = supports options)");
1750 columns = 76 / width;
1751 if (columns == 0)
1752 columns = 1;
1753 lines = (NCMDS + columns - 1) / columns;
1754 for (i = 0; i < lines; i++) {
1755 cprintf(stdout, " ");
1756 for (j = 0; j < columns; j++) {
1757 c = ctab + j * lines + i;
1758 cprintf(stdout, "%s", c->name);
1759 w = strlen(c->name);
1760 if (! CMD_IMPLEMENTED(c)) {
1761 CPUTC('-', stdout);
1762 w++;
1763 }
1764 if (CMD_HAS_OPTIONS(c)) {
1765 CPUTC('+', stdout);
1766 w++;
1767 }
1768 if (c + lines >= &ctab[NCMDS])
1769 break;
1770 while (w < width) {
1771 CPUTC(' ', stdout);
1772 w++;
1773 }
1774 }
1775 cprintf(stdout, "\r\n");
1776 }
1777 (void) fflush(stdout);
1778 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1779 return;
1780 }
1781 c = lookup(ctab, s);
1782 if (c == (struct tab *)0) {
1783 reply(502, "Unknown command '%s'.", s);
1784 return;
1785 }
1786 if (CMD_IMPLEMENTED(c))
1787 reply(214, "Syntax: %s%s %s", htype, c->name, c->help);
1788 else
1789 reply(504, "%s%-*s\t%s; not implemented.", htype, width,
1790 c->name, c->help);
1791}
1792
1793/*
1794 * Check that the structures used for a PORT, LPRT or EPRT command are
1795 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
1796 * If family != -1 check that his_addr.su_family == family.
1797 */
1798static void
1799port_check(const char *cmd, int family)
1800{
1801 char h1[NI_MAXHOST], h2[NI_MAXHOST];
1802 char s1[NI_MAXHOST], s2[NI_MAXHOST];
1803#ifdef NI_WITHSCOPEID
1804 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1805#else
1806 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1807#endif
1808
1809 if (epsvall) {
1810 reply(501, "%s disallowed after EPSV ALL", cmd);
1811 return;
1812 }
1813
1814 if (family != -1 && his_addr.su_family != family) {
1815 port_check_fail:
1816 reply(500, "Illegal %s command rejected", cmd);
1817 return;
1818 }
1819
1820 if (data_dest.su_family != his_addr.su_family)
1821 goto port_check_fail;
1822
1823 /* be paranoid, if told so */
1824 if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
1825#ifdef INET6
1826 /*
1827 * be paranoid, there are getnameinfo implementation that does
1828 * not present scopeid portion
1829 */
1830 if (data_dest.su_family == AF_INET6 &&
1831 data_dest.su_scope_id != his_addr.su_scope_id)
1832 goto port_check_fail;
1833#endif
1834
1835 if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
1836 h1, sizeof(h1), s1, sizeof(s1), niflags))
1837 goto port_check_fail;
1838 if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1839 h2, sizeof(h2), s2, sizeof(s2), niflags))
1840 goto port_check_fail;
1841
1842 if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
1843 goto port_check_fail;
1844 }
1845
1846 usedefault = 0;
1847 if (pdata >= 0) {
1848 (void) close(pdata);
1849 pdata = -1;
1850 }
1851 reply(200, "%s command successful.", cmd);
1852}