1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * kdc/do_as_req.c 9 * 10 * Copyright 1990,1991 by the Massachusetts Institute of Technology. 11 * All Rights Reserved. 12 * 13 * Export of this software from the United States of America may 14 * require a specific license from the United States Government. 15 * It is the responsibility of any person or organization contemplating 16 * export to obtain such a license before exporting. 17 * 18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 19 * distribute this software and its documentation for any purpose and 20 * without fee is hereby granted, provided that the above copyright 21 * notice appear in all copies and that both that copyright notice and 22 * this permission notice appear in supporting documentation, and that 23 * the name of M.I.T. not be used in advertising or publicity pertaining 24 * to distribution of the software without specific, written prior 25 * permission. Furthermore if you modify this software you must label 26 * your software as modified software and not distribute it in such a 27 * fashion that it might be confused with the original M.I.T. software. 28 * M.I.T. makes no representations about the suitability of 29 * this software for any purpose. It is provided "as is" without express 30 * or implied warranty. 31 * 32 * 33 * KDC Routines to deal with AS_REQ's 34 */ 35 36 #define NEED_SOCKETS 37 #include "k5-int.h" 38 #include "com_err.h" 39 40 #include <syslog.h> 41 #ifdef HAVE_NETINET_IN_H 42 #include <sys/types.h> 43 #include <netinet/in.h> 44 #ifndef hpux 45 #include <arpa/inet.h> 46 #endif /* hpux */ 47 #endif /* HAVE_NETINET_IN_H */ 48 49 #include "kdc_util.h" 50 #include "policy.h" 51 #include "adm.h" 52 #include "adm_proto.h" 53 #include "extern.h" 54 55 static krb5_error_code prepare_error_as (krb5_kdc_req *, int, krb5_data *, 56 krb5_data **, const char *); 57 58 /*ARGSUSED*/ 59 krb5_error_code 60 process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, 61 const krb5_fulladdr *from, krb5_data **response) 62 { 63 krb5_db_entry client, server; 64 krb5_kdc_rep reply; 65 krb5_enc_kdc_rep_part reply_encpart; 66 krb5_ticket ticket_reply; 67 krb5_enc_tkt_part enc_tkt_reply; 68 krb5_error_code errcode; 69 int c_nprincs = 0, s_nprincs = 0; 70 krb5_boolean more; 71 krb5_timestamp kdc_time, authtime, etime = 0; 72 krb5_keyblock session_key; 73 krb5_keyblock encrypting_key; 74 const char *status; 75 krb5_key_data *server_key, *client_key; 76 krb5_enctype useenctype; 77 #ifdef KRBCONF_KDC_MODIFIES_KDB 78 krb5_boolean update_client = 0; 79 #endif /* KRBCONF_KDC_MODIFIES_KDB */ 80 krb5_data e_data; 81 register int i; 82 krb5_timestamp until, rtime; 83 long long tmp_client_times, tmp_server_times, tmp_realm_times; 84 char *cname = 0, *sname = 0; 85 const char *fromstring = 0; 86 char ktypestr[128]; 87 char rep_etypestr[128]; 88 char fromstringbuf[70]; 89 void *pa_context = NULL; 90 struct in_addr from_in4; /* IPv4 address of sender */ 91 92 ticket_reply.enc_part.ciphertext.data = 0; 93 e_data.data = 0; 94 (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock)); 95 reply.padata = 0; /* avoid bogus free in error_out */ 96 (void) memset(&session_key, 0, sizeof(krb5_keyblock)); 97 enc_tkt_reply.authorization_data = NULL; 98 99 ktypes2str(ktypestr, sizeof(ktypestr), 100 request->nktypes, request->ktype); 101 102 (void) memcpy(&from_in4, from->address->contents, /* SUNW */ 103 sizeof (struct in_addr)); 104 105 fromstring = inet_ntop(ADDRTYPE2FAMILY (from->address->addrtype), 106 &from_in4, 107 fromstringbuf, sizeof(fromstringbuf)); 108 if (!fromstring) 109 fromstring = "<unknown>"; 110 111 if (!request->client) { 112 status = "NULL_CLIENT"; 113 errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; 114 goto errout; 115 } 116 if ((errcode = krb5_unparse_name(kdc_context, request->client, &cname))) { 117 status = "UNPARSING_CLIENT"; 118 goto errout; 119 } 120 limit_string(cname); 121 if (!request->server) { 122 status = "NULL_SERVER"; 123 errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 124 goto errout; 125 } 126 if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) { 127 status = "UNPARSING_SERVER"; 128 goto errout; 129 } 130 limit_string(sname); 131 132 c_nprincs = 1; 133 if ((errcode = krb5_db_get_principal(kdc_context, request->client, 134 &client, &c_nprincs, &more))) { 135 status = "LOOKING_UP_CLIENT"; 136 c_nprincs = 0; 137 goto errout; 138 } 139 if (more) { 140 status = "NON-UNIQUE_CLIENT"; 141 errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; 142 goto errout; 143 } else if (c_nprincs != 1) { 144 status = "CLIENT_NOT_FOUND"; 145 #ifdef KRBCONF_VAGUE_ERRORS 146 errcode = KRB5KRB_ERR_GENERIC; 147 #else 148 errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; 149 #endif 150 goto errout; 151 } 152 153 s_nprincs = 1; 154 if ((errcode = krb5_db_get_principal(kdc_context, request->server, &server, 155 &s_nprincs, &more))) { 156 status = "LOOKING_UP_SERVER"; 157 goto errout; 158 } 159 if (more) { 160 status = "NON-UNIQUE_SERVER"; 161 errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; 162 goto errout; 163 } else if (s_nprincs != 1) { 164 status = "SERVER_NOT_FOUND"; 165 errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 166 goto errout; 167 } 168 169 if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { 170 status = "TIMEOFDAY"; 171 goto errout; 172 } 173 174 if ((errcode = validate_as_request(request, client, server, 175 kdc_time, &status))) { 176 if (!status) 177 status = "UNKNOWN_REASON"; 178 errcode += ERROR_TABLE_BASE_krb5; 179 goto errout; 180 } 181 182 /* 183 * Select the keytype for the ticket session key. 184 */ 185 if ((useenctype = select_session_keytype(kdc_context, &server, 186 request->nktypes, 187 request->ktype)) == 0) { 188 /* unsupported ktype */ 189 status = "BAD_ENCRYPTION_TYPE"; 190 errcode = KRB5KDC_ERR_ETYPE_NOSUPP; 191 goto errout; 192 } 193 194 if ((errcode = krb5_c_make_random_key(kdc_context, useenctype, 195 &session_key))) { 196 /* random key failed */ 197 status = "RANDOM_KEY_FAILED"; 198 goto errout; 199 } 200 201 ticket_reply.server = request->server; 202 203 enc_tkt_reply.flags = 0; 204 setflag(enc_tkt_reply.flags, TKT_FLG_INITIAL); 205 206 /* It should be noted that local policy may affect the */ 207 /* processing of any of these flags. For example, some */ 208 /* realms may refuse to issue renewable tickets */ 209 210 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) 211 setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); 212 213 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) 214 setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE); 215 216 if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) 217 setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); 218 219 enc_tkt_reply.session = &session_key; 220 enc_tkt_reply.client = request->client; 221 enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; 222 enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */ 223 224 enc_tkt_reply.times.authtime = kdc_time; 225 226 if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { 227 setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED); 228 setflag(enc_tkt_reply.flags, TKT_FLG_INVALID); 229 enc_tkt_reply.times.starttime = request->from; 230 } else 231 enc_tkt_reply.times.starttime = kdc_time; 232 233 until = (request->till == 0) ? kdc_infinity : request->till; 234 /* These numbers could easily be large 235 * use long long variables to ensure that they don't 236 * result in negative values when added. 237 */ 238 239 tmp_client_times = (long long) enc_tkt_reply.times.starttime + client.max_life; 240 241 tmp_server_times = (long long) enc_tkt_reply.times.starttime + server.max_life; 242 243 tmp_realm_times = (long long) enc_tkt_reply.times.starttime + max_life_for_realm; 244 245 enc_tkt_reply.times.endtime = 246 min(until, 247 min(tmp_client_times, 248 min(tmp_server_times, 249 min(tmp_realm_times,KRB5_KDB_EXPIRATION)))); 250 251 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && 252 !isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) && 253 (enc_tkt_reply.times.endtime < request->till)) { 254 255 /* we set the RENEWABLE option for later processing */ 256 257 setflag(request->kdc_options, KDC_OPT_RENEWABLE); 258 request->rtime = request->till; 259 } 260 rtime = (request->rtime == 0) ? kdc_infinity : request->rtime; 261 262 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) { 263 /* 264 * XXX Should we squelch the output renew_till to be no 265 * earlier than the endtime of the ticket? 266 */ 267 setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE); 268 tmp_client_times = (double) enc_tkt_reply.times.starttime + client.max_renewable_life; 269 270 tmp_server_times = (double) enc_tkt_reply.times.starttime + server.max_renewable_life; 271 272 tmp_realm_times = (double) enc_tkt_reply.times.starttime + max_renewable_life_for_realm; 273 274 enc_tkt_reply.times.renew_till = 275 min(rtime, min(tmp_client_times, 276 min(tmp_server_times, 277 min(tmp_realm_times,KRB5_KDB_EXPIRATION)))); 278 } else 279 enc_tkt_reply.times.renew_till = 0; /* XXX */ 280 281 /* starttime is optional, and treated as authtime if not present. 282 so we can nuke it if it matches */ 283 if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) 284 enc_tkt_reply.times.starttime = 0; 285 286 enc_tkt_reply.caddrs = request->addresses; 287 enc_tkt_reply.authorization_data = 0; 288 289 /* 290 * Check the preauthentication if it is there. 291 */ 292 if (request->padata) { 293 errcode = check_padata(kdc_context, &client, req_pkt, request, 294 &enc_tkt_reply, &pa_context, &e_data); 295 if (errcode) { 296 #ifdef KRBCONF_KDC_MODIFIES_KDB 297 /* 298 * Note: this doesn't work if you're using slave servers!!! 299 * It also causes the database to be modified (and thus 300 * need to be locked) frequently. 301 */ 302 if (client.fail_auth_count < KRB5_MAX_FAIL_COUNT) { 303 client.fail_auth_count = client.fail_auth_count + 1; 304 if (client.fail_auth_count == KRB5_MAX_FAIL_COUNT) { 305 client.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; 306 } 307 } 308 client.last_failed = kdc_time; 309 update_client = 1; 310 #endif 311 status = "PREAUTH_FAILED"; 312 #ifdef KRBCONF_VAGUE_ERRORS 313 errcode = KRB5KRB_ERR_GENERIC; 314 #endif 315 goto errout; 316 } 317 } 318 319 /* 320 * Final check before handing out ticket: If the client requires 321 * preauthentication, verify that the proper kind of 322 * preauthentication was carried out. 323 */ 324 status = missing_required_preauth(&client, &server, &enc_tkt_reply); 325 if (status) { 326 errcode = KRB5KDC_ERR_PREAUTH_REQUIRED; 327 get_preauth_hint_list(request, &client, &server, &e_data); 328 goto errout; 329 } 330 331 ticket_reply.enc_part2 = &enc_tkt_reply; 332 333 /* 334 * Find the server key 335 */ 336 if ((errcode = krb5_dbe_find_enctype(kdc_context, &server, 337 -1, /* ignore keytype */ 338 -1, /* Ignore salttype */ 339 0, /* Get highest kvno */ 340 &server_key))) { 341 status = "FINDING_SERVER_KEY"; 342 goto errout; 343 } 344 345 /* convert server.key into a real key (it may be encrypted 346 in the database) */ 347 if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, 348 server_key, &encrypting_key, 349 NULL))) { 350 status = "DECRYPT_SERVER_KEY"; 351 goto errout; 352 } 353 354 errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, &ticket_reply); 355 krb5_free_keyblock_contents(kdc_context, &encrypting_key); 356 encrypting_key.contents = 0; 357 if (errcode) { 358 status = "ENCRYPTING_TICKET"; 359 goto errout; 360 } 361 ticket_reply.enc_part.kvno = server_key->key_data_kvno; 362 363 /* 364 * Find the appropriate client key. We search in the order specified 365 * by request keytype list. 366 */ 367 client_key = (krb5_key_data *) NULL; 368 for (i = 0; i < request->nktypes; i++) { 369 useenctype = request->ktype[i]; 370 if (!krb5_c_valid_enctype(useenctype)) 371 continue; 372 373 if (!krb5_dbe_find_enctype(kdc_context, &client, useenctype, -1, 374 0, &client_key)) 375 break; 376 } 377 if (!(client_key)) { 378 /* Cannot find an appropriate key */ 379 status = "CANT_FIND_CLIENT_KEY"; 380 errcode = KRB5KDC_ERR_ETYPE_NOSUPP; 381 goto errout; 382 } 383 384 /* convert client.key_data into a real key */ 385 if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, 386 client_key, &encrypting_key, 387 NULL))) { 388 status = "DECRYPT_CLIENT_KEY"; 389 goto errout; 390 } 391 encrypting_key.enctype = useenctype; 392 393 /* Start assembling the response */ 394 reply.msg_type = KRB5_AS_REP; 395 reply.client = request->client; 396 reply.ticket = &ticket_reply; 397 reply_encpart.session = &session_key; 398 if ((errcode = fetch_last_req_info(&client, &reply_encpart.last_req))) { 399 status = "FETCH_LAST_REQ"; 400 goto errout; 401 } 402 reply_encpart.nonce = request->nonce; 403 404 /* 405 * Take the minimum of expiration or pw_expiration if not zero. 406 */ 407 if (client.expiration != 0 && client.pw_expiration != 0) 408 etime = min(client.expiration, client.pw_expiration); 409 else 410 etime = client.expiration ? client.expiration : client.pw_expiration; 411 412 reply_encpart.key_exp = etime; 413 reply_encpart.flags = enc_tkt_reply.flags; 414 reply_encpart.server = ticket_reply.server; 415 416 /* copy the time fields EXCEPT for authtime; it's location 417 is used for ktime */ 418 reply_encpart.times = enc_tkt_reply.times; 419 reply_encpart.times.authtime = authtime = kdc_time; 420 421 reply_encpart.caddrs = enc_tkt_reply.caddrs; 422 423 /* Fetch the padata info to be returned */ 424 errcode = return_padata(kdc_context, &client, req_pkt, request, 425 &reply, client_key, &encrypting_key, &pa_context); 426 if (errcode) { 427 status = "KDC_RETURN_PADATA"; 428 goto errout; 429 } 430 431 /* now encode/encrypt the response */ 432 433 reply.enc_part.enctype = encrypting_key.enctype; 434 435 errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, &reply_encpart, 436 0, &encrypting_key, &reply, response); 437 krb5_free_keyblock_contents(kdc_context, &encrypting_key); 438 encrypting_key.contents = 0; 439 reply.enc_part.kvno = client_key->key_data_kvno; 440 441 if (errcode) { 442 status = "ENCODE_KDC_REP"; 443 goto errout; 444 } 445 446 /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we 447 can use them in raw form if needed. But, we don't... */ 448 memset(reply.enc_part.ciphertext.data, 0, reply.enc_part.ciphertext.length); 449 free(reply.enc_part.ciphertext.data); 450 451 /* SUNW14resync: 452 * The third argument to audit_krb5kdc_as_req() is zero as the local 453 * portnumber is no longer passed to process_as_req(). 454 */ 455 audit_krb5kdc_as_req(&from_in4, (in_port_t)from->port, 0, 456 cname, sname, 0); 457 rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), &reply); 458 krb5_klog_syslog(LOG_INFO, 459 "AS_REQ (%s) %s: ISSUE: authtime %d, " 460 "%s, %s for %s", 461 ktypestr, 462 fromstring, authtime, 463 rep_etypestr, 464 cname, sname); 465 466 #ifdef KRBCONF_KDC_MODIFIES_KDB 467 /* 468 * If we get this far, we successfully did the AS_REQ. 469 */ 470 client.last_success = kdc_time; 471 client.fail_auth_count = 0; 472 update_client = 1; 473 #endif /* KRBCONF_KDC_MODIFIES_KDB */ 474 475 errout: 476 if (pa_context) 477 free_padata_context(kdc_context, &pa_context); 478 479 if (status) { 480 const char *emsg = NULL; 481 if (errcode) 482 emsg = krb5_get_error_message (kdc_context, errcode); 483 484 audit_krb5kdc_as_req(&from_in4, (in_port_t)from->port, 485 0, cname, sname, errcode); 486 krb5_klog_syslog(LOG_INFO, "AS_REQ (%s) %s: %s: %s for %s%s%s", 487 ktypestr, 488 fromstring, status, 489 cname ? cname : "<unknown client>", 490 sname ? sname : "<unknown server>", 491 errcode ? ", " : "", 492 errcode ? emsg : ""); 493 if (errcode) 494 krb5_free_error_message (kdc_context, emsg); 495 } 496 if (errcode) { 497 int got_err = 0; 498 if (status == 0) { 499 status = krb5_get_error_message (kdc_context, errcode); 500 got_err = 1; 501 } 502 errcode -= ERROR_TABLE_BASE_krb5; 503 if (errcode < 0 || errcode > 128) 504 errcode = KRB_ERR_GENERIC; 505 506 errcode = prepare_error_as(request, errcode, &e_data, response, 507 status); 508 if (got_err) { 509 krb5_free_error_message (kdc_context, status); 510 status = 0; 511 } 512 } 513 514 if (enc_tkt_reply.authorization_data != NULL) 515 krb5_free_authdata(kdc_context, enc_tkt_reply.authorization_data); 516 if (encrypting_key.contents) 517 krb5_free_keyblock_contents(kdc_context, &encrypting_key); 518 if (reply.padata) 519 krb5_free_pa_data(kdc_context, reply.padata); 520 521 if (cname) 522 free(cname); 523 if (sname) 524 free(sname); 525 if (c_nprincs) { 526 #ifdef KRBCONF_KDC_MODIFIES_KDB 527 if (update_client) { 528 krb5_db_put_principal(kdc_context, &client, &c_nprincs); 529 /* 530 * ptooey. We want krb5_db_sync() or something like that. 531 */ 532 krb5_db_fini(kdc_context); 533 if (kdc_active_realm->realm_dbname) 534 krb5_db_set_name(kdc_active_realm->realm_context, 535 kdc_active_realm->realm_dbname); 536 krb5_db_init(kdc_context); 537 /* Reset master key */ 538 krb5_db_set_mkey(kdc_context, &kdc_active_realm->realm_mkey); 539 } 540 #endif /* KRBCONF_KDC_MODIFIES_KDB */ 541 krb5_db_free_principal(kdc_context, &client, c_nprincs); 542 } 543 if (s_nprincs) 544 krb5_db_free_principal(kdc_context, &server, s_nprincs); 545 if (session_key.contents) 546 krb5_free_keyblock_contents(kdc_context, &session_key); 547 if (ticket_reply.enc_part.ciphertext.data) { 548 memset(ticket_reply.enc_part.ciphertext.data , 0, 549 ticket_reply.enc_part.ciphertext.length); 550 free(ticket_reply.enc_part.ciphertext.data); 551 } 552 553 krb5_free_data_contents(kdc_context, &e_data); 554 555 return errcode; 556 } 557 558 static krb5_error_code 559 prepare_error_as (krb5_kdc_req *request, int error, krb5_data *e_data, 560 krb5_data **response, const char *status) 561 { 562 krb5_error errpkt; 563 krb5_error_code retval; 564 krb5_data *scratch; 565 566 errpkt.ctime = request->nonce; 567 errpkt.cusec = 0; 568 569 if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime, 570 &errpkt.susec))) 571 return(retval); 572 errpkt.error = error; 573 errpkt.server = request->server; 574 errpkt.client = request->client; 575 errpkt.text.length = strlen(status)+1; 576 if (!(errpkt.text.data = malloc(errpkt.text.length))) 577 return ENOMEM; 578 (void) strcpy(errpkt.text.data, status); 579 580 if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) { 581 free(errpkt.text.data); 582 return ENOMEM; 583 } 584 if (e_data && e_data->data) { 585 errpkt.e_data = *e_data; 586 } else { 587 errpkt.e_data.length = 0; 588 errpkt.e_data.data = 0; 589 } 590 591 retval = krb5_mk_error(kdc_context, &errpkt, scratch); 592 free(errpkt.text.data); 593 if (retval) 594 free(scratch); 595 else 596 *response = scratch; 597 598 return retval; 599 } 600