1/* Kerberos4 SASL plugin 2 * Rob Siemborski 3 * Tim Martin 4 * $Id: kerberos4.c,v 1.100 2009/03/10 16:27:52 mel Exp $ 5 */ 6/* 7 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The name "Carnegie Mellon University" must not be used to 22 * endorse or promote products derived from this software without 23 * prior written permission. For permission or any other legal 24 * details, please contact 25 * Office of Technology Transfer 26 * Carnegie Mellon University 27 * 5000 Forbes Avenue 28 * Pittsburgh, PA 15213-3890 29 * (412) 268-4387, fax: (412) 268-7395 30 * tech-transfer@andrew.cmu.edu 31 * 32 * 4. Redistributions of any form whatsoever must retain the following 33 * acknowledgment: 34 * "This product includes software developed by Computing Services 35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 36 * 37 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 38 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 39 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 40 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 42 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 43 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 44 */ 45 46#include <config.h> 47#include <stdlib.h> 48#include <string.h> 49#include <krb.h> 50 51#ifdef WITH_DES 52# ifdef WITH_SSL_DES 53# include <openssl/des.h> 54# else 55# include <des.h> 56# endif /* WITH_SSL_DES */ 57#endif /* WITH_DES */ 58 59#ifdef WIN32 60# include <winsock2.h> 61#elif defined(macintosh) 62#include <kcglue_krb.h> 63#else 64# include <sys/param.h> 65# include <sys/socket.h> 66# include <netinet/in.h> 67# include <arpa/inet.h> 68# include <netdb.h> 69#endif /* WIN32 */ 70#ifdef HAVE_UNISTD_H 71#include <unistd.h> 72#endif 73#include <fcntl.h> 74#include <sasl.h> 75#include <saslutil.h> 76#include <saslplug.h> 77 78#include <errno.h> 79#include <ctype.h> 80 81#include "plugin_common.h" 82 83#ifdef macintosh 84/* 85 * krb.h doenst include some functions and mac compiler is picky 86 * about declartions 87 */ 88#include <extra_krb.h> 89#include <sasl_kerberos4_plugin_decl.h> 90#endif 91 92#ifdef WIN32 93/* This must be after sasl.h, saslutil.h */ 94# include "saslKERBEROSV4.h" 95 96/* KClient doesn't define this */ 97typedef struct krb_principal { 98 char name[ANAME_SZ]; 99 char instance[INST_SZ]; 100 char realm[REALM_SZ]; 101} krb_principal; 102 103/* This isn't defined under WIN32. For access() */ 104#ifndef R_OK 105#define R_OK 04 106#endif 107/* we also need io.h for access() prototype */ 108#include <io.h> 109#endif /* WIN32 */ 110 111#ifdef sun 112/* gotta define gethostname ourselves on suns */ 113extern int gethostname(char *, int); 114#endif 115 116/***************************** Common Section *****************************/ 117 118static const char plugin_id[] = "$Id: kerberos4.c,v 1.100 2009/03/10 16:27:52 mel Exp $"; 119 120#ifndef KEYFILE 121#define KEYFILE "/etc/srvtab"; 122#endif 123 124#define KRB_SECFLAG_NONE (1) 125#define KRB_SECFLAG_INTEGRITY (2) 126#define KRB_SECFLAG_ENCRYPTION (4) 127#define KRB_SECFLAGS (7) 128#define KRB_SECFLAG_CREDENTIALS (8) 129 130#define KRB_DES_SECURITY_BITS (56) 131#define KRB_INTEGRITY_BITS (1) 132 133typedef enum Krb_sec { 134 KRB_SEC_NONE = 0, 135 KRB_SEC_INTEGRITY = 1, 136 KRB_SEC_ENCRYPTION = 2 137} Krb_sec_t; 138 139typedef struct context { 140 int state; 141 142 int challenge; /* this is the challenge (32 bit int) used 143 for the authentication */ 144 145 char *service; /* kerberos service */ 146 char instance[ANAME_SZ]; 147 char pname[ANAME_SZ]; 148 char pinst[INST_SZ]; 149 char prealm[REALM_SZ]; 150 char *hostname; /* hostname */ 151 char *realm; /* kerberos realm */ 152 char *auth; /* */ 153 154 CREDENTIALS credentials; 155 156 des_cblock key; /* session key */ 157 des_cblock session; /* session key */ 158 159 des_key_schedule init_keysched; /* key schedule for initialization */ 160 des_key_schedule enc_keysched; /* encryption key schedule */ 161 des_key_schedule dec_keysched; /* decryption key schedule */ 162 163 164 struct sockaddr_in ip_local; /* local ip address and port. 165 needed for layers */ 166 struct sockaddr_in ip_remote; /* remote ip address and port. 167 needed for layers */ 168 169 const sasl_utils_t *utils; /* this is useful to have around */ 170 171 Krb_sec_t sec_type; 172 char *encode_buf; /* For encoding/decoding mem management */ 173 char *decode_buf; 174 char *decode_once_buf; 175 unsigned encode_buf_len; 176 unsigned decode_buf_len; 177 unsigned decode_once_buf_len; 178 buffer_info_t *enc_in_buf; 179 180 decode_context_t decode_context; 181 182 char *out_buf; /* per-step mem management */ 183 unsigned out_buf_len; 184 185 const char *user; /* used by client */ 186 187 int secflags; /* client/server supports layers? */ 188 189 long time_sec; /* These are used to make sure we are getting */ 190 char time_5ms; /* strictly increasing timestamps */ 191 192} context_t; 193 194#define KRB_LOCK_MUTEX(utils) \ 195 if(((sasl_utils_t *)(utils))->mutex_lock(krb_mutex) != 0) { \ 196 ((sasl_utils_t *)(utils))->seterror(((sasl_utils_t *)(utils))->conn, \ 197 0, "error locking mutex"); \ 198 return SASL_FAIL; \ 199 } 200#define KRB_UNLOCK_MUTEX(utils) \ 201 if(((sasl_utils_t *)(utils))->mutex_unlock(krb_mutex) != 0) { \ 202 ((sasl_utils_t *)(utils))->seterror(((sasl_utils_t *)(utils))->conn, \ 203 0, "error unlocking mutex"); \ 204 return SASL_FAIL; \ 205 } 206 207/* Mutex for not-thread-safe kerberos 4 library */ 208static void *krb_mutex = NULL; 209static char *srvtab = NULL; 210static unsigned refcount = 0; 211 212static int kerberosv4_encode(void *context, 213 const struct iovec *invec, 214 unsigned numiov, 215 const char **output, 216 unsigned *outputlen) 217{ 218 int len, ret; 219 context_t *text = (context_t *)context; 220 struct buffer_info *inblob, bufinfo; 221 222 if(numiov > 1) { 223 ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf); 224 if(ret != SASL_OK) return ret; 225 inblob = text->enc_in_buf; 226 } else { 227 bufinfo.data = invec[0].iov_base; 228 bufinfo.curlen = invec[0].iov_len; 229 inblob = &bufinfo; 230 } 231 232 ret = _plug_buf_alloc(text->utils, &(text->encode_buf), 233 &text->encode_buf_len, inblob->curlen+40); 234 235 if(ret != SASL_OK) return ret; 236 237 KRB_LOCK_MUTEX(text->utils); 238 239 if (text->sec_type == KRB_SEC_ENCRYPTION) { 240 /* Type incompatibility on 4th arg probably means you're 241 building against krb4 in MIT krb5, but got the OpenSSL 242 headers in your way. You need to not use openssl/des.h with 243 MIT kerberos. */ 244 len=krb_mk_priv(inblob->data, (text->encode_buf+4), 245 inblob->curlen, text->init_keysched, 246 &text->session, &text->ip_local, 247 &text->ip_remote); 248 } else if (text->sec_type == KRB_SEC_INTEGRITY) { 249 len=krb_mk_safe(inblob->data, (text->encode_buf+4), 250 inblob->curlen, 251 &text->session, &text->ip_local, &text->ip_remote); 252 } else { 253 len = -1; 254 } 255 256 KRB_UNLOCK_MUTEX(text->utils); 257 258 /* returns -1 on error */ 259 if (len==-1) return SASL_FAIL; 260 261 /* now copy in the len of the buffer in network byte order */ 262 *outputlen=len+4; 263 len=htonl(len); 264 memcpy(text->encode_buf, &len, 4); 265 266 /* Setup the const pointer */ 267 *output = text->encode_buf; 268 269 return SASL_OK; 270} 271 272static int kerberosv4_decode_packet(void *context, 273 const char *input, unsigned inputlen, 274 char **output, unsigned *outputlen) 275{ 276 context_t *text = (context_t *) context; 277 int result; 278 MSG_DAT data; 279 280 memset(&data,0,sizeof(MSG_DAT)); 281 282 KRB_LOCK_MUTEX(text->utils); 283 284 if (text->sec_type == KRB_SEC_ENCRYPTION) { 285 result=krb_rd_priv(input, inputlen, text->init_keysched, 286 &text->session, &text->ip_remote, 287 &text->ip_local, &data); 288 } else if (text->sec_type == KRB_SEC_INTEGRITY) { 289 result = krb_rd_safe(input, inputlen, 290 &text->session, &text->ip_remote, 291 &text->ip_local, &data); 292 } else { 293 KRB_UNLOCK_MUTEX(text->utils); 294 text->utils->seterror(text->utils->conn, 0, 295 "KERBEROS_4 decode called with KRB_SEC_NONE"); 296 return SASL_FAIL; 297 } 298 299 KRB_UNLOCK_MUTEX(text->utils); 300 301 /* see if the krb library gave us a failure */ 302 if (result != 0) { 303 text->utils->seterror(text->utils->conn, 0, get_krb_err_txt(result)); 304 return SASL_FAIL; 305 } 306 307 /* check to make sure the timestamps are ok */ 308 if ((data.time_sec < text->time_sec) || /* if an earlier time */ 309 (((data.time_sec == text->time_sec) && /* or the exact same time */ 310 (data.time_5ms < text->time_5ms)))) 311 { 312 text->utils->seterror(text->utils->conn, 0, "timestamps not ok"); 313 return SASL_FAIL; 314 } 315 316 text->time_sec = data.time_sec; 317 text->time_5ms = data.time_5ms; 318 319 result = _plug_buf_alloc(text->utils, &text->decode_once_buf, 320 &text->decode_once_buf_len, 321 data.app_length + 1); 322 if(result != SASL_OK) 323 return result; 324 325 *output = text->decode_once_buf; 326 *outputlen = data.app_length; 327 memcpy(*output, data.app_data, data.app_length); 328 (*output)[*outputlen] = '\0'; 329 330 return SASL_OK; 331} 332 333static int kerberosv4_decode(void *context, 334 const char *input, unsigned inputlen, 335 const char **output, unsigned *outputlen) 336{ 337 context_t *text = (context_t *) context; 338 int ret; 339 340 ret = _plug_decode(&text->decode_context, input, inputlen, 341 &text->decode_buf, &text->decode_buf_len, outputlen, 342 kerberosv4_decode_packet, text); 343 344 *output = text->decode_buf; 345 346 return ret; 347} 348 349static int new_text(const sasl_utils_t *utils, context_t **text) 350{ 351 context_t *ret = (context_t *) utils->malloc(sizeof(context_t)); 352 353 if (ret == NULL) { 354 MEMERROR(utils); 355 return SASL_NOMEM; 356 } 357 358 memset(ret, 0, sizeof(context_t)); 359 360 ret->state = 1; 361 ret->utils = utils; 362 363 *text = ret; 364 365 return SASL_OK; 366} 367 368static void kerberosv4_common_mech_dispose(void *conn_context, 369 const sasl_utils_t *utils) 370{ 371 context_t *text = (context_t *)conn_context; 372 373 if(!text) return; 374 375 _plug_decode_free(&text->decode_context); 376 if (text->encode_buf) utils->free(text->encode_buf); 377 if (text->decode_buf) utils->free(text->decode_buf); 378 if (text->decode_once_buf) utils->free(text->decode_once_buf); 379 if (text->out_buf) utils->free(text->out_buf); 380 if (text->enc_in_buf) { 381 if(text->enc_in_buf->data) utils->free(text->enc_in_buf->data); 382 utils->free(text->enc_in_buf); 383 } 384 /* no need to free userid, it's just the interaction result */ 385 386 utils->free(text); 387} 388 389static void 390kerberosv4_common_mech_free(void *glob_context __attribute__((unused)), 391 const sasl_utils_t *utils) 392{ 393 if (krb_mutex) { 394 utils->mutex_free(krb_mutex); 395 krb_mutex = NULL; /* in case we need to re-use it */ 396 } 397 refcount--; 398 if (srvtab && !refcount) { 399 utils->free(srvtab); 400 srvtab = NULL; 401 } 402} 403 404/***************************** Server Section *****************************/ 405 406static int cando_sec(sasl_security_properties_t *props, 407 int external_ssf, 408 int secflag) 409{ 410 int need; 411 int musthave; 412 413 if(props->maxbufsize == 0) { 414 need = musthave = 0; 415 } else { 416 need = props->max_ssf - external_ssf; 417 musthave = props->min_ssf - external_ssf; 418 } 419 420 switch (secflag) { 421 case KRB_SECFLAG_NONE: 422 if (musthave <= 0) 423 return 1; 424 break; 425 case KRB_SECFLAG_INTEGRITY: 426 if ((musthave <= KRB_INTEGRITY_BITS) 427 && (KRB_INTEGRITY_BITS <= need)) 428 return 1; 429 break; 430 case KRB_SECFLAG_ENCRYPTION: 431 if ((musthave <= KRB_DES_SECURITY_BITS) 432 && (KRB_DES_SECURITY_BITS <= need)) 433 return 1; 434 break; 435 case KRB_SECFLAG_CREDENTIALS: 436 if (props->security_flags & SASL_SEC_PASS_CREDENTIALS) 437 return 1; 438 break; 439 } 440 return 0; 441} 442 443static int ipv4_ipfromstring(const sasl_utils_t *utils, const char *addr, 444 struct sockaddr_in *out) 445{ 446 struct sockaddr_storage ss; 447 int result; 448 449 result = _plug_ipfromstring(utils, addr, 450 (struct sockaddr *)&ss, sizeof(ss)); 451 if (result != SASL_OK) { 452 /* couldn't get local IP address */ 453 return result; 454 } 455 /* Kerberos_V4 supports only IPv4 */ 456 if (((struct sockaddr *)&ss)->sa_family != AF_INET) 457 return SASL_FAIL; 458 memcpy(out, &ss, sizeof(struct sockaddr_in)); 459 460 return SASL_OK; 461} 462 463#ifndef macintosh 464static int 465kerberosv4_server_mech_new(void *glob_context __attribute__((unused)), 466 sasl_server_params_t *sparams, 467 const char *challenge __attribute__((unused)), 468 unsigned challen __attribute__((unused)), 469 void **conn_context) 470{ 471 return new_text(sparams->utils, (context_t **) conn_context); 472} 473 474static int kerberosv4_server_mech_step(void *conn_context, 475 sasl_server_params_t *sparams, 476 const char *clientin, 477 unsigned clientinlen, 478 const char **serverout, 479 unsigned *serveroutlen, 480 sasl_out_params_t *oparams) 481{ 482 context_t *text = (context_t *) conn_context; 483 int result; 484 485 *serverout = NULL; 486 *serveroutlen = 0; 487 488 switch (text->state) { 489 490 case 1: { 491 /* random 32-bit number */ 492 int randocts, nchal; 493 494 /* shouldn't we check for erroneous client input here?!? */ 495 496 sparams->utils->rand(sparams->utils->rpool,(char *) &randocts , 497 sizeof(randocts)); 498 text->challenge=randocts; 499 nchal = htonl(text->challenge); 500 501 result = _plug_buf_alloc(text->utils, &text->out_buf, 502 &text->out_buf_len, 5); 503 if (result != SASL_OK) return result; 504 505 memcpy(text->out_buf,&nchal,4); 506 *serverout = text->out_buf; 507 *serveroutlen = 4; 508 509 text->state = 2; 510 511 return SASL_CONTINUE; 512 } 513 514 case 2: { 515 int nchal; 516 unsigned char sout[8]; 517 AUTH_DAT ad; 518 KTEXT_ST ticket; 519 unsigned lup; 520 struct sockaddr_in addr; 521 char *dot; 522 523 /* received authenticator */ 524 525 /* create ticket */ 526 if (clientinlen > MAX_KTXT_LEN) { 527 text->utils->seterror(text->utils->conn,0, 528 "request larger than maximum ticket size"); 529 return SASL_FAIL; 530 } 531 532 ticket.length=clientinlen; 533 for (lup = 0; lup < clientinlen; lup++) 534 ticket.dat[lup] = clientin[lup]; 535 536 KRB_LOCK_MUTEX(sparams->utils); 537 538 text->realm = krb_realmofhost(sparams->serverFQDN); 539 540 /* get instance */ 541 strncpy (text->instance, krb_get_phost (sparams->serverFQDN), 542 sizeof (text->instance)); 543 544 KRB_UNLOCK_MUTEX(sparams->utils); 545 546 text->instance[sizeof(text->instance)-1] = 0; 547 548 /* At some sites, krb_get_phost() sensibly but 549 * atypically returns FQDNs, versus the first component, 550 * which is what we need for RFC2222 section 7.1 551 */ 552 dot = strchr(text->instance, '.'); 553 if (dot) *dot = '\0'; 554 555 memset(&addr, 0, sizeof(struct sockaddr_in)); 556 557#ifndef KRB4_IGNORE_IP_ADDRESS 558 /* (we ignore IP addresses in krb4 tickets at CMU to facilitate moving 559 from machine to machine) */ 560 561 /* get ip number in addr*/ 562 result = ipv4_ipfromstring(sparams->utils, sparams->ipremoteport, &addr); 563 if (result != SASL_OK || !sparams->ipremoteport) { 564 SETERROR(text->utils, "couldn't get remote IP address"); 565 return result; 566 } 567#endif 568 569 /* check ticket */ 570 571 KRB_LOCK_MUTEX(sparams->utils); 572 result = krb_rd_req(&ticket, (char *) sparams->service, text->instance, 573 addr.sin_addr.s_addr, &ad, srvtab); 574 KRB_UNLOCK_MUTEX(sparams->utils); 575 576 if (result) { /* if fails mechanism fails */ 577 text->utils->seterror(text->utils->conn,0, 578 "krb_rd_req failed service=%s instance=%s error code=%s (%i)", 579 sparams->service, text->instance,get_krb_err_txt(result),result); 580 return SASL_BADAUTH; 581 } 582 583 /* 8 octets of data 584 * 1-4 checksum+1 585 * 5 security layers 586 * 6-8max cipher text buffer size 587 * use DES ECB in the session key 588 */ 589 590 nchal=htonl(text->challenge+1); 591 memcpy(sout, &nchal, 4); 592 sout[4]= 0; 593 if (cando_sec(&sparams->props, sparams->external_ssf, 594 KRB_SECFLAG_NONE)) 595 sout[4] |= KRB_SECFLAG_NONE; 596 if (cando_sec(&sparams->props, sparams->external_ssf, 597 KRB_SECFLAG_INTEGRITY)) 598 sout[4] |= KRB_SECFLAG_INTEGRITY; 599 if (cando_sec(&sparams->props, sparams->external_ssf, 600 KRB_SECFLAG_ENCRYPTION)) 601 sout[4] |= KRB_SECFLAG_ENCRYPTION; 602 if (cando_sec(&sparams->props, sparams->external_ssf, 603 KRB_SECFLAG_CREDENTIALS)) 604 sout[4] |= KRB_SECFLAG_CREDENTIALS; 605 606 if(sparams->props.maxbufsize) { 607 int tmpmaxbuf = (sparams->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : sparams->props.maxbufsize; 608 609 sout[5]=((tmpmaxbuf >> 16) & 0xFF); 610 sout[6]=((tmpmaxbuf >> 8) & 0xFF); 611 sout[7]=(tmpmaxbuf & 0xFF); 612 } else { 613 /* let's say we can support up to 64K */ 614 /* no inherent inability with our layers to support more */ 615 616 sout[5]=0x00; /* max ciphertext buffer size */ 617 sout[6]=0xFF; 618 sout[7]=0xFF; 619 } 620 621 memcpy(text->session, ad.session, 8); 622 memcpy(text->pname, ad.pname, sizeof(text->pname)); 623 memcpy(text->pinst, ad.pinst, sizeof(text->pinst)); 624 memcpy(text->prealm, ad.prealm, sizeof(text->prealm)); 625 des_key_sched(&ad.session, text->init_keysched); 626 627 /* make keyschedule for encryption and decryption */ 628 des_key_sched(&ad.session, text->enc_keysched); 629 des_key_sched(&ad.session, text->dec_keysched); 630 631 des_ecb_encrypt((des_cblock *)sout, 632 (des_cblock *)sout, 633 text->init_keysched, 634 DES_ENCRYPT); 635 636 result = _plug_buf_alloc(text->utils, &text->out_buf, 637 &text->out_buf_len, 9); 638 if(result != SASL_OK) 639 return result; 640 641 memcpy(text->out_buf,&sout,8); 642 *serverout = text->out_buf; 643 *serveroutlen = 8; 644 645 text->state = 3; 646 647 return SASL_CONTINUE; 648 } 649 650 case 3: { 651 int result; 652 int testnum; 653 int flag; 654 unsigned char *in; 655 656 if ((clientinlen == 0) || (clientinlen % 8 != 0)) { 657 text->utils->seterror(text->utils->conn,0, 658 "Response to challengs is not a multiple of 8 octets (a DES block)"); 659 return SASL_FAIL; 660 } 661 662 /* we need to make a copy because des does in place decrpytion */ 663 in = sparams->utils->malloc(clientinlen + 1); 664 if (in == NULL) { 665 MEMERROR(sparams->utils); 666 return SASL_NOMEM; 667 } 668 669 memcpy(in, clientin, clientinlen); 670 in[clientinlen] = '\0'; 671 672 /* decrypt; verify checksum */ 673 674 des_pcbc_encrypt((des_cblock *)in, 675 (des_cblock *)in, 676 clientinlen, 677 text->init_keysched, 678 &text->session, 679 DES_DECRYPT); 680 681 testnum = (in[0]*256*256*256)+(in[1]*256*256)+(in[2]*256)+in[3]; 682 683 if (testnum != text->challenge) { 684 SETERROR(sparams->utils, "incorrect response to challenge"); 685 return SASL_BADAUTH; 686 } 687 688 if (!cando_sec(&sparams->props, sparams->external_ssf, 689 in[4] & KRB_SECFLAGS)) { 690 SETERROR(sparams->utils, 691 "invalid security property specified"); 692 return SASL_BADPROT; 693 } 694 695 oparams->encode = &kerberosv4_encode; 696 oparams->decode = &kerberosv4_decode; 697 698 switch (in[4] & KRB_SECFLAGS) { 699 case KRB_SECFLAG_NONE: 700 text->sec_type = KRB_SEC_NONE; 701 oparams->encode = NULL; 702 oparams->decode = NULL; 703 oparams->mech_ssf = 0; 704 break; 705 case KRB_SECFLAG_INTEGRITY: 706 text->sec_type = KRB_SEC_INTEGRITY; 707 oparams->mech_ssf = KRB_INTEGRITY_BITS; 708 break; 709 case KRB_SECFLAG_ENCRYPTION: 710 text->sec_type = KRB_SEC_ENCRYPTION; 711 oparams->mech_ssf = KRB_DES_SECURITY_BITS; 712 break; 713 default: 714 /* Mark that we tried */ 715 oparams->mech_ssf = 2; 716 SETERROR(sparams->utils, "not a supported encryption layer"); 717 return SASL_BADPROT; 718 } 719 720 /* get ip data */ 721 /* get ip number in addr*/ 722 result = ipv4_ipfromstring(sparams->utils, 723 sparams->iplocalport, &(text->ip_local)); 724 if (result != SASL_OK) { 725 SETERROR(sparams->utils, "couldn't get local ip address"); 726 /* couldn't get local IP address */ 727 return result; 728 } 729 730 result = ipv4_ipfromstring(sparams->utils, 731 sparams->ipremoteport, &(text->ip_remote)); 732 if (result != SASL_OK) { 733 SETERROR(sparams->utils, "couldn't get remote ip address"); 734 /* couldn't get remote IP address */ 735 return result; 736 } 737 738 /* fill in oparams */ 739 oparams->maxoutbuf = (in[5] << 16) + (in[6] << 8) + in[7]; 740 if(oparams->mech_ssf) { 741 /* FIXME: Likely to be too large */ 742 oparams->maxoutbuf -= 50; 743 } 744 745 if (sparams->canon_user) { 746 char *user=NULL, *authid=NULL; 747 size_t ulen = 0, alen = strlen(text->pname); 748 int ret, cflag = SASL_CU_AUTHID | SASL_CU_EXTERNALLY_VERIFIED; 749 750 if (text->pinst[0]) { 751 alen += strlen(text->pinst) + 1 /* for the . */; 752 } 753 flag = 0; 754 if (strcmp(text->realm, text->prealm)) { 755 alen += strlen(text->prealm) + 1 /* for the @ */; 756 flag = 1; 757 } 758 759 authid = sparams->utils->malloc(alen + 1); 760 if (!authid) { 761 MEMERROR(sparams->utils); 762 return SASL_NOMEM; 763 } 764 765 strcpy(authid, text->pname); 766 if (text->pinst[0]) { 767 strcat(authid, "."); 768 strcat(authid, text->pinst); 769 } 770 if (flag) { 771 strcat(authid, "@"); 772 strcat(authid, text->prealm); 773 } 774 775 if (in[8]) { 776 user = sparams->utils->malloc(strlen((char *) in + 8) + 1); 777 if (!user) { 778 MEMERROR(sparams->utils); 779 return SASL_NOMEM; 780 } 781 782 strcpy(user, (char *) in + 8); 783 ulen = strlen(user); 784 } else { 785 cflag |= SASL_CU_AUTHZID; 786 } 787 788 ret = sparams->canon_user(sparams->utils->conn, authid, alen, 789 cflag, oparams); 790 sparams->utils->free(authid); 791 if (ret != SASL_OK) { 792 if (user) 793 sparams->utils->free(user); 794 return ret; 795 } 796 797 if (user) { 798 ret = sparams->canon_user(sparams->utils->conn, user, ulen, 799 SASL_CU_AUTHZID, oparams); 800 801 sparams->utils->free(user); 802 } 803 804 if (ret != SASL_OK) return ret; 805 } 806 807 /* nothing more to do; authenticated */ 808 oparams->doneflag = 1; 809 oparams->param_version = 0; 810 811 /* used by layers */ 812 _plug_decode_init(&text->decode_context, text->utils, 813 (sparams->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : 814 sparams->props.maxbufsize); 815 816 sparams->utils->free(in); 817 818 return SASL_OK; 819 } 820 821 default: 822 sparams->utils->log(NULL, SASL_LOG_ERR, 823 "Invalid Kerberos server step %d\n", text->state); 824 return SASL_FAIL; 825 } 826 827 return SASL_FAIL; /* should never get here */ 828} 829 830static int kerberosv4_mech_avail(void *glob_context __attribute__((unused)), 831 sasl_server_params_t *sparams, 832 void **conn_context __attribute__((unused))) 833{ 834 struct sockaddr_in addr; 835 836 if (!sparams->iplocalport || !sparams->ipremoteport 837 || ipv4_ipfromstring(sparams->utils, 838 sparams->iplocalport, &addr) != SASL_OK 839 || ipv4_ipfromstring(sparams->utils, 840 sparams->ipremoteport, &addr) != SASL_OK) { 841 SETERROR(sparams->utils, 842 "KERBEROS_V4 unavailable due to lack of IPv4 information"); 843 return SASL_NOMECH; 844 } 845 846 return SASL_OK; 847} 848 849 850static sasl_server_plug_t kerberosv4_server_plugins[] = 851{ 852 { 853 "KERBEROS_V4", /* mech_name */ 854 KRB_DES_SECURITY_BITS, /* max_ssf */ 855 SASL_SEC_NOPLAINTEXT 856 | SASL_SEC_NOACTIVE 857 | SASL_SEC_NOANONYMOUS 858 | SASL_SEC_MUTUAL_AUTH, /* security_flags */ 859 SASL_FEAT_SERVER_FIRST 860 | SASL_FEAT_ALLOWS_PROXY, /* features */ 861 NULL, /* glob_context */ 862 &kerberosv4_server_mech_new, /* mech_new */ 863 &kerberosv4_server_mech_step, /* mech_step */ 864 &kerberosv4_common_mech_dispose,/* mech_dispose */ 865 &kerberosv4_common_mech_free, /* mech_free */ 866 NULL, /* setpass */ 867 NULL, /* user_query */ 868 NULL, /* idle */ 869 &kerberosv4_mech_avail, /* mech_avail */ 870 NULL /* spare */ 871 } 872}; 873#endif /* macintosh */ 874 875int kerberos4_server_plug_init(const sasl_utils_t *utils, 876 int maxversion, 877 int *out_version, 878 sasl_server_plug_t **pluglist, 879 int *plugcount) 880{ 881#ifdef macintosh 882 return SASL_BADVERS; 883#else 884 const char *ret; 885 unsigned int rl; 886 887 if (maxversion < SASL_SERVER_PLUG_VERSION) { 888 return SASL_BADVERS; 889 } 890 891 892 if (!krb_mutex) { 893 krb_mutex = utils->mutex_alloc(); 894 if(!krb_mutex) { 895 return SASL_FAIL; 896 } 897 } 898 899 if (!srvtab) { 900 utils->getopt(utils->getopt_context, 901 "KERBEROS_V4", "srvtab", &ret, &rl); 902 903 if (ret == NULL) { 904 ret = KEYFILE; 905 rl = strlen(ret); 906 } 907 908 srvtab = utils->malloc(sizeof(char) * (rl + 1)); 909 if(!srvtab) { 910 MEMERROR(utils); 911 return SASL_NOMEM; 912 } 913 914 strcpy(srvtab, ret); 915 } 916 917 refcount++; 918 919 /* fail if we can't open the srvtab file */ 920 if (access(srvtab, R_OK) != 0) { 921 utils->log(NULL, SASL_LOG_ERR, 922 "can't access srvtab file %s: %m", srvtab, errno); 923 if(!(--refcount)) { 924 utils->free(srvtab); 925 srvtab=NULL; 926 } 927 return SASL_FAIL; 928 } 929 930 *out_version = SASL_SERVER_PLUG_VERSION; 931 *pluglist = kerberosv4_server_plugins; 932 *plugcount = 1; 933 934 return SASL_OK; 935#endif 936} 937 938/***************************** Client Section *****************************/ 939 940static int 941kerberosv4_client_mech_new(void *glob_context __attribute__((unused)), 942 sasl_client_params_t *params, 943 void **conn_context) 944{ 945 return new_text(params->utils, (context_t **) conn_context); 946} 947 948static int kerberosv4_client_mech_step(void *conn_context, 949 sasl_client_params_t *cparams, 950 const char *serverin, 951 unsigned serverinlen, 952 sasl_interact_t **prompt_need, 953 const char **clientout, 954 unsigned *clientoutlen, 955 sasl_out_params_t *oparams) 956{ 957 context_t *text = (context_t *) conn_context; 958 KTEXT_ST authent; 959 int ret; 960 961 *clientout = NULL; 962 *clientoutlen = 0; 963 964 authent.length = MAX_KTXT_LEN; 965 966 switch (text->state) { 967 968 case 1: { 969 /* We should've just recieved a 32-bit number in network byte order. 970 * We want to reply with an authenticator. */ 971 int result; 972 KTEXT_ST ticket; 973 char *dot; 974 975 memset(&ticket, 0L, sizeof(ticket)); 976 ticket.length = MAX_KTXT_LEN; 977 978 if (serverinlen != 4) { 979 text->utils->seterror(text->utils->conn, 0, 980 "server challenge not 4 bytes long"); 981 return SASL_BADPROT; 982 } 983 984 memcpy(&text->challenge, serverin, 4); 985 986 text->challenge=ntohl(text->challenge); 987 988 if (cparams->serverFQDN == NULL) { 989 cparams->utils->log(NULL, SASL_LOG_ERR, 990 "no 'serverFQDN' set"); 991 SETERROR(text->utils, "paramater error"); 992 return SASL_BADPARAM; 993 } 994 if (cparams->service == NULL) { 995 cparams->utils->log(NULL, SASL_LOG_ERR, 996 "no 'service' set"); 997 SETERROR(text->utils, "paramater error"); 998 return SASL_BADPARAM; 999 } 1000 1001 KRB_LOCK_MUTEX(cparams->utils); 1002 text->realm=krb_realmofhost(cparams->serverFQDN); 1003 text->hostname=(char *) cparams->serverFQDN; 1004 1005 /* the instance of the principal we're authenticating with */ 1006 strncpy (text->instance, krb_get_phost (cparams->serverFQDN), 1007 sizeof (text->instance)); 1008 1009 /* text->instance is NULL terminated unless it was too long */ 1010 text->instance[sizeof(text->instance)-1] = '\0'; 1011 1012 /* At some sites, krb_get_phost() sensibly but 1013 * atypically returns FQDNs, versus the first component, 1014 * which is what we need for RFC2222 section 7.1 1015 */ 1016 dot = strchr(text->instance, '.'); 1017 if (dot) *dot = '\0'; 1018 1019#ifndef macintosh 1020 if ((result = krb_mk_req(&ticket, (char *) cparams->service, 1021 text->instance, text->realm, text->challenge))) 1022#else 1023 memset(&text->credentials,0,sizeof(text->credentials)); 1024 if (kcglue_krb_mk_req(ticket.dat, 1025 &ticket.length, 1026 cparams->service, 1027 text->instance, 1028 text->realm, 1029 text->challenge, 1030 &text->credentials.session, 1031 text->credentials.pname, 1032 text->credentials.pinst) != 0) 1033#endif 1034 { 1035 KRB_UNLOCK_MUTEX(cparams->utils); 1036 1037 text->utils->seterror(text->utils->conn,SASL_NOLOG, 1038 "krb_mk_req() failed"); 1039 1040 cparams->utils->log(NULL, SASL_LOG_ERR, 1041 "krb_mk_req() failed: %s (%d)", 1042 get_krb_err_txt(result), result); 1043 return SASL_FAIL; 1044 } 1045 1046 KRB_UNLOCK_MUTEX(cparams->utils); 1047 1048 ret = _plug_buf_alloc(text->utils, &(text->out_buf), 1049 &(text->out_buf_len), ticket.length); 1050 if (ret != SASL_OK) return ret; 1051 1052 memcpy(text->out_buf, ticket.dat, ticket.length); 1053 1054 *clientout = text->out_buf; 1055 *clientoutlen = ticket.length; 1056 1057 text->state = 2; 1058 1059 return SASL_CONTINUE; 1060 } 1061 1062 /* challenge #2 */ 1063 case 2: { 1064 int need = 0; 1065 int musthave = 0; 1066 int testnum; 1067 int nchal; 1068 unsigned char *sout = NULL; 1069 unsigned len; 1070 unsigned char in[8]; 1071 int result; 1072 int servermaxbuf; 1073 char *buf; 1074 int user_result = SASL_OK; 1075 1076 /* try to get the authid */ 1077 if (text->user == NULL) { 1078 user_result = _plug_get_userid(cparams->utils, &text->user, 1079 prompt_need); 1080 1081 if (user_result != SASL_OK && user_result != SASL_INTERACT) 1082 return user_result; 1083 } 1084 1085 /* free prompts we got */ 1086 if (prompt_need && *prompt_need) { 1087 cparams->utils->free(*prompt_need); 1088 *prompt_need = NULL; 1089 } 1090 1091 /* if there are prompts not filled in */ 1092 if (user_result == SASL_INTERACT) { 1093 /* make the prompt list */ 1094 int result = 1095 _plug_make_prompts(cparams->utils, prompt_need, 1096 user_result == SASL_INTERACT ? 1097 "Please enter your authorization name" : NULL, NULL, 1098 NULL, NULL, 1099 NULL, NULL, 1100 NULL, NULL, NULL, 1101 NULL, NULL, NULL); 1102 if (result!=SASL_OK) return result; 1103 1104 return SASL_INTERACT; 1105 } 1106 1107 /* must be 8 octets */ 1108 if (serverinlen!=8) { 1109 SETERROR(cparams->utils, 1110 "server response not 8 bytes long"); 1111 return SASL_BADAUTH; 1112 } 1113 1114 memcpy(in, serverin, 8); 1115 1116#ifndef macintosh 1117 /* get credentials */ 1118 KRB_LOCK_MUTEX(cparams->utils); 1119 result = krb_get_cred((char *)cparams->service, 1120 text->instance, 1121 text->realm, 1122 &text->credentials); 1123 KRB_UNLOCK_MUTEX(cparams->utils); 1124 1125 if(result != 0) { 1126 cparams->utils->log(NULL, SASL_LOG_ERR, 1127 "krb_get_cred() failed: %s (%d)", 1128 get_krb_err_txt(result), result); 1129 SETERROR(cparams->utils, "krb_get_cred() failed"); 1130 return SASL_BADAUTH; 1131 } 1132#endif 1133 memcpy(text->session, text->credentials.session, 8); 1134 1135 /* make key schedule for encryption and decryption */ 1136 des_key_sched(&text->session, text->init_keysched); 1137 des_key_sched(&text->session, text->enc_keysched); 1138 des_key_sched(&text->session, text->dec_keysched); 1139 1140 /* decrypt from server */ 1141 des_ecb_encrypt((des_cblock *)in, (des_cblock *)in, 1142 text->init_keysched, DES_DECRYPT); 1143 1144 /* convert to 32bit int */ 1145 testnum = (in[0]*256*256*256)+(in[1]*256*256)+(in[2]*256)+in[3]; 1146 1147 /* verify data 1st 4 octets must be equal to chal+1 */ 1148 if (testnum != text->challenge+1) { 1149 SETERROR(cparams->utils,"server response incorrect"); 1150 return SASL_BADAUTH; 1151 } 1152 1153 /* construct 8 octets 1154 * 1-4 - original checksum 1155 * 5 - bitmask of sec layer 1156 * 6-8 max buffer size 1157 */ 1158 if (cparams->props.min_ssf > 1159 KRB_DES_SECURITY_BITS + cparams->external_ssf) { 1160 SETERROR(cparams->utils, 1161 "minimum ssf too strong for this mechanism"); 1162 return SASL_TOOWEAK; 1163 } else if (cparams->props.min_ssf > cparams->props.max_ssf) { 1164 SETERROR(cparams->utils, 1165 "minimum ssf larger than maximum ssf"); 1166 return SASL_BADPARAM; 1167 } 1168 1169 /* create stuff to send to server */ 1170 sout = (char *) 1171 cparams->utils->malloc(9+(text->user ? strlen(text->user) : 0)+9); 1172 if (!sout) { 1173 MEMERROR(cparams->utils); 1174 return SASL_NOMEM; 1175 } 1176 1177 nchal = htonl(text->challenge); 1178 memcpy(sout, &nchal, 4); 1179 1180 /* need bits of layer */ 1181 if(cparams->props.maxbufsize == 0) { 1182 need = musthave = 0; 1183 } else { 1184 need = cparams->props.max_ssf - cparams->external_ssf; 1185 musthave = cparams->props.min_ssf - cparams->external_ssf; 1186 } 1187 1188 oparams->decode = &kerberosv4_decode; 1189 oparams->encode = &kerberosv4_encode; 1190 1191 if ((in[4] & KRB_SECFLAG_ENCRYPTION) 1192 && (need>=56) && (musthave <= 56)) { 1193 /* encryption */ 1194 text->sec_type = KRB_SEC_ENCRYPTION; 1195 oparams->mech_ssf = 56; 1196 sout[4] = KRB_SECFLAG_ENCRYPTION; 1197 /* using encryption layer */ 1198 } else if ((in[4] & KRB_SECFLAG_INTEGRITY) 1199 && (need >= 1) && (musthave <= 1)) { 1200 /* integrity */ 1201 text->sec_type = KRB_SEC_INTEGRITY; 1202 oparams->mech_ssf=1; 1203 sout[4] = KRB_SECFLAG_INTEGRITY; 1204 /* using integrity layer */ 1205 } else if ((in[4] & KRB_SECFLAG_NONE) && (musthave <= 0)) { 1206 /* no layer */ 1207 text->sec_type = KRB_SEC_NONE; 1208 oparams->encode=NULL; 1209 oparams->decode=NULL; 1210 oparams->mech_ssf=0; 1211 sout[4] = KRB_SECFLAG_NONE; 1212 } else { 1213 /* Mark that we tried */ 1214 oparams->mech_ssf=2; 1215 SETERROR(cparams->utils, 1216 "unable to agree on layers with server"); 1217 return SASL_BADPROT; 1218 } 1219 1220 servermaxbuf = in[5]*256*256+in[6]*256+in[7]; 1221 oparams->maxoutbuf = servermaxbuf; 1222 if (oparams->mech_ssf) { 1223 /* FIXME: Likely to be too large */ 1224 oparams->maxoutbuf -= 50; 1225 } 1226 1227 if(cparams->props.maxbufsize) { 1228 int tmpmaxbuf = ( cparams->props.maxbufsize > 0xFFFFFF ) ? 0xFFFFFF : cparams->props.maxbufsize; 1229 1230 sout[5]=((tmpmaxbuf >> 16) & 0xFF); 1231 sout[6]=((tmpmaxbuf >> 8) & 0xFF); 1232 sout[7]=(tmpmaxbuf & 0xFF); 1233 } else { 1234 /* let's say we can support up to 64K */ 1235 /* no inherent inability with our layers to support more */ 1236 1237 sout[5]=0x00; /* max ciphertext buffer size */ 1238 sout[6]=0xFF; 1239 sout[7]=0xFF; 1240 } 1241 1242 sout[8] = 0x00; /* just to be safe */ 1243 1244 /* append userid */ 1245 len = 9; /* 8 + trailing NULL */ 1246 if (text->user) { 1247 strcpy((char *)sout + 8, text->user); 1248 len += strlen(text->user); 1249 } 1250 1251 /* append 0 based octets so is multiple of 8 */ 1252 while(len % 8) { 1253 sout[len]=0; 1254 len++; 1255 } 1256 sout[len]=0; 1257 1258 des_pcbc_encrypt((des_cblock *)sout, 1259 (des_cblock *)sout, 1260 len, 1261 text->init_keysched, 1262 (des_cblock *)text->session, 1263 DES_ENCRYPT); 1264 1265 result = _plug_buf_alloc(text->utils, &text->out_buf, 1266 &text->out_buf_len, len); 1267 if (result != SASL_OK) return result; 1268 1269 memcpy(text->out_buf, sout, len); 1270 1271 *clientout = text->out_buf; 1272 *clientoutlen = len; 1273 1274 /* nothing more to do; should be authenticated */ 1275 if(cparams->iplocalport) { 1276 result = ipv4_ipfromstring(cparams->utils, 1277 cparams->iplocalport, 1278 &(text->ip_local)); 1279 if (result != SASL_OK) { 1280 /* couldn't get local IP address */ 1281 return result; 1282 } 1283 } 1284 1285 if (cparams->ipremoteport) { 1286 result = ipv4_ipfromstring(cparams->utils, 1287 cparams->ipremoteport, 1288 &(text->ip_remote)); 1289 if (result != SASL_OK) { 1290 /* couldn't get local IP address */ 1291 return result; 1292 } 1293 } 1294 1295 buf = cparams->utils->malloc(strlen(text->credentials.pname) 1296 + strlen(text->credentials.pinst) 1297 + 2); 1298 if (!buf) { 1299 MEMERROR(cparams->utils); 1300 return SASL_NOMEM; 1301 } 1302 strcpy(buf, text->credentials.pname); 1303 if (text->credentials.pinst[0]) { 1304 strcat(buf, "."); 1305 strcat(buf, text->credentials.pinst); 1306 } 1307 1308 if (text->user && !text->user[0]) { 1309 text->user = NULL; 1310 } 1311 1312 ret = cparams->canon_user(cparams->utils->conn, buf, 0, 1313 SASL_CU_AUTHID, oparams); 1314 if (ret != SASL_OK) { 1315 cparams->utils->free(buf); 1316 cparams->utils->free(sout); 1317 return ret; 1318 } 1319 1320 if (!text->user) { 1321 /* 0 in length fields means use strlen() */ 1322 ret = cparams->canon_user(cparams->utils->conn, buf, 0, 1323 SASL_CU_AUTHZID, oparams); 1324 } else { 1325 ret = cparams->canon_user(cparams->utils->conn, text->user, 0, 1326 SASL_CU_AUTHZID, oparams); 1327 } 1328 1329 cparams->utils->free(buf); 1330 1331 oparams->doneflag = 1; 1332 oparams->param_version = 0; 1333 1334 /* used by layers */ 1335 _plug_decode_init(&text->decode_context, text->utils, 1336 (cparams->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : 1337 cparams->props.maxbufsize); 1338 1339 if (sout) cparams->utils->free(sout); 1340 1341 return SASL_OK; 1342 } 1343 1344 default: 1345 cparams->utils->log(NULL, SASL_LOG_ERR, 1346 "Invalid Kerberos client step %d\n", text->state); 1347 return SASL_FAIL; 1348 } 1349 1350 return SASL_FAIL; /* should never get here */ 1351} 1352 1353static const long kerberosv4_required_prompts[] = { 1354 SASL_CB_LIST_END 1355}; 1356 1357static sasl_client_plug_t kerberosv4_client_plugins[] = 1358{ 1359 { 1360 "KERBEROS_V4", /* mech_name */ 1361 KRB_DES_SECURITY_BITS, /* max_ssf */ 1362 SASL_SEC_NOPLAINTEXT 1363 | SASL_SEC_NOACTIVE 1364 | SASL_SEC_NOANONYMOUS 1365 | SASL_SEC_MUTUAL_AUTH, /* security_flags */ 1366 SASL_FEAT_NEEDSERVERFQDN 1367 | SASL_FEAT_SERVER_FIRST 1368 | SASL_FEAT_ALLOWS_PROXY, /* features */ 1369 kerberosv4_required_prompts, /* required_prompts */ 1370 NULL, /* glob_context */ 1371 &kerberosv4_client_mech_new, /* mech_new */ 1372 &kerberosv4_client_mech_step, /* mech_step */ 1373 &kerberosv4_common_mech_dispose,/* mech_dispose */ 1374 &kerberosv4_common_mech_free, /* mech_free */ 1375 NULL, /* idle */ 1376 NULL, /* spare */ 1377 NULL /* spare */ 1378 } 1379}; 1380 1381int kerberos4_client_plug_init(const sasl_utils_t *utils, 1382 int maxversion, 1383 int *out_version, 1384 sasl_client_plug_t **pluglist, 1385 int *plugcount) 1386{ 1387 if (maxversion < SASL_CLIENT_PLUG_VERSION) { 1388 SETERROR(utils, "Wrong KERBEROS_V4 version"); 1389 return SASL_BADVERS; 1390 } 1391 1392 if(!krb_mutex) { 1393 krb_mutex = utils->mutex_alloc(); 1394 if(!krb_mutex) { 1395 return SASL_FAIL; 1396 } 1397 } 1398 1399 *out_version = SASL_CLIENT_PLUG_VERSION; 1400 *pluglist = kerberosv4_client_plugins; 1401 *plugcount = 1; 1402 1403 refcount++; 1404 1405 return SASL_OK; 1406} 1407