rename roken base64, fixes #107
[heimdal.git] / appl / ftp / ftp / security.c
blob43180099322f76afd7c47a601a436aa96217e175
1 /*
2 * Copyright (c) 1998-2002, 2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #ifdef FTP_SERVER
35 #include "ftpd_locl.h"
36 #else
37 #include "ftp_locl.h"
38 #endif
40 RCSID("$Id$");
42 static enum protection_level command_prot;
43 static enum protection_level data_prot;
44 static size_t buffer_size;
46 struct buffer {
47 void *data;
48 size_t size;
49 size_t index;
50 int eof_flag;
53 static struct buffer in_buffer, out_buffer;
54 int sec_complete;
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" }
66 static const char *
67 level_to_name(enum protection_level level)
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";
76 #ifndef FTP_SERVER /* not used in server */
77 static enum protection_level
78 name_to_level(const char *name)
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;
86 #endif
88 #ifdef FTP_SERVER
90 static struct sec_server_mech *mechs[] = {
91 #ifdef KRB5
92 &gss_server_mech,
93 #endif
94 NULL
97 static struct sec_server_mech *mech;
99 #else
101 static struct sec_client_mech *mechs[] = {
102 #ifdef KRB5
103 &gss_client_mech,
104 #endif
105 NULL
108 static struct sec_client_mech *mech;
110 #endif
112 static void *app_data;
115 sec_getc(FILE *F)
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);
126 static int
127 block_read(int fd, void *buf, size_t len)
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;
140 return p - (unsigned char*)buf;
143 static int
144 block_write(int fd, void *buf, size_t len)
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;
155 return p - (unsigned char*)buf;
158 static int
159 sec_get_data(int fd, struct buffer *buf, int level)
161 int len;
162 int b;
163 void *tmp;
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;
185 static size_t
186 buffer_read(struct buffer *buf, void *dataptr, size_t len)
188 len = min(len, buf->size - buf->index);
189 memcpy(dataptr, (char*)buf->data + buf->index, len);
190 buf->index += len;
191 return len;
194 static size_t
195 buffer_write(struct buffer *buf, void *dataptr, size_t len)
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;
208 memcpy((char*)buf->data + buf->index, dataptr, len);
209 buf->index += len;
210 return len;
214 sec_read(int fd, void *dataptr, int length)
216 size_t len;
217 int rx = 0;
219 if(sec_complete == 0 || data_prot == 0)
220 return read(fd, dataptr, length);
222 if(in_buffer.eof_flag){
223 in_buffer.eof_flag = 0;
224 return 0;
227 len = buffer_read(&in_buffer, dataptr, length);
228 length -= len;
229 rx += len;
230 dataptr = (char*)dataptr + len;
232 while(length){
233 int ret;
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;
243 len = buffer_read(&in_buffer, dataptr, length);
244 length -= len;
245 rx += len;
246 dataptr = (char*)dataptr + len;
248 return rx;
251 static int
252 sec_send(int fd, char *from, int length)
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;
265 sec_fflush(FILE *F)
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;
272 sec_send(fileno(F), NULL, 0);
274 fflush(F);
275 return 0;
279 sec_write(int fd, char *dataptr, int length)
281 int len = buffer_size;
282 int tx = 0;
284 if(data_prot == prot_clear)
285 return write(fd, dataptr, length);
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;
296 return tx;
300 sec_vfprintf2(FILE *f, const char *fmt, va_list ap)
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;
318 sec_fprintf2(FILE *f, const char *fmt, ...)
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;
329 sec_putc(int c, FILE *F)
331 char ch = c;
332 if(data_prot == prot_clear)
333 return putc(c, F);
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;
340 return c;
344 sec_read_msg(char *s, int level)
346 int len;
347 char *buf;
348 int return_code;
350 buf = malloc(strlen(s));
351 len = rk_base64_decode(s + 4, buf); /* XXX */
353 len = (*mech->decode)(app_data, buf, len, level);
354 if(len < 0)
355 return -1;
357 buf[len] = '\0';
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;
371 sec_vfprintf(FILE *f, const char *fmt, va_list ap)
373 char *buf;
374 void *enc;
375 int len;
376 if(!sec_complete)
377 return vfprintf(f, fmt, ap);
379 if (vasprintf(&buf, fmt, ap) == -1) {
380 printf("Failed to allocate command.\n");
381 return -1;
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;
389 if(rk_base64_encode(enc, len, &buf) < 0){
390 free(enc);
391 printf("Out of memory base64-encoding.\n");
392 return -1;
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;
415 sec_fprintf(FILE *f, const char *fmt, ...)
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;
425 /* end common stuff */
427 #ifdef FTP_SERVER
429 int ccc_passed;
431 void
432 auth(char *auth_name)
434 int i;
435 void *tmp;
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;
444 app_data = tmp;
446 if(mech->init && (*mech->init)(app_data) != 0) {
447 reply(431, "Unable to accept %s at this time", mech->name);
448 return;
450 if(mech->auth) {
451 (*mech->auth)(app_data);
452 return;
454 if(mech->adat)
455 reply(334, "Send authorization data.");
456 else
457 reply(234, "Authorization complete.");
458 return;
461 free (app_data);
462 app_data = NULL;
463 reply(504, "%s is unknown to me", auth_name);
466 void
467 adat(char *auth_data)
469 if(mech && !sec_complete) {
470 void *buf = malloc(strlen(auth_data));
471 size_t len;
472 len = rk_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-" : "");
479 void pbsz(int size)
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;
489 if(new != size)
490 reply(200, "PBSZ=%lu", (unsigned long)new);
491 else
492 reply(200, "OK");
495 void
496 prot(char *pl)
498 int p = -1;
500 if(buffer_size == 0){
501 reply(503, "No protection buffer size negotiated.");
502 return;
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;
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));
526 }else{
527 reply(503, "Incomplete security data exchange.");
531 void ccc(void)
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.");
543 void mec(char *msg, enum protection_level level)
545 void *buf;
546 size_t len, buf_size;
547 if(!sec_complete) {
548 reply(503, "Incomplete security data exchange.");
549 return;
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;
557 len = rk_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;
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;
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);
576 /* ------------------------------------------------------------ */
579 sec_userok(char *userstr)
581 if(sec_complete)
582 return (*mech->userok)(app_data, userstr);
583 return 0;
587 sec_session(char *user)
589 if(sec_complete && mech->session)
590 return (*mech->session)(app_data, user);
591 return 0;
594 char *ftp_command;
596 void
597 new_ftp_command(char *command)
599 ftp_command = command;
602 void
603 delete_ftp_command(void)
605 free(ftp_command);
606 ftp_command = NULL;
610 secure_command(void)
612 return ftp_command != NULL;
615 enum protection_level
616 get_command_prot(void)
618 return command_prot;
621 #else /* FTP_SERVER */
623 void
624 sec_status(void)
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");
638 static int
639 sec_prot_internal(int level)
641 int ret;
642 char *p;
643 unsigned int s = 1048576;
645 int old_verbose = verbose;
646 verbose = 0;
648 if(!sec_complete){
649 printf("No security data exchange has taken place.\n");
650 return -1;
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;
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;
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;
673 data_prot = (enum protection_level)level;
674 return 0;
677 enum protection_level
678 set_command_prot(enum protection_level level)
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;
689 command_prot = level;
690 return old;
693 void
694 sec_prot(int argc, char **argv)
696 int level = -1;
698 if(argc > 3)
699 goto usage;
701 if(argc == 1) {
702 sec_status();
703 return;
705 if(!sec_complete) {
706 printf("No security data exchange has taken place.\n");
707 code = -1;
708 return;
710 level = name_to_level(argv[argc - 1]);
712 if(level == -1)
713 goto usage;
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;
722 if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
723 if(sec_prot_internal(level) < 0){
724 code = -1;
725 return;
727 } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) {
728 if(set_command_prot(level) < 0) {
729 code = -1;
730 return;
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;
742 void
743 sec_prot_command(int argc, char **argv)
745 int level;
747 if(argc > 2)
748 goto usage;
750 if(!sec_complete) {
751 printf("No security data exchange has taken place.\n");
752 code = -1;
753 return;
756 if(argc == 1) {
757 sec_status();
758 } else {
759 level = name_to_level(argv[1]);
760 if(level == -1)
761 goto usage;
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;
769 if(set_command_prot(level) < 0) {
770 code = -1;
771 return;
774 code = 0;
775 return;
776 usage:
777 printf("usage: %s [clear|safe|confidential|private]\n",
778 argv[0]);
779 code = -1;
782 static enum protection_level request_data_prot;
784 void
785 sec_set_protection_level(void)
787 if(sec_complete && data_prot != request_data_prot)
788 sec_prot_internal(request_data_prot);
793 sec_request_prot(char *level)
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;
803 sec_login(char *host)
805 int ret;
806 struct sec_client_mech **m;
807 int old_verbose = verbose;
809 verbose = -1; /* shut up all messages this will produce (they
810 are usually not very user friendly) */
812 for(m = mechs; *m && (*m)->name; m++) {
813 void *tmp;
815 tmp = realloc(app_data, (*m)->size);
816 if (tmp == NULL) {
817 warnx ("realloc %lu failed", (unsigned long)(*m)->size);
818 return -1;
820 app_data = tmp;
822 if((*m)->init && (*(*m)->init)(app_data) != 0) {
823 printf("Skipping %s...\n", (*m)->name);
824 continue;
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;
839 continue;
842 ret = (*(*m)->auth)(app_data, host);
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;
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;
859 break;
862 verbose = old_verbose;
863 return *m == NULL;
866 void
867 sec_end(void)
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;
878 sec_complete = 0;
879 data_prot = (enum protection_level)0;
882 #endif /* FTP_SERVER */