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