]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - crypto/heimdal/appl/ftp/ftp/security.c
import of heimdal 0.3f
[FreeBSD/FreeBSD.git] / crypto / heimdal / appl / ftp / ftp / security.c
1 /*
2  * Copyright (c) 1998-2001 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifdef FTP_SERVER
35 #include "ftpd_locl.h"
36 #else
37 #include "ftp_locl.h"
38 #endif
39
40 RCSID("$Id: security.c,v 1.18 2001/02/07 10:49:43 assar Exp $");
41
42 static enum protection_level command_prot;
43 static enum protection_level data_prot;
44 static size_t buffer_size;
45
46 struct buffer {
47     void *data;
48     size_t size;
49     size_t index;
50     int eof_flag;
51 };
52
53 static struct buffer in_buffer, out_buffer;
54 int sec_complete;
55
56 static struct {
57     enum protection_level level;
58     const char *name;
59 } level_names[] = {
60     { prot_clear, "clear" },
61     { prot_safe, "safe" },
62     { prot_confidential, "confidential" },
63     { prot_private, "private" }
64 };
65
66 static const char *
67 level_to_name(enum protection_level level)
68 {
69     int i;
70     for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
71         if(level_names[i].level == level)
72             return level_names[i].name;
73     return "unknown";
74 }
75
76 #ifndef FTP_SERVER /* not used in server */
77 static enum protection_level 
78 name_to_level(const char *name)
79 {
80     int i;
81     for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
82         if(!strncasecmp(level_names[i].name, name, strlen(name)))
83             return level_names[i].level;
84     return (enum protection_level)-1;
85 }
86 #endif
87
88 #ifdef FTP_SERVER
89
90 static struct sec_server_mech *mechs[] = {
91 #ifdef KRB5
92     &gss_server_mech,
93 #endif
94 #ifdef KRB4
95     &krb4_server_mech,
96 #endif
97     NULL
98 };
99
100 static struct sec_server_mech *mech;
101
102 #else
103
104 static struct sec_client_mech *mechs[] = {
105 #ifdef KRB5
106     &gss_client_mech,
107 #endif
108 #ifdef KRB4
109     &krb4_client_mech,
110 #endif
111     NULL
112 };
113
114 static struct sec_client_mech *mech;
115
116 #endif
117
118 static void *app_data;
119
120 int
121 sec_getc(FILE *F)
122 {
123     if(sec_complete && data_prot) {
124         char c;
125         if(sec_read(fileno(F), &c, 1) <= 0)
126             return EOF;
127         return c;
128     } else
129         return getc(F);
130 }
131
132 static int
133 block_read(int fd, void *buf, size_t len)
134 {
135     unsigned char *p = buf;
136     int b;
137     while(len) {
138         b = read(fd, p, len);
139         if (b == 0)
140             return 0;
141         else if (b < 0)
142             return -1;
143         len -= b;
144         p += b;
145     }
146     return p - (unsigned char*)buf;
147 }
148
149 static int
150 block_write(int fd, void *buf, size_t len)
151 {
152     unsigned char *p = buf;
153     int b;
154     while(len) {
155         b = write(fd, p, len);
156         if(b < 0)
157             return -1;
158         len -= b;
159         p += b;
160     }
161     return p - (unsigned char*)buf;
162 }
163
164 static int
165 sec_get_data(int fd, struct buffer *buf, int level)
166 {
167     int len;
168     int b;
169     void *tmp;
170
171     b = block_read(fd, &len, sizeof(len));
172     if (b == 0)
173         return 0;
174     else if (b < 0)
175         return -1;
176     len = ntohl(len);
177     tmp = realloc(buf->data, len);
178     if (tmp == NULL)
179         return -1;
180     buf->data = tmp;
181     b = block_read(fd, buf->data, len);
182     if (b == 0)
183         return 0;
184     else if (b < 0)
185         return -1;
186     buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
187     buf->index = 0;
188     return 0;
189 }
190
191 static size_t
192 buffer_read(struct buffer *buf, void *data, size_t len)
193 {
194     len = min(len, buf->size - buf->index);
195     memcpy(data, (char*)buf->data + buf->index, len);
196     buf->index += len;
197     return len;
198 }
199
200 static size_t
201 buffer_write(struct buffer *buf, void *data, size_t len)
202 {
203     if(buf->index + len > buf->size) {
204         void *tmp;
205         if(buf->data == NULL)
206             tmp = malloc(1024);
207         else
208             tmp = realloc(buf->data, buf->index + len);
209         if(tmp == NULL)
210             return -1;
211         buf->data = tmp;
212         buf->size = buf->index + len;
213     }
214     memcpy((char*)buf->data + buf->index, data, len);
215     buf->index += len;
216     return len;
217 }
218
219 int
220 sec_read(int fd, void *data, int length)
221 {
222     size_t len;
223     int rx = 0;
224
225     if(sec_complete == 0 || data_prot == 0)
226         return read(fd, data, length);
227
228     if(in_buffer.eof_flag){
229         in_buffer.eof_flag = 0;
230         return 0;
231     }
232     
233     len = buffer_read(&in_buffer, data, length);
234     length -= len;
235     rx += len;
236     data = (char*)data + len;
237     
238     while(length){
239         int ret;
240
241         ret = sec_get_data(fd, &in_buffer, data_prot);
242         if (ret < 0)
243             return -1;
244         if(ret == 0 && in_buffer.size == 0) {
245             if(rx)
246                 in_buffer.eof_flag = 1;
247             return rx;
248         }
249         len = buffer_read(&in_buffer, data, length);
250         length -= len;
251         rx += len;
252         data = (char*)data + len;
253     }
254     return rx;
255 }
256
257 static int
258 sec_send(int fd, char *from, int length)
259 {
260     int bytes;
261     void *buf;
262     bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
263     bytes = htonl(bytes);
264     block_write(fd, &bytes, sizeof(bytes));
265     block_write(fd, buf, ntohl(bytes));
266     free(buf);
267     return length;
268 }
269
270 int
271 sec_fflush(FILE *F)
272 {
273     if(data_prot != prot_clear) {
274         if(out_buffer.index > 0){
275             sec_write(fileno(F), out_buffer.data, out_buffer.index);
276             out_buffer.index = 0;
277         }
278         sec_send(fileno(F), NULL, 0);
279     }
280     fflush(F);
281     return 0;
282 }
283
284 int
285 sec_write(int fd, char *data, int length)
286 {
287     int len = buffer_size;
288     int tx = 0;
289       
290     if(data_prot == prot_clear)
291         return write(fd, data, length);
292
293     len -= (*mech->overhead)(app_data, data_prot, len);
294     while(length){
295         if(length < len)
296             len = length;
297         sec_send(fd, data, len);
298         length -= len;
299         data += len;
300         tx += len;
301     }
302     return tx;
303 }
304
305 int
306 sec_vfprintf2(FILE *f, const char *fmt, va_list ap)
307 {
308     char *buf;
309     int ret;
310     if(data_prot == prot_clear)
311         return vfprintf(f, fmt, ap);
312     else {
313         vasprintf(&buf, fmt, ap);
314         ret = buffer_write(&out_buffer, buf, strlen(buf));
315         free(buf);
316         return ret;
317     }
318 }
319
320 int
321 sec_fprintf2(FILE *f, const char *fmt, ...)
322 {
323     int ret;
324     va_list ap;
325     va_start(ap, fmt);
326     ret = sec_vfprintf2(f, fmt, ap);
327     va_end(ap);
328     return ret;
329 }
330
331 int
332 sec_putc(int c, FILE *F)
333 {
334     char ch = c;
335     if(data_prot == prot_clear)
336         return putc(c, F);
337     
338     buffer_write(&out_buffer, &ch, 1);
339     if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
340         sec_write(fileno(F), out_buffer.data, out_buffer.index);
341         out_buffer.index = 0;
342     }
343     return c;
344 }
345
346 int
347 sec_read_msg(char *s, int level)
348 {
349     int len;
350     char *buf;
351     int code;
352     
353     buf = malloc(strlen(s));
354     len = base64_decode(s + 4, buf); /* XXX */
355     
356     len = (*mech->decode)(app_data, buf, len, level);
357     if(len < 0)
358         return -1;
359     
360     buf[len] = '\0';
361
362     if(buf[3] == '-')
363         code = 0;
364     else
365         sscanf(buf, "%d", &code);
366     if(buf[len-1] == '\n')
367         buf[len-1] = '\0';
368     strcpy(s, buf);
369     free(buf);
370     return code;
371 }
372
373 int
374 sec_vfprintf(FILE *f, const char *fmt, va_list ap)
375 {
376     char *buf;
377     void *enc;
378     int len;
379     if(!sec_complete)
380         return vfprintf(f, fmt, ap);
381     
382     vasprintf(&buf, fmt, ap);
383     len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
384     free(buf);
385     if(len < 0) {
386         printf("Failed to encode command.\n");
387         return -1;
388     }
389     if(base64_encode(enc, len, &buf) < 0){
390         printf("Out of memory base64-encoding.\n");
391         return -1;
392     }
393 #ifdef FTP_SERVER
394     if(command_prot == prot_safe)
395         fprintf(f, "631 %s\r\n", buf);
396     else if(command_prot == prot_private)
397         fprintf(f, "632 %s\r\n", buf);
398     else if(command_prot == prot_confidential)
399         fprintf(f, "633 %s\r\n", buf);
400 #else
401     if(command_prot == prot_safe)
402         fprintf(f, "MIC %s", buf);
403     else if(command_prot == prot_private)
404         fprintf(f, "ENC %s", buf);
405     else if(command_prot == prot_confidential)
406         fprintf(f, "CONF %s", buf);
407 #endif
408     free(buf);
409     return 0;
410 }
411
412 int
413 sec_fprintf(FILE *f, const char *fmt, ...)
414 {
415     va_list ap;
416     int ret;
417     va_start(ap, fmt);
418     ret = sec_vfprintf(f, fmt, ap);
419     va_end(ap);
420     return ret;
421 }
422
423 /* end common stuff */
424
425 #ifdef FTP_SERVER
426
427 void
428 auth(char *auth_name)
429 {
430     int i;
431     void *tmp;
432
433     for(i = 0; (mech = mechs[i]) != NULL; i++){
434         if(!strcasecmp(auth_name, mech->name)){
435             tmp = realloc(app_data, mech->size);
436             if (tmp == NULL) {
437                 reply(431, "Unable to accept %s at this time", mech->name);
438                 return;
439             }
440             app_data = tmp;
441
442             if(mech->init && (*mech->init)(app_data) != 0) {
443                 reply(431, "Unable to accept %s at this time", mech->name);
444                 return;
445             }
446             if(mech->auth) {
447                 (*mech->auth)(app_data);
448                 return;
449             }
450             if(mech->adat)
451                 reply(334, "Send authorization data.");
452             else
453                 reply(234, "Authorization complete.");
454             return;
455         }
456     }
457     free (app_data);
458     app_data = NULL;
459     reply(504, "%s is unknown to me", auth_name);
460 }
461
462 void
463 adat(char *auth_data)
464 {
465     if(mech && !sec_complete) {
466         void *buf = malloc(strlen(auth_data));
467         size_t len;
468         len = base64_decode(auth_data, buf);
469         (*mech->adat)(app_data, buf, len);
470         free(buf);
471     } else
472         reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
473 }
474
475 void pbsz(int size)
476 {
477     size_t new = size;
478     if(!sec_complete)
479         reply(503, "Incomplete security data exchange.");
480     if(mech->pbsz)
481         new = (*mech->pbsz)(app_data, size);
482     if(buffer_size != new){
483         buffer_size = size;
484     }
485     if(new != size)
486         reply(200, "PBSZ=%lu", (unsigned long)new);
487     else
488         reply(200, "OK");
489 }
490
491 void
492 prot(char *pl)
493 {
494     int p = -1;
495
496     if(buffer_size == 0){
497         reply(503, "No protection buffer size negotiated.");
498         return;
499     }
500
501     if(!strcasecmp(pl, "C"))
502         p = prot_clear;
503     else if(!strcasecmp(pl, "S"))
504         p = prot_safe;
505     else if(!strcasecmp(pl, "E"))
506         p = prot_confidential;
507     else if(!strcasecmp(pl, "P"))
508         p = prot_private;
509     else {
510         reply(504, "Unrecognized protection level.");
511         return;
512     }
513     
514     if(sec_complete){
515         if((*mech->check_prot)(app_data, p)){
516             reply(536, "%s does not support %s protection.", 
517                   mech->name, level_to_name(p));
518         }else{
519             data_prot = (enum protection_level)p;
520             reply(200, "Data protection is %s.", level_to_name(p));
521         }
522     }else{
523         reply(503, "Incomplete security data exchange.");
524     }
525 }
526
527 void ccc(void)
528 {
529     if(sec_complete){
530         if(mech->ccc && (*mech->ccc)(app_data) == 0)
531             command_prot = data_prot = prot_clear;
532         else
533             reply(534, "You must be joking.");
534     }else
535         reply(503, "Incomplete security data exchange.");
536 }
537
538 void mec(char *msg, enum protection_level level)
539 {
540     void *buf;
541     size_t len;
542     if(!sec_complete) {
543         reply(503, "Incomplete security data exchange.");
544         return;
545     }
546     buf = malloc(strlen(msg) + 2); /* XXX go figure out where that 2
547                                       comes from :-) */
548     len = base64_decode(msg, buf);
549     command_prot = level;
550     if(len == (size_t)-1) {
551         reply(501, "Failed to base64-decode command");
552         return;
553     }
554     len = (*mech->decode)(app_data, buf, len, level);
555     if(len == (size_t)-1) {
556         reply(535, "Failed to decode command");
557         return;
558     }
559     ((char*)buf)[len] = '\0';
560     if(strstr((char*)buf, "\r\n") == NULL)
561         strcat((char*)buf, "\r\n");
562     new_ftp_command(buf);
563 }
564
565 /* ------------------------------------------------------------ */
566
567 int
568 sec_userok(char *user)
569 {
570     if(sec_complete)
571         return (*mech->userok)(app_data, user);
572     return 0;
573 }
574
575 char *ftp_command;
576
577 void
578 new_ftp_command(char *command)
579 {
580     ftp_command = command;
581 }
582
583 void
584 delete_ftp_command(void)
585 {
586     free(ftp_command);
587     ftp_command = NULL;
588 }
589
590 int
591 secure_command(void)
592 {
593     return ftp_command != NULL;
594 }
595
596 enum protection_level
597 get_command_prot(void)
598 {
599     return command_prot;
600 }
601
602 #else /* FTP_SERVER */
603
604 void
605 sec_status(void)
606 {
607     if(sec_complete){
608         printf("Using %s for authentication.\n", mech->name);
609         printf("Using %s command channel.\n", level_to_name(command_prot));
610         printf("Using %s data channel.\n", level_to_name(data_prot));
611         if(buffer_size > 0)
612             printf("Protection buffer size: %lu.\n", 
613                    (unsigned long)buffer_size);
614     }else{
615         printf("Not using any security mechanism.\n");
616     }
617 }
618
619 static int
620 sec_prot_internal(int level)
621 {
622     int ret;
623     char *p;
624     unsigned int s = 1048576;
625
626     int old_verbose = verbose;
627     verbose = 0;
628
629     if(!sec_complete){
630         printf("No security data exchange has taken place.\n");
631         return -1;
632     }
633
634     if(level){
635         ret = command("PBSZ %u", s);
636         if(ret != COMPLETE){
637             printf("Failed to set protection buffer size.\n");
638             return -1;
639         }
640         buffer_size = s;
641         p = strstr(reply_string, "PBSZ=");
642         if(p)
643             sscanf(p, "PBSZ=%u", &s);
644         if(s < buffer_size)
645             buffer_size = s;
646     }
647     verbose = old_verbose;
648     ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
649     if(ret != COMPLETE){
650         printf("Failed to set protection level.\n");
651         return -1;
652     }
653     
654     data_prot = (enum protection_level)level;
655     return 0;
656 }
657
658 enum protection_level
659 set_command_prot(enum protection_level level)
660 {
661     enum protection_level old = command_prot;
662     command_prot = level;
663     return old;
664 }
665
666 void
667 sec_prot(int argc, char **argv)
668 {
669     int level = -1;
670
671     if(argc < 2 || argc > 3)
672         goto usage;
673     if(!sec_complete) {
674         printf("No security data exchange has taken place.\n");
675         code = -1;
676         return;
677     }
678     level = name_to_level(argv[argc - 1]);
679     
680     if(level == -1)
681         goto usage;
682     
683     if((*mech->check_prot)(app_data, level)) {
684         printf("%s does not implement %s protection.\n", 
685                mech->name, level_to_name(level));
686         code = -1;
687         return;
688     }
689     
690     if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
691         if(sec_prot_internal(level) < 0){
692             code = -1;
693             return;
694         }
695     } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0)
696         set_command_prot(level);
697     else
698         goto usage;
699     code = 0;
700     return;
701  usage:
702     printf("usage: %s [command|data] [clear|safe|confidential|private]\n",
703            argv[0]);
704     code = -1;
705 }
706
707 static enum protection_level request_data_prot;
708
709 void
710 sec_set_protection_level(void)
711 {
712     if(sec_complete && data_prot != request_data_prot)
713         sec_prot_internal(request_data_prot);
714 }
715
716
717 int
718 sec_request_prot(char *level)
719 {
720     int l = name_to_level(level);
721     if(l == -1)
722         return -1;
723     request_data_prot = (enum protection_level)l;
724     return 0;
725 }
726
727 int
728 sec_login(char *host)
729 {
730     int ret;
731     struct sec_client_mech **m;
732     int old_verbose = verbose;
733
734     verbose = -1; /* shut up all messages this will produce (they
735                      are usually not very user friendly) */
736     
737     for(m = mechs; *m && (*m)->name; m++) {
738         void *tmp;
739
740         tmp = realloc(app_data, (*m)->size);
741         if (tmp == NULL) {
742             warnx ("realloc %u failed", (*m)->size);
743             return -1;
744         }
745         app_data = tmp;
746             
747         if((*m)->init && (*(*m)->init)(app_data) != 0) {
748             printf("Skipping %s...\n", (*m)->name);
749             continue;
750         }
751         printf("Trying %s...\n", (*m)->name);
752         ret = command("AUTH %s", (*m)->name);
753         if(ret != CONTINUE){
754             if(code == 504){
755                 printf("%s is not supported by the server.\n", (*m)->name);
756             }else if(code == 534){
757                 printf("%s rejected as security mechanism.\n", (*m)->name);
758             }else if(ret == ERROR) {
759                 printf("The server doesn't support the FTP "
760                        "security extensions.\n");
761                 verbose = old_verbose;
762                 return -1;
763             }
764             continue;
765         }
766
767         ret = (*(*m)->auth)(app_data, host);
768         
769         if(ret == AUTH_CONTINUE)
770             continue;
771         else if(ret != AUTH_OK){
772             /* mechanism is supposed to output error string */
773             verbose = old_verbose;
774             return -1;
775         }
776         mech = *m;
777         sec_complete = 1;
778         command_prot = prot_safe;
779         break;
780     }
781     
782     verbose = old_verbose;
783     return *m == NULL;
784 }
785
786 void
787 sec_end(void)
788 {
789     if (mech != NULL) {
790         if(mech->end)
791             (*mech->end)(app_data);
792         if (app_data != NULL) {
793             memset(app_data, 0, mech->size);
794             free(app_data);
795             app_data = NULL;
796         }
797     }
798     sec_complete = 0;
799     data_prot = (enum protection_level)0;
800 }
801
802 #endif /* FTP_SERVER */
803