Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / appl / ftp / ftp / security.c
1 /*
2  * Copyright (c) 1998, 1999 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.15 1999/12/02 16:58:30 joda 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
170     b = block_read(fd, &len, sizeof(len));
171     if (b == 0)
172         return 0;
173     else if (b < 0)
174         return -1;
175     len = ntohl(len);
176     buf->data = realloc(buf->data, len);
177     b = block_read(fd, buf->data, len);
178     if (b == 0)
179         return 0;
180     else if (b < 0)
181         return -1;
182     buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
183     buf->index = 0;
184     return 0;
185 }
186
187 static size_t
188 buffer_read(struct buffer *buf, void *data, size_t len)
189 {
190     len = min(len, buf->size - buf->index);
191     memcpy(data, (char*)buf->data + buf->index, len);
192     buf->index += len;
193     return len;
194 }
195
196 static size_t
197 buffer_write(struct buffer *buf, void *data, size_t len)
198 {
199     if(buf->index + len > buf->size) {
200         void *tmp;
201         if(buf->data == NULL)
202             tmp = malloc(1024);
203         else
204             tmp = realloc(buf->data, buf->index + len);
205         if(tmp == NULL)
206             return -1;
207         buf->data = tmp;
208         buf->size = buf->index + len;
209     }
210     memcpy((char*)buf->data + buf->index, data, len);
211     buf->index += len;
212     return len;
213 }
214
215 int
216 sec_read(int fd, void *data, int length)
217 {
218     size_t len;
219     int rx = 0;
220
221     if(sec_complete == 0 || data_prot == 0)
222         return read(fd, data, length);
223
224     if(in_buffer.eof_flag){
225         in_buffer.eof_flag = 0;
226         return 0;
227     }
228     
229     len = buffer_read(&in_buffer, data, length);
230     length -= len;
231     rx += len;
232     data = (char*)data + len;
233     
234     while(length){
235         if(sec_get_data(fd, &in_buffer, data_prot) < 0)
236             return -1;
237         if(in_buffer.size == 0) {
238             if(rx)
239                 in_buffer.eof_flag = 1;
240             return rx;
241         }
242         len = buffer_read(&in_buffer, data, length);
243         length -= len;
244         rx += len;
245         data = (char*)data + len;
246     }
247     return rx;
248 }
249
250 static int
251 sec_send(int fd, char *from, int length)
252 {
253     int bytes;
254     void *buf;
255     bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
256     bytes = htonl(bytes);
257     block_write(fd, &bytes, sizeof(bytes));
258     block_write(fd, buf, ntohl(bytes));
259     free(buf);
260     return length;
261 }
262
263 int
264 sec_fflush(FILE *F)
265 {
266     if(data_prot != prot_clear) {
267         if(out_buffer.index > 0){
268             sec_write(fileno(F), out_buffer.data, out_buffer.index);
269             out_buffer.index = 0;
270         }
271         sec_send(fileno(F), NULL, 0);
272     }
273     fflush(F);
274     return 0;
275 }
276
277 int
278 sec_write(int fd, char *data, int length)
279 {
280     int len = buffer_size;
281     int tx = 0;
282       
283     if(data_prot == prot_clear)
284         return write(fd, data, length);
285
286     len -= (*mech->overhead)(app_data, data_prot, len);
287     while(length){
288         if(length < len)
289             len = length;
290         sec_send(fd, data, len);
291         length -= len;
292         data += len;
293         tx += len;
294     }
295     return tx;
296 }
297
298 int
299 sec_vfprintf2(FILE *f, const char *fmt, va_list ap)
300 {
301     char *buf;
302     int ret;
303     if(data_prot == prot_clear)
304         return vfprintf(f, fmt, ap);
305     else {
306         vasprintf(&buf, fmt, ap);
307         ret = buffer_write(&out_buffer, buf, strlen(buf));
308         free(buf);
309         return ret;
310     }
311 }
312
313 int
314 sec_fprintf2(FILE *f, const char *fmt, ...)
315 {
316     int ret;
317     va_list ap;
318     va_start(ap, fmt);
319     ret = sec_vfprintf2(f, fmt, ap);
320     va_end(ap);
321     return ret;
322 }
323
324 int
325 sec_putc(int c, FILE *F)
326 {
327     char ch = c;
328     if(data_prot == prot_clear)
329         return putc(c, F);
330     
331     buffer_write(&out_buffer, &ch, 1);
332     if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
333         sec_write(fileno(F), out_buffer.data, out_buffer.index);
334         out_buffer.index = 0;
335     }
336     return c;
337 }
338
339 int
340 sec_read_msg(char *s, int level)
341 {
342     int len;
343     char *buf;
344     int code;
345     
346     buf = malloc(strlen(s));
347     len = base64_decode(s + 4, buf); /* XXX */
348     
349     len = (*mech->decode)(app_data, buf, len, level);
350     if(len < 0)
351         return -1;
352     
353     buf[len] = '\0';
354
355     if(buf[3] == '-')
356         code = 0;
357     else
358         sscanf(buf, "%d", &code);
359     if(buf[len-1] == '\n')
360         buf[len-1] = '\0';
361     strcpy(s, buf);
362     free(buf);
363     return code;
364 }
365
366 int
367 sec_vfprintf(FILE *f, const char *fmt, va_list ap)
368 {
369     char *buf;
370     void *enc;
371     int len;
372     if(!sec_complete)
373         return vfprintf(f, fmt, ap);
374     
375     vasprintf(&buf, fmt, ap);
376     len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
377     free(buf);
378     if(len < 0) {
379         printf("Failed to encode command.\n");
380         return -1;
381     }
382     if(base64_encode(enc, len, &buf) < 0){
383         printf("Out of memory base64-encoding.\n");
384         return -1;
385     }
386 #ifdef FTP_SERVER
387     if(command_prot == prot_safe)
388         fprintf(f, "631 %s\r\n", buf);
389     else if(command_prot == prot_private)
390         fprintf(f, "632 %s\r\n", buf);
391     else if(command_prot == prot_confidential)
392         fprintf(f, "633 %s\r\n", buf);
393 #else
394     if(command_prot == prot_safe)
395         fprintf(f, "MIC %s", buf);
396     else if(command_prot == prot_private)
397         fprintf(f, "ENC %s", buf);
398     else if(command_prot == prot_confidential)
399         fprintf(f, "CONF %s", buf);
400 #endif
401     free(buf);
402     return 0;
403 }
404
405 int
406 sec_fprintf(FILE *f, const char *fmt, ...)
407 {
408     va_list ap;
409     int ret;
410     va_start(ap, fmt);
411     ret = sec_vfprintf(f, fmt, ap);
412     va_end(ap);
413     return ret;
414 }
415
416 /* end common stuff */
417
418 #ifdef FTP_SERVER
419
420 void
421 auth(char *auth_name)
422 {
423     int i;
424     for(i = 0; (mech = mechs[i]) != NULL; i++){
425         if(!strcasecmp(auth_name, mech->name)){
426             app_data = realloc(app_data, mech->size);
427             if(mech->init && (*mech->init)(app_data) != 0) {
428                 reply(431, "Unable to accept %s at this time", mech->name);
429                 return;
430             }
431             if(mech->auth) {
432                 (*mech->auth)(app_data);
433                 return;
434             }
435             if(mech->adat)
436                 reply(334, "Send authorization data.");
437             else
438                 reply(234, "Authorization complete.");
439             return;
440         }
441     }
442     free (app_data);
443     reply(504, "%s is unknown to me", auth_name);
444 }
445
446 void
447 adat(char *auth_data)
448 {
449     if(mech && !sec_complete) {
450         void *buf = malloc(strlen(auth_data));
451         size_t len;
452         len = base64_decode(auth_data, buf);
453         (*mech->adat)(app_data, buf, len);
454         free(buf);
455     } else
456         reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
457 }
458
459 void pbsz(int size)
460 {
461     size_t new = size;
462     if(!sec_complete)
463         reply(503, "Incomplete security data exchange.");
464     if(mech->pbsz)
465         new = (*mech->pbsz)(app_data, size);
466     if(buffer_size != new){
467         buffer_size = size;
468     }
469     if(new != size)
470         reply(200, "PBSZ=%lu", (unsigned long)new);
471     else
472         reply(200, "OK");
473 }
474
475 void
476 prot(char *pl)
477 {
478     int p = -1;
479
480     if(buffer_size == 0){
481         reply(503, "No protection buffer size negotiated.");
482         return;
483     }
484
485     if(!strcasecmp(pl, "C"))
486         p = prot_clear;
487     else if(!strcasecmp(pl, "S"))
488         p = prot_safe;
489     else if(!strcasecmp(pl, "E"))
490         p = prot_confidential;
491     else if(!strcasecmp(pl, "P"))
492         p = prot_private;
493     else {
494         reply(504, "Unrecognized protection level.");
495         return;
496     }
497     
498     if(sec_complete){
499         if((*mech->check_prot)(app_data, p)){
500             reply(536, "%s does not support %s protection.", 
501                   mech->name, level_to_name(p));
502         }else{
503             data_prot = (enum protection_level)p;
504             reply(200, "Data protection is %s.", level_to_name(p));
505         }
506     }else{
507         reply(503, "Incomplete security data exchange.");
508     }
509 }
510
511 void ccc(void)
512 {
513     if(sec_complete){
514         if(mech->ccc && (*mech->ccc)(app_data) == 0)
515             command_prot = data_prot = prot_clear;
516         else
517             reply(534, "You must be joking.");
518     }else
519         reply(503, "Incomplete security data exchange.");
520 }
521
522 void mec(char *msg, enum protection_level level)
523 {
524     void *buf;
525     size_t len;
526     if(!sec_complete) {
527         reply(503, "Incomplete security data exchange.");
528         return;
529     }
530     buf = malloc(strlen(msg) + 2); /* XXX go figure out where that 2
531                                       comes from :-) */
532     len = base64_decode(msg, buf);
533     command_prot = level;
534     if(len == (size_t)-1) {
535         reply(501, "Failed to base64-decode command");
536         return;
537     }
538     len = (*mech->decode)(app_data, buf, len, level);
539     if(len == (size_t)-1) {
540         reply(535, "Failed to decode command");
541         return;
542     }
543     ((char*)buf)[len] = '\0';
544     if(strstr((char*)buf, "\r\n") == NULL)
545         strcat((char*)buf, "\r\n");
546     new_ftp_command(buf);
547 }
548
549 /* ------------------------------------------------------------ */
550
551 int
552 sec_userok(char *user)
553 {
554     if(sec_complete)
555         return (*mech->userok)(app_data, user);
556     return 0;
557 }
558
559 char *ftp_command;
560
561 void
562 new_ftp_command(char *command)
563 {
564     ftp_command = command;
565 }
566
567 void
568 delete_ftp_command(void)
569 {
570     free(ftp_command);
571     ftp_command = NULL;
572 }
573
574 int
575 secure_command(void)
576 {
577     return ftp_command != NULL;
578 }
579
580 enum protection_level
581 get_command_prot(void)
582 {
583     return command_prot;
584 }
585
586 #else /* FTP_SERVER */
587
588 void
589 sec_status(void)
590 {
591     if(sec_complete){
592         printf("Using %s for authentication.\n", mech->name);
593         printf("Using %s command channel.\n", level_to_name(command_prot));
594         printf("Using %s data channel.\n", level_to_name(data_prot));
595         if(buffer_size > 0)
596             printf("Protection buffer size: %lu.\n", 
597                    (unsigned long)buffer_size);
598     }else{
599         printf("Not using any security mechanism.\n");
600     }
601 }
602
603 static int
604 sec_prot_internal(int level)
605 {
606     int ret;
607     char *p;
608     unsigned int s = 1048576;
609
610     int old_verbose = verbose;
611     verbose = 0;
612
613     if(!sec_complete){
614         printf("No security data exchange has taken place.\n");
615         return -1;
616     }
617
618     if(level){
619         ret = command("PBSZ %u", s);
620         if(ret != COMPLETE){
621             printf("Failed to set protection buffer size.\n");
622             return -1;
623         }
624         buffer_size = s;
625         p = strstr(reply_string, "PBSZ=");
626         if(p)
627             sscanf(p, "PBSZ=%u", &s);
628         if(s < buffer_size)
629             buffer_size = s;
630     }
631     verbose = old_verbose;
632     ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
633     if(ret != COMPLETE){
634         printf("Failed to set protection level.\n");
635         return -1;
636     }
637     
638     data_prot = (enum protection_level)level;
639     return 0;
640 }
641
642 enum protection_level
643 set_command_prot(enum protection_level level)
644 {
645     enum protection_level old = command_prot;
646     command_prot = level;
647     return old;
648 }
649
650 void
651 sec_prot(int argc, char **argv)
652 {
653     int level = -1;
654
655     if(argc < 2 || argc > 3)
656         goto usage;
657     if(!sec_complete) {
658         printf("No security data exchange has taken place.\n");
659         code = -1;
660         return;
661     }
662     level = name_to_level(argv[argc - 1]);
663     
664     if(level == -1)
665         goto usage;
666     
667     if((*mech->check_prot)(app_data, level)) {
668         printf("%s does not implement %s protection.\n", 
669                mech->name, level_to_name(level));
670         code = -1;
671         return;
672     }
673     
674     if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
675         if(sec_prot_internal(level) < 0){
676             code = -1;
677             return;
678         }
679     } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0)
680         set_command_prot(level);
681     else
682         goto usage;
683     code = 0;
684     return;
685  usage:
686     printf("usage: %s [command|data] [clear|safe|confidential|private]\n",
687            argv[0]);
688     code = -1;
689 }
690
691 static enum protection_level request_data_prot;
692
693 void
694 sec_set_protection_level(void)
695 {
696     if(sec_complete && data_prot != request_data_prot)
697         sec_prot_internal(request_data_prot);
698 }
699
700
701 int
702 sec_request_prot(char *level)
703 {
704     int l = name_to_level(level);
705     if(l == -1)
706         return -1;
707     request_data_prot = (enum protection_level)l;
708     return 0;
709 }
710
711 int
712 sec_login(char *host)
713 {
714     int ret;
715     struct sec_client_mech **m;
716     int old_verbose = verbose;
717
718     verbose = -1; /* shut up all messages this will produce (they
719                      are usually not very user friendly) */
720     
721     for(m = mechs; *m && (*m)->name; m++) {
722         void *tmp;
723
724         tmp = realloc(app_data, (*m)->size);
725         if (tmp == NULL) {
726             warnx ("realloc %u failed", (*m)->size);
727             return -1;
728         }
729         app_data = tmp;
730             
731         if((*m)->init && (*(*m)->init)(app_data) != 0) {
732             printf("Skipping %s...\n", (*m)->name);
733             continue;
734         }
735         printf("Trying %s...\n", (*m)->name);
736         ret = command("AUTH %s", (*m)->name);
737         if(ret != CONTINUE){
738             if(code == 504){
739                 printf("%s is not supported by the server.\n", (*m)->name);
740             }else if(code == 534){
741                 printf("%s rejected as security mechanism.\n", (*m)->name);
742             }else if(ret == ERROR) {
743                 printf("The server doesn't support the FTP "
744                        "security extensions.\n");
745                 verbose = old_verbose;
746                 return -1;
747             }
748             continue;
749         }
750
751         ret = (*(*m)->auth)(app_data, host);
752         
753         if(ret == AUTH_CONTINUE)
754             continue;
755         else if(ret != AUTH_OK){
756             /* mechanism is supposed to output error string */
757             verbose = old_verbose;
758             return -1;
759         }
760         mech = *m;
761         sec_complete = 1;
762         command_prot = prot_safe;
763         break;
764     }
765     
766     verbose = old_verbose;
767     return *m == NULL;
768 }
769
770 void
771 sec_end(void)
772 {
773     if (mech != NULL) {
774         if(mech->end)
775             (*mech->end)(app_data);
776         memset(app_data, 0, mech->size);
777         free(app_data);
778         app_data = NULL;
779     }
780     sec_complete = 0;
781     data_prot = (enum protection_level)0;
782 }
783
784 #endif /* FTP_SERVER */
785