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