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