]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/appl/ftp/ftpd/security.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / appl / ftp / ftpd / security.c
1 /*
2  * Copyright (c) 1998-2002, 2005 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$");
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 prot_invalid;
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     NULL
95 };
96
97 static struct sec_server_mech *mech;
98
99 #else
100
101 static struct sec_client_mech *mechs[] = {
102 #ifdef KRB5
103     &gss_client_mech,
104 #endif
105     NULL
106 };
107
108 static struct sec_client_mech *mech;
109
110 #endif
111
112 static void *app_data;
113
114 int
115 sec_getc(FILE *F)
116 {
117     if(sec_complete && data_prot) {
118         char c;
119         if(sec_read(fileno(F), &c, 1) <= 0)
120             return EOF;
121         return c;
122     } else
123         return getc(F);
124 }
125
126 static int
127 block_read(int fd, void *buf, size_t len)
128 {
129     unsigned char *p = buf;
130     int b;
131     while(len) {
132         b = read(fd, p, len);
133         if (b == 0)
134             return 0;
135         else if (b < 0)
136             return -1;
137         len -= b;
138         p += b;
139     }
140     return p - (unsigned char*)buf;
141 }
142
143 static int
144 block_write(int fd, void *buf, size_t len)
145 {
146     unsigned char *p = buf;
147     int b;
148     while(len) {
149         b = write(fd, p, len);
150         if(b < 0)
151             return -1;
152         len -= b;
153         p += b;
154     }
155     return p - (unsigned char*)buf;
156 }
157
158 static int
159 sec_get_data(int fd, struct buffer *buf, int level)
160 {
161     int len;
162     int b;
163     void *tmp;
164
165     b = block_read(fd, &len, sizeof(len));
166     if (b == 0)
167         return 0;
168     else if (b < 0)
169         return -1;
170     len = ntohl(len);
171     tmp = realloc(buf->data, len);
172     if (tmp == NULL)
173         return -1;
174     buf->data = tmp;
175     b = block_read(fd, buf->data, len);
176     if (b == 0)
177         return 0;
178     else if (b < 0)
179         return -1;
180     buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
181     buf->index = 0;
182     return 0;
183 }
184
185 static size_t
186 buffer_read(struct buffer *buf, void *dataptr, size_t len)
187 {
188     len = min(len, buf->size - buf->index);
189     memcpy(dataptr, (char*)buf->data + buf->index, len);
190     buf->index += len;
191     return len;
192 }
193
194 static size_t
195 buffer_write(struct buffer *buf, void *dataptr, size_t len)
196 {
197     if(buf->index + len > buf->size) {
198         void *tmp;
199         if(buf->data == NULL)
200             tmp = malloc(1024);
201         else
202             tmp = realloc(buf->data, buf->index + len);
203         if(tmp == NULL)
204             return -1;
205         buf->data = tmp;
206         buf->size = buf->index + len;
207     }
208     memcpy((char*)buf->data + buf->index, dataptr, len);
209     buf->index += len;
210     return len;
211 }
212
213 int
214 sec_read(int fd, void *dataptr, int length)
215 {
216     size_t len;
217     int rx = 0;
218
219     if(sec_complete == 0 || data_prot == 0)
220         return read(fd, dataptr, length);
221
222     if(in_buffer.eof_flag){
223         in_buffer.eof_flag = 0;
224         return 0;
225     }
226
227     len = buffer_read(&in_buffer, dataptr, length);
228     length -= len;
229     rx += len;
230     dataptr = (char*)dataptr + len;
231
232     while(length){
233         int ret;
234
235         ret = sec_get_data(fd, &in_buffer, data_prot);
236         if (ret < 0)
237             return -1;
238         if(ret == 0 && in_buffer.size == 0) {
239             if(rx)
240                 in_buffer.eof_flag = 1;
241             return rx;
242         }
243         len = buffer_read(&in_buffer, dataptr, length);
244         length -= len;
245         rx += len;
246         dataptr = (char*)dataptr + len;
247     }
248     return rx;
249 }
250
251 static int
252 sec_send(int fd, char *from, int length)
253 {
254     int bytes;
255     void *buf;
256     bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
257     bytes = htonl(bytes);
258     block_write(fd, &bytes, sizeof(bytes));
259     block_write(fd, buf, ntohl(bytes));
260     free(buf);
261     return length;
262 }
263
264 int
265 sec_fflush(FILE *F)
266 {
267     if(data_prot != prot_clear) {
268         if(out_buffer.index > 0){
269             sec_write(fileno(F), out_buffer.data, out_buffer.index);
270             out_buffer.index = 0;
271         }
272         sec_send(fileno(F), NULL, 0);
273     }
274     fflush(F);
275     return 0;
276 }
277
278 int
279 sec_write(int fd, char *dataptr, int length)
280 {
281     int len = buffer_size;
282     int tx = 0;
283
284     if(data_prot == prot_clear)
285         return write(fd, dataptr, length);
286
287     len -= (*mech->overhead)(app_data, data_prot, len);
288     while(length){
289         if(length < len)
290             len = length;
291         sec_send(fd, dataptr, len);
292         length -= len;
293         dataptr += len;
294         tx += len;
295     }
296     return tx;
297 }
298
299 int
300 sec_vfprintf2(FILE *f, const char *fmt, va_list ap)
301 {
302     char *buf;
303     int ret;
304     if(data_prot == prot_clear)
305         return vfprintf(f, fmt, ap);
306     else {
307         int len;
308         len = vasprintf(&buf, fmt, ap);
309         if (len == -1)
310             return len;
311         ret = buffer_write(&out_buffer, buf, len);
312         free(buf);
313         return ret;
314     }
315 }
316
317 int
318 sec_fprintf2(FILE *f, const char *fmt, ...)
319 {
320     int ret;
321     va_list ap;
322     va_start(ap, fmt);
323     ret = sec_vfprintf2(f, fmt, ap);
324     va_end(ap);
325     return ret;
326 }
327
328 int
329 sec_putc(int c, FILE *F)
330 {
331     char ch = c;
332     if(data_prot == prot_clear)
333         return putc(c, F);
334
335     buffer_write(&out_buffer, &ch, 1);
336     if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
337         sec_write(fileno(F), out_buffer.data, out_buffer.index);
338         out_buffer.index = 0;
339     }
340     return c;
341 }
342
343 int
344 sec_read_msg(char *s, int level)
345 {
346     int len;
347     char *buf;
348     int return_code;
349
350     buf = malloc(strlen(s));
351     len = base64_decode(s + 4, buf); /* XXX */
352
353     len = (*mech->decode)(app_data, buf, len, level);
354     if(len < 0)
355         return -1;
356
357     buf[len] = '\0';
358
359     if(buf[3] == '-')
360         return_code = 0;
361     else
362         sscanf(buf, "%d", &return_code);
363     if(buf[len-1] == '\n')
364         buf[len-1] = '\0';
365     strcpy(s, buf);
366     free(buf);
367     return return_code;
368 }
369
370 int
371 sec_vfprintf(FILE *f, const char *fmt, va_list ap)
372 {
373     char *buf;
374     void *enc;
375     int len;
376     if(!sec_complete)
377         return vfprintf(f, fmt, ap);
378
379     if (vasprintf(&buf, fmt, ap) == -1) {
380         printf("Failed to allocate command.\n");
381         return -1;
382     }
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         free(enc);
391         printf("Out of memory base64-encoding.\n");
392         return -1;
393     }
394     free(enc);
395 #ifdef FTP_SERVER
396     if(command_prot == prot_safe)
397         fprintf(f, "631 %s\r\n", buf);
398     else if(command_prot == prot_private)
399         fprintf(f, "632 %s\r\n", buf);
400     else if(command_prot == prot_confidential)
401         fprintf(f, "633 %s\r\n", buf);
402 #else
403     if(command_prot == prot_safe)
404         fprintf(f, "MIC %s", buf);
405     else if(command_prot == prot_private)
406         fprintf(f, "ENC %s", buf);
407     else if(command_prot == prot_confidential)
408         fprintf(f, "CONF %s", buf);
409 #endif
410     free(buf);
411     return 0;
412 }
413
414 int
415 sec_fprintf(FILE *f, const char *fmt, ...)
416 {
417     va_list ap;
418     int ret;
419     va_start(ap, fmt);
420     ret = sec_vfprintf(f, fmt, ap);
421     va_end(ap);
422     return ret;
423 }
424
425 /* end common stuff */
426
427 #ifdef FTP_SERVER
428
429 int ccc_passed;
430
431 void
432 auth(char *auth_name)
433 {
434     int i;
435     void *tmp;
436
437     for(i = 0; (mech = mechs[i]) != NULL; i++){
438         if(!strcasecmp(auth_name, mech->name)){
439             tmp = realloc(app_data, mech->size);
440             if (tmp == NULL) {
441                 reply(431, "Unable to accept %s at this time", mech->name);
442                 return;
443             }
444             app_data = tmp;
445
446             if(mech->init && (*mech->init)(app_data) != 0) {
447                 reply(431, "Unable to accept %s at this time", mech->name);
448                 return;
449             }
450             if(mech->auth) {
451                 (*mech->auth)(app_data);
452                 return;
453             }
454             if(mech->adat)
455                 reply(334, "Send authorization data.");
456             else
457                 reply(234, "Authorization complete.");
458             return;
459         }
460     }
461     free (app_data);
462     app_data = NULL;
463     reply(504, "%s is unknown to me", auth_name);
464 }
465
466 void
467 adat(char *auth_data)
468 {
469     if(mech && !sec_complete) {
470         void *buf = malloc(strlen(auth_data));
471         size_t len;
472         len = base64_decode(auth_data, buf);
473         (*mech->adat)(app_data, buf, len);
474         free(buf);
475     } else
476         reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
477 }
478
479 void pbsz(int size)
480 {
481     size_t new = size;
482     if(!sec_complete)
483         reply(503, "Incomplete security data exchange.");
484     if(mech->pbsz)
485         new = (*mech->pbsz)(app_data, size);
486     if(buffer_size != new){
487         buffer_size = size;
488     }
489     if(new != size)
490         reply(200, "PBSZ=%lu", (unsigned long)new);
491     else
492         reply(200, "OK");
493 }
494
495 void
496 prot(char *pl)
497 {
498     int p = -1;
499
500     if(buffer_size == 0){
501         reply(503, "No protection buffer size negotiated.");
502         return;
503     }
504
505     if(!strcasecmp(pl, "C"))
506         p = prot_clear;
507     else if(!strcasecmp(pl, "S"))
508         p = prot_safe;
509     else if(!strcasecmp(pl, "E"))
510         p = prot_confidential;
511     else if(!strcasecmp(pl, "P"))
512         p = prot_private;
513     else {
514         reply(504, "Unrecognized protection level.");
515         return;
516     }
517
518     if(sec_complete){
519         if((*mech->check_prot)(app_data, p)){
520             reply(536, "%s does not support %s protection.",
521                   mech->name, level_to_name(p));
522         }else{
523             data_prot = (enum protection_level)p;
524             reply(200, "Data protection is %s.", level_to_name(p));
525         }
526     }else{
527         reply(503, "Incomplete security data exchange.");
528     }
529 }
530
531 void ccc(void)
532 {
533     if(sec_complete){
534         if(mech->ccc && (*mech->ccc)(app_data) == 0) {
535             command_prot = data_prot = prot_clear;
536             ccc_passed = 1;
537         } else
538             reply(534, "You must be joking.");
539     }else
540         reply(503, "Incomplete security data exchange.");
541 }
542
543 void mec(char *msg, enum protection_level level)
544 {
545     void *buf;
546     size_t len, buf_size;
547     if(!sec_complete) {
548         reply(503, "Incomplete security data exchange.");
549         return;
550     }
551     buf_size = strlen(msg) + 2;
552     buf = malloc(buf_size);
553     if (buf == NULL) {
554         reply(501, "Failed to allocate %lu", (unsigned long)buf_size);
555         return;
556     }
557     len = base64_decode(msg, buf);
558     command_prot = level;
559     if(len == (size_t)-1) {
560         free(buf);
561         reply(501, "Failed to base64-decode command");
562         return;
563     }
564     len = (*mech->decode)(app_data, buf, len, level);
565     if(len == (size_t)-1) {
566         free(buf);
567         reply(535, "Failed to decode command");
568         return;
569     }
570     ((char*)buf)[len] = '\0';
571     if(strstr((char*)buf, "\r\n") == NULL)
572         strlcat((char*)buf, "\r\n", buf_size);
573     new_ftp_command(buf);
574 }
575
576 /* ------------------------------------------------------------ */
577
578 int
579 sec_userok(char *userstr)
580 {
581     if(sec_complete)
582         return (*mech->userok)(app_data, userstr);
583     return 0;
584 }
585
586 int
587 sec_session(char *user)
588 {
589     if(sec_complete && mech->session)
590         return (*mech->session)(app_data, user);
591     return 0;
592 }
593
594 char *ftp_command;
595
596 void
597 new_ftp_command(char *command)
598 {
599     ftp_command = command;
600 }
601
602 void
603 delete_ftp_command(void)
604 {
605     free(ftp_command);
606     ftp_command = NULL;
607 }
608
609 int
610 secure_command(void)
611 {
612     return ftp_command != NULL;
613 }
614
615 enum protection_level
616 get_command_prot(void)
617 {
618     return command_prot;
619 }
620
621 #else /* FTP_SERVER */
622
623 void
624 sec_status(void)
625 {
626     if(sec_complete){
627         printf("Using %s for authentication.\n", mech->name);
628         printf("Using %s command channel.\n", level_to_name(command_prot));
629         printf("Using %s data channel.\n", level_to_name(data_prot));
630         if(buffer_size > 0)
631             printf("Protection buffer size: %lu.\n",
632                    (unsigned long)buffer_size);
633     }else{
634         printf("Not using any security mechanism.\n");
635     }
636 }
637
638 static int
639 sec_prot_internal(int level)
640 {
641     int ret;
642     char *p;
643     unsigned int s = 1048576;
644
645     int old_verbose = verbose;
646     verbose = 0;
647
648     if(!sec_complete){
649         printf("No security data exchange has taken place.\n");
650         return -1;
651     }
652
653     if(level){
654         ret = command("PBSZ %u", s);
655         if(ret != COMPLETE){
656             printf("Failed to set protection buffer size.\n");
657             return -1;
658         }
659         buffer_size = s;
660         p = strstr(reply_string, "PBSZ=");
661         if(p)
662             sscanf(p, "PBSZ=%u", &s);
663         if(s < buffer_size)
664             buffer_size = s;
665     }
666     verbose = old_verbose;
667     ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
668     if(ret != COMPLETE){
669         printf("Failed to set protection level.\n");
670         return -1;
671     }
672
673     data_prot = (enum protection_level)level;
674     return 0;
675 }
676
677 enum protection_level
678 set_command_prot(enum protection_level level)
679 {
680     int ret;
681     enum protection_level old = command_prot;
682     if(level != command_prot && level == prot_clear) {
683         ret = command("CCC");
684         if(ret != COMPLETE) {
685             printf("Failed to clear command channel.\n");
686             return prot_invalid;
687         }
688     }
689     command_prot = level;
690     return old;
691 }
692
693 void
694 sec_prot(int argc, char **argv)
695 {
696     int level = -1;
697
698     if(argc > 3)
699         goto usage;
700
701     if(argc == 1) {
702         sec_status();
703         return;
704     }
705     if(!sec_complete) {
706         printf("No security data exchange has taken place.\n");
707         code = -1;
708         return;
709     }
710     level = name_to_level(argv[argc - 1]);
711
712     if(level == -1)
713         goto usage;
714
715     if((*mech->check_prot)(app_data, level)) {
716         printf("%s does not implement %s protection.\n",
717                mech->name, level_to_name(level));
718         code = -1;
719         return;
720     }
721
722     if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
723         if(sec_prot_internal(level) < 0){
724             code = -1;
725             return;
726         }
727     } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) {
728         if(set_command_prot(level) < 0) {
729             code = -1;
730             return;
731         }
732     } else
733         goto usage;
734     code = 0;
735     return;
736  usage:
737     printf("usage: %s [command|data] [clear|safe|confidential|private]\n",
738            argv[0]);
739     code = -1;
740 }
741
742 void
743 sec_prot_command(int argc, char **argv)
744 {
745     int level;
746
747     if(argc > 2)
748         goto usage;
749
750     if(!sec_complete) {
751         printf("No security data exchange has taken place.\n");
752         code = -1;
753         return;
754     }
755
756     if(argc == 1) {
757         sec_status();
758     } else {
759         level = name_to_level(argv[1]);
760         if(level == -1)
761             goto usage;
762
763         if((*mech->check_prot)(app_data, level)) {
764             printf("%s does not implement %s protection.\n",
765                    mech->name, level_to_name(level));
766             code = -1;
767             return;
768         }
769         if(set_command_prot(level) < 0) {
770             code = -1;
771             return;
772         }
773     }
774     code = 0;
775     return;
776  usage:
777     printf("usage: %s [clear|safe|confidential|private]\n",
778            argv[0]);
779     code = -1;
780 }
781
782 static enum protection_level request_data_prot;
783
784 void
785 sec_set_protection_level(void)
786 {
787     if(sec_complete && data_prot != request_data_prot)
788         sec_prot_internal(request_data_prot);
789 }
790
791
792 int
793 sec_request_prot(char *level)
794 {
795     int l = name_to_level(level);
796     if(l == -1)
797         return -1;
798     request_data_prot = (enum protection_level)l;
799     return 0;
800 }
801
802 int
803 sec_login(char *host)
804 {
805     int ret;
806     struct sec_client_mech **m;
807     int old_verbose = verbose;
808
809     verbose = -1; /* shut up all messages this will produce (they
810                      are usually not very user friendly) */
811
812     for(m = mechs; *m && (*m)->name; m++) {
813         void *tmp;
814
815         tmp = realloc(app_data, (*m)->size);
816         if (tmp == NULL) {
817             warnx ("realloc %lu failed", (unsigned long)(*m)->size);
818             return -1;
819         }
820         app_data = tmp;
821
822         if((*m)->init && (*(*m)->init)(app_data) != 0) {
823             printf("Skipping %s...\n", (*m)->name);
824             continue;
825         }
826         printf("Trying %s...\n", (*m)->name);
827         ret = command("AUTH %s", (*m)->name);
828         if(ret != CONTINUE){
829             if(code == 504){
830                 printf("%s is not supported by the server.\n", (*m)->name);
831             }else if(code == 534){
832                 printf("%s rejected as security mechanism.\n", (*m)->name);
833             }else if(ret == ERROR) {
834                 printf("The server doesn't support the FTP "
835                        "security extensions.\n");
836                 verbose = old_verbose;
837                 return -1;
838             }
839             continue;
840         }
841
842         ret = (*(*m)->auth)(app_data, host);
843
844         if(ret == AUTH_CONTINUE)
845             continue;
846         else if(ret != AUTH_OK){
847             /* mechanism is supposed to output error string */
848             verbose = old_verbose;
849             return -1;
850         }
851         mech = *m;
852         sec_complete = 1;
853         if(doencrypt) {
854             command_prot = prot_private;
855             request_data_prot = prot_private;
856         } else {
857             command_prot = prot_safe;
858         }
859         break;
860     }
861
862     verbose = old_verbose;
863     return *m == NULL;
864 }
865
866 void
867 sec_end(void)
868 {
869     if (mech != NULL) {
870         if(mech->end)
871             (*mech->end)(app_data);
872         if (app_data != NULL) {
873             memset(app_data, 0, mech->size);
874             free(app_data);
875             app_data = NULL;
876         }
877     }
878     sec_complete = 0;
879     data_prot = (enum protection_level)0;
880 }
881
882 #endif /* FTP_SERVER */
883