10Sstevel@tonic-gate /* 2*107Sbugbomb * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 30Sstevel@tonic-gate * Use is subject to license terms. 40Sstevel@tonic-gate */ 50Sstevel@tonic-gate 60Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 70Sstevel@tonic-gate 80Sstevel@tonic-gate /* 90Sstevel@tonic-gate * kdc/do_as_req.c 100Sstevel@tonic-gate * 110Sstevel@tonic-gate * Copyright 1990,1991 by the Massachusetts Institute of Technology. 120Sstevel@tonic-gate * All Rights Reserved. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * Export of this software from the United States of America may 150Sstevel@tonic-gate * require a specific license from the United States Government. 160Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating 170Sstevel@tonic-gate * export to obtain such a license before exporting. 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 200Sstevel@tonic-gate * distribute this software and its documentation for any purpose and 210Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright 220Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and 230Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that 240Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining 250Sstevel@tonic-gate * to distribution of the software without specific, written prior 260Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label 270Sstevel@tonic-gate * your software as modified software and not distribute it in such a 280Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software. 290Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of 300Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express 310Sstevel@tonic-gate * or implied warranty. 320Sstevel@tonic-gate * 330Sstevel@tonic-gate * 340Sstevel@tonic-gate * KDC Routines to deal with AS_REQ's 350Sstevel@tonic-gate */ 360Sstevel@tonic-gate 370Sstevel@tonic-gate #include "k5-int.h" 380Sstevel@tonic-gate #include "com_err.h" 390Sstevel@tonic-gate 400Sstevel@tonic-gate #include <syslog.h> 410Sstevel@tonic-gate #ifdef HAVE_NETINET_IN_H 420Sstevel@tonic-gate #include <sys/types.h> 430Sstevel@tonic-gate #include <netinet/in.h> 440Sstevel@tonic-gate #ifndef hpux 450Sstevel@tonic-gate #include <arpa/inet.h> 460Sstevel@tonic-gate #endif /* hpux */ 470Sstevel@tonic-gate #endif /* HAVE_NETINET_IN_H */ 480Sstevel@tonic-gate 490Sstevel@tonic-gate #include "kdc_util.h" 500Sstevel@tonic-gate #include "policy.h" 510Sstevel@tonic-gate #include "adm.h" 520Sstevel@tonic-gate #include "adm_proto.h" 530Sstevel@tonic-gate #include "extern.h" 540Sstevel@tonic-gate 550Sstevel@tonic-gate static krb5_error_code prepare_error_as PROTOTYPE((krb5_kdc_req *, 560Sstevel@tonic-gate int, 570Sstevel@tonic-gate krb5_data *, 580Sstevel@tonic-gate krb5_data **)); 590Sstevel@tonic-gate 600Sstevel@tonic-gate /*ARGSUSED*/ 610Sstevel@tonic-gate krb5_error_code 620Sstevel@tonic-gate process_as_req(request, from, portnum, response) 630Sstevel@tonic-gate register krb5_kdc_req *request; 640Sstevel@tonic-gate const krb5_fulladdr *from; /* who sent it ? */ 650Sstevel@tonic-gate int portnum; 660Sstevel@tonic-gate krb5_data **response; /* filled in with a response packet */ 670Sstevel@tonic-gate { 680Sstevel@tonic-gate 690Sstevel@tonic-gate krb5_db_entry client, server; 700Sstevel@tonic-gate krb5_kdc_rep reply; 710Sstevel@tonic-gate krb5_enc_kdc_rep_part reply_encpart; 720Sstevel@tonic-gate krb5_ticket ticket_reply; 730Sstevel@tonic-gate krb5_enc_tkt_part enc_tkt_reply; 740Sstevel@tonic-gate krb5_error_code errcode; 750Sstevel@tonic-gate int c_nprincs = 0, s_nprincs = 0; 760Sstevel@tonic-gate krb5_boolean more; 770Sstevel@tonic-gate krb5_timestamp kdc_time, authtime; 780Sstevel@tonic-gate krb5_keyblock session_key; 790Sstevel@tonic-gate krb5_keyblock encrypting_key; 800Sstevel@tonic-gate const char *status; 810Sstevel@tonic-gate krb5_key_data *server_key, *client_key; 820Sstevel@tonic-gate krb5_enctype useenctype; 830Sstevel@tonic-gate #ifdef KRBCONF_KDC_MODIFIES_KDB 840Sstevel@tonic-gate krb5_boolean update_client = 0; 850Sstevel@tonic-gate #endif /* KRBCONF_KDC_MODIFIES_KDB */ 860Sstevel@tonic-gate krb5_data e_data; 870Sstevel@tonic-gate register int i; 880Sstevel@tonic-gate krb5_timestamp until, rtime; 890Sstevel@tonic-gate long long tmp_client_times, tmp_server_times, tmp_realm_times; 900Sstevel@tonic-gate char *cname = 0, *sname = 0, *fromstring = 0; 910Sstevel@tonic-gate struct in_addr from_in4; /* IPv4 address of sender */ 920Sstevel@tonic-gate 930Sstevel@tonic-gate ticket_reply.enc_part.ciphertext.data = 0; 940Sstevel@tonic-gate e_data.data = 0; 950Sstevel@tonic-gate reply.padata = 0; /* avoid bogus free in error_out */ 960Sstevel@tonic-gate (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock)); 970Sstevel@tonic-gate (void) memset(&session_key, 0, sizeof(krb5_keyblock)); 980Sstevel@tonic-gate 990Sstevel@tonic-gate #ifdef HAVE_NETINET_IN_H 1000Sstevel@tonic-gate if (from->address->addrtype == ADDRTYPE_INET) { 1010Sstevel@tonic-gate (void) memcpy(&from_in4, from->address->contents, /* SUNW */ 1020Sstevel@tonic-gate sizeof (struct in_addr)); 1030Sstevel@tonic-gate fromstring = inet_ntoa(from_in4); 1040Sstevel@tonic-gate } 1050Sstevel@tonic-gate #endif 1060Sstevel@tonic-gate if (!fromstring) 1070Sstevel@tonic-gate fromstring = "<unknown>"; 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate if (!request->client) { 1100Sstevel@tonic-gate status = "NULL_CLIENT"; 1110Sstevel@tonic-gate errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; 1120Sstevel@tonic-gate goto errout; 1130Sstevel@tonic-gate } 1140Sstevel@tonic-gate if ((errcode = krb5_unparse_name(kdc_context, request->client, &cname))) { 1150Sstevel@tonic-gate status = "UNPARSING_CLIENT"; 1160Sstevel@tonic-gate goto errout; 1170Sstevel@tonic-gate } 1180Sstevel@tonic-gate limit_string(cname); 1190Sstevel@tonic-gate if (!request->server) { 1200Sstevel@tonic-gate status = "NULL_SERVER"; 1210Sstevel@tonic-gate errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 1220Sstevel@tonic-gate goto errout; 1230Sstevel@tonic-gate } 1240Sstevel@tonic-gate if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) { 1250Sstevel@tonic-gate status = "UNPARSING_SERVER"; 1260Sstevel@tonic-gate goto errout; 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate limit_string(sname); 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate c_nprincs = 1; 1310Sstevel@tonic-gate if ((errcode = krb5_db_get_principal(kdc_context, request->client, 1320Sstevel@tonic-gate &client, &c_nprincs, &more))) { 1330Sstevel@tonic-gate status = "LOOKING_UP_CLIENT"; 1340Sstevel@tonic-gate c_nprincs = 0; 1350Sstevel@tonic-gate goto errout; 1360Sstevel@tonic-gate } 1370Sstevel@tonic-gate if (more) { 1380Sstevel@tonic-gate status = "NON-UNIQUE_CLIENT"; 1390Sstevel@tonic-gate errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; 1400Sstevel@tonic-gate goto errout; 1410Sstevel@tonic-gate } else if (c_nprincs != 1) { 1420Sstevel@tonic-gate status = "CLIENT_NOT_FOUND"; 1430Sstevel@tonic-gate #ifdef KRBCONF_VAGUE_ERRORS 1440Sstevel@tonic-gate errcode = KRB5KRB_ERR_GENERIC; 1450Sstevel@tonic-gate #else 1460Sstevel@tonic-gate errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; 1470Sstevel@tonic-gate #endif 1480Sstevel@tonic-gate goto errout; 1490Sstevel@tonic-gate } 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate s_nprincs = 1; 1520Sstevel@tonic-gate if ((errcode = krb5_db_get_principal(kdc_context, request->server, &server, 1530Sstevel@tonic-gate &s_nprincs, &more))) { 1540Sstevel@tonic-gate status = "LOOKING_UP_SERVER"; 1550Sstevel@tonic-gate goto errout; 1560Sstevel@tonic-gate } 1570Sstevel@tonic-gate if (more) { 1580Sstevel@tonic-gate status = "NON-UNIQUE_SERVER"; 1590Sstevel@tonic-gate errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; 1600Sstevel@tonic-gate goto errout; 1610Sstevel@tonic-gate } else if (s_nprincs != 1) { 1620Sstevel@tonic-gate status = "SERVER_NOT_FOUND"; 1630Sstevel@tonic-gate errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 1640Sstevel@tonic-gate goto errout; 1650Sstevel@tonic-gate } 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { 1680Sstevel@tonic-gate status = "TIMEOFDAY"; 1690Sstevel@tonic-gate goto errout; 1700Sstevel@tonic-gate } 1710Sstevel@tonic-gate 1720Sstevel@tonic-gate if ((errcode = validate_as_request(request, client, server, 1730Sstevel@tonic-gate kdc_time, &status))) { 1740Sstevel@tonic-gate if (!status) 1750Sstevel@tonic-gate status = "UNKNOWN_REASON"; 1760Sstevel@tonic-gate errcode += ERROR_TABLE_BASE_krb5; 1770Sstevel@tonic-gate goto errout; 1780Sstevel@tonic-gate } 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate /* 1810Sstevel@tonic-gate * Select the keytype for the ticket session key. 1820Sstevel@tonic-gate */ 1830Sstevel@tonic-gate if ((useenctype = select_session_keytype(kdc_context, &server, 1840Sstevel@tonic-gate request->nktypes, 1850Sstevel@tonic-gate request->ktype)) == 0) { 1860Sstevel@tonic-gate /* unsupported ktype */ 1870Sstevel@tonic-gate status = "BAD_ENCRYPTION_TYPE"; 1880Sstevel@tonic-gate errcode = KRB5KDC_ERR_ETYPE_NOSUPP; 1890Sstevel@tonic-gate goto errout; 1900Sstevel@tonic-gate } 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate if ((errcode = krb5_c_make_random_key(kdc_context, useenctype, 1930Sstevel@tonic-gate &session_key))) { 1940Sstevel@tonic-gate /* random key failed */ 1950Sstevel@tonic-gate status = "RANDOM_KEY_FAILED"; 1960Sstevel@tonic-gate goto errout; 1970Sstevel@tonic-gate } 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate ticket_reply.server = request->server; 2000Sstevel@tonic-gate 2010Sstevel@tonic-gate enc_tkt_reply.flags = 0; 2020Sstevel@tonic-gate setflag(enc_tkt_reply.flags, TKT_FLG_INITIAL); 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate /* It should be noted that local policy may affect the */ 2050Sstevel@tonic-gate /* processing of any of these flags. For example, some */ 2060Sstevel@tonic-gate /* realms may refuse to issue renewable tickets */ 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) 2090Sstevel@tonic-gate setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) 2120Sstevel@tonic-gate setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE); 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) 2150Sstevel@tonic-gate setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate enc_tkt_reply.session = &session_key; 2180Sstevel@tonic-gate enc_tkt_reply.client = request->client; 2190Sstevel@tonic-gate enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; 2200Sstevel@tonic-gate enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */ 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate enc_tkt_reply.times.authtime = kdc_time; 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { 2250Sstevel@tonic-gate setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED); 2260Sstevel@tonic-gate setflag(enc_tkt_reply.flags, TKT_FLG_INVALID); 2270Sstevel@tonic-gate enc_tkt_reply.times.starttime = request->from; 2280Sstevel@tonic-gate } else 2290Sstevel@tonic-gate enc_tkt_reply.times.starttime = kdc_time; 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate until = (request->till == 0) ? kdc_infinity : request->till; 2320Sstevel@tonic-gate /* These numbers could easily be large 2330Sstevel@tonic-gate * use long long variables to ensure that they don't 2340Sstevel@tonic-gate * result in negative values when added. 2350Sstevel@tonic-gate */ 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate tmp_client_times = (long long) enc_tkt_reply.times.starttime + client.max_life; 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate tmp_server_times = (long long) enc_tkt_reply.times.starttime + server.max_life; 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate tmp_realm_times = (long long) enc_tkt_reply.times.starttime + max_life_for_realm; 2420Sstevel@tonic-gate 2430Sstevel@tonic-gate enc_tkt_reply.times.endtime = 2440Sstevel@tonic-gate min(until, 2450Sstevel@tonic-gate min(tmp_client_times, 2460Sstevel@tonic-gate min(tmp_server_times, 2470Sstevel@tonic-gate min(tmp_realm_times,KRB5_KDB_EXPIRATION)))); 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && 2500Sstevel@tonic-gate !isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) && 2510Sstevel@tonic-gate (enc_tkt_reply.times.endtime < request->till)) { 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate /* we set the RENEWABLE option for later processing */ 2540Sstevel@tonic-gate 2550Sstevel@tonic-gate setflag(request->kdc_options, KDC_OPT_RENEWABLE); 2560Sstevel@tonic-gate request->rtime = request->till; 2570Sstevel@tonic-gate } 2580Sstevel@tonic-gate rtime = (request->rtime == 0) ? kdc_infinity : request->rtime; 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) { 2610Sstevel@tonic-gate /* 2620Sstevel@tonic-gate * XXX Should we squelch the output renew_till to be no 2630Sstevel@tonic-gate * earlier than the endtime of the ticket? 2640Sstevel@tonic-gate */ 2650Sstevel@tonic-gate setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE); 2660Sstevel@tonic-gate tmp_client_times = (double) enc_tkt_reply.times.starttime + client.max_renewable_life; 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate tmp_server_times = (double) enc_tkt_reply.times.starttime + server.max_renewable_life; 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate tmp_realm_times = (double) enc_tkt_reply.times.starttime + max_renewable_life_for_realm; 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate enc_tkt_reply.times.renew_till = 2730Sstevel@tonic-gate min(rtime, min(tmp_client_times, 2740Sstevel@tonic-gate min(tmp_server_times, 2750Sstevel@tonic-gate min(tmp_realm_times,KRB5_KDB_EXPIRATION)))); 2760Sstevel@tonic-gate } else 2770Sstevel@tonic-gate enc_tkt_reply.times.renew_till = 0; /* XXX */ 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate /* starttime is optional, and treated as authtime if not present. 2800Sstevel@tonic-gate so we can nuke it if it matches */ 2810Sstevel@tonic-gate if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) 2820Sstevel@tonic-gate enc_tkt_reply.times.starttime = 0; 2830Sstevel@tonic-gate 2840Sstevel@tonic-gate enc_tkt_reply.caddrs = request->addresses; 2850Sstevel@tonic-gate enc_tkt_reply.authorization_data = 0; 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate /* 2880Sstevel@tonic-gate * Check the preauthentication if it is there. 2890Sstevel@tonic-gate */ 2900Sstevel@tonic-gate if (request->padata) { 2910Sstevel@tonic-gate errcode = check_padata(kdc_context, &client, request, &enc_tkt_reply); 2920Sstevel@tonic-gate if (errcode) { 2930Sstevel@tonic-gate #ifdef KRBCONF_KDC_MODIFIES_KDB 2940Sstevel@tonic-gate /* 2950Sstevel@tonic-gate * Note: this doesn't work if you're using slave servers!!! 2960Sstevel@tonic-gate * It also causes the database to be modified (and thus 2970Sstevel@tonic-gate * need to be locked) frequently. 2980Sstevel@tonic-gate */ 2990Sstevel@tonic-gate if (client.fail_auth_count < KRB5_MAX_FAIL_COUNT) { 3000Sstevel@tonic-gate client.fail_auth_count = client.fail_auth_count + 1; 3010Sstevel@tonic-gate if (client.fail_auth_count == KRB5_MAX_FAIL_COUNT) { 3020Sstevel@tonic-gate client.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate } 3050Sstevel@tonic-gate client.last_failed = kdc_time; 3060Sstevel@tonic-gate update_client = 1; 3070Sstevel@tonic-gate #endif 3080Sstevel@tonic-gate status = "PREAUTH_FAILED"; 3090Sstevel@tonic-gate #ifdef KRBCONF_VAGUE_ERRORS 3100Sstevel@tonic-gate errcode = KRB5KRB_ERR_GENERIC; 3110Sstevel@tonic-gate #endif 3120Sstevel@tonic-gate goto errout; 3130Sstevel@tonic-gate } 3140Sstevel@tonic-gate } 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate /* 3170Sstevel@tonic-gate * Final check before handing out ticket: If the client requires 3180Sstevel@tonic-gate * preauthentication, verify that the proper kind of 3190Sstevel@tonic-gate * preauthentication was carried out. 3200Sstevel@tonic-gate */ 3210Sstevel@tonic-gate status = missing_required_preauth(&client, &server, &enc_tkt_reply); 3220Sstevel@tonic-gate if (status) { 3230Sstevel@tonic-gate errcode = KRB5KDC_ERR_PREAUTH_REQUIRED; 3240Sstevel@tonic-gate get_preauth_hint_list(request, &client, &server, &e_data); 3250Sstevel@tonic-gate goto errout; 3260Sstevel@tonic-gate } 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate ticket_reply.enc_part2 = &enc_tkt_reply; 3290Sstevel@tonic-gate 3300Sstevel@tonic-gate /* 3310Sstevel@tonic-gate * Find the server key 3320Sstevel@tonic-gate */ 3330Sstevel@tonic-gate if ((errcode = krb5_dbe_find_enctype(kdc_context, &server, 3340Sstevel@tonic-gate -1, /* ignore keytype */ 3350Sstevel@tonic-gate -1, /* Ignore salttype */ 3360Sstevel@tonic-gate 0, /* Get highest kvno */ 3370Sstevel@tonic-gate &server_key))) { 3380Sstevel@tonic-gate status = "FINDING_SERVER_KEY"; 3390Sstevel@tonic-gate goto errout; 3400Sstevel@tonic-gate } 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate /* convert server.key into a real key (it may be encrypted 3430Sstevel@tonic-gate in the database) */ 3440Sstevel@tonic-gate if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, 3450Sstevel@tonic-gate server_key, &encrypting_key, 3460Sstevel@tonic-gate NULL))) { 3470Sstevel@tonic-gate status = "DECRYPT_SERVER_KEY"; 3480Sstevel@tonic-gate goto errout; 3490Sstevel@tonic-gate } 3500Sstevel@tonic-gate if ((encrypting_key.enctype == ENCTYPE_DES_CBC_CRC) && 3510Sstevel@tonic-gate (isflagset(server.attributes, KRB5_KDB_SUPPORT_DESMD5))) 3520Sstevel@tonic-gate encrypting_key.enctype = ENCTYPE_DES_CBC_MD5; 3530Sstevel@tonic-gate 3540Sstevel@tonic-gate errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, &ticket_reply); 3550Sstevel@tonic-gate krb5_free_keyblock_contents(kdc_context, &encrypting_key); 3560Sstevel@tonic-gate encrypting_key.contents = 0; 3570Sstevel@tonic-gate if (errcode) { 3580Sstevel@tonic-gate status = "ENCRYPTING_TICKET"; 3590Sstevel@tonic-gate goto errout; 3600Sstevel@tonic-gate } 3610Sstevel@tonic-gate ticket_reply.enc_part.kvno = server_key->key_data_kvno; 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate /* 3640Sstevel@tonic-gate * Find the appropriate client key. We search in the order specified 3650Sstevel@tonic-gate * by request keytype list. 3660Sstevel@tonic-gate */ 3670Sstevel@tonic-gate client_key = (krb5_key_data *) NULL; 3680Sstevel@tonic-gate for (i = 0; i < request->nktypes; i++) { 3690Sstevel@tonic-gate useenctype = request->ktype[i]; 3700Sstevel@tonic-gate if (!valid_enctype(useenctype)) 3710Sstevel@tonic-gate continue; 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate if (!krb5_dbe_find_enctype(kdc_context, &client, useenctype, -1, 3740Sstevel@tonic-gate 0, &client_key)) 3750Sstevel@tonic-gate break; 3760Sstevel@tonic-gate } 3770Sstevel@tonic-gate if (!(client_key)) { 3780Sstevel@tonic-gate /* Cannot find an appropriate key */ 3790Sstevel@tonic-gate status = "CANT_FIND_CLIENT_KEY"; 3800Sstevel@tonic-gate errcode = KRB5KDC_ERR_ETYPE_NOSUPP; 3810Sstevel@tonic-gate goto errout; 3820Sstevel@tonic-gate } 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate /* convert client.key_data into a real key */ 3850Sstevel@tonic-gate if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, 3860Sstevel@tonic-gate client_key, &encrypting_key, 3870Sstevel@tonic-gate NULL))) { 3880Sstevel@tonic-gate status = "DECRYPT_CLIENT_KEY"; 3890Sstevel@tonic-gate goto errout; 3900Sstevel@tonic-gate } 3910Sstevel@tonic-gate encrypting_key.enctype = useenctype; 3920Sstevel@tonic-gate 3930Sstevel@tonic-gate /* Start assembling the response */ 3940Sstevel@tonic-gate reply.msg_type = KRB5_AS_REP; 3950Sstevel@tonic-gate reply.client = request->client; 3960Sstevel@tonic-gate reply.ticket = &ticket_reply; 3970Sstevel@tonic-gate reply_encpart.session = &session_key; 3980Sstevel@tonic-gate if ((errcode = fetch_last_req_info(&client, &reply_encpart.last_req))) { 3990Sstevel@tonic-gate status = "FETCH_LAST_REQ"; 4000Sstevel@tonic-gate goto errout; 4010Sstevel@tonic-gate } 4020Sstevel@tonic-gate reply_encpart.nonce = request->nonce; 4030Sstevel@tonic-gate reply_encpart.key_exp = client.expiration; 4040Sstevel@tonic-gate reply_encpart.flags = enc_tkt_reply.flags; 4050Sstevel@tonic-gate reply_encpart.server = ticket_reply.server; 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate /* copy the time fields EXCEPT for authtime; it's location 4080Sstevel@tonic-gate is used for ktime */ 4090Sstevel@tonic-gate reply_encpart.times = enc_tkt_reply.times; 4100Sstevel@tonic-gate reply_encpart.times.authtime = authtime = kdc_time; 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate reply_encpart.caddrs = enc_tkt_reply.caddrs; 4130Sstevel@tonic-gate 4140Sstevel@tonic-gate /* Fetch the padata info to be returned */ 4150Sstevel@tonic-gate errcode = return_padata(kdc_context, &client, request, &reply, client_key, 4160Sstevel@tonic-gate &encrypting_key); 4170Sstevel@tonic-gate if (errcode) { 4180Sstevel@tonic-gate status = "KDC_RETURN_PADATA"; 4190Sstevel@tonic-gate goto errout; 4200Sstevel@tonic-gate } 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate /* now encode/encrypt the response */ 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate reply.enc_part.enctype = encrypting_key.enctype; 4250Sstevel@tonic-gate 4260Sstevel@tonic-gate errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, &reply_encpart, 4270Sstevel@tonic-gate 0, &encrypting_key, &reply, response); 4280Sstevel@tonic-gate krb5_free_keyblock_contents(kdc_context, &encrypting_key); 4290Sstevel@tonic-gate encrypting_key.contents = 0; 4300Sstevel@tonic-gate reply.enc_part.kvno = client_key->key_data_kvno; 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate if (errcode) { 4330Sstevel@tonic-gate status = "ENCODE_KDC_REP"; 4340Sstevel@tonic-gate goto errout; 4350Sstevel@tonic-gate } 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we 4380Sstevel@tonic-gate can use them in raw form if needed. But, we don't... */ 4390Sstevel@tonic-gate memset(reply.enc_part.ciphertext.data, 0, reply.enc_part.ciphertext.length); 4400Sstevel@tonic-gate free(reply.enc_part.ciphertext.data); 4410Sstevel@tonic-gate 4420Sstevel@tonic-gate audit_krb5kdc_as_req(&from_in4, (in_port_t)from->port, (in_port_t)portnum, 4430Sstevel@tonic-gate cname, sname, 0); 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate krb5_klog_syslog(LOG_INFO, "AS_REQ %s(%d): ISSUE: authtime %d, %s for %s", 4460Sstevel@tonic-gate fromstring, portnum, authtime, cname, sname); 4470Sstevel@tonic-gate 4480Sstevel@tonic-gate #ifdef KRBCONF_KDC_MODIFIES_KDB 4490Sstevel@tonic-gate /* 4500Sstevel@tonic-gate * If we get this far, we successfully did the AS_REQ. 4510Sstevel@tonic-gate */ 4520Sstevel@tonic-gate client.last_success = kdc_time; 4530Sstevel@tonic-gate client.fail_auth_count = 0; 4540Sstevel@tonic-gate update_client = 1; 4550Sstevel@tonic-gate #endif /* KRBCONF_KDC_MODIFIES_KDB */ 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate errout: 4580Sstevel@tonic-gate if (status) { 4590Sstevel@tonic-gate audit_krb5kdc_as_req(&from_in4, (in_port_t)from->port, 4600Sstevel@tonic-gate (in_port_t)portnum, cname, sname, errcode); 4610Sstevel@tonic-gate krb5_klog_syslog(LOG_INFO, "AS_REQ %s(%d): %s: %s for %s%s%s", 4620Sstevel@tonic-gate fromstring, portnum, status, 4630Sstevel@tonic-gate cname ? cname : "<unknown client>", 4640Sstevel@tonic-gate sname ? sname : "<unknown server>", 4650Sstevel@tonic-gate errcode ? ", " : "", 4660Sstevel@tonic-gate errcode ? error_message(errcode) : ""); 4670Sstevel@tonic-gate } 4680Sstevel@tonic-gate if (errcode) { 4690Sstevel@tonic-gate errcode -= ERROR_TABLE_BASE_krb5; 4700Sstevel@tonic-gate if (errcode < 0 || errcode > 128) 4710Sstevel@tonic-gate errcode = KRB_ERR_GENERIC; 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate errcode = prepare_error_as(request, errcode, &e_data, response); 4740Sstevel@tonic-gate } 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate krb5_free_keyblock_contents(kdc_context, &encrypting_key); 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate if (reply.padata) 4790Sstevel@tonic-gate krb5_free_pa_data(kdc_context, reply.padata); 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate if (cname) 4820Sstevel@tonic-gate free(cname); 4830Sstevel@tonic-gate if (sname) 4840Sstevel@tonic-gate free(sname); 4850Sstevel@tonic-gate if (c_nprincs) { 4860Sstevel@tonic-gate #ifdef KRBCONF_KDC_MODIFIES_KDB 4870Sstevel@tonic-gate if (update_client) { 4880Sstevel@tonic-gate krb5_db_put_principal(kdc_context, &client, &c_nprincs); 4890Sstevel@tonic-gate /* 4900Sstevel@tonic-gate * ptooey. We want krb5_db_sync() or something like that. 4910Sstevel@tonic-gate */ 4920Sstevel@tonic-gate krb5_db_fini(kdc_context); 4930Sstevel@tonic-gate if (kdc_active_realm->realm_dbname) 4940Sstevel@tonic-gate krb5_db_set_name(kdc_active_realm->realm_context, 4950Sstevel@tonic-gate kdc_active_realm->realm_dbname); 4960Sstevel@tonic-gate krb5_db_init(kdc_context); 4970Sstevel@tonic-gate /* Reset master key */ 4980Sstevel@tonic-gate krb5_db_set_mkey(kdc_context, &kdc_active_realm->realm_encblock); 4990Sstevel@tonic-gate } 5000Sstevel@tonic-gate #endif /* KRBCONF_KDC_MODIFIES_KDB */ 5010Sstevel@tonic-gate krb5_db_free_principal(kdc_context, &client, c_nprincs); 5020Sstevel@tonic-gate } 5030Sstevel@tonic-gate if (s_nprincs) 5040Sstevel@tonic-gate krb5_db_free_principal(kdc_context, &server, s_nprincs); 5050Sstevel@tonic-gate if (session_key.contents) 5060Sstevel@tonic-gate krb5_free_keyblock_contents(kdc_context, &session_key); 5070Sstevel@tonic-gate if (ticket_reply.enc_part.ciphertext.data) { 5080Sstevel@tonic-gate memset(ticket_reply.enc_part.ciphertext.data , 0, 5090Sstevel@tonic-gate ticket_reply.enc_part.ciphertext.length); 5100Sstevel@tonic-gate free(ticket_reply.enc_part.ciphertext.data); 5110Sstevel@tonic-gate } 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate krb5_free_data_contents(kdc_context, &e_data); 5140Sstevel@tonic-gate 5150Sstevel@tonic-gate return errcode; 5160Sstevel@tonic-gate } 5170Sstevel@tonic-gate 5180Sstevel@tonic-gate static krb5_error_code 5190Sstevel@tonic-gate prepare_error_as (request, error, e_data, response) 5200Sstevel@tonic-gate register krb5_kdc_req *request; 5210Sstevel@tonic-gate int error; 5220Sstevel@tonic-gate krb5_data *e_data; 5230Sstevel@tonic-gate krb5_data **response; 5240Sstevel@tonic-gate { 5250Sstevel@tonic-gate krb5_error errpkt; 5260Sstevel@tonic-gate krb5_error_code retval; 5270Sstevel@tonic-gate krb5_data *scratch; 5280Sstevel@tonic-gate 5290Sstevel@tonic-gate errpkt.ctime = request->nonce; 5300Sstevel@tonic-gate errpkt.cusec = 0; 5310Sstevel@tonic-gate 5320Sstevel@tonic-gate if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime, 5330Sstevel@tonic-gate &errpkt.susec))) 5340Sstevel@tonic-gate return(retval); 5350Sstevel@tonic-gate errpkt.error = error; 5360Sstevel@tonic-gate errpkt.server = request->server; 5370Sstevel@tonic-gate errpkt.client = request->client; 5380Sstevel@tonic-gate errpkt.text.length = strlen(error_message(error+KRB5KDC_ERR_NONE))+1; 5390Sstevel@tonic-gate if (!(errpkt.text.data = malloc(errpkt.text.length))) 5400Sstevel@tonic-gate return ENOMEM; 5410Sstevel@tonic-gate (void) strcpy(errpkt.text.data, error_message(error+KRB5KDC_ERR_NONE)); 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) { 5440Sstevel@tonic-gate free(errpkt.text.data); 5450Sstevel@tonic-gate return ENOMEM; 5460Sstevel@tonic-gate } 5470Sstevel@tonic-gate if (e_data && e_data->data) { 5480Sstevel@tonic-gate errpkt.e_data = *e_data; 5490Sstevel@tonic-gate } else { 5500Sstevel@tonic-gate errpkt.e_data.length = 0; 5510Sstevel@tonic-gate errpkt.e_data.data = 0; 5520Sstevel@tonic-gate } 5530Sstevel@tonic-gate 5540Sstevel@tonic-gate retval = krb5_mk_error(kdc_context, &errpkt, scratch); 5550Sstevel@tonic-gate free(errpkt.text.data); 556*107Sbugbomb if (retval) 557*107Sbugbomb free(scratch); 558*107Sbugbomb else 5590Sstevel@tonic-gate *response = scratch; 560*107Sbugbomb 5610Sstevel@tonic-gate return retval; 5620Sstevel@tonic-gate } 563