1 /* 2 * Copyright 2005 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 #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 PROTOTYPE((krb5_kdc_req *, 56 int, 57 krb5_data *, 58 krb5_data **)); 59 60 /*ARGSUSED*/ 61 krb5_error_code 62 process_as_req(request, from, portnum, response) 63 register krb5_kdc_req *request; 64 const krb5_fulladdr *from; /* who sent it ? */ 65 int portnum; 66 krb5_data **response; /* filled in with a response packet */ 67 { 68 69 krb5_db_entry client, server; 70 krb5_kdc_rep reply; 71 krb5_enc_kdc_rep_part reply_encpart; 72 krb5_ticket ticket_reply; 73 krb5_enc_tkt_part enc_tkt_reply; 74 krb5_error_code errcode; 75 int c_nprincs = 0, s_nprincs = 0; 76 krb5_boolean more; 77 krb5_timestamp kdc_time, authtime; 78 krb5_keyblock session_key; 79 krb5_keyblock encrypting_key; 80 const char *status; 81 krb5_key_data *server_key, *client_key; 82 krb5_enctype useenctype; 83 #ifdef KRBCONF_KDC_MODIFIES_KDB 84 krb5_boolean update_client = 0; 85 #endif /* KRBCONF_KDC_MODIFIES_KDB */ 86 krb5_data e_data; 87 register int i; 88 krb5_timestamp until, rtime; 89 long long tmp_client_times, tmp_server_times, tmp_realm_times; 90 char *cname = 0, *sname = 0, *fromstring = 0; 91 struct in_addr from_in4; /* IPv4 address of sender */ 92 93 ticket_reply.enc_part.ciphertext.data = 0; 94 e_data.data = 0; 95 reply.padata = 0; /* avoid bogus free in error_out */ 96 (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock)); 97 (void) memset(&session_key, 0, sizeof(krb5_keyblock)); 98 99 #ifdef HAVE_NETINET_IN_H 100 if (from->address->addrtype == ADDRTYPE_INET) { 101 (void) memcpy(&from_in4, from->address->contents, /* SUNW */ 102 sizeof (struct in_addr)); 103 fromstring = inet_ntoa(from_in4); 104 } 105 #endif 106 if (!fromstring) 107 fromstring = "<unknown>"; 108 109 if (!request->client) { 110 status = "NULL_CLIENT"; 111 errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; 112 goto errout; 113 } 114 if ((errcode = krb5_unparse_name(kdc_context, request->client, &cname))) { 115 status = "UNPARSING_CLIENT"; 116 goto errout; 117 } 118 limit_string(cname); 119 if (!request->server) { 120 status = "NULL_SERVER"; 121 errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 122 goto errout; 123 } 124 if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) { 125 status = "UNPARSING_SERVER"; 126 goto errout; 127 } 128 limit_string(sname); 129 130 c_nprincs = 1; 131 if ((errcode = krb5_db_get_principal(kdc_context, request->client, 132 &client, &c_nprincs, &more))) { 133 status = "LOOKING_UP_CLIENT"; 134 c_nprincs = 0; 135 goto errout; 136 } 137 if (more) { 138 status = "NON-UNIQUE_CLIENT"; 139 errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; 140 goto errout; 141 } else if (c_nprincs != 1) { 142 status = "CLIENT_NOT_FOUND"; 143 #ifdef KRBCONF_VAGUE_ERRORS 144 errcode = KRB5KRB_ERR_GENERIC; 145 #else 146 errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; 147 #endif 148 goto errout; 149 } 150 151 s_nprincs = 1; 152 if ((errcode = krb5_db_get_principal(kdc_context, request->server, &server, 153 &s_nprincs, &more))) { 154 status = "LOOKING_UP_SERVER"; 155 goto errout; 156 } 157 if (more) { 158 status = "NON-UNIQUE_SERVER"; 159 errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; 160 goto errout; 161 } else if (s_nprincs != 1) { 162 status = "SERVER_NOT_FOUND"; 163 errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 164 goto errout; 165 } 166 167 if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { 168 status = "TIMEOFDAY"; 169 goto errout; 170 } 171 172 if ((errcode = validate_as_request(request, client, server, 173 kdc_time, &status))) { 174 if (!status) 175 status = "UNKNOWN_REASON"; 176 errcode += ERROR_TABLE_BASE_krb5; 177 goto errout; 178 } 179 180 /* 181 * Select the keytype for the ticket session key. 182 */ 183 if ((useenctype = select_session_keytype(kdc_context, &server, 184 request->nktypes, 185 request->ktype)) == 0) { 186 /* unsupported ktype */ 187 status = "BAD_ENCRYPTION_TYPE"; 188 errcode = KRB5KDC_ERR_ETYPE_NOSUPP; 189 goto errout; 190 } 191 192 if ((errcode = krb5_c_make_random_key(kdc_context, useenctype, 193 &session_key))) { 194 /* random key failed */ 195 status = "RANDOM_KEY_FAILED"; 196 goto errout; 197 } 198 199 ticket_reply.server = request->server; 200 201 enc_tkt_reply.flags = 0; 202 setflag(enc_tkt_reply.flags, TKT_FLG_INITIAL); 203 204 /* It should be noted that local policy may affect the */ 205 /* processing of any of these flags. For example, some */ 206 /* realms may refuse to issue renewable tickets */ 207 208 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) 209 setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); 210 211 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) 212 setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE); 213 214 if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) 215 setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); 216 217 enc_tkt_reply.session = &session_key; 218 enc_tkt_reply.client = request->client; 219 enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; 220 enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */ 221 222 enc_tkt_reply.times.authtime = kdc_time; 223 224 if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { 225 setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED); 226 setflag(enc_tkt_reply.flags, TKT_FLG_INVALID); 227 enc_tkt_reply.times.starttime = request->from; 228 } else 229 enc_tkt_reply.times.starttime = kdc_time; 230 231 until = (request->till == 0) ? kdc_infinity : request->till; 232 /* These numbers could easily be large 233 * use long long variables to ensure that they don't 234 * result in negative values when added. 235 */ 236 237 tmp_client_times = (long long) enc_tkt_reply.times.starttime + client.max_life; 238 239 tmp_server_times = (long long) enc_tkt_reply.times.starttime + server.max_life; 240 241 tmp_realm_times = (long long) enc_tkt_reply.times.starttime + max_life_for_realm; 242 243 enc_tkt_reply.times.endtime = 244 min(until, 245 min(tmp_client_times, 246 min(tmp_server_times, 247 min(tmp_realm_times,KRB5_KDB_EXPIRATION)))); 248 249 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && 250 !isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) && 251 (enc_tkt_reply.times.endtime < request->till)) { 252 253 /* we set the RENEWABLE option for later processing */ 254 255 setflag(request->kdc_options, KDC_OPT_RENEWABLE); 256 request->rtime = request->till; 257 } 258 rtime = (request->rtime == 0) ? kdc_infinity : request->rtime; 259 260 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) { 261 /* 262 * XXX Should we squelch the output renew_till to be no 263 * earlier than the endtime of the ticket? 264 */ 265 setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE); 266 tmp_client_times = (double) enc_tkt_reply.times.starttime + client.max_renewable_life; 267 268 tmp_server_times = (double) enc_tkt_reply.times.starttime + server.max_renewable_life; 269 270 tmp_realm_times = (double) enc_tkt_reply.times.starttime + max_renewable_life_for_realm; 271 272 enc_tkt_reply.times.renew_till = 273 min(rtime, min(tmp_client_times, 274 min(tmp_server_times, 275 min(tmp_realm_times,KRB5_KDB_EXPIRATION)))); 276 } else 277 enc_tkt_reply.times.renew_till = 0; /* XXX */ 278 279 /* starttime is optional, and treated as authtime if not present. 280 so we can nuke it if it matches */ 281 if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) 282 enc_tkt_reply.times.starttime = 0; 283 284 enc_tkt_reply.caddrs = request->addresses; 285 enc_tkt_reply.authorization_data = 0; 286 287 /* 288 * Check the preauthentication if it is there. 289 */ 290 if (request->padata) { 291 errcode = check_padata(kdc_context, &client, request, &enc_tkt_reply); 292 if (errcode) { 293 #ifdef KRBCONF_KDC_MODIFIES_KDB 294 /* 295 * Note: this doesn't work if you're using slave servers!!! 296 * It also causes the database to be modified (and thus 297 * need to be locked) frequently. 298 */ 299 if (client.fail_auth_count < KRB5_MAX_FAIL_COUNT) { 300 client.fail_auth_count = client.fail_auth_count + 1; 301 if (client.fail_auth_count == KRB5_MAX_FAIL_COUNT) { 302 client.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; 303 } 304 } 305 client.last_failed = kdc_time; 306 update_client = 1; 307 #endif 308 status = "PREAUTH_FAILED"; 309 #ifdef KRBCONF_VAGUE_ERRORS 310 errcode = KRB5KRB_ERR_GENERIC; 311 #endif 312 goto errout; 313 } 314 } 315 316 /* 317 * Final check before handing out ticket: If the client requires 318 * preauthentication, verify that the proper kind of 319 * preauthentication was carried out. 320 */ 321 status = missing_required_preauth(&client, &server, &enc_tkt_reply); 322 if (status) { 323 errcode = KRB5KDC_ERR_PREAUTH_REQUIRED; 324 get_preauth_hint_list(request, &client, &server, &e_data); 325 goto errout; 326 } 327 328 ticket_reply.enc_part2 = &enc_tkt_reply; 329 330 /* 331 * Find the server key 332 */ 333 if ((errcode = krb5_dbe_find_enctype(kdc_context, &server, 334 -1, /* ignore keytype */ 335 -1, /* Ignore salttype */ 336 0, /* Get highest kvno */ 337 &server_key))) { 338 status = "FINDING_SERVER_KEY"; 339 goto errout; 340 } 341 342 /* convert server.key into a real key (it may be encrypted 343 in the database) */ 344 if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, 345 server_key, &encrypting_key, 346 NULL))) { 347 status = "DECRYPT_SERVER_KEY"; 348 goto errout; 349 } 350 if ((encrypting_key.enctype == ENCTYPE_DES_CBC_CRC) && 351 (isflagset(server.attributes, KRB5_KDB_SUPPORT_DESMD5))) 352 encrypting_key.enctype = ENCTYPE_DES_CBC_MD5; 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 (!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 reply_encpart.key_exp = client.expiration; 404 reply_encpart.flags = enc_tkt_reply.flags; 405 reply_encpart.server = ticket_reply.server; 406 407 /* copy the time fields EXCEPT for authtime; it's location 408 is used for ktime */ 409 reply_encpart.times = enc_tkt_reply.times; 410 reply_encpart.times.authtime = authtime = kdc_time; 411 412 reply_encpart.caddrs = enc_tkt_reply.caddrs; 413 414 /* Fetch the padata info to be returned */ 415 errcode = return_padata(kdc_context, &client, request, &reply, client_key, 416 &encrypting_key); 417 if (errcode) { 418 status = "KDC_RETURN_PADATA"; 419 goto errout; 420 } 421 422 /* now encode/encrypt the response */ 423 424 reply.enc_part.enctype = encrypting_key.enctype; 425 426 errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, &reply_encpart, 427 0, &encrypting_key, &reply, response); 428 krb5_free_keyblock_contents(kdc_context, &encrypting_key); 429 encrypting_key.contents = 0; 430 reply.enc_part.kvno = client_key->key_data_kvno; 431 432 if (errcode) { 433 status = "ENCODE_KDC_REP"; 434 goto errout; 435 } 436 437 /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we 438 can use them in raw form if needed. But, we don't... */ 439 memset(reply.enc_part.ciphertext.data, 0, reply.enc_part.ciphertext.length); 440 free(reply.enc_part.ciphertext.data); 441 442 audit_krb5kdc_as_req(&from_in4, (in_port_t)from->port, (in_port_t)portnum, 443 cname, sname, 0); 444 445 krb5_klog_syslog(LOG_INFO, "AS_REQ %s(%d): ISSUE: authtime %d, %s for %s", 446 fromstring, portnum, authtime, cname, sname); 447 448 #ifdef KRBCONF_KDC_MODIFIES_KDB 449 /* 450 * If we get this far, we successfully did the AS_REQ. 451 */ 452 client.last_success = kdc_time; 453 client.fail_auth_count = 0; 454 update_client = 1; 455 #endif /* KRBCONF_KDC_MODIFIES_KDB */ 456 457 errout: 458 if (status) { 459 audit_krb5kdc_as_req(&from_in4, (in_port_t)from->port, 460 (in_port_t)portnum, cname, sname, errcode); 461 krb5_klog_syslog(LOG_INFO, "AS_REQ %s(%d): %s: %s for %s%s%s", 462 fromstring, portnum, status, 463 cname ? cname : "<unknown client>", 464 sname ? sname : "<unknown server>", 465 errcode ? ", " : "", 466 errcode ? error_message(errcode) : ""); 467 } 468 if (errcode) { 469 errcode -= ERROR_TABLE_BASE_krb5; 470 if (errcode < 0 || errcode > 128) 471 errcode = KRB_ERR_GENERIC; 472 473 errcode = prepare_error_as(request, errcode, &e_data, response); 474 } 475 476 krb5_free_keyblock_contents(kdc_context, &encrypting_key); 477 478 if (reply.padata) 479 krb5_free_pa_data(kdc_context, reply.padata); 480 481 if (cname) 482 free(cname); 483 if (sname) 484 free(sname); 485 if (c_nprincs) { 486 #ifdef KRBCONF_KDC_MODIFIES_KDB 487 if (update_client) { 488 krb5_db_put_principal(kdc_context, &client, &c_nprincs); 489 /* 490 * ptooey. We want krb5_db_sync() or something like that. 491 */ 492 krb5_db_fini(kdc_context); 493 if (kdc_active_realm->realm_dbname) 494 krb5_db_set_name(kdc_active_realm->realm_context, 495 kdc_active_realm->realm_dbname); 496 krb5_db_init(kdc_context); 497 /* Reset master key */ 498 krb5_db_set_mkey(kdc_context, &kdc_active_realm->realm_encblock); 499 } 500 #endif /* KRBCONF_KDC_MODIFIES_KDB */ 501 krb5_db_free_principal(kdc_context, &client, c_nprincs); 502 } 503 if (s_nprincs) 504 krb5_db_free_principal(kdc_context, &server, s_nprincs); 505 if (session_key.contents) 506 krb5_free_keyblock_contents(kdc_context, &session_key); 507 if (ticket_reply.enc_part.ciphertext.data) { 508 memset(ticket_reply.enc_part.ciphertext.data , 0, 509 ticket_reply.enc_part.ciphertext.length); 510 free(ticket_reply.enc_part.ciphertext.data); 511 } 512 513 krb5_free_data_contents(kdc_context, &e_data); 514 515 return errcode; 516 } 517 518 static krb5_error_code 519 prepare_error_as (request, error, e_data, response) 520 register krb5_kdc_req *request; 521 int error; 522 krb5_data *e_data; 523 krb5_data **response; 524 { 525 krb5_error errpkt; 526 krb5_error_code retval; 527 krb5_data *scratch; 528 529 errpkt.ctime = request->nonce; 530 errpkt.cusec = 0; 531 532 if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime, 533 &errpkt.susec))) 534 return(retval); 535 errpkt.error = error; 536 errpkt.server = request->server; 537 errpkt.client = request->client; 538 errpkt.text.length = strlen(error_message(error+KRB5KDC_ERR_NONE))+1; 539 if (!(errpkt.text.data = malloc(errpkt.text.length))) 540 return ENOMEM; 541 (void) strcpy(errpkt.text.data, error_message(error+KRB5KDC_ERR_NONE)); 542 543 if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) { 544 free(errpkt.text.data); 545 return ENOMEM; 546 } 547 if (e_data && e_data->data) { 548 errpkt.e_data = *e_data; 549 } else { 550 errpkt.e_data.length = 0; 551 errpkt.e_data.data = 0; 552 } 553 554 retval = krb5_mk_error(kdc_context, &errpkt, scratch); 555 free(errpkt.text.data); 556 if (retval) 557 free(scratch); 558 else 559 *response = scratch; 560 561 return retval; 562 } 563