kerberos4.c revision 90926
160484Sobrien/* 260484Sobrien * Copyright (c) 1997 - 2000 Kungliga Tekniska H�gskolan 360484Sobrien * (Royal Institute of Technology, Stockholm, Sweden). 460484Sobrien * All rights reserved. 560484Sobrien * 6130561Sobrien * Redistribution and use in source and binary forms, with or without 760484Sobrien * modification, are permitted provided that the following conditions 833965Sjdp * are met: 933965Sjdp * 1033965Sjdp * 1. Redistributions of source code must retain the above copyright 1133965Sjdp * notice, this list of conditions and the following disclaimer. 12218822Sdim * 1333965Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1460484Sobrien * notice, this list of conditions and the following disclaimer in the 1533965Sjdp * 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#include "kdc_locl.h" 35 36RCSID("$Id: kerberos4.c,v 1.39 2001/09/20 09:34:42 assar Exp $"); 37 38#ifdef KRB4 39 40#ifndef swap32 41static u_int32_t 42swap32(u_int32_t x) 43{ 44 return ((x << 24) & 0xff000000) | 45 ((x << 8) & 0xff0000) | 46 ((x >> 8) & 0xff00) | 47 ((x >> 24) & 0xff); 48} 49#endif /* swap32 */ 50 51int 52maybe_version4(unsigned char *buf, int len) 53{ 54 return len > 0 && *buf == 4; 55} 56 57static void 58make_err_reply(krb5_data *reply, int code, const char *msg) 59{ 60 KTEXT_ST er; 61 62 /* name, instance and realm are not checked in most (all?) 63 implementations; msg is also never used, but we send it anyway 64 (for debugging purposes) */ 65 66 if(msg == NULL) 67 msg = krb_get_err_text(code); 68 cr_err_reply(&er, "", "", "", kdc_time, code, (char*)msg); 69 krb5_data_copy(reply, er.dat, er.length); 70} 71 72static krb5_boolean 73valid_princ(krb5_context context, krb5_principal princ) 74{ 75 krb5_error_code ret; 76 char *s; 77 hdb_entry *ent; 78 79 ret = krb5_unparse_name(context, princ, &s); 80 if (ret) 81 return FALSE; 82 ret = db_fetch(princ, &ent); 83 if (ret) { 84 kdc_log(7, "Lookup %s failed: %s", s, 85 krb5_get_err_text (context, ret)); 86 free(s); 87 return FALSE; 88 } 89 kdc_log(7, "Lookup %s succeeded", s); 90 free(s); 91 free_ent(ent); 92 return TRUE; 93} 94 95krb5_error_code 96db_fetch4(const char *name, const char *instance, const char *realm, 97 hdb_entry **ent) 98{ 99 krb5_principal p; 100 krb5_error_code ret; 101 102 ret = krb5_425_conv_principal_ext(context, name, instance, realm, 103 valid_princ, 0, &p); 104 if(ret) 105 return ret; 106 ret = db_fetch(p, ent); 107 krb5_free_principal(context, p); 108 return ret; 109} 110 111krb5_error_code 112get_des_key(hdb_entry *principal, krb5_boolean is_server, 113 krb5_boolean prefer_afs_key, Key **ret_key) 114{ 115 Key *v5_key = NULL, *v4_key = NULL, *afs_key = NULL, *server_key = NULL; 116 int i; 117 krb5_enctype etypes[] = { ETYPE_DES_CBC_MD5, 118 ETYPE_DES_CBC_MD4, 119 ETYPE_DES_CBC_CRC }; 120 121 for(i = 0; 122 i < sizeof(etypes)/sizeof(etypes[0]) 123 && (v5_key == NULL || v4_key == NULL || 124 afs_key == NULL || server_key == NULL); 125 ++i) { 126 Key *key = NULL; 127 while(hdb_next_enctype2key(context, principal, etypes[i], &key) == 0) { 128 if(key->salt == NULL) { 129 if(v5_key == NULL) 130 v5_key = key; 131 } else if(key->salt->type == hdb_pw_salt && 132 key->salt->salt.length == 0) { 133 if(v4_key == NULL) 134 v4_key = key; 135 } else if(key->salt->type == hdb_afs3_salt) { 136 if(afs_key == NULL) 137 afs_key = key; 138 } else if(server_key == NULL) 139 server_key = key; 140 } 141 } 142 143 if(prefer_afs_key) { 144 if(afs_key) 145 *ret_key = afs_key; 146 else if(v4_key) 147 *ret_key = v4_key; 148 else if(v5_key) 149 *ret_key = v5_key; 150 else if(is_server && server_key) 151 *ret_key = server_key; 152 else 153 return KERB_ERR_NULL_KEY; 154 } else { 155 if(v4_key) 156 *ret_key = v4_key; 157 else if(afs_key) 158 *ret_key = afs_key; 159 else if(v5_key) 160 *ret_key = v5_key; 161 else if(is_server && server_key) 162 *ret_key = server_key; 163 else 164 return KERB_ERR_NULL_KEY; 165 } 166 167 if((*ret_key)->key.keyvalue.length == 0) 168 return KERB_ERR_NULL_KEY; 169 return 0; 170} 171 172#define RCHECK(X, L) if(X){make_err_reply(reply, KFAILURE, "Packet too short"); goto L;} 173 174/* 175 * Process the v4 request in `buf, len' (received from `addr' 176 * (with string `from'). 177 * Return an error code and a reply in `reply'. 178 */ 179 180krb5_error_code 181do_version4(unsigned char *buf, 182 size_t len, 183 krb5_data *reply, 184 const char *from, 185 struct sockaddr_in *addr) 186{ 187 krb5_storage *sp; 188 krb5_error_code ret; 189 hdb_entry *client = NULL, *server = NULL; 190 Key *ckey, *skey; 191 int8_t pvno; 192 int8_t msg_type; 193 int lsb; 194 char *name = NULL, *inst = NULL, *realm = NULL; 195 char *sname = NULL, *sinst = NULL; 196 int32_t req_time; 197 time_t max_life; 198 u_int8_t life; 199 char client_name[256]; 200 char server_name[256]; 201 202 if(!enable_v4) { 203 kdc_log(0, "Rejected version 4 request from %s", from); 204 make_err_reply(reply, KDC_GEN_ERR, "function not enabled"); 205 return 0; 206 } 207 208 sp = krb5_storage_from_mem(buf, len); 209 RCHECK(krb5_ret_int8(sp, &pvno), out); 210 if(pvno != 4){ 211 kdc_log(0, "Protocol version mismatch (%d)", pvno); 212 make_err_reply(reply, KDC_PKT_VER, NULL); 213 goto out; 214 } 215 RCHECK(krb5_ret_int8(sp, &msg_type), out); 216 lsb = msg_type & 1; 217 msg_type &= ~1; 218 switch(msg_type){ 219 case AUTH_MSG_KDC_REQUEST: 220 RCHECK(krb5_ret_stringz(sp, &name), out1); 221 RCHECK(krb5_ret_stringz(sp, &inst), out1); 222 RCHECK(krb5_ret_stringz(sp, &realm), out1); 223 RCHECK(krb5_ret_int32(sp, &req_time), out1); 224 if(lsb) 225 req_time = swap32(req_time); 226 RCHECK(krb5_ret_int8(sp, &life), out1); 227 RCHECK(krb5_ret_stringz(sp, &sname), out1); 228 RCHECK(krb5_ret_stringz(sp, &sinst), out1); 229 snprintf (client_name, sizeof(client_name), 230 "%s.%s@%s", name, inst, realm); 231 snprintf (server_name, sizeof(server_name), 232 "%s.%s@%s", sname, sinst, v4_realm); 233 234 kdc_log(0, "AS-REQ %s from %s for %s", 235 client_name, from, server_name); 236 237 ret = db_fetch4(name, inst, realm, &client); 238 if(ret) { 239 kdc_log(0, "Client not found in database: %s: %s", 240 client_name, krb5_get_err_text(context, ret)); 241 make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, NULL); 242 goto out1; 243 } 244 ret = db_fetch4(sname, sinst, v4_realm, &server); 245 if(ret){ 246 kdc_log(0, "Server not found in database: %s: %s", 247 server_name, krb5_get_err_text(context, ret)); 248 make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, NULL); 249 goto out1; 250 } 251 252 ret = check_flags (client, client_name, 253 server, server_name, 254 TRUE); 255 if (ret) { 256 /* good error code? */ 257 make_err_reply(reply, KERB_ERR_NAME_EXP, NULL); 258 goto out1; 259 } 260 261 /* 262 * There's no way to do pre-authentication in v4 and thus no 263 * good error code to return if preauthentication is required. 264 */ 265 266 if (require_preauth 267 || client->flags.require_preauth 268 || server->flags.require_preauth) { 269 kdc_log(0, 270 "Pre-authentication required for v4-request: " 271 "%s for %s", 272 client_name, server_name); 273 make_err_reply(reply, KERB_ERR_NULL_KEY, NULL); 274 goto out1; 275 } 276 277 ret = get_des_key(client, FALSE, FALSE, &ckey); 278 if(ret){ 279 kdc_log(0, "no suitable DES key for client"); 280 make_err_reply(reply, KDC_NULL_KEY, 281 "no suitable DES key for client"); 282 goto out1; 283 } 284 285#if 0 286 /* this is not necessary with the new code in libkrb */ 287 /* find a properly salted key */ 288 while(ckey->salt == NULL || ckey->salt->salt.length != 0) 289 ret = hdb_next_keytype2key(context, client, KEYTYPE_DES, &ckey); 290 if(ret){ 291 kdc_log(0, "No version-4 salted key in database -- %s.%s@%s", 292 name, inst, realm); 293 make_err_reply(reply, KDC_NULL_KEY, 294 "No version-4 salted key in database"); 295 goto out1; 296 } 297#endif 298 299 ret = get_des_key(server, TRUE, FALSE, &skey); 300 if(ret){ 301 kdc_log(0, "no suitable DES key for server"); 302 /* XXX */ 303 make_err_reply(reply, KDC_NULL_KEY, 304 "no suitable DES key for server"); 305 goto out1; 306 } 307 308 max_life = krb_life_to_time(0, life); 309 if(client->max_life) 310 max_life = min(max_life, *client->max_life); 311 if(server->max_life) 312 max_life = min(max_life, *server->max_life); 313 314 life = krb_time_to_life(kdc_time, kdc_time + max_life); 315 316 { 317 KTEXT_ST cipher, ticket; 318 KTEXT r; 319 des_cblock session; 320 321 des_new_random_key(&session); 322 323 krb_create_ticket(&ticket, 0, name, inst, v4_realm, 324 addr->sin_addr.s_addr, session, life, kdc_time, 325 sname, sinst, skey->key.keyvalue.data); 326 327 create_ciph(&cipher, session, sname, sinst, v4_realm, 328 life, server->kvno, &ticket, kdc_time, 329 ckey->key.keyvalue.data); 330 memset(&session, 0, sizeof(session)); 331 r = create_auth_reply(name, inst, realm, req_time, 0, 332 client->pw_end ? *client->pw_end : 0, 333 client->kvno, &cipher); 334 krb5_data_copy(reply, r->dat, r->length); 335 memset(&cipher, 0, sizeof(cipher)); 336 memset(&ticket, 0, sizeof(ticket)); 337 } 338 out1: 339 break; 340 case AUTH_MSG_APPL_REQUEST: { 341 int8_t kvno; 342 int8_t ticket_len; 343 int8_t req_len; 344 KTEXT_ST auth; 345 AUTH_DAT ad; 346 size_t pos; 347 krb5_principal tgt_princ = NULL; 348 hdb_entry *tgt = NULL; 349 Key *tkey; 350 351 RCHECK(krb5_ret_int8(sp, &kvno), out2); 352 RCHECK(krb5_ret_stringz(sp, &realm), out2); 353 354 ret = krb5_425_conv_principal(context, "krbtgt", realm, v4_realm, 355 &tgt_princ); 356 if(ret){ 357 kdc_log(0, "Converting krbtgt principal: %s", 358 krb5_get_err_text(context, ret)); 359 make_err_reply(reply, KFAILURE, 360 "Failed to convert v4 principal (krbtgt)"); 361 goto out2; 362 } 363 364 ret = db_fetch(tgt_princ, &tgt); 365 if(ret){ 366 char *s; 367 s = kdc_log_msg(0, "Ticket-granting ticket not " 368 "found in database: krbtgt.%s@%s: %s", 369 realm, v4_realm, 370 krb5_get_err_text(context, ret)); 371 make_err_reply(reply, KFAILURE, s); 372 free(s); 373 goto out2; 374 } 375 376 if(tgt->kvno != kvno){ 377 kdc_log(0, "tgs-req with old kvno %d (current %d) for " 378 "krbtgt.%s@%s", kvno, tgt->kvno, realm, v4_realm); 379 make_err_reply(reply, KDC_AUTH_EXP, 380 "old krbtgt kvno used"); 381 goto out2; 382 } 383 384 ret = get_des_key(tgt, TRUE, FALSE, &tkey); 385 if(ret){ 386 kdc_log(0, "no suitable DES key for krbtgt"); 387 /* XXX */ 388 make_err_reply(reply, KDC_NULL_KEY, 389 "no suitable DES key for krbtgt"); 390 goto out2; 391 } 392 393 RCHECK(krb5_ret_int8(sp, &ticket_len), out2); 394 RCHECK(krb5_ret_int8(sp, &req_len), out2); 395 396 pos = sp->seek(sp, ticket_len + req_len, SEEK_CUR); 397 398 memset(&auth, 0, sizeof(auth)); 399 memcpy(&auth.dat, buf, pos); 400 auth.length = pos; 401 krb_set_key(tkey->key.keyvalue.data, 0); 402 403 krb_ignore_ip_address = !check_ticket_addresses; 404 405 ret = krb_rd_req(&auth, "krbtgt", realm, 406 addr->sin_addr.s_addr, &ad, 0); 407 if(ret){ 408 kdc_log(0, "krb_rd_req: %s", krb_get_err_text(ret)); 409 make_err_reply(reply, ret, NULL); 410 goto out2; 411 } 412 413 RCHECK(krb5_ret_int32(sp, &req_time), out2); 414 if(lsb) 415 req_time = swap32(req_time); 416 RCHECK(krb5_ret_int8(sp, &life), out2); 417 RCHECK(krb5_ret_stringz(sp, &sname), out2); 418 RCHECK(krb5_ret_stringz(sp, &sinst), out2); 419 snprintf (server_name, sizeof(server_name), 420 "%s.%s@%s", 421 sname, sinst, v4_realm); 422 423 kdc_log(0, "TGS-REQ %s.%s@%s from %s for %s", 424 ad.pname, ad.pinst, ad.prealm, from, server_name); 425 426 if(strcmp(ad.prealm, realm)){ 427 kdc_log(0, "Can't hop realms %s -> %s", realm, ad.prealm); 428 make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, 429 "Can't hop realms"); 430 goto out2; 431 } 432 433 if(strcmp(sname, "changepw") == 0){ 434 kdc_log(0, "Bad request for changepw ticket"); 435 make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, 436 "Can't authorize password change based on TGT"); 437 goto out2; 438 } 439 440#if 0 441 ret = db_fetch4(ad.pname, ad.pinst, ad.prealm, &client); 442 if(ret){ 443 char *s; 444 s = kdc_log_msg(0, "Client not found in database: %s.%s@%s: %s", 445 ad.pname, ad.pinst, ad.prealm, 446 krb5_get_err_text(context, ret)); 447 make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, s); 448 free(s); 449 goto out2; 450 } 451#endif 452 453 ret = db_fetch4(sname, sinst, v4_realm, &server); 454 if(ret){ 455 char *s; 456 s = kdc_log_msg(0, "Server not found in database: %s: %s", 457 server_name, krb5_get_err_text(context, ret)); 458 make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, s); 459 free(s); 460 goto out2; 461 } 462 463 ret = check_flags (NULL, NULL, 464 server, server_name, 465 FALSE); 466 if (ret) { 467 /* good error code? */ 468 make_err_reply(reply, KERB_ERR_NAME_EXP, NULL); 469 goto out2; 470 } 471 472 ret = get_des_key(server, TRUE, FALSE, &skey); 473 if(ret){ 474 kdc_log(0, "no suitable DES key for server"); 475 /* XXX */ 476 make_err_reply(reply, KDC_NULL_KEY, 477 "no suitable DES key for server"); 478 goto out2; 479 } 480 481 max_life = krb_life_to_time(ad.time_sec, ad.life); 482 max_life = min(max_life, krb_life_to_time(kdc_time, life)); 483 life = min(life, krb_time_to_life(kdc_time, max_life)); 484 max_life = krb_life_to_time(0, life); 485#if 0 486 if(client->max_life) 487 max_life = min(max_life, *client->max_life); 488#endif 489 if(server->max_life) 490 max_life = min(max_life, *server->max_life); 491 492 { 493 KTEXT_ST cipher, ticket; 494 KTEXT r; 495 des_cblock session; 496 des_new_random_key(&session); 497 krb_create_ticket(&ticket, 0, ad.pname, ad.pinst, ad.prealm, 498 addr->sin_addr.s_addr, &session, life, kdc_time, 499 sname, sinst, skey->key.keyvalue.data); 500 501 create_ciph(&cipher, session, sname, sinst, v4_realm, 502 life, server->kvno, &ticket, 503 kdc_time, &ad.session); 504 505 memset(&session, 0, sizeof(session)); 506 memset(ad.session, 0, sizeof(ad.session)); 507 508 r = create_auth_reply(ad.pname, ad.pinst, ad.prealm, 509 req_time, 0, 0, 0, &cipher); 510 krb5_data_copy(reply, r->dat, r->length); 511 memset(&cipher, 0, sizeof(cipher)); 512 memset(&ticket, 0, sizeof(ticket)); 513 } 514 out2: 515 if(tgt_princ) 516 krb5_free_principal(context, tgt_princ); 517 if(tgt) 518 free_ent(tgt); 519 break; 520 } 521 522 case AUTH_MSG_ERR_REPLY: 523 break; 524 default: 525 kdc_log(0, "Unknown message type: %d from %s", 526 msg_type, from); 527 528 make_err_reply(reply, KFAILURE, "Unknown message type"); 529 } 530out: 531 if(name) 532 free(name); 533 if(inst) 534 free(inst); 535 if(realm) 536 free(realm); 537 if(sname) 538 free(sname); 539 if(sinst) 540 free(sinst); 541 if(client) 542 free_ent(client); 543 if(server) 544 free_ent(server); 545 krb5_storage_free(sp); 546 return 0; 547} 548 549 550#define ETYPE_DES_PCBC 17 /* XXX */ 551 552krb5_error_code 553encrypt_v4_ticket(void *buf, size_t len, des_cblock *key, EncryptedData *reply) 554{ 555 des_key_schedule schedule; 556 557 reply->etype = ETYPE_DES_PCBC; 558 reply->kvno = NULL; 559 reply->cipher.length = len; 560 reply->cipher.data = malloc(len); 561 if(len != 0 && reply->cipher.data == NULL) 562 return ENOMEM; 563 des_set_key(key, schedule); 564 des_pcbc_encrypt(buf, 565 reply->cipher.data, 566 len, 567 schedule, 568 key, 569 DES_ENCRYPT); 570 memset(schedule, 0, sizeof(schedule)); 571 return 0; 572} 573 574krb5_error_code 575encode_v4_ticket(void *buf, size_t len, const EncTicketPart *et, 576 const PrincipalName *service, size_t *size) 577{ 578 krb5_storage *sp; 579 krb5_error_code ret; 580 char name[40], inst[40], realm[40]; 581 char sname[40], sinst[40]; 582 583 { 584 krb5_principal princ; 585 principalname2krb5_principal(&princ, 586 *service, 587 et->crealm); 588 ret = krb5_524_conv_principal(context, 589 princ, 590 sname, 591 sinst, 592 realm); 593 krb5_free_principal(context, princ); 594 if(ret) 595 return ret; 596 597 principalname2krb5_principal(&princ, 598 et->cname, 599 et->crealm); 600 601 ret = krb5_524_conv_principal(context, 602 princ, 603 name, 604 inst, 605 realm); 606 krb5_free_principal(context, princ); 607 } 608 if(ret) 609 return ret; 610 611 sp = krb5_storage_emem(); 612 613 krb5_store_int8(sp, 0); /* flags */ 614 krb5_store_stringz(sp, name); 615 krb5_store_stringz(sp, inst); 616 krb5_store_stringz(sp, realm); 617 { 618 unsigned char tmp[4] = { 0, 0, 0, 0 }; 619 int i; 620 if(et->caddr){ 621 for(i = 0; i < et->caddr->len; i++) 622 if(et->caddr->val[i].addr_type == AF_INET && 623 et->caddr->val[i].address.length == 4){ 624 memcpy(tmp, et->caddr->val[i].address.data, 4); 625 break; 626 } 627 } 628 sp->store(sp, tmp, sizeof(tmp)); 629 } 630 631 if((et->key.keytype != ETYPE_DES_CBC_MD5 && 632 et->key.keytype != ETYPE_DES_CBC_MD4 && 633 et->key.keytype != ETYPE_DES_CBC_CRC) || 634 et->key.keyvalue.length != 8) 635 return -1; 636 sp->store(sp, et->key.keyvalue.data, 8); 637 638 { 639 time_t start = et->starttime ? *et->starttime : et->authtime; 640 krb5_store_int8(sp, krb_time_to_life(start, et->endtime)); 641 krb5_store_int32(sp, start); 642 } 643 644 krb5_store_stringz(sp, sname); 645 krb5_store_stringz(sp, sinst); 646 647 { 648 krb5_data data; 649 krb5_storage_to_data(sp, &data); 650 krb5_storage_free(sp); 651 *size = (data.length + 7) & ~7; /* pad to 8 bytes */ 652 if(*size > len) 653 return -1; 654 memset((unsigned char*)buf - *size + 1, 0, *size); 655 memcpy((unsigned char*)buf - *size + 1, data.data, data.length); 656 krb5_data_free(&data); 657 } 658 return 0; 659} 660 661#endif /* KRB4 */ 662