No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / appl / ftp / ftpd / security.c
blob391230d5b831c492cd326ce9357292cabb6d9718
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.
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("$Heimdal: security.c 21225 2007-06-20 10:16:02Z lha $"
41 "$NetBSD$");
43 static enum protection_level command_prot;
44 static enum protection_level data_prot;
45 static size_t buffer_size;
47 struct buffer {
48 void *data;
49 size_t size;
50 size_t index;
51 int eof_flag;
54 static struct buffer in_buffer, out_buffer;
55 int sec_complete;
57 static struct {
58 enum protection_level level;
59 const char *name;
60 } level_names[] = {
61 { prot_clear, "clear" },
62 { prot_safe, "safe" },
63 { prot_confidential, "confidential" },
64 { prot_private, "private" }
67 static const char *
68 level_to_name(enum protection_level level)
70 int i;
71 for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
72 if(level_names[i].level == level)
73 return level_names[i].name;
74 return "unknown";
77 #ifndef FTP_SERVER /* not used in server */
78 static enum protection_level
79 name_to_level(const char *name)
81 int i;
82 for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
83 if(!strncasecmp(level_names[i].name, name, strlen(name)))
84 return level_names[i].level;
85 return (enum protection_level)-1;
87 #endif
89 #ifdef FTP_SERVER
91 static struct sec_server_mech *mechs[] = {
92 #ifdef KRB5
93 &gss_server_mech,
94 #endif
95 #ifdef KRB4
96 &krb4_server_mech,
97 #endif
98 NULL
101 static struct sec_server_mech *mech;
103 #else
105 static struct sec_client_mech *mechs[] = {
106 #ifdef KRB5
107 &gss_client_mech,
108 #endif
109 #ifdef KRB4
110 &krb4_client_mech,
111 #endif
112 NULL
115 static struct sec_client_mech *mech;
117 #endif
119 static void *app_data;
122 sec_getc(FILE *F)
124 if(sec_complete && data_prot) {
125 char c;
126 if(sec_read(fileno(F), &c, 1) <= 0)
127 return EOF;
128 return c;
129 } else
130 return getc(F);
133 static int
134 block_read(int fd, void *buf, size_t len)
136 unsigned char *p = buf;
137 int b;
138 while(len) {
139 b = read(fd, p, len);
140 if (b == 0)
141 return 0;
142 else if (b < 0)
143 return -1;
144 len -= b;
145 p += b;
147 return p - (unsigned char*)buf;
150 static int
151 block_write(int fd, void *buf, size_t len)
153 unsigned char *p = buf;
154 int b;
155 while(len) {
156 b = write(fd, p, len);
157 if(b < 0)
158 return -1;
159 len -= b;
160 p += b;
162 return p - (unsigned char*)buf;
165 static int
166 sec_get_data(int fd, struct buffer *buf, int level)
168 int len;
169 int b;
170 void *tmp;
172 b = block_read(fd, &len, sizeof(len));
173 if (b == 0)
174 return 0;
175 else if (b < 0)
176 return -1;
177 len = ntohl(len);
178 tmp = realloc(buf->data, len);
179 if (tmp == NULL)
180 return -1;
181 buf->data = tmp;
182 b = block_read(fd, buf->data, len);
183 if (b == 0)
184 return 0;
185 else if (b < 0)
186 return -1;
187 buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
188 buf->index = 0;
189 return 0;
192 static size_t
193 buffer_read(struct buffer *buf, void *dataptr, size_t len)
195 len = min(len, buf->size - buf->index);
196 memcpy(dataptr, (char*)buf->data + buf->index, len);
197 buf->index += len;
198 return len;
201 static size_t
202 buffer_write(struct buffer *buf, void *dataptr, size_t len)
204 if(buf->index + len > buf->size) {
205 void *tmp;
206 if(buf->data == NULL)
207 tmp = malloc(1024);
208 else
209 tmp = realloc(buf->data, buf->index + len);
210 if(tmp == NULL)
211 return -1;
212 buf->data = tmp;
213 buf->size = buf->index + len;
215 memcpy((char*)buf->data + buf->index, dataptr, len);
216 buf->index += len;
217 return len;
221 sec_read(int fd, void *dataptr, int length)
223 size_t len;
224 int rx = 0;
226 if(sec_complete == 0 || data_prot == 0)
227 return read(fd, dataptr, length);
229 if(in_buffer.eof_flag){
230 in_buffer.eof_flag = 0;
231 return 0;
234 len = buffer_read(&in_buffer, dataptr, length);
235 length -= len;
236 rx += len;
237 dataptr = (char*)dataptr + len;
239 while(length){
240 int ret;
242 ret = sec_get_data(fd, &in_buffer, data_prot);
243 if (ret < 0)
244 return -1;
245 if(ret == 0 && in_buffer.size == 0) {
246 if(rx)
247 in_buffer.eof_flag = 1;
248 return rx;
250 len = buffer_read(&in_buffer, dataptr, length);
251 length -= len;
252 rx += len;
253 dataptr = (char*)dataptr + len;
255 return rx;
258 static int
259 sec_send(int fd, char *from, int length)
261 int bytes;
262 void *buf;
263 bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
264 bytes = htonl(bytes);
265 block_write(fd, &bytes, sizeof(bytes));
266 block_write(fd, buf, ntohl(bytes));
267 free(buf);
268 return length;
272 sec_fflush(FILE *F)
274 if(data_prot != prot_clear) {
275 if(out_buffer.index > 0){
276 sec_write(fileno(F), out_buffer.data, out_buffer.index);
277 out_buffer.index = 0;
279 sec_send(fileno(F), NULL, 0);
281 fflush(F);
282 return 0;
286 sec_write(int fd, char *dataptr, int length)
288 int len = buffer_size;
289 int tx = 0;
291 if(data_prot == prot_clear)
292 return write(fd, dataptr, length);
294 len -= (*mech->overhead)(app_data, data_prot, len);
295 while(length){
296 if(length < len)
297 len = length;
298 sec_send(fd, dataptr, len);
299 length -= len;
300 dataptr += len;
301 tx += len;
303 return tx;
307 sec_vfprintf2(FILE *f, const char *fmt, va_list ap)
309 char *buf;
310 int ret;
311 if(data_prot == prot_clear)
312 return vfprintf(f, fmt, ap);
313 else {
314 int len;
315 len = vasprintf(&buf, fmt, ap);
316 if (len == -1)
317 return len;
318 ret = buffer_write(&out_buffer, buf, len);
319 free(buf);
320 return ret;
325 sec_fprintf2(FILE *f, const char *fmt, ...)
327 int ret;
328 va_list ap;
329 va_start(ap, fmt);
330 ret = sec_vfprintf2(f, fmt, ap);
331 va_end(ap);
332 return ret;
336 sec_putc(int c, FILE *F)
338 char ch = c;
339 if(data_prot == prot_clear)
340 return putc(c, F);
342 buffer_write(&out_buffer, &ch, 1);
343 if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
344 sec_write(fileno(F), out_buffer.data, out_buffer.index);
345 out_buffer.index = 0;
347 return c;
351 sec_read_msg(char *s, int level)
353 int len;
354 char *buf;
355 int return_code;
357 buf = malloc(strlen(s));
358 len = base64_decode(s + 4, buf); /* XXX */
360 len = (*mech->decode)(app_data, buf, len, level);
361 if(len < 0)
362 return -1;
364 buf[len] = '\0';
366 if(buf[3] == '-')
367 return_code = 0;
368 else
369 sscanf(buf, "%d", &return_code);
370 if(buf[len-1] == '\n')
371 buf[len-1] = '\0';
372 strcpy(s, buf);
373 free(buf);
374 return return_code;
378 sec_vfprintf(FILE *f, const char *fmt, va_list ap)
380 char *buf;
381 void *enc;
382 int len;
383 if(!sec_complete)
384 return vfprintf(f, fmt, ap);
386 if (vasprintf(&buf, fmt, ap) == -1) {
387 printf("Failed to allocate command.\n");
388 return -1;
390 len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
391 free(buf);
392 if(len < 0) {
393 printf("Failed to encode command.\n");
394 return -1;
396 if(base64_encode(enc, len, &buf) < 0){
397 free(enc);
398 printf("Out of memory base64-encoding.\n");
399 return -1;
401 free(enc);
402 #ifdef FTP_SERVER
403 if(command_prot == prot_safe)
404 fprintf(f, "631 %s\r\n", buf);
405 else if(command_prot == prot_private)
406 fprintf(f, "632 %s\r\n", buf);
407 else if(command_prot == prot_confidential)
408 fprintf(f, "633 %s\r\n", buf);
409 #else
410 if(command_prot == prot_safe)
411 fprintf(f, "MIC %s", buf);
412 else if(command_prot == prot_private)
413 fprintf(f, "ENC %s", buf);
414 else if(command_prot == prot_confidential)
415 fprintf(f, "CONF %s", buf);
416 #endif
417 free(buf);
418 return 0;
422 sec_fprintf(FILE *f, const char *fmt, ...)
424 va_list ap;
425 int ret;
426 va_start(ap, fmt);
427 ret = sec_vfprintf(f, fmt, ap);
428 va_end(ap);
429 return ret;
432 /* end common stuff */
434 #ifdef FTP_SERVER
436 int ccc_passed;
438 void
439 auth(char *auth_name)
441 int i;
442 void *tmp;
444 for(i = 0; (mech = mechs[i]) != NULL; i++){
445 if(!strcasecmp(auth_name, mech->name)){
446 tmp = realloc(app_data, mech->size);
447 if (tmp == NULL) {
448 reply(431, "Unable to accept %s at this time", mech->name);
449 return;
451 app_data = tmp;
453 if(mech->init && (*mech->init)(app_data) != 0) {
454 reply(431, "Unable to accept %s at this time", mech->name);
455 return;
457 if(mech->auth) {
458 (*mech->auth)(app_data);
459 return;
461 if(mech->adat)
462 reply(334, "Send authorization data.");
463 else
464 reply(234, "Authorization complete.");
465 return;
468 free (app_data);
469 app_data = NULL;
470 reply(504, "%s is unknown to me", auth_name);
473 void
474 adat(char *auth_data)
476 if(mech && !sec_complete) {
477 void *buf = malloc(strlen(auth_data));
478 size_t len;
479 len = base64_decode(auth_data, buf);
480 (*mech->adat)(app_data, buf, len);
481 free(buf);
482 } else
483 reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
486 void pbsz(int size)
488 size_t new = size;
489 if(!sec_complete)
490 reply(503, "Incomplete security data exchange.");
491 if(mech->pbsz)
492 new = (*mech->pbsz)(app_data, size);
493 if(buffer_size != new){
494 buffer_size = size;
496 if(new != size)
497 reply(200, "PBSZ=%lu", (unsigned long)new);
498 else
499 reply(200, "OK");
502 void
503 prot(char *pl)
505 int p = -1;
507 if(buffer_size == 0){
508 reply(503, "No protection buffer size negotiated.");
509 return;
512 if(!strcasecmp(pl, "C"))
513 p = prot_clear;
514 else if(!strcasecmp(pl, "S"))
515 p = prot_safe;
516 else if(!strcasecmp(pl, "E"))
517 p = prot_confidential;
518 else if(!strcasecmp(pl, "P"))
519 p = prot_private;
520 else {
521 reply(504, "Unrecognized protection level.");
522 return;
525 if(sec_complete){
526 if((*mech->check_prot)(app_data, p)){
527 reply(536, "%s does not support %s protection.",
528 mech->name, level_to_name(p));
529 }else{
530 data_prot = (enum protection_level)p;
531 reply(200, "Data protection is %s.", level_to_name(p));
533 }else{
534 reply(503, "Incomplete security data exchange.");
538 void ccc(void)
540 if(sec_complete){
541 if(mech->ccc && (*mech->ccc)(app_data) == 0) {
542 command_prot = data_prot = prot_clear;
543 ccc_passed = 1;
544 } else
545 reply(534, "You must be joking.");
546 }else
547 reply(503, "Incomplete security data exchange.");
550 void mec(char *msg, enum protection_level level)
552 void *buf;
553 size_t len, buf_size;
554 if(!sec_complete) {
555 reply(503, "Incomplete security data exchange.");
556 return;
558 buf_size = strlen(msg) + 2;
559 buf = malloc(buf_size);
560 len = base64_decode(msg, buf);
561 command_prot = level;
562 if(len == (size_t)-1) {
563 reply(501, "Failed to base64-decode command");
564 return;
566 len = (*mech->decode)(app_data, buf, len, level);
567 if(len == (size_t)-1) {
568 reply(535, "Failed to decode command");
569 return;
571 ((char*)buf)[len] = '\0';
572 if(strstr((char*)buf, "\r\n") == NULL)
573 strlcat((char*)buf, "\r\n", buf_size);
574 new_ftp_command(buf);
577 /* ------------------------------------------------------------ */
580 sec_userok(char *userstr)
582 if(sec_complete)
583 return (*mech->userok)(app_data, userstr);
584 return 0;
588 sec_session(char *user)
590 if(sec_complete && mech->session)
591 return (*mech->session)(app_data, user);
592 return 0;
595 char *ftp_command;
597 void
598 new_ftp_command(char *command)
600 ftp_command = command;
603 void
604 delete_ftp_command(void)
606 free(ftp_command);
607 ftp_command = NULL;
611 secure_command(void)
613 return ftp_command != NULL;
616 enum protection_level
617 get_command_prot(void)
619 return command_prot;
622 #else /* FTP_SERVER */
624 void
625 sec_status(void)
627 if(sec_complete){
628 printf("Using %s for authentication.\n", mech->name);
629 printf("Using %s command channel.\n", level_to_name(command_prot));
630 printf("Using %s data channel.\n", level_to_name(data_prot));
631 if(buffer_size > 0)
632 printf("Protection buffer size: %lu.\n",
633 (unsigned long)buffer_size);
634 }else{
635 printf("Not using any security mechanism.\n");
639 static int
640 sec_prot_internal(int level)
642 int ret;
643 char *p;
644 unsigned int s = 1048576;
646 int old_verbose = verbose;
647 verbose = 0;
649 if(!sec_complete){
650 printf("No security data exchange has taken place.\n");
651 return -1;
654 if(level){
655 ret = command("PBSZ %u", s);
656 if(ret != COMPLETE){
657 printf("Failed to set protection buffer size.\n");
658 return -1;
660 buffer_size = s;
661 p = strstr(reply_string, "PBSZ=");
662 if(p)
663 sscanf(p, "PBSZ=%u", &s);
664 if(s < buffer_size)
665 buffer_size = s;
667 verbose = old_verbose;
668 ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
669 if(ret != COMPLETE){
670 printf("Failed to set protection level.\n");
671 return -1;
674 data_prot = (enum protection_level)level;
675 return 0;
678 enum protection_level
679 set_command_prot(enum protection_level level)
681 int ret;
682 enum protection_level old = command_prot;
683 if(level != command_prot && level == prot_clear) {
684 ret = command("CCC");
685 if(ret != COMPLETE) {
686 printf("Failed to clear command channel.\n");
687 return -1;
690 command_prot = level;
691 return old;
694 void
695 sec_prot(int argc, char **argv)
697 int level = -1;
699 if(argc > 3)
700 goto usage;
702 if(argc == 1) {
703 sec_status();
704 return;
706 if(!sec_complete) {
707 printf("No security data exchange has taken place.\n");
708 code = -1;
709 return;
711 level = name_to_level(argv[argc - 1]);
713 if(level == -1)
714 goto usage;
716 if((*mech->check_prot)(app_data, level)) {
717 printf("%s does not implement %s protection.\n",
718 mech->name, level_to_name(level));
719 code = -1;
720 return;
723 if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
724 if(sec_prot_internal(level) < 0){
725 code = -1;
726 return;
728 } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) {
729 if(set_command_prot(level) < 0) {
730 code = -1;
731 return;
733 } else
734 goto usage;
735 code = 0;
736 return;
737 usage:
738 printf("usage: %s [command|data] [clear|safe|confidential|private]\n",
739 argv[0]);
740 code = -1;
743 void
744 sec_prot_command(int argc, char **argv)
746 int level;
748 if(argc > 2)
749 goto usage;
751 if(!sec_complete) {
752 printf("No security data exchange has taken place.\n");
753 code = -1;
754 return;
757 if(argc == 1) {
758 sec_status();
759 } else {
760 level = name_to_level(argv[1]);
761 if(level == -1)
762 goto usage;
764 if((*mech->check_prot)(app_data, level)) {
765 printf("%s does not implement %s protection.\n",
766 mech->name, level_to_name(level));
767 code = -1;
768 return;
770 if(set_command_prot(level) < 0) {
771 code = -1;
772 return;
775 code = 0;
776 return;
777 usage:
778 printf("usage: %s [clear|safe|confidential|private]\n",
779 argv[0]);
780 code = -1;
783 static enum protection_level request_data_prot;
785 void
786 sec_set_protection_level(void)
788 if(sec_complete && data_prot != request_data_prot)
789 sec_prot_internal(request_data_prot);
794 sec_request_prot(char *level)
796 int l = name_to_level(level);
797 if(l == -1)
798 return -1;
799 request_data_prot = (enum protection_level)l;
800 return 0;
804 sec_login(char *host)
806 int ret;
807 struct sec_client_mech **m;
808 int old_verbose = verbose;
810 verbose = -1; /* shut up all messages this will produce (they
811 are usually not very user friendly) */
813 for(m = mechs; *m && (*m)->name; m++) {
814 void *tmp;
816 tmp = realloc(app_data, (*m)->size);
817 if (tmp == NULL) {
818 warnx ("realloc %lu failed", (unsigned long)(*m)->size);
819 return -1;
821 app_data = tmp;
823 if((*m)->init && (*(*m)->init)(app_data) != 0) {
824 printf("Skipping %s...\n", (*m)->name);
825 continue;
827 printf("Trying %s...\n", (*m)->name);
828 ret = command("AUTH %s", (*m)->name);
829 if(ret != CONTINUE){
830 if(code == 504){
831 printf("%s is not supported by the server.\n", (*m)->name);
832 }else if(code == 534){
833 printf("%s rejected as security mechanism.\n", (*m)->name);
834 }else if(ret == ERROR) {
835 printf("The server doesn't support the FTP "
836 "security extensions.\n");
837 verbose = old_verbose;
838 return -1;
840 continue;
843 ret = (*(*m)->auth)(app_data, host);
845 if(ret == AUTH_CONTINUE)
846 continue;
847 else if(ret != AUTH_OK){
848 /* mechanism is supposed to output error string */
849 verbose = old_verbose;
850 return -1;
852 mech = *m;
853 sec_complete = 1;
854 if(doencrypt) {
855 command_prot = prot_private;
856 request_data_prot = prot_private;
857 } else {
858 command_prot = prot_safe;
860 break;
863 verbose = old_verbose;
864 return *m == NULL;
867 void
868 sec_end(void)
870 if (mech != NULL) {
871 if(mech->end)
872 (*mech->end)(app_data);
873 if (app_data != NULL) {
874 memset(app_data, 0, mech->size);
875 free(app_data);
876 app_data = NULL;
879 sec_complete = 0;
880 data_prot = (enum protection_level)0;
883 #endif /* FTP_SERVER */