1 /*
2 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4
5 /****************************************************************************
6 Copyright (c) 1999,2000,2001 WU-FTPD Development Group.
7 All rights reserved.
8
9 Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
10 The Regents of the University of California.
11 Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
12 Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
13 Portions Copyright (c) 1989 Massachusetts Institute of Technology.
14 Portions Copyright (c) 1998 Sendmail, Inc.
15 Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
16 Portions Copyright (c) 1997 by Stan Barber.
17 Portions Copyright (c) 1997 by Kent Landfield.
18 Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
19 Free Software Foundation, Inc.
20
21 Use and distribution of this software and its source code are governed
22 by the terms and conditions of the WU-FTPD Software License ("LICENSE").
23
24 If you did not receive a copy of the license, it may be obtained online
25 at http://www.wu-ftpd.org/license.html.
26
27 $Id: ftpcmd.y,v 1.27.2.2 2001/11/29 17:01:38 wuftpd Exp $
28
29 ****************************************************************************/
30 /*
31 * Grammar for FTP commands.
32 * See RFC 959.
33 */
34
35 %{
36 #include "config.h"
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <arpa/ftp.h>
44 #include <stdio.h>
45 #include <signal.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <pwd.h>
49 #include <setjmp.h>
50 #ifdef HAVE_SYS_SYSLOG_H
51 #include <sys/syslog.h>
52 #endif
53 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
54 #include <syslog.h>
55 #endif
56 #include <time.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <limits.h>
60 #include <alloca.h>
61 #include "extensions.h"
62 #include "pathnames.h"
63 #include "proto.h"
64
65 #if defined(USE_TLS) || defined(USE_GSS)
66 static int pbsz_command_issued = 0;
67 char *cur_auth_type = NULL;
68 extern char *protnames[];
69 #endif /* defined(USE_TLS) || defined(USE_GSS) */
70
71 #if defined(USE_GSS)
72 #include "gssutil.h"
73
74 extern gss_info_t gss_info;
75 #endif /* defined(USE_GSS) */
76
77 extern int dolreplies;
78 #ifndef INTERNAL_LS
79 extern char ls_long[];
80 extern char ls_short[];
81 #endif
82 extern struct SOCKSTORAGE data_dest;
83 extern struct SOCKSTORAGE his_addr;
84 extern int logged_in;
85 extern struct passwd *pw;
86 extern int anonymous;
87 extern int logging;
88 extern int log_commands;
89 extern int log_security;
90 extern int type;
91 extern int form;
92 extern int debug;
93 extern unsigned int timeout_idle;
94 extern unsigned int timeout_maxidle;
95 extern int pdata;
96 extern char hostname[], remotehost[], *remoteident;
97 extern char remoteaddr[];
98 extern char chroot_path[];
99 extern char guestpw[], authuser[]; /* added. _H */
100 extern char proctitle[];
101 extern char *globerr;
102 extern int usedefault;
103 extern int transflag;
104 extern char tmpline[];
105 extern int data;
106 extern int errno;
107 extern char *home;
108
109 off_t restart_point;
110 int yyerrorcalled;
111
112 extern char *strunames[];
113 extern char *typenames[];
114 extern char *modenames[];
115 extern char *formnames[];
116 extern int restricted_user; /* global flag indicating if user is restricted to home directory */
117
118 #ifdef TRANSFER_COUNT
119 extern off_t data_count_total;
120 extern off_t byte_count_total;
121 extern off_t byte_count_in;
122 extern int file_count_total;
123 extern int xfer_count_total;
124 #endif
125
126 extern int retrieve_is_data;
127
128 #ifdef VIRTUAL
129 extern int virtual_mode;
130 extern int virtual_ftpaccess;
131 extern char virtual_email[];
132 #endif
133
134 #ifdef IGNORE_NOOP
135 static int alarm_running = 0;
136 #endif
137
138 static unsigned short cliport = 0;
139 static struct in_addr cliaddr;
140 static int cmd_type;
141 static int cmd_form;
142 static int cmd_bytesz;
143 char cbuf[16 * BUFSIZ];
144 char *fromname;
145
146 #ifndef L_FORMAT /* Autoconf detects this... */
147 #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
148 #define L_FORMAT "qd"
149 #else
150 #ifdef _AIX42
151 #define L_FORMAT "lld"
152 #else
153 #ifdef SOLARIS_2
154 #define L_FORMAT "ld"
155 #else
156 #define L_FORMAT "d"
157 #endif
158 #endif
159 #endif
160 #endif
161
162 #ifdef INET6
163 extern int epsv_all;
164 int lport_error;
165 #endif
166
167 /* Debian linux bison fix: moved this up, added forward decls */
168
169 struct tab {
170 char *name;
171 short token;
172 short state;
173 short implemented; /* 1 if command is implemented */
174 char *help;
175 };
176
177 extern struct tab cmdtab[];
178 extern struct tab sitetab[];
179
180 static void toolong(int);
181 void help(struct tab *ctab, char *s);
182 struct tab *lookup(register struct tab *p, char *cmd);
183 int yylex(void);
184
185 static char *nullstr = "(null)";
186 #define CHECKNULL(p) ((p) ? (p) : nullstr)
187
188 extern int pasv_allowed(const char *remoteaddr);
189 extern int port_allowed(const char *remoteaddr);
190 %}
191
192 %token
193 A B C E F I
194 L N P R S T
195
196 SP CRLF COMMA STRING NUMBER
197
198 USER PASS ACCT REIN QUIT PORT
199 PASV TYPE STRU MODE RETR STOR
200 APPE MLFL MAIL MSND MSOM MSAM
201 MRSQ MRCP ALLO REST RNFR RNTO
202 ABOR DELE CWD LIST NLST SITE
203 STAT HELP NOOP MKD RMD PWD
204 CDUP STOU SMNT SYST SIZE MDTM
205 EPRT EPSV LPRT LPSV
206 PROT PBSZ AUTH ADAT CCC
207
208 UMASK IDLE CHMOD GROUP GPASS NEWER
209 MINFO INDEX EXEC ALIAS CDPATH GROUPS
210 CHECKMETHOD CHECKSUM
211
212 LEXERR
213
214 %union {
215 char *String;
216 int Number;
217 }
218
219 %type <String> STRING password pathname pathstring username method
220 %type <Number> NUMBER byte_size check_login form_code
221 %type <Number> struct_code mode_code octal_number
222 %type <Number> prot_code
223
224 %start cmd_list
225
226 %%
227
228 cmd_list: /* empty */
229 | cmd_list cmd
230 = {
231 if (fromname) {
232 free(fromname);
233 fromname = NULL;
234 }
235 restart_point = 0;
236 }
237 | cmd_list rcmd
238 ;
239
240 cmd: USER SP username CRLF
241 = {
242 user($3);
243 if (log_commands)
244 syslog(LOG_INFO, "USER %s", $3);
245 free($3);
246 }
247 | PASS SP password CRLF
248 = {
249 if (log_commands)
250 if (anonymous)
251 syslog(LOG_INFO, "PASS %s", $3);
252 else
253 syslog(LOG_INFO, "PASS password");
254
255 pass($3);
256 free($3);
257 }
258 | PORT check_login SP host_port CRLF
259 = {
260 if (log_commands)
261 syslog(LOG_INFO, "PORT");
262 /* H* port fix, part B: admonish the twit.
263 Also require login before PORT works */
264 if ($2) {
265 #ifndef DISABLE_PORT
266 #ifdef INET6
267 if (epsv_all) {
268 reply(501, "PORT not allowed after EPSV ALL");
269 goto prt_done;
270 }
271 #endif
272 if (((sock_cmp_inaddr(&his_addr, cliaddr) == 0)
273 || port_allowed(inet_ntoa(cliaddr)))
274 && (ntohs(cliport) >= IPPORT_RESERVED)) {
275 usedefault = 0;
276 if (pdata >= 0) {
277 (void) close(pdata);
278 pdata = -1;
279 }
280 SET_SOCK_FAMILY(data_dest, SOCK_FAMILY(his_addr));
281 SET_SOCK_PORT(data_dest, cliport);
282 SET_SOCK_ADDR4(data_dest, cliaddr);
283 reply(200, "PORT command successful.");
284 }
285 else {
286 #endif /* DISABLE_PORT */
287 reply(502, "Illegal PORT Command");
288 prt_done:
289 usedefault = 1;
290 syslog(LOG_WARNING, "refused PORT %s,%d from %s",
291 inet_ntoa(cliaddr), ntohs(cliport), remoteident);
292 #ifndef DISABLE_PORT
293 }
294 #endif
295 }
296 }
297 | EPRT check_login SP STRING CRLF
298 = {
299 #ifdef INET6
300 if (log_commands)
301 syslog(LOG_INFO, "EPRT");
302 if ($2 && $4 != NULL) {
303 #ifndef DISABLE_PORT
304 char d, fmt[32], addr[INET6_ADDRSTRLEN + 1];
305 int proto;
306 unsigned short port;
307
308 if (epsv_all) {
309 reply(501, "EPRT not allowed after EPSV ALL");
310 goto eprt_done;
311 }
312 d = *((char *)$4);
313 if ((d < 33) || (d > 126)) {
314 reply(501, "Bad delimiter '%c' (%d).", d, d);
315 goto eprt_done;
316 }
317 if (d == '%')
318 (void) snprintf(fmt, sizeof(fmt),
319 "%%%1$c%%d%%%1$c%%%2$d[^%%%1$c]%%%1$c%%hu%%%1$c",
320 d, INET6_ADDRSTRLEN);
321 else
322 (void) snprintf(fmt, sizeof(fmt),
323 "%1$c%%d%1$c%%%2$d[^%1$c]%1$c%%hu%1$c",
324 d, INET6_ADDRSTRLEN);
325
326 if (sscanf((const char *)$4, fmt, &proto, addr, &port) != 3) {
327 reply(501, "EPRT bad format.");
328 goto eprt_done;
329 }
330 port = htons(port);
331
332 switch (proto) {
333 case 1:
334 SET_SOCK_FAMILY(data_dest, AF_INET);
335 break;
336 case 2:
337 memset(&data_dest, 0, sizeof(struct sockaddr_in6));
338 SET_SOCK_FAMILY(data_dest, AF_INET6);
339 break;
340 default:
341 reply(522, "Network protocol not supported, use (1,2)");
342 goto eprt_done;
343 }
344 if (inet_pton(SOCK_FAMILY(data_dest), addr, SOCK_ADDR(data_dest))
345 != 1) {
346 reply(501, "Bad address %s.", addr);
347 goto eprt_done;
348 }
349
350 if (((sock_cmp_addr(&his_addr, &data_dest) == 0)
351 || port_allowed(inet_stop(&data_dest)))
352 && (ntohs(port) >= IPPORT_RESERVED)) {
353 usedefault = 0;
354 if (pdata >= 0) {
355 (void) close(pdata);
356 pdata = -1;
357 }
358 SET_SOCK_PORT(data_dest, port);
359 SET_SOCK_SCOPE(data_dest, his_addr);
360 reply(200, "EPRT command successful.");
361 }
362 else {
363 #endif /* DISABLE_PORT */
364 reply(502, "Illegal EPRT Command");
365 eprt_done:
366 usedefault = 1;
367 syslog(LOG_WARNING, "refused EPRT %s from %s",
368 $4, remoteident);
369 #ifndef DISABLE_PORT
370 }
371 #endif
372 }
373 if ($4 != NULL)
374 free($4);
375 #endif /* INET6 */
376 }
377 | LPRT check_login SP host_lport CRLF
378 = {
379 #ifdef INET6
380 if (log_commands)
381 syslog(LOG_INFO, "LPRT");
382 if ($2) {
383 #ifndef DISABLE_PORT
384 if (lport_error)
385 goto lprt_done;
386 if (((sock_cmp_addr(&his_addr, &data_dest) == 0)
387 || port_allowed(inet_stop(&data_dest)))
388 && (SOCK_PORT(data_dest) >= IPPORT_RESERVED)) {
389 usedefault = 0;
390 if (pdata >= 0) {
391 (void) close(pdata);
392 pdata = -1;
393 }
394 SET_SOCK_SCOPE(data_dest, his_addr);
395 reply(200, "LPRT command successful.");
396 }
397 else {
398 #endif /* DISABLE_PORT */
399 reply(502, "Illegal LPRT Command");
400 lprt_done:
401 usedefault = 1;
402 syslog(LOG_WARNING, "refused LPRT from %s", remoteident);
403 #ifndef DISABLE_PORT
404 }
405 #endif
406 }
407 #endif /* INET6 */
408 }
409 | PASV check_login CRLF
410 = {
411 /* Require login for PASV, too. This actually fixes a bug -- telnet to an
412 unfixed wu-ftpd and type PASV first off, and it crashes! */
413 if (log_commands)
414 syslog(LOG_INFO, "PASV");
415 if ($2)
416 #if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
417 #ifdef INET6
418 if (epsv_all)
419 reply(501, "PASV not allowed after EPSV ALL");
420 else
421 #endif
422 passive(TYPE_PASV, 0);
423 #else
424 reply(502, "Illegal PASV Command");
425 #endif
426 }
427 | EPSV check_login CRLF
428 = {
429 #ifdef INET6
430 if (log_commands)
431 syslog(LOG_INFO, "EPSV");
432 if ($2)
433 #if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
434 passive(TYPE_EPSV, 0);
435 #else
436 reply(502, "Illegal EPSV Command");
437 #endif
438 #endif /* INET6 */
439 }
440 | EPSV check_login SP STRING CRLF
441 = {
442 #ifdef INET6
443 if (log_commands)
444 syslog(LOG_INFO, "EPSV");
445 if ($2 && $4 != NULL)
446 #if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
447 if (strcasecmp((const char *)$4, "ALL") == 0) {
448 epsv_all = 1;
449 reply(200, "EPSV ALL command successful.");
450 }
451 else {
452 int af;
453 char *endp;
454
455 af = strtoul((char *)$4, &endp, 0);
456 if (*endp)
457 reply(501, "'EPSV %s':" "command not understood.", $4);
458 else {
459 /* Not allowed to specify address family 0 */
460 if (af == 0)
461 af = -1;
462 passive(TYPE_EPSV, af);
463 }
464 }
465 #else
466 reply(502, "Illegal EPSV Command");
467 #endif
468 if ($4 != NULL)
469 free($4);
470 #endif /* INET6 */
471 }
472 | LPSV check_login CRLF
473 = {
474 #ifdef INET6
475 if (log_commands)
476 syslog(LOG_INFO, "LPSV");
477 if ($2)
478 #if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
479 if (epsv_all)
480 reply(501, "LPSV not allowed after EPSV ALL");
481 else
482 passive(TYPE_LPSV, 0);
483 #else
484 reply(502, "Illegal LPSV Command");
485 #endif
486 #endif /* INET6 */
487 }
488 | TYPE check_login SP type_code CRLF
489 = {
490 if (log_commands)
491 syslog(LOG_INFO, "TYPE %s", typenames[cmd_type]);
492 if ($2)
493 switch (cmd_type) {
494
495 case TYPE_A:
496 if (cmd_form == FORM_N) {
497 reply(200, "Type set to A.");
498 type = cmd_type;
499 form = cmd_form;
500 }
501 else
502 reply(504, "Form must be N.");
503 break;
504
505 case TYPE_E:
506 reply(504, "Type E not implemented.");
507 break;
508
509 case TYPE_I:
510 reply(200, "Type set to I.");
511 type = cmd_type;
512 break;
513
514 case TYPE_L:
515 #if NBBY == 8
516 if (cmd_bytesz == 8) {
517 reply(200,
518 "Type set to L (byte size 8).");
519 type = cmd_type;
520 }
521 else
522 reply(504, "Byte size must be 8.");
523 #else /* NBBY == 8 */
524 #error UNIMPLEMENTED for NBBY != 8
525 #endif /* NBBY == 8 */
526 }
527 }
528 | STRU check_login SP struct_code CRLF
529 = {
530 if (log_commands)
531 syslog(LOG_INFO, "STRU %s", strunames[$4]);
532 if ($2)
533 switch ($4) {
534
535 case STRU_F:
536 reply(200, "STRU F ok.");
537 break;
538
539 default:
540 reply(504, "Unimplemented STRU type.");
541 }
542 }
543 | MODE check_login SP mode_code CRLF
544 = {
545 if (log_commands)
546 syslog(LOG_INFO, "MODE %s", modenames[$4]);
547 if ($2)
548 switch ($4) {
549
550 case MODE_S:
551 reply(200, "MODE S ok.");
552 break;
553
554 default:
555 reply(502, "Unimplemented MODE type.");
556 }
557 }
558 | ALLO check_login SP NUMBER CRLF
559 = {
560 if (log_commands)
561 syslog(LOG_INFO, "ALLO %d", $4);
562 if ($2)
563 reply(202, "ALLO command ignored.");
564 }
565 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
566 = {
567 if (log_commands)
568 syslog(LOG_INFO, "ALLO %d R %d", $4, $8);
569 if ($2)
570 reply(202, "ALLO command ignored.");
571 }
572 | RETR check_login SP pathname CRLF
573 = {
574 if (log_commands)
575 syslog(LOG_INFO, "RETR %s", CHECKNULL($4));
576 if ($2 && $4 != NULL && !restrict_check($4)) {
577 retrieve_is_data = 1;
578 retrieve((char *) NULL, $4);
579 }
580 if ($4 != NULL)
581 free($4);
582 }
583 | STOR check_login SP pathname CRLF
584 = {
585 if (log_commands)
586 syslog(LOG_INFO, "STOR %s", CHECKNULL($4));
587 if ($2 && $4 != NULL && !restrict_check($4))
588 store($4, "w", 0);
589 if ($4 != NULL)
590 free($4);
591 }
592 | APPE check_login SP pathname CRLF
593 = {
594 if (log_commands)
595 syslog(LOG_INFO, "APPE %s", CHECKNULL($4));
596 if ($2 && $4 != NULL && !restrict_check($4))
597 store($4, "a", 0);
598 if ($4 != NULL)
599 free($4);
600 }
601 | NLST check_login CRLF
602 = {
603 if (log_commands)
604 syslog(LOG_INFO, "NLST");
605 if ($2 && !restrict_check("."))
606 send_file_list("");
607 }
608 | NLST check_login SP STRING CRLF
609 = {
610 if (log_commands)
611 syslog(LOG_INFO, "NLST %s", $4);
612 if ($2 && $4 && !restrict_check($4))
613 send_file_list($4);
614 if ($4 != NULL)
615 free($4);
616 }
617 | LIST check_login CRLF
618 = {
619 if (log_commands)
620 syslog(LOG_INFO, "LIST");
621 if ($2 && !restrict_check(".")) {
622 retrieve_is_data = 0;
623 #ifndef INTERNAL_LS
624 if (anonymous && dolreplies)
625 retrieve(ls_long, "");
626 else
627 retrieve(ls_short, "");
628 #else
629 ls(NULL, 0);
630 #endif
631 }
632 }
633 | LIST check_login SP pathname CRLF
634 = {
635 if (log_commands)
636 syslog(LOG_INFO, "LIST %s", CHECKNULL($4));
637 if ($2 && $4 != NULL && !restrict_list_check($4)) {
638 retrieve_is_data = 0;
639 #ifndef INTERNAL_LS
640 if (anonymous && dolreplies)
641 retrieve(ls_long, $4);
642 else
643 retrieve(ls_short, $4);
644 #else
645 ls($4, 0);
646 #endif
647 }
648 if ($4 != NULL)
649 free($4);
650 }
651 | STAT check_login SP pathname CRLF
652 = {
653 if (log_commands)
654 syslog(LOG_INFO, "STAT %s", CHECKNULL($4));
655 if ($2 && $4 != NULL && !restrict_check($4))
656 statfilecmd($4);
657 if ($4 != NULL)
658 free($4);
659 }
660 | STAT check_login CRLF
661 = {
662 if (log_commands)
663 syslog(LOG_INFO, "STAT");
664 if ($2)
665 statcmd();
666 }
667 | DELE check_login SP pathname CRLF
668 = {
669 if (log_commands)
670 syslog(LOG_INFO, "DELE %s", CHECKNULL($4));
671 if ($2 && $4 != NULL && !restrict_check($4))
672 delete($4);
673 if ($4 != NULL)
674 free($4);
675 }
676 | RNTO check_login SP pathname CRLF
677 = {
678 if (log_commands)
679 syslog(LOG_INFO, "RNTO %s", CHECKNULL($4));
680 if ($2 && $4 && !restrict_check($4)) {
681 if (fromname) {
682 renamecmd(fromname, $4);
683 free(fromname);
684 fromname = NULL;
685 }
686 else {
687 reply(503, "Bad sequence of commands.");
688 }
689 }
690 if ($4)
691 free($4);
692 }
693 | ABOR check_login CRLF
694 = {
695 if (log_commands)
696 syslog(LOG_INFO, "ABOR");
697 if ($2)
698 reply(225, "ABOR command successful.");
699 }
700 | CWD check_login CRLF
701 = {
702 if (log_commands)
703 syslog(LOG_INFO, "CWD");
704 if ($2 && !restrict_check(home))
705 cwd(home);
706 }
707 | CWD check_login SP pathname CRLF
708 = {
709 if (log_commands)
710 syslog(LOG_INFO, "CWD %s", CHECKNULL($4));
711 if ($2 && $4 != NULL && !restrict_check($4))
712 cwd($4);
713 if ($4 != NULL)
714 free($4);
715 }
716 | HELP check_login CRLF
717 = {
718 if (log_commands)
719 syslog(LOG_INFO, "HELP");
720 if ($2)
721 help(cmdtab, (char *) NULL);
722 }
723 | HELP check_login SP STRING CRLF
724 = {
725 register char *cp = (char *) $4;
726
727 if (log_commands)
728 syslog(LOG_INFO, "HELP %s", $4);
729 if ($2)
730 if (strncasecmp(cp, "SITE", 4) == 0) {
731 cp = (char *) $4 + 4;
732 if (*cp == ' ')
733 cp++;
734 if (*cp)
735 help(sitetab, cp);
736 else
737 help(sitetab, (char *) NULL);
738 }
739 else
740 help(cmdtab, $4);
741 if ($4 != NULL)
742 free($4);
743 }
744 | NOOP check_login CRLF
745 = {
746 if (log_commands)
747 syslog(LOG_INFO, "NOOP");
748 if ($2)
749 reply(200, "NOOP command successful.");
750 }
751 | MKD check_login SP pathname CRLF
752 = {
753 if (log_commands)
754 syslog(LOG_INFO, "MKD %s", CHECKNULL($4));
755 if ($2 && $4 != NULL && !restrict_check($4))
756 makedir($4);
757 if ($4 != NULL)
758 free($4);
759 }
760 | RMD check_login SP pathname CRLF
761 = {
762 if (log_commands)
763 syslog(LOG_INFO, "RMD %s", CHECKNULL($4));
764 if ($2 && $4 != NULL && !restrict_check($4))
765 removedir($4);
766 if ($4 != NULL)
767 free($4);
768 }
769 | PWD check_login CRLF
770 = {
771 if (log_commands)
772 syslog(LOG_INFO, "PWD");
773 if ($2)
774 pwd();
775 }
776 | CDUP check_login CRLF
777 = {
778 if (log_commands)
779 syslog(LOG_INFO, "CDUP");
780 if ($2)
781 if (!test_restriction(".."))
782 cwd("..");
783 else
784 ack("CWD");
785 }
786
787 | SITE check_login SP HELP CRLF
788 = {
789 if (log_commands)
790 syslog(LOG_INFO, "SITE HELP");
791 if ($2)
792 help(sitetab, (char *) NULL);
793 }
794 | SITE check_login SP HELP SP STRING CRLF
795 = {
796 if (log_commands)
797 syslog(LOG_INFO, "SITE HELP %s", $6);
798 if ($2)
799 help(sitetab, $6);
800 if ($6 != NULL)
801 free($6);
802 }
803 | SITE check_login SP UMASK CRLF
804 = {
805 mode_t oldmask;
806
807 if (log_commands)
808 syslog(LOG_INFO, "SITE UMASK");
809 if ($2) {
810 oldmask = umask(0);
811 (void) umask(oldmask);
812 reply(200, "Current UMASK is %03o", oldmask);
813 }
814 }
815 | SITE check_login SP UMASK SP octal_number CRLF
816 = {
817 mode_t oldmask;
818 struct aclmember *entry = NULL;
819 int ok = 1;
820
821 if (log_commands)
822 syslog(LOG_INFO, "SITE UMASK %03o", $6);
823 if ($2) {
824 /* check for umask permission */
825 while (getaclentry("umask", &entry) && ARG0 && ARG1 != NULL) {
826 if (type_match(ARG1))
827 if (*ARG0 == 'n')
828 ok = 0;
829 }
830 if (ok && !restricted_user) {
831 if (($6 < 0) || ($6 > 0777)) {
832 reply(501, "Bad UMASK value");
833 }
834 else {
835 oldmask = umask((mode_t) $6);
836 reply(200, "UMASK set to %03o (was %03o)", $6, oldmask);
837 }
838 }
839 else
840 reply(553, "Permission denied on server. (umask)");
841 }
842 }
843 | SITE check_login SP CHMOD SP octal_number SP pathname CRLF
844 = {
845 struct aclmember *entry = NULL;
846 int ok = (anonymous ? 0 : 1);
847
848 if (log_commands)
849 syslog(LOG_INFO, "SITE CHMOD %03o %s", $6, CHECKNULL($8));
850 if ($2 && $8) {
851 /* check for chmod permission */
852 while (getaclentry("chmod", &entry) && ARG0 && ARG1 != NULL) {
853 if (type_match(ARG1))
854 if (anonymous) {
855 if (*ARG0 == 'y')
856 ok = 1;
857 }
858 else if (*ARG0 == 'n')
859 ok = 0;
860 }
861 if (ok) {
862 #ifdef UNRESTRICTED_CHMOD
863 if (chmod($8, (mode_t) $6) < 0)
864 #else
865 if (($6 < 0) || ($6 > 0777))
866 reply(501,
867 "CHMOD: Mode value must be between 0 and 0777");
868 else if (chmod($8, (mode_t) $6) < 0)
869 #endif
870 perror_reply(550, $8);
871 else {
872 char path[MAXPATHLEN];
873
874 wu_realpath($8, path, chroot_path);
875
876 if (log_security)
877 if (anonymous) {
878 syslog(LOG_NOTICE, "%s of %s changed permissions for %s", guestpw, remoteident, path);
879 }
880 else {
881 syslog(LOG_NOTICE, "%s of %s changed permissions for %s", pw->pw_name,
882 remoteident, path);
883 }
884 reply(200, "CHMOD command successful.");
885 }
886 }
887 else
888 reply(553, "Permission denied on server. (chmod)");
889 }
890 if ($8 != NULL)
891 free($8);
892 }
893 | SITE check_login SP IDLE CRLF
894 = {
895 if (log_commands)
896 syslog(LOG_INFO, "SITE IDLE");
897 if ($2)
898 reply(200,
899 "Current IDLE time limit is %d seconds; max %d",
900 timeout_idle, timeout_maxidle);
901 }
902 | SITE check_login SP IDLE SP NUMBER CRLF
903 = {
904 if (log_commands)
905 syslog(LOG_INFO, "SITE IDLE %d", $6);
906 if ($2)
907 if ($6 < 30 || $6 > timeout_maxidle) {
908 reply(501,
909 "Maximum IDLE time must be between 30 and %d seconds",
910 timeout_maxidle);
911 }
912 else {
913 timeout_idle = $6;
914 reply(200, "Maximum IDLE time set to %d seconds", timeout_idle);
915 }
916 }
917 | SITE check_login SP GROUP SP username CRLF
918 = {
919 #ifndef NO_PRIVATE
920 if (log_commands)
921 syslog(LOG_INFO, "SITE GROUP %s", $6);
922 if (!restricted_user && $2 && $6)
923 priv_group($6);
924 free($6);
925 #endif /* !NO_PRIVATE */
926 }
927 | SITE check_login SP GPASS SP password CRLF
928 = {
929 #ifndef NO_PRIVATE
930 if (log_commands)
931 syslog(LOG_INFO, "SITE GPASS password");
932 if (!restricted_user && $2 && $6)
933 priv_gpass($6);
934 free($6);
935 #endif /* !NO_PRIVATE */
936 }
937 | SITE check_login SP GPASS CRLF
938 = {
939 #ifndef NO_PRIVATE
940 if (log_commands)
941 syslog(LOG_INFO, "SITE GPASS");
942 if (!restricted_user && $2)
943 priv_gpass(NULL);
944 #endif /* !NO_PRIVATE */
945 }
946 | SITE check_login SP NEWER SP STRING CRLF
947 = {
948 if (log_commands)
949 syslog(LOG_INFO, "SITE NEWER %s", $6);
950 #ifdef SITE_NEWER
951 if ($2 && $6 && !restrict_check("."))
952 newer($6, ".", 0);
953 #else
954 reply(502, "Command no longer honored by this server");
955 #endif
956 free($6);
957 }
958 | SITE check_login SP NEWER SP STRING SP pathname CRLF
959 = {
960 if (log_commands)
961 syslog(LOG_INFO, "SITE NEWER %s %s", $6,
962 CHECKNULL($8));
963 #ifdef SITE_NEWER
964 if ($2 && $6 && $8 && !restrict_check($8))
965 newer($6, $8, 0);
966 #else
967 reply(502, "Command no longer honored by this server");
968 #endif
969 free($6);
970 if ($8)
971 free($8);
972 }
973 | SITE check_login SP MINFO SP STRING CRLF
974 = {
975 if (log_commands)
976 syslog(LOG_INFO, "SITE MINFO %s", $6);
977 #ifdef SITE_NEWER
978 if ($2 && $6 && !restrict_check("."))
979 newer($6, ".", 1);
980 #else
981 reply(502, "Command no longer honored by this server");
982 #endif
983 free($6);
984 }
985 | SITE check_login SP MINFO SP STRING SP pathname CRLF
986 = {
987 if (log_commands)
988 syslog(LOG_INFO, "SITE MINFO %s %s", $6,
989 CHECKNULL($8));
990 #ifdef SITE_NEWER
991 if ($2 && $6 && $8 && !restrict_check($8))
992 newer($6, $8, 1);
993 #else
994 reply(502, "Command no longer honored by this server");
995 #endif
996 free($6);
997 if ($8)
998 free($8);
999 }
1000 | SITE check_login SP INDEX SP STRING CRLF
1001 = {
1002 /* this is just for backward compatibility since we
1003 * thought of INDEX before we thought of EXEC
1004 */
1005 if (!restricted_user && $2 != 0 && $6 != NULL) {
1006 char buf[MAXPATHLEN];
1007 if (strlen($6) + 7 <= sizeof(buf)) {
1008 (void) snprintf(buf, sizeof(buf), "index %s", (char *) $6);
1009 (void) site_exec(buf);
1010 }
1011 }
1012 if ($6 != NULL)
1013 free($6);
1014 }
1015 | SITE check_login SP EXEC SP STRING CRLF
1016 = {
1017 if (!restricted_user && $2 != 0 && $6 != NULL) {
1018 (void) site_exec((char *) $6);
1019 }
1020 if ($6 != NULL)
1021 free($6);
1022 }
1023
1024 | STOU check_login
1025 = {
1026 char *default_filename = "ftp";
1027 if (log_commands)
1028 syslog(LOG_INFO, "STOU");
1029 if ($2 && !restrict_check(default_filename))
1030 store(default_filename, "w", 1);
1031 }
1032 | STOU check_login SP pathname CRLF
1033 = {
1034 if (log_commands)
1035 syslog(LOG_INFO, "STOU %s", CHECKNULL($4));
1036 if ($2 && $4 && !restrict_check($4))
1037 store($4, "w", 1);
1038 if ($4 != NULL)
1039 free($4);
1040 }
1041 | SYST check_login CRLF
1042 = {
1043 if (log_commands)
1044 syslog(LOG_INFO, "SYST");
1045 if ($2)
1046 #ifdef BSD
1047 reply(215, "UNIX Type: L%d Version: BSD-%d", NBBY, BSD);
1048 #elif defined(SOLARIS_2)
1049 reply(215, "UNIX Type: L%d Version: SUNOS", NBBY);
1050 #elif defined(unix) || defined(__unix__)
1051 reply(215, "UNIX Type: L%d", NBBY);
1052 #else
1053 reply(215, "UNKNOWN Type: L%d", NBBY);
1054 #endif /* BSD */
1055 }
1056
1057 /*
1058 * SIZE is not in RFC959, but Postel has blessed it and
1059 * it will be in the updated RFC.
1060 *
1061 * Return size of file in a format suitable for
1062 * using with RESTART (we just count bytes).
1063 */
1064 | SIZE check_login SP pathname CRLF
1065 = {
1066 if (log_commands)
1067 syslog(LOG_INFO, "SIZE %s", CHECKNULL($4));
1068 if ($2 && $4 && !restrict_check($4)) {
1069 sizecmd($4);
1070 }
1071 if ($4 != NULL)
1072 free($4);
1073 }
1074
1075 /*
1076 * MDTM is not in RFC959, but Postel has blessed it and
1077 * it will be in the updated RFC.
1078 *
1079 * Return modification time of file as an ISO 3307
1080 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
1081 * where xxx is the fractional second (of any precision,
1082 * not necessarily 3 digits)
1083 */
1084 | MDTM check_login SP pathname CRLF
1085 = {
1086 if (log_commands)
1087 syslog(LOG_INFO, "MDTM %s", CHECKNULL($4));
1088 if ($2 && $4 && !restrict_check($4)) {
1089 struct stat stbuf;
1090
1091 if (stat($4, &stbuf) < 0)
1092 perror_reply(550, $4);
1093 else if ((stbuf.st_mode & S_IFMT) != S_IFREG) {
1094 reply(550, "%s: not a plain file.",
1095 $4);
1096 }
1097 else {
1098 register struct tm *t;
1099 t = gmtime(&stbuf.st_mtime);
1100 reply(213,
1101 "%04d%02d%02d%02d%02d%02d",
1102 t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1103 t->tm_hour, t->tm_min, t->tm_sec);
1104 }
1105 }
1106 if ($4 != NULL)
1107 free($4);
1108 }
1109 | QUIT CRLF
1110 = {
1111 if (log_commands)
1112 syslog(LOG_INFO, "QUIT");
1113 #ifdef TRANSFER_COUNT
1114 if (logged_in) {
1115 lreply(221, "You have transferred %" L_FORMAT " bytes in %d files.", data_count_total, file_count_total);
1116 lreply(221, "Total traffic for this session was %" L_FORMAT " bytes in %d transfers.", byte_count_total, xfer_count_total);
1117 lreply(221, "Thank you for using the FTP service on %s.", hostname);
1118 }
1119 #endif /* TRANSFER_COUNT */
1120 reply(221, "Goodbye.");
1121 dologout(0);
1122 }
1123 | error CRLF
1124 = {
1125 yyerrok;
1126 }
1127 ;
1128
1129 rcmd: RNFR check_login SP pathname CRLF
1130 = {
1131
1132 if (log_commands)
1133 syslog(LOG_INFO, "RNFR %s", CHECKNULL($4));
1134 if ($2)
1135 restart_point = 0;
1136 if (fromname) {
1137 free(fromname);
1138 fromname = NULL;
1139 }
1140 if ($2 && $4 && !restrict_check($4)) {
1141 fromname = renamefrom($4);
1142 }
1143 if (fromname == NULL && $4)
1144 free($4);
1145 }
1146 | REST check_login SP STRING CRLF
1147 = {
1148 if (log_commands)
1149 syslog(LOG_INFO, "REST %s", CHECKNULL($4));
1150 if ($2 && $4 != NULL) {
1151 char *endp;
1152
1153 if (fromname) {
1154 free(fromname);
1155 fromname = NULL;
1156 }
1157 errno = 0;
1158 #if _FILE_OFFSET_BITS == 64
1159 restart_point = strtoll($4, &endp, 10);
1160 #else
1161 restart_point = strtol($4, &endp, 10);
1162 #endif
1163 if ((errno == 0) && (restart_point >= 0) && (*endp == '\0')) {
1164 reply(350, "Restarting at %" L_FORMAT
1165 ". Send STORE or RETRIEVE to initiate transfer.",
1166 restart_point);
1167 }
1168 else {
1169 restart_point = 0;
1170 reply(501, "Bad value for REST: %s", $4);
1171 }
1172 }
1173 if ($4 != NULL)
1174 free($4);
1175 }
1176
1177 | SITE check_login SP ALIAS CRLF
1178 = {
1179 if (log_commands)
1180 syslog(LOG_INFO, "SITE ALIAS");
1181 if ($2)
1182 alias((char *) NULL);
1183 }
1184 | SITE check_login SP ALIAS SP STRING CRLF
1185 = {
1186 if (log_commands)
1187 syslog(LOG_INFO, "SITE ALIAS %s", $6);
1188 if ($2)
1189 alias($6);
1190 if ($6 != NULL)
1191 free($6);
1192 }
1193 | SITE check_login SP GROUPS CRLF
1194 = {
1195 if (log_commands)
1196 syslog(LOG_INFO, "SITE GROUPS");
1197 if ($2)
1198 print_groups();
1199 }
1200 | SITE check_login SP CDPATH CRLF
1201 = {
1202 if (log_commands)
1203 syslog(LOG_INFO, "SITE CDPATH");
1204 if ($2)
1205 cdpath();
1206 }
1207 | SITE check_login SP CHECKMETHOD SP method CRLF
1208 = {
1209 if (log_commands)
1210 syslog(LOG_INFO, "SITE CHECKMETHOD %s", CHECKNULL($6));
1211 if (($2) && ($6 != NULL))
1212 SetCheckMethod($6);
1213 if ($6 != NULL)
1214 free($6);
1215 }
1216 | SITE check_login SP CHECKMETHOD CRLF
1217 = {
1218 if (log_commands)
1219 syslog(LOG_INFO, "SITE CHECKMETHOD");
1220 if ($2)
1221 ShowCheckMethod();
1222 }
1223 | SITE check_login SP CHECKSUM SP pathname CRLF
1224 = {
1225 if (log_commands)
1226 syslog(LOG_INFO, "SITE CHECKSUM %s", CHECKNULL($6));
1227 if (($2) && ($6 != NULL) && (!restrict_check($6)))
1228 CheckSum($6);
1229 if ($6 != NULL)
1230 free($6);
1231 }
1232 | SITE check_login SP CHECKSUM CRLF
1233 = {
1234 if (log_commands)
1235 syslog(LOG_INFO, "SITE CHECKSUM");
1236 if ($2)
1237 CheckSumLastFile();
1238 }
1239 | PBSZ SP STRING CRLF
1240 = {
1241 #if defined(USE_TLS) || defined(USE_GSS)
1242 if (log_commands)
1243 syslog(LOG_INFO, "PBSZ %s", $3);
1244 {
1245 int sz = 0;
1246 #if defined(USE_GSS)
1247 sz = gss_setpbsz((char *)$3);
1248 #else
1249 reply(200, "PBSZ=%d", sz);
1250 #endif /* defined(USE_GSS) */
1251 pbsz_command_issued = 1;
1252 }
1253 #endif /* defined(USE_TLS) || defined(USE_GSS) */
1254 if ($3 != NULL)
1255 free((char *)$3);
1256 }
1257 | AUTH SP STRING CRLF
1258 = {
1259 #if defined(USE_TLS) || defined(USE_GSS)
1260 register char *cp = (char *) $3;
1261 if (log_commands)
1262 syslog(LOG_INFO, "AUTH %s", $3);
1263 /* convert to UPPER case as per RFC 2228 */
1264 while (*cp) {
1265 *cp = toupper(*cp);
1266 cp++;
1267 }
1268 #if defined(USE_GSS)
1269 if (!strcmp((char *) $3, "GSSAPI")) {
1270 if (cur_auth_type != NULL) {
1271 reply(534, "Authentication type already set to %s",
1272 cur_auth_type);
1273 syslog(LOG_ERR, "Rejecting duplicate AUTH command");
1274 } else {
1275 cur_auth_type = strdup((char *)$3);
1276 reply(334, "Using AUTH type %s; ADAT must follow",
1277 cur_auth_type);
1278 }
1279 } else
1280 #endif /* defined(USE_GSS) */
1281 {
1282 /*
1283 * Previous auth_type did not work, clear the string.
1284 */
1285 if (cur_auth_type != NULL) {
1286 free(cur_auth_type);
1287 cur_auth_type = NULL;
1288 }
1289 reply(504,"AUTH %s not supported.", $3);
1290 }
1291 #endif /* !(defined(USE_TLS)) && !defined(USE_GSS) */
1292 if ($3 != NULL)
1293 free((char *)$3);
1294 }
1295 | PROT SP prot_code CRLF
1296 = {
1297 #if defined(USE_TLS) || defined(USE_GSS)
1298 if (log_commands)
1299 syslog(LOG_INFO, "PROT %s", protnames[$3]);
1300 {
1301 if (!pbsz_command_issued) {
1302 reply(503, "PROT command not valid before PBSZ.");
1303 } else {
1304 switch ($3) {
1305 case PROT_P:
1306 reply(200, "PROT P ok.");
1307 #if defined(USE_GSS)
1308 gss_info.data_prot = PROT_P;
1309 #endif /* defined(USE_GSS) */
1310 break;
1311 case PROT_C:
1312 reply(200, "PROT C ok.");
1313 #if defined(USE_GSS)
1314 gss_info.data_prot = PROT_C;
1315 #endif /* defined(USE_GSS) */
1316 break;
1317 case PROT_E:
1318 reply(536, "PROT E unsupported");
1319 break;
1320 case PROT_S:
1321 #if defined(USE_GSS)
1322 reply(200, "PROT S ok.");
1323 gss_info.data_prot = PROT_S;
1324 #endif /* defined(USE_GSS) */
1325 break;
1326 default:
1327 reply(504, "Invalid PROT type.");
1328 }
1329 #if defined(USE_GSS)
1330 gss_adjust_buflen();
1331 #endif /* defined(USE_GSS) */
1332 }
1333 }
1334 #endif /* !(defined(USE_TLS) && !defined(USE_GSS)) */
1335 }
1336 | ADAT SP STRING CRLF
1337 = {
1338 #if defined(USE_GSS)
1339 if (log_commands)
1340 syslog(LOG_INFO, "ADAT %s", $3);
1341 if (cur_auth_type == NULL || strcmp(cur_auth_type, "GSSAPI")) {
1342 reply(503, "Must identify AUTH GSSAPI before sending ADAT");
1343 } else
1344 (void) gss_adat((char *)$3);
1345 #endif
1346 if ($3 != NULL)
1347 free((char *)$3);
1348 }
1349 | CCC CRLF
1350 = {
1351 #if defined(USE_GSS)
1352 if (log_commands)
1353 syslog(LOG_INFO, "CCC");
1354 ccc();
1355 #endif /* defined(USE_GSS) */
1356 }
1357 ;
1358
1359 username: STRING
1360 ;
1361
1362 password: /* empty */
1363 = {
1364 $$ = (char *) malloc(1);
1365 $$[0] = '\0';
1366 }
1367 | STRING
1368 ;
1369
1370 byte_size: NUMBER
1371 ;
1372
1373 host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER
1374 = {
1375 register char *a, *p;
1376
1377 a = (char *) &cliaddr;
1378 a[0] = $1;
1379 a[1] = $3;
1380 a[2] = $5;
1381 a[3] = $7;
1382 p = (char *) &cliport;
1383 p[0] = $9;
1384 p[1] = $11;
1385 }
1386 ;
1387
1388 host_lport: NUMBER COMMA NUMBER COMMA
1389 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
1390 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
1391 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
1392 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
1393 NUMBER COMMA NUMBER COMMA NUMBER
1394 = {
1395 #ifdef INET6
1396 char *a, *p;
1397 struct sockaddr_in6 *data_dest_sin6;
1398
1399 lport_error = 0;
1400 if (epsv_all) {
1401 reply(501, "LPRT not allowed after EPSV ALL");
1402 lport_error = 1;
1403 goto lport_done6;
1404 }
1405 if ($1 != 6) {
1406 reply(521, "Supported address families are (4, 6)");
1407 lport_error = 1;
1408 goto lport_done6;
1409 }
1410 if (($3 != 16) || ($37 != 2)) {
1411 reply(501, "Bad length.");
1412 lport_error = 1;
1413 goto lport_done6;
1414 }
1415 memset(&data_dest, 0, sizeof(struct sockaddr_in6));
1416 data_dest_sin6 = (struct sockaddr_in6 *) &data_dest;
1417 data_dest_sin6->sin6_family = AF_INET6;
1418 a = (char *)&data_dest_sin6->sin6_addr;
1419 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
1420 a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19;
1421 a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27;
1422 a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
1423 p = (char *)&data_dest_sin6->sin6_port;
1424 p[0] = $39; p[1] = $41;
1425 lport_done6:;
1426 #endif /* INET6 */
1427 }
1428 | NUMBER COMMA NUMBER COMMA
1429 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
1430 NUMBER COMMA NUMBER COMMA NUMBER
1431 = {
1432 #ifdef INET6
1433 char *a, *p;
1434 struct sockaddr_in *data_dest_sin;
1435
1436 lport_error = 0;
1437 if (epsv_all) {
1438 reply(501, "LPRT not allowed after EPSV ALL");
1439 lport_error = 1;
1440 goto lport_done4;
1441 }
1442 if ($1 != 4) {
1443 reply(521, "Supported address families are (4, 6)");
1444 lport_error = 1;
1445 goto lport_done4;
1446 }
1447 if (($3 != 4) || ($13 != 2)) {
1448 reply(501, "Bad length.");
1449 lport_error = 1;
1450 goto lport_done4;
1451 }
1452 data_dest_sin = (struct sockaddr_in *) &data_dest;
1453 data_dest_sin->sin_family = AF_INET;
1454 a = (char *)&data_dest_sin->sin_addr;
1455 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
1456 p = (char *)&data_dest_sin->sin_port;
1457 p[0] = $15; p[1] = $17;
1458 lport_done4:;
1459 #endif /* INET6 */
1460 }
1461 ;
1462
1463 form_code: N
1464 = {
1465 $$ = FORM_N;
1466 }
1467 | T
1468 = {
1469 $$ = FORM_T;
1470 }
1471 | C
1472 = {
1473 $$ = FORM_C;
1474 }
1475 ;
1476
1477 type_code: A
1478 = {
1479 cmd_type = TYPE_A;
1480 cmd_form = FORM_N;
1481 }
1482 | A SP form_code
1483 = {
1484 cmd_type = TYPE_A;
1485 cmd_form = $3;
1486 }
1487 | E
1488 = {
1489 cmd_type = TYPE_E;
1490 cmd_form = FORM_N;
1491 }
1492 | E SP form_code
1493 = {
1494 cmd_type = TYPE_E;
1495 cmd_form = $3;
1496 }
1497 | I
1498 = {
1499 cmd_type = TYPE_I;
1500 }
1501 | L
1502 = {
1503 cmd_type = TYPE_L;
1504 cmd_bytesz = NBBY;
1505 }
1506 | L SP byte_size
1507 = {
1508 cmd_type = TYPE_L;
1509 cmd_bytesz = $3;
1510 }
1511 /* this is for a bug in the BBN ftp */
1512 | L byte_size
1513 = {
1514 cmd_type = TYPE_L;
1515 cmd_bytesz = $2;
1516 }
1517 ;
1518
1519 prot_code: C
1520 = {
1521 #if defined(USE_GSS)
1522 $$ = PROT_C;
1523 #endif
1524 }
1525 | P
1526 = {
1527 #if defined(USE_GSS)
1528 $$ = PROT_P;
1529 #endif
1530 }
1531 | S
1532 = {
1533 #if defined(USE_GSS)
1534 $$ = PROT_S;
1535 #endif
1536 }
1537 | E
1538 = {
1539 #if defined(USE_GSS)
1540 $$ = PROT_E;
1541 #endif
1542 }
1543 ;
1544
1545 struct_code: F
1546 = {
1547 $$ = STRU_F;
1548 }
1549 | R
1550 = {
1551 $$ = STRU_R;
1552 }
1553 | P
1554 = {
1555 $$ = STRU_P;
1556 }
1557 ;
1558
1559 mode_code: S
1560 = {
1561 $$ = MODE_S;
1562 }
1563 | B
1564 = {
1565 $$ = MODE_B;
1566 }
1567 | C
1568 = {
1569 $$ = MODE_C;
1570 }
1571 ;
1572
1573 pathname: pathstring
1574 = {
1575 /*
1576 * Problem: this production is used for all pathname
1577 * processing, but only gives a 550 error reply.
1578 * This is a valid reply in some cases but not in others.
1579 */
1580 if (restricted_user && logged_in && $1 && strncmp($1, "/", 1) == 0) {
1581 /*
1582 * This remaps the root so it is appearently at the user's home
1583 * rather than the real root/chroot.
1584 */
1585 size_t len = strlen($1) + 2;
1586 char **globlist;
1587 char *t = calloc(len, sizeof(char));
1588 if (t == NULL) {
1589 errno = EAGAIN;
1590 perror_reply(550, $1);
1591 $$ = NULL;
1592 }
1593 else {
1594 t[0] = '~';
1595 t[1] = '\0';
1596 if (strncmp($1, "/../", 4) == 0)
1597 (void) strlcat(t, $1 + 3, len);
1598 else if (strcmp($1, "/..") != 0)
1599 (void) strlcat(t, $1, len);
1600 globlist = ftpglob(t, B_TRUE);
1601 if (globerr) {
1602 reply(550, "%s", globerr);
1603 $$ = NULL;
1604 if (globlist) {
1605 blkfree(globlist);
1606 free((char *) globlist);
1607 }
1608 }
1609 else if (globlist && *globlist) {
1610 $$ = *globlist;
1611 blkfree(&globlist[1]);
1612 free((char *) globlist);
1613 }
1614 else {
1615 if (globlist) {
1616 blkfree(globlist);
1617 free((char *) globlist);
1618 }
1619 errno = ENOENT;
1620 perror_reply(550, $1);
1621 $$ = NULL;
1622 }
1623 free(t);
1624 }
1625 free($1);
1626 }
1627 else if (logged_in && $1 && strncmp($1, "~", 1) == 0) {
1628 char **globlist;
1629
1630 globlist = ftpglob($1, B_TRUE);
1631 if (globerr) {
1632 reply(550, "%s", globerr);
1633 $$ = NULL;
1634 if (globlist) {
1635 blkfree(globlist);
1636 free((char *) globlist);
1637 }
1638 }
1639 else if (globlist && *globlist) {
1640 $$ = *globlist;
1641 blkfree(&globlist[1]);
1642 free((char *) globlist);
1643 }
1644 else {
1645 if (globlist) {
1646 blkfree(globlist);
1647 free((char *) globlist);
1648 }
1649 errno = ENOENT;
1650 perror_reply(550, $1);
1651 $$ = NULL;
1652 }
1653 free($1);
1654 }
1655 else
1656 $$ = $1;
1657 }
1658 ;
1659
1660 pathstring: STRING
1661 ;
1662
1663 method: STRING
1664 ;
1665
1666 octal_number: NUMBER
1667 = {
1668 register int ret, dec, multby, digit;
1669
1670 /*
1671 * Convert a number that was read as decimal number
1672 * to what it would be if it had been read as octal.
1673 */
1674 dec = $1;
1675 multby = 1;
1676 ret = 0;
1677 while (dec) {
1678 digit = dec % 10;
1679 if (digit > 7) {
1680 ret = -1;
1681 break;
1682 }
1683 ret += digit * multby;
1684 multby *= 8;
1685 dec /= 10;
1686 }
1687 $$ = ret;
1688 }
1689 ;
1690
1691 check_login: /* empty */
1692 = {
1693 if (logged_in)
1694 $$ = 1;
1695 else {
1696 if (log_commands)
1697 syslog(LOG_INFO, "cmd failure - not logged in");
1698 reply(530, "Please login with USER and PASS.");
1699 $$ = 0;
1700 yyerrorcalled = 1;
1701 }
1702 }
1703 ;
1704
1705 %%
1706
1707 extern jmp_buf errcatch;
1708
1709 #define CMD 0 /* beginning of command */
1710 #define ARGS 1 /* expect miscellaneous arguments */
1711 #define STR1 2 /* expect SP followed by STRING */
1712 #define STR2 3 /* expect STRING */
1713 #define OSTR 4 /* optional SP then STRING */
1714 #define ZSTR1 5 /* SP then optional STRING */
1715 #define ZSTR2 6 /* optional STRING after SP */
1716 #define SITECMD 7 /* SITE command */
1717 #define NSTR 8 /* Number followed by a string */
1718 #define STR3 9 /* expect STRING followed by optional SP then STRING */
1719
1720 struct tab cmdtab[] =
1721 { /* In order defined in RFC 765 */
1722 {"USER", USER, STR1, 1, "<sp> username"},
1723 {"PASS", PASS, ZSTR1, 1, "<sp> password"},
1724 {"ACCT", ACCT, STR1, 0, "(specify account)"},
1725 {"SMNT", SMNT, ARGS, 0, "(structure mount)"},
1726 {"REIN", REIN, ARGS, 0, "(reinitialize server state)"},
1727 {"QUIT", QUIT, ARGS, 1, "(terminate service)",},
1728 {"PORT", PORT, ARGS, 1, "<sp> h1, h2, h3, h4, p1, p2"},
1729 {"PASV", PASV, ARGS, 1, "(set server in passive mode)"},
1730 #ifdef INET6
1731 {"EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|"},
1732 {"EPSV", EPSV, OSTR, 1, "[<sp> af|ALL]"},
1733 {"LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, ..., pal, p1, p2, ..."},
1734 {"LPSV", LPSV, ARGS, 1, "(set server in long passive mode)"},
1735 #endif
1736 {"TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]"},
1737 {"STRU", STRU, ARGS, 1, "(specify file structure)"},
1738 {"MODE", MODE, ARGS, 1, "(specify transfer mode)"},
1739 {"RETR", RETR, STR1, 1, "<sp> file-name"},
1740 {"STOR", STOR, STR1, 1, "<sp> file-name"},
1741 {"APPE", APPE, STR1, 1, "<sp> file-name"},
1742 {"MLFL", MLFL, OSTR, 0, "(mail file)"},
1743 {"MAIL", MAIL, OSTR, 0, "(mail to user)"},
1744 {"MSND", MSND, OSTR, 0, "(mail send to terminal)"},
1745 {"MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)"},
1746 {"MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)"},
1747 {"MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)"},
1748 {"MRCP", MRCP, STR1, 0, "(mail recipient)"},
1749 {"ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)"},
1750 {"REST", REST, STR1, 1, "(restart command)"},
1751 {"RNFR", RNFR, STR1, 1, "<sp> file-name"},
1752 {"RNTO", RNTO, STR1, 1, "<sp> file-name"},
1753 {"ABOR", ABOR, ARGS, 1, "(abort operation)"},
1754 {"DELE", DELE, STR1, 1, "<sp> file-name"},
1755 {"CWD", CWD, OSTR, 1, "[ <sp> directory-name ]"},
1756 {"XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]"},
1757 {"LIST", LIST, OSTR, 1, "[ <sp> path-name ]"},
1758 {"NLST", NLST, OSTR, 1, "[ <sp> path-name ]"},
1759 {"SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]"},
1760 {"SYST", SYST, ARGS, 1, "(get type of operating system)"},
1761 {"STAT", STAT, OSTR, 1, "[ <sp> path-name ]"},
1762 {"HELP", HELP, OSTR, 1, "[ <sp> <string> ]"},
1763 {"NOOP", NOOP, ARGS, 1, ""},
1764 {"MKD", MKD, STR1, 1, "<sp> path-name"},
1765 {"XMKD", MKD, STR1, 1, "<sp> path-name"},
1766 {"RMD", RMD, STR1, 1, "<sp> path-name"},
1767 {"XRMD", RMD, STR1, 1, "<sp> path-name"},
1768 {"PWD", PWD, ARGS, 1, "(return current directory)"},
1769 {"XPWD", PWD, ARGS, 1, "(return current directory)"},
1770 {"CDUP", CDUP, ARGS, 1, "(change to parent directory)"},
1771 {"XCUP", CDUP, ARGS, 1, "(change to parent directory)"},
1772 {"STOU", STOU, OSTR, 1, "[ <sp> file-name ]"},
1773 {"SIZE", SIZE, OSTR, 1, "<sp> path-name"},
1774 {"MDTM", MDTM, OSTR, 1, "<sp> path-name"},
1775 #if defined(USE_TLS) || defined(USE_GSS)
1776 {"PROT", PROT, ARGS, 1, "<sp> protection-level"},
1777 {"PBSZ", PBSZ, STR1, 1, "<sp> protection-buffer-size"},
1778 {"AUTH", AUTH, STR1, 1, "<sp> authentication-mechanism"},
1779 {"ADAT", ADAT, STR1, 1, "<sp> authentication-data"},
1780 #if defined(USE_GSS)
1781 {"CCC", CCC, ARGS, 1, "(clear command channel)"},
1782 #endif
1783 #endif /* defined(USE_TLS) || defined(USE_GSS) */
1784 {NULL, 0, 0, 0, 0}
1785 };
1786
1787 struct tab sitetab[] =
1788 {
1789 {"UMASK", UMASK, ARGS, 1, "[ <sp> umask ]"},
1790 {"IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]"},
1791 {"CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name"},
1792 {"HELP", HELP, OSTR, 1, "[ <sp> <string> ]"},
1793 {"GROUP", GROUP, STR1, 1, "<sp> access-group"},
1794 {"GPASS", GPASS, OSTR, 1, "<sp> access-password"},
1795 {"NEWER", NEWER, STR3, 1, "<sp> YYYYMMDDHHMMSS [ <sp> path-name ]"},
1796 {"MINFO", MINFO, STR3, 1, "<sp> YYYYMMDDHHMMSS [ <sp> path-name ]"},
1797 {"INDEX", INDEX, STR1, 1, "<sp> pattern"},
1798 {"EXEC", EXEC, STR1, 1, "<sp> command [ <sp> arguments ]"},
1799 {"ALIAS", ALIAS, OSTR, 1, "[ <sp> alias ] "},
1800 {"CDPATH", CDPATH, OSTR, 1, "[ <sp> ] "},
1801 {"GROUPS", GROUPS, OSTR, 1, "[ <sp> ] "},
1802 {"CHECKMETHOD", CHECKMETHOD, OSTR, 1, "[ <sp> crc|md5 ]"},
1803 {"CHECKSUM", CHECKSUM, OSTR, 1, "[ <sp> file-name ]"},
1804 {NULL, 0, 0, 0, 0}
1805 };
1806
lookup(register struct tab * p,char * cmd)1807 struct tab *lookup(register struct tab *p, char *cmd)
1808 {
1809 for (; p->name != NULL; p++)
1810 if (strcmp(cmd, p->name) == 0)
1811 return (p);
1812 return (0);
1813 }
1814
1815 #include <arpa/telnet.h>
1816
1817 /*
1818 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1819 */
wu_getline(char * s,int n,register FILE * iop)1820 char *wu_getline(char *s, int n, register FILE *iop)
1821 {
1822 register int c;
1823 register char *cs;
1824 char *passtxt = "PASS password\r\n";
1825
1826 cs = s;
1827 /* tmpline may contain saved command from urgent mode interruption */
1828 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1829 *cs++ = tmpline[c];
1830 if (tmpline[c] == '\n') {
1831 *cs++ = '\0';
1832 if (debug) {
1833 if (strncasecmp(passtxt, s, 5) == 0)
1834 syslog(LOG_DEBUG, "command: %s", passtxt);
1835 else
1836 syslog(LOG_DEBUG, "command: %s", s);
1837 }
1838 tmpline[0] = '\0';
1839 return (s);
1840 }
1841 if (c == 0)
1842 tmpline[0] = '\0';
1843 }
1844 retry:
1845 while ((c = getc(iop)) != EOF) {
1846 #ifdef TRANSFER_COUNT
1847 byte_count_total++;
1848 byte_count_in++;
1849 #endif
1850 c &= 0377;
1851 if (c == IAC) {
1852 if ((c = getc(iop)) != EOF) {
1853 #ifdef TRANSFER_COUNT
1854 byte_count_total++;
1855 byte_count_in++;
1856 #endif
1857 c &= 0377;
1858 switch (c) {
1859 case WILL:
1860 case WONT:
1861 c = getc(iop);
1862 #ifdef TRANSFER_COUNT
1863 byte_count_total++;
1864 byte_count_in++;
1865 #endif
1866 printf("%c%c%c", IAC, DONT, 0377 & c);
1867 (void) fflush(stdout);
1868 continue;
1869 case DO:
1870 case DONT:
1871 c = getc(iop);
1872 #ifdef TRANSFER_COUNT
1873 byte_count_total++;
1874 byte_count_in++;
1875 #endif
1876 printf("%c%c%c", IAC, WONT, 0377 & c);
1877 (void) fflush(stdout);
1878 continue;
1879 case IAC:
1880 break;
1881 default:
1882 continue; /* ignore command */
1883 }
1884 }
1885 }
1886 *cs++ = c;
1887 if (--n <= 0) {
1888 /* If command does not fit to buffer then discard the rest. */
1889 while (c != '\n' && (c = getc(iop)) != EOF)
1890 ;
1891 reply(500, "Command too long");
1892 break;
1893 }
1894 if (c == '\n')
1895 break;
1896 }
1897
1898 if (c == EOF && cs == s) {
1899 if (ferror(iop) && (errno == EINTR))
1900 goto retry;
1901 return (NULL);
1902 }
1903
1904 *cs++ = '\0';
1905
1906 #if defined(USE_GSS)
1907 if (IS_GSSAUTH(cur_auth_type) &&
1908 (gss_info.authstate & GSS_ADAT_DONE) &&
1909 gss_info.context != GSS_C_NO_CONTEXT) {
1910 s = sec_decode_command(s);
1911 } else if (IS_GSSAUTH(cur_auth_type) &&
1912 (!strncmp(s, "ENC", 3) || !strncmp(s, "MIC", 3) ||
1913 !strncmp(s, "CONF", 4)) &&
1914 !(gss_info.authstate & GSS_ADAT_DONE)) {
1915 if (debug)
1916 syslog(LOG_DEBUG, "command: %s", s);
1917 reply(503, "Must perform authentication before sending protected commands");
1918 *s = '\0';
1919 return(s);
1920 }
1921 #endif /* USE_GSS */
1922 if (debug) {
1923 if (strncasecmp(passtxt, s, 5) == 0)
1924 syslog(LOG_DEBUG, "command: %s", passtxt);
1925 else
1926 syslog(LOG_DEBUG, "command: %s", s);
1927 }
1928 return (s);
1929 }
1930
toolong(int a)1931 static void toolong(int a) /* signal that caused this function to be called */
1932 {
1933 time_t now;
1934
1935 reply(421,
1936 "Timeout (%d seconds): closing control connection.", timeout_idle);
1937 (void) time(&now);
1938 if (logging) {
1939 syslog(LOG_INFO,
1940 "User %s timed out after %d seconds at %.24s",
1941 (pw ? pw->pw_name : "unknown"), timeout_idle, ctime(&now));
1942 }
1943 dologout(1);
1944 }
1945
yylex(void)1946 int yylex(void)
1947 {
1948 static int cpos, state;
1949 register char *cp, *cp2;
1950 register struct tab *p;
1951 int n;
1952 time_t now;
1953 char c = '\0';
1954 extern time_t limit_time;
1955 extern time_t login_time;
1956
1957 for (;;) {
1958 switch (state) {
1959
1960 case CMD:
1961 yyerrorcalled = 0;
1962
1963 setproctitle("%s: IDLE", proctitle);
1964
1965 if (is_shutdown(!logged_in, 0) != 0) {
1966 reply(221, "Server shutting down. Goodbye.");
1967 dologout(0);
1968 }
1969
1970 time(&now);
1971 if ((limit_time > 0) && (((now - login_time) / 60) >= limit_time)) {
1972 reply(221, "Time limit reached. Goodbye.");
1973 dologout(0);
1974 }
1975
1976 #ifdef IGNORE_NOOP
1977 if (!alarm_running) {
1978 (void) signal(SIGALRM, toolong);
1979 (void) alarm((unsigned) timeout_idle);
1980 alarm_running = 1;
1981 }
1982 #else
1983 (void) signal(SIGALRM, toolong);
1984 (void) alarm((unsigned) timeout_idle);
1985 #endif
1986 if (wu_getline(cbuf, sizeof(cbuf) - 1, stdin) == NULL) {
1987 (void) alarm(0);
1988 reply(221, "You could at least say goodbye.");
1989 dologout(0);
1990 }
1991 #ifndef IGNORE_NOOP
1992 (void) alarm(0);
1993 #endif
1994 if ((cp = strchr(cbuf, '\r'))) {
1995 *cp++ = '\n';
1996 *cp = '\0';
1997 }
1998 if ((cp = strpbrk(cbuf, " \n")))
1999 cpos = cp - cbuf;
2000 if (cpos == 0)
2001 cpos = 4;
2002 c = cbuf[cpos];
2003 cbuf[cpos] = '\0';
2004 upper(cbuf);
2005 #ifdef IGNORE_NOOP
2006 if (strncasecmp(cbuf, "NOOP", 4) != 0) {
2007 (void) alarm(0);
2008 alarm_running = 0;
2009 }
2010 #endif
2011 p = lookup(cmdtab, cbuf);
2012 cbuf[cpos] = c;
2013 if (strncasecmp(cbuf, "PASS", 4) != 0 &&
2014 strncasecmp(cbuf, "SITE GPASS", 10) != 0) {
2015 if ((cp = strchr(cbuf, '\n')))
2016 *cp = '\0';
2017 setproctitle("%s: %s", proctitle, cbuf);
2018 if (cp)
2019 *cp = '\n';
2020 }
2021 if (p != 0) {
2022 if (p->implemented == 0) {
2023 nack(p->name);
2024 longjmp(errcatch, 0);
2025 /* NOTREACHED */
2026 }
2027 state = p->state;
2028 yylval.String = p->name;
2029 return (p->token);
2030 }
2031 break;
2032
2033 case SITECMD:
2034 if (cbuf[cpos] == ' ') {
2035 cpos++;
2036 return (SP);
2037 }
2038 cp = &cbuf[cpos];
2039 if ((cp2 = strpbrk(cp, " \n")))
2040 cpos = cp2 - cbuf;
2041 c = cbuf[cpos];
2042 cbuf[cpos] = '\0';
2043 upper(cp);
2044 p = lookup(sitetab, cp);
2045 cbuf[cpos] = c;
2046 if (p != 0) {
2047 #ifndef PARANOID /* what GOOD is SITE *, anyways?! _H */
2048 if (p->implemented == 0) {
2049 #else
2050 if (1) {
2051 syslog(LOG_WARNING, "refused SITE %s %s from %s of %s",
2052 p->name, &cbuf[cpos],
2053 anonymous ? guestpw : authuser, remoteident);
2054 #endif /* PARANOID */
2055 state = CMD;
2056 nack(p->name);
2057 longjmp(errcatch, 0);
2058 /* NOTREACHED */
2059 }
2060 state = p->state;
2061 yylval.String = p->name;
2062 return (p->token);
2063 }
2064 state = CMD;
2065 break;
2066
2067 case OSTR:
2068 if (cbuf[cpos] == '\n') {
2069 state = CMD;
2070 return (CRLF);
2071 }
2072 /* FALLTHROUGH */
2073
2074 case STR1:
2075 case ZSTR1:
2076 dostr1:
2077 if (cbuf[cpos] == ' ') {
2078 cpos++;
2079 if (state == OSTR)
2080 state = STR2;
2081 else
2082 ++state;
2083 return (SP);
2084 }
2085 break;
2086
2087 case ZSTR2:
2088 if (cbuf[cpos] == '\n') {
2089 state = CMD;
2090 return (CRLF);
2091 }
2092 /* FALLTHROUGH */
2093
2094 case STR2:
2095 cp = &cbuf[cpos];
2096 n = strlen(cp);
2097 cpos += n - 1;
2098 /*
2099 * Make sure the string is nonempty and \n terminated.
2100 */
2101 if (n > 1 && cbuf[cpos] == '\n') {
2102 cbuf[cpos] = '\0';
2103 yylval.String = copy(cp);
2104 cbuf[cpos] = '\n';
2105 state = ARGS;
2106 return (STRING);
2107 }
2108 break;
2109
2110 case NSTR:
2111 if (cbuf[cpos] == ' ') {
2112 cpos++;
2113 return (SP);
2114 }
2115 if (isdigit(cbuf[cpos])) {
2116 cp = &cbuf[cpos];
2117 while (isdigit(cbuf[++cpos]));
2118 c = cbuf[cpos];
2119 cbuf[cpos] = '\0';
2120 yylval.Number = atoi(cp);
2121 cbuf[cpos] = c;
2122 state = STR1;
2123 return (NUMBER);
2124 }
2125 state = STR1;
2126 goto dostr1;
2127
2128 case STR3:
2129 if (cbuf[cpos] == ' ') {
2130 cpos++;
2131 return (SP);
2132 }
2133
2134 cp = &cbuf[cpos];
2135 cp2 = strpbrk(cp, " \n");
2136 if (cp2 != NULL) {
2137 c = *cp2;
2138 *cp2 = '\0';
2139 }
2140 n = strlen(cp);
2141 cpos += n;
2142 /*
2143 * Make sure the string is nonempty and SP terminated.
2144 */
2145 if ((cp2 - cp) > 1) {
2146 yylval.String = copy(cp);
2147 cbuf[cpos] = c;
2148 state = OSTR;
2149 return (STRING);
2150 }
2151 break;
2152
2153 case ARGS:
2154 if (isdigit(cbuf[cpos])) {
2155 cp = &cbuf[cpos];
2156 while (isdigit(cbuf[++cpos]));
2157 c = cbuf[cpos];
2158 cbuf[cpos] = '\0';
2159 yylval.Number = atoi(cp);
2160 cbuf[cpos] = c;
2161 return (NUMBER);
2162 }
2163 switch (cbuf[cpos++]) {
2164
2165 case '\n':
2166 state = CMD;
2167 return (CRLF);
2168
2169 case ' ':
2170 return (SP);
2171
2172 case ',':
2173 return (COMMA);
2174
2175 case 'A':
2176 case 'a':
2177 return (A);
2178
2179 case 'B':
2180 case 'b':
2181 return (B);
2182
2183 case 'C':
2184 case 'c':
2185 return (C);
2186
2187 case 'E':
2188 case 'e':
2189 return (E);
2190
2191 case 'F':
2192 case 'f':
2193 return (F);
2194
2195 case 'I':
2196 case 'i':
2197 return (I);
2198
2199 case 'L':
2200 case 'l':
2201 return (L);
2202
2203 case 'N':
2204 case 'n':
2205 return (N);
2206
2207 case 'P':
2208 case 'p':
2209 return (P);
2210
2211 case 'R':
2212 case 'r':
2213 return (R);
2214
2215 case 'S':
2216 case 's':
2217 return (S);
2218
2219 case 'T':
2220 case 't':
2221 return (T);
2222
2223 }
2224 break;
2225
2226 default:
2227 fatal("Unknown state in scanner.");
2228 }
2229 if (yyerrorcalled == 0) {
2230 if ((cp = strchr(cbuf, '\n')) != NULL)
2231 *cp = '\0';
2232 if (logged_in)
2233 reply(500, "'%s': command not understood.", cbuf);
2234 else
2235 reply(530, "Please login with USER and PASS.");
2236 }
2237 state = CMD;
2238 longjmp(errcatch, 0);
2239 }
2240 }
2241
2242 void upper(char *s)
2243 {
2244 while (*s != '\0') {
2245 if (islower(*s))
2246 *s = toupper(*s);
2247 s++;
2248 }
2249 }
2250
2251 char *copy(char *s)
2252 {
2253 char *p;
2254
2255 p = strdup(s);
2256 if (p == NULL)
2257 fatal("Ran out of memory.");
2258 return (p);
2259 }
2260
2261 void help(struct tab *ctab, char *s)
2262 {
2263 struct aclmember *entry = NULL;
2264 struct tab *c;
2265 size_t width, NCMDS;
2266 char *type;
2267
2268 if (ctab == sitetab)
2269 type = "SITE ";
2270 else
2271 type = "";
2272 width = 0, NCMDS = 0;
2273 for (c = ctab; c->name != NULL; c++) {
2274 size_t len = strlen(c->name);
2275
2276 if (len > width)
2277 width = len;
2278 NCMDS++;
2279 }
2280 width = (width + 8) & ~7;
2281 if (s == 0) {
2282 register size_t i, j, w;
2283 size_t columns, lines;
2284
2285 lreply(214, "The following %scommands are recognized %s.",
2286 type, "(* =>'s unimplemented)");
2287 columns = 76 / width;
2288 if (columns == 0)
2289 columns = 1;
2290 lines = (NCMDS + columns - 1) / columns;
2291 for (i = 0; i < lines; i++) {
2292 char line[BUFSIZ], *ptr = line;
2293 ptr += strlcpy(line, " ", sizeof(line));
2294 for (j = 0; j < columns; j++) {
2295 c = ctab + j * lines + i;
2296 (void) snprintf(ptr, line + sizeof(line) - ptr, "%s%c",
2297 c->name, c->implemented ? ' ' : '*');
2298 w = strlen(c->name) + 1;
2299 ptr += w;
2300 if (c + lines >= &ctab[NCMDS])
2301 break;
2302 while (w < width) {
2303 *(ptr++) = ' ';
2304 w++;
2305 }
2306 }
2307 *ptr = '\0';
2308 lreply(0, "%s", line);
2309 }
2310 (void) fflush(stdout);
2311 #ifdef VIRTUAL
2312 if (virtual_mode && !virtual_ftpaccess && virtual_email[0] != '\0')
2313 reply(214, "Direct comments to %s.", virtual_email);
2314 else
2315 #endif
2316 if ((getaclentry("email", &entry)) && ARG0)
2317 reply(214, "Direct comments to %s.", ARG0);
2318 else
2319 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
2320 return;
2321 }
2322 upper(s);
2323 c = lookup(ctab, s);
2324 if (c == (struct tab *) NULL) {
2325 reply(502, "Unknown command %s.", s);
2326 return;
2327 }
2328 if (c->implemented)
2329 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
2330 else
2331 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
2332 c->name, c->help);
2333 }
2334
2335 void sizecmd(char *filename)
2336 {
2337 switch (type) {
2338 case TYPE_L:
2339 case TYPE_I:{
2340 struct stat stbuf;
2341 if (stat(filename, &stbuf) < 0 ||
2342 (stbuf.st_mode & S_IFMT) != S_IFREG)
2343 reply(550, "%s: not a plain file.", filename);
2344 else
2345 reply(213, "%" L_FORMAT, stbuf.st_size);
2346 break;
2347 }
2348 case TYPE_A:{
2349 FILE *fin;
2350 register int c;
2351 register off_t count;
2352 struct stat stbuf;
2353 fin = fopen(filename, "r");
2354 if (fin == NULL) {
2355 perror_reply(550, filename);
2356 return;
2357 }
2358 if (fstat(fileno(fin), &stbuf) < 0 ||
2359 (stbuf.st_mode & S_IFMT) != S_IFREG) {
2360 reply(550, "%s: not a plain file.", filename);
2361 (void) fclose(fin);
2362 return;
2363 }
2364
2365 count = 0;
2366 while ((c = getc(fin)) != EOF) {
2367 if (c == '\n') /* will get expanded to \r\n */
2368 count++;
2369 count++;
2370 }
2371 (void) fclose(fin);
2372
2373 reply(213, "%" L_FORMAT, count);
2374 break;
2375 }
2376 default:
2377 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
2378 }
2379 }
2380
2381 void site_exec(char *cmd)
2382 {
2383 #ifdef PARANOID
2384 syslog(LOG_CRIT, "REFUSED SITE_EXEC (slipped through!!): %s", cmd);
2385 #else
2386 char buf[MAXPATHLEN];
2387 char *sp = (char *) strchr(cmd, ' '), *slash, *t;
2388 FILE *cmdf;
2389
2390
2391 /* sanitize the command-string */
2392
2393 if (sp == 0) {
2394 while ((slash = strchr(cmd, '/')) != 0)
2395 cmd = slash + 1;
2396 }
2397 else {
2398 while (sp && (slash = (char *) strchr(cmd, '/'))
2399 && (slash < sp))
2400 cmd = slash + 1;
2401 }
2402
2403 for (t = cmd; *t && !isspace(*t); t++) {
2404 if (isupper(*t)) {
2405 *t = tolower(*t);
2406 }
2407 }
2408
2409 /* build the command */
2410 if (strlen(_PATH_EXECPATH) + strlen(cmd) + 2 > sizeof(buf))
2411 return;
2412 (void) snprintf(buf, sizeof(buf), "%s/%s", _PATH_EXECPATH, cmd);
2413
2414 cmdf = ftpd_popen(buf, "r", 0);
2415 if (!cmdf) {
2416 perror_reply(550, cmd);
2417 if (log_commands)
2418 syslog(LOG_INFO, "SITE EXEC (FAIL: %m): %s", cmd);
2419 }
2420 else {
2421 int lines = 0;
2422 int maxlines = 0;
2423 struct aclmember *entry = NULL;
2424 char class[BUFSIZ];
2425 int maxfound = 0;
2426 int defmaxlines = 20;
2427 int which;
2428
2429 (void) acl_getclass(class);
2430 while ((getaclentry("site-exec-max-lines", &entry)) && ARG0) {
2431 if (ARG1)
2432 for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
2433 if (!strcasecmp(ARG[which], class)) {
2434 maxlines = atoi(ARG0);
2435 maxfound = 1;
2436 }
2437 if (!strcmp(ARG[which], "*"))
2438 defmaxlines = atoi(ARG0);
2439 }
2440 else
2441 defmaxlines = atoi(ARG0);
2442 }
2443 if (!maxfound)
2444 maxlines = defmaxlines;
2445 lreply(200, "%s", cmd);
2446 while (fgets(buf, sizeof buf, cmdf)) {
2447 size_t len = strlen(buf);
2448
2449 if (len > 0 && buf[len - 1] == '\n')
2450 buf[--len] = '\0';
2451 lreply(200, "%s", buf);
2452 if (maxlines <= 0)
2453 ++lines;
2454 else if (++lines >= maxlines) {
2455 lreply(200, "*** Truncated ***");
2456 break;
2457 }
2458 }
2459 reply(200, " (end of '%s')", cmd);
2460 if (log_commands)
2461 syslog(LOG_INFO, "SITE EXEC (lines: %d): %s", lines, cmd);
2462 ftpd_pclose(cmdf);
2463 }
2464 #endif /* PARANOID */
2465 }
2466
2467 void alias(char *s)
2468 {
2469 struct aclmember *entry = NULL;
2470
2471 if (s != (char *) NULL) {
2472 while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL)
2473 if (!strcmp(ARG0, s)) {
2474 reply(214, "%s is an alias for %s.", ARG0, ARG1);
2475 return;
2476 }
2477 reply(502, "Unknown alias %s.", s);
2478 return;
2479 }
2480
2481 lreply(214, "The following aliases are available.");
2482
2483 while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL)
2484 lreply(0, " %-8s %s", ARG0, ARG1);
2485 (void) fflush(stdout);
2486
2487 reply(214, "");
2488 }
2489
2490 void cdpath(void)
2491 {
2492 struct aclmember *entry = NULL;
2493
2494 lreply(214, "The cdpath is:");
2495 while (getaclentry("cdpath", &entry) && ARG0 != NULL)
2496 lreply(0, " %s", ARG0);
2497 (void) fflush(stdout);
2498 reply(214, "");
2499 }
2500
2501 void print_groups(void)
2502 {
2503 gid_t *groups;
2504 int ngroups;
2505 int maxgrp;
2506
2507 maxgrp = getgroups(0, NULL);
2508
2509 groups = alloca(maxgrp * sizeof (gid_t));
2510
2511 if ((ngroups = getgroups(maxgrp, groups)) < 0) {
2512 return;
2513 }
2514
2515 lreply(214, "Group membership is:");
2516 ngroups--;
2517
2518 for (; ngroups >= 0; ngroups--)
2519 lreply(214, " %d", groups[ngroups]);
2520
2521 (void) fflush(stdout);
2522 reply(214, "");
2523 }
2524