4 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. M.I.T. makes no representations about the suitability of
20 * this software for any purpose. It is provided "as is" without express
21 * or implied warranty.
24 * KDC Routines to deal with AS_REQ's
31 #ifdef HAVE_NETINET_IN_H
32 #include <sys/types.h>
33 #include <netinet/in.h>
35 #include <arpa/inet.h>
37 #endif /* HAVE_NETINET_IN_H */
42 #include "adm_proto.h"
45 static krb5_error_code prepare_error_as PROTOTYPE((krb5_kdc_req *,
52 process_as_req(request, from, portnum, response)
53 register krb5_kdc_req *request;
54 const krb5_fulladdr *from; /* who sent it ? */
56 krb5_data **response; /* filled in with a response packet */
59 krb5_db_entry client, server;
61 krb5_enc_kdc_rep_part reply_encpart;
62 krb5_ticket ticket_reply;
63 krb5_enc_tkt_part enc_tkt_reply;
64 krb5_error_code errcode;
65 int c_nprincs = 0, s_nprincs = 0;
67 krb5_timestamp kdc_time, authtime;
68 krb5_keyblock session_key;
69 krb5_keyblock encrypting_key;
71 krb5_key_data *server_key, *client_key;
72 krb5_enctype useenctype;
73 #ifdef KRBCONF_KDC_MODIFIES_KDB
74 krb5_boolean update_client = 0;
75 #endif /* KRBCONF_KDC_MODIFIES_KDB */
78 krb5_timestamp until, rtime;
79 char *cname = 0, *sname = 0, *fromstring = 0;
81 ticket_reply.enc_part.ciphertext.data = 0;
83 encrypting_key.contents = 0;
84 session_key.contents = 0;
86 #ifdef HAVE_NETINET_IN_H
87 if (from->address->addrtype == ADDRTYPE_INET)
88 fromstring = (char *) inet_ntoa(*(struct in_addr *)from->address->contents);
91 fromstring = "<unknown>";
93 if (!request->client) {
94 status = "NULL_CLIENT";
95 errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
98 if ((errcode = krb5_unparse_name(kdc_context, request->client, &cname))) {
99 status = "UNPARSING_CLIENT";
103 if (!request->server) {
104 status = "NULL_SERVER";
105 errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
108 if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) {
109 status = "UNPARSING_SERVER";
115 if ((errcode = krb5_db_get_principal(kdc_context, request->client,
116 &client, &c_nprincs, &more))) {
117 status = "LOOKING_UP_CLIENT";
122 status = "NON-UNIQUE_CLIENT";
123 errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
125 } else if (c_nprincs != 1) {
126 status = "CLIENT_NOT_FOUND";
127 #ifdef KRBCONF_VAGUE_ERRORS
128 errcode = KRB5KRB_ERR_GENERIC;
130 errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
136 if ((errcode = krb5_db_get_principal(kdc_context, request->server, &server,
137 &s_nprincs, &more))) {
138 status = "LOOKING_UP_SERVER";
142 status = "NON-UNIQUE_SERVER";
143 errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
145 } else if (s_nprincs != 1) {
146 status = "SERVER_NOT_FOUND";
147 errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
151 if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) {
152 status = "TIMEOFDAY";
156 if ((errcode = validate_as_request(request, client, server,
157 kdc_time, &status))) {
159 status = "UNKNOWN_REASON";
160 errcode += ERROR_TABLE_BASE_krb5;
165 * Select the keytype for the ticket session key.
167 if ((useenctype = select_session_keytype(kdc_context, &server,
169 request->ktype)) == 0) {
170 /* unsupported ktype */
171 status = "BAD_ENCRYPTION_TYPE";
172 errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
176 if ((errcode = krb5_c_make_random_key(kdc_context, useenctype,
178 status = "RANDOM_KEY_FAILED";
182 ticket_reply.server = request->server;
184 enc_tkt_reply.flags = 0;
185 setflag(enc_tkt_reply.flags, TKT_FLG_INITIAL);
187 /* It should be noted that local policy may affect the */
188 /* processing of any of these flags. For example, some */
189 /* realms may refuse to issue renewable tickets */
191 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE))
192 setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
194 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE))
195 setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE);
197 if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE))
198 setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE);
200 enc_tkt_reply.session = &session_key;
201 enc_tkt_reply.client = request->client;
202 enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
203 enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */
205 enc_tkt_reply.times.authtime = kdc_time;
207 if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) {
208 setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED);
209 setflag(enc_tkt_reply.flags, TKT_FLG_INVALID);
210 enc_tkt_reply.times.starttime = request->from;
212 enc_tkt_reply.times.starttime = kdc_time;
214 until = (request->till == 0) ? kdc_infinity : request->till;
216 enc_tkt_reply.times.endtime =
218 min(enc_tkt_reply.times.starttime + client.max_life,
219 min(enc_tkt_reply.times.starttime + server.max_life,
220 enc_tkt_reply.times.starttime + max_life_for_realm)));
222 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
223 !isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) &&
224 (enc_tkt_reply.times.endtime < request->till)) {
226 /* we set the RENEWABLE option for later processing */
228 setflag(request->kdc_options, KDC_OPT_RENEWABLE);
229 request->rtime = request->till;
231 rtime = (request->rtime == 0) ? kdc_infinity : request->rtime;
233 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) {
235 * XXX Should we squelch the output renew_till to be no
236 * earlier than the endtime of the ticket?
238 setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE);
239 enc_tkt_reply.times.renew_till =
240 min(rtime, enc_tkt_reply.times.starttime +
241 min(client.max_renewable_life,
242 min(server.max_renewable_life,
243 max_renewable_life_for_realm)));
245 enc_tkt_reply.times.renew_till = 0; /* XXX */
247 /* starttime is optional, and treated as authtime if not present.
248 so we can nuke it if it matches */
249 if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
250 enc_tkt_reply.times.starttime = 0;
252 enc_tkt_reply.caddrs = request->addresses;
253 enc_tkt_reply.authorization_data = 0;
256 * Check the preauthentication if it is there.
258 if (request->padata) {
259 errcode = check_padata(kdc_context, &client, request, &enc_tkt_reply);
261 #ifdef KRBCONF_KDC_MODIFIES_KDB
263 * Note: this doesn't work if you're using slave servers!!!
264 * It also causes the database to be modified (and thus
265 * need to be locked) frequently.
267 if (client.fail_auth_count < KRB5_MAX_FAIL_COUNT) {
268 client.fail_auth_count = client.fail_auth_count + 1;
269 if (client.fail_auth_count == KRB5_MAX_FAIL_COUNT) {
270 client.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
273 client.last_failed = kdc_time;
276 status = "PREAUTH_FAILED";
277 #ifdef KRBCONF_VAGUE_ERRORS
278 errcode = KRB5KRB_ERR_GENERIC;
285 * Final check before handing out ticket: If the client requires
286 * preauthentication, verify that the proper kind of
287 * preauthentication was carried out.
289 status = missing_required_preauth(&client, &server, &enc_tkt_reply);
291 errcode = KRB5KDC_ERR_PREAUTH_REQUIRED;
292 get_preauth_hint_list(request, &client, &server, &e_data);
296 ticket_reply.enc_part2 = &enc_tkt_reply;
299 * Find the server key
301 if ((errcode = krb5_dbe_find_enctype(kdc_context, &server,
302 -1, /* ignore keytype */
303 -1, /* Ignore salttype */
304 0, /* Get highest kvno */
306 status = "FINDING_SERVER_KEY";
310 /* convert server.key into a real key (it may be encrypted
312 if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
313 server_key, &encrypting_key,
315 status = "DECRYPT_SERVER_KEY";
318 if ((encrypting_key.enctype == ENCTYPE_DES_CBC_CRC) &&
319 (isflagset(server.attributes, KRB5_KDB_SUPPORT_DESMD5)))
320 encrypting_key.enctype = ENCTYPE_DES_CBC_MD5;
322 errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, &ticket_reply);
323 krb5_free_keyblock_contents(kdc_context, &encrypting_key);
324 encrypting_key.contents = 0;
326 status = "ENCRYPTING_TICKET";
329 ticket_reply.enc_part.kvno = server_key->key_data_kvno;
332 * Find the appropriate client key. We search in the order specified
333 * by request keytype list.
335 client_key = (krb5_key_data *) NULL;
336 for (i = 0; i < request->nktypes; i++) {
337 useenctype = request->ktype[i];
338 if (!valid_enctype(useenctype))
341 if (!krb5_dbe_find_enctype(kdc_context, &client, useenctype, -1,
346 /* Cannot find an appropriate key */
347 status = "CANT_FIND_CLIENT_KEY";
348 errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
352 /* convert client.key_data into a real key */
353 if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
354 client_key, &encrypting_key,
356 status = "DECRYPT_CLIENT_KEY";
359 encrypting_key.enctype = useenctype;
361 /* Start assembling the response */
362 reply.msg_type = KRB5_AS_REP;
364 reply.client = request->client;
365 reply.ticket = &ticket_reply;
366 reply_encpart.session = &session_key;
367 if ((errcode = fetch_last_req_info(&client, &reply_encpart.last_req))) {
368 status = "FETCH_LAST_REQ";
371 reply_encpart.nonce = request->nonce;
372 reply_encpart.key_exp = client.expiration;
373 reply_encpart.flags = enc_tkt_reply.flags;
374 reply_encpart.server = ticket_reply.server;
376 /* copy the time fields EXCEPT for authtime; it's location
378 reply_encpart.times = enc_tkt_reply.times;
379 reply_encpart.times.authtime = authtime = kdc_time;
381 reply_encpart.caddrs = enc_tkt_reply.caddrs;
383 /* Fetch the padata info to be returned */
384 errcode = return_padata(kdc_context, &client, request, &reply, client_key,
387 status = "KDC_RETURN_PADATA";
391 /* now encode/encrypt the response */
393 reply.enc_part.enctype = encrypting_key.enctype;
395 errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, &reply_encpart,
396 0, &encrypting_key, &reply, response);
397 krb5_free_keyblock_contents(kdc_context, &encrypting_key);
398 encrypting_key.contents = 0;
399 reply.enc_part.kvno = client_key->key_data_kvno;
402 status = "ENCODE_KDC_REP";
406 /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
407 can use them in raw form if needed. But, we don't... */
408 memset(reply.enc_part.ciphertext.data, 0, reply.enc_part.ciphertext.length);
409 free(reply.enc_part.ciphertext.data);
411 krb5_klog_syslog(LOG_INFO, "AS_REQ %s(%d): ISSUE: authtime %d, %s for %s",
412 fromstring, portnum, authtime, cname, sname);
414 #ifdef KRBCONF_KDC_MODIFIES_KDB
416 * If we get this far, we successfully did the AS_REQ.
418 client.last_success = kdc_time;
419 client.fail_auth_count = 0;
421 #endif /* KRBCONF_KDC_MODIFIES_KDB */
425 krb5_klog_syslog(LOG_INFO, "AS_REQ %s(%d): %s: %s for %s%s%s",
426 fromstring, portnum, status,
427 cname ? cname : "<unknown client>",
428 sname ? sname : "<unknown server>",
430 errcode ? error_message(errcode) : "");
432 errcode -= ERROR_TABLE_BASE_krb5;
433 if (errcode < 0 || errcode > 128)
434 errcode = KRB_ERR_GENERIC;
436 errcode = prepare_error_as(request, errcode, &e_data, response);
439 krb5_free_keyblock_contents(kdc_context, &encrypting_key);
446 #ifdef KRBCONF_KDC_MODIFIES_KDB
448 krb5_db_put_principal(kdc_context, &client, &c_nprincs);
450 * ptooey. We want krb5_db_sync() or something like that.
452 krb5_db_fini(kdc_context);
453 if (kdc_active_realm->realm_dbname)
454 krb5_db_set_name(kdc_active_realm->realm_context,
455 kdc_active_realm->realm_dbname);
456 krb5_db_init(kdc_context);
457 /* Reset master key */
458 krb5_db_set_mkey(kdc_context, &kdc_active_realm->realm_encblock);
460 #endif /* KRBCONF_KDC_MODIFIES_KDB */
461 krb5_db_free_principal(kdc_context, &client, c_nprincs);
464 krb5_db_free_principal(kdc_context, &server, s_nprincs);
465 if (session_key.contents)
466 krb5_free_keyblock_contents(kdc_context, &session_key);
467 if (ticket_reply.enc_part.ciphertext.data) {
468 memset(ticket_reply.enc_part.ciphertext.data , 0,
469 ticket_reply.enc_part.ciphertext.length);
470 free(ticket_reply.enc_part.ciphertext.data);
473 krb5_free_data_contents(kdc_context, &e_data);
478 static krb5_error_code
479 prepare_error_as (request, error, e_data, response)
480 register krb5_kdc_req *request;
483 krb5_data **response;
486 krb5_error_code retval;
489 errpkt.ctime = request->nonce;
492 if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime,
495 errpkt.error = error;
496 errpkt.server = request->server;
497 errpkt.client = request->client;
498 errpkt.text.length = strlen(error_message(error+KRB5KDC_ERR_NONE))+1;
499 if (!(errpkt.text.data = malloc(errpkt.text.length)))
501 (void) strcpy(errpkt.text.data, error_message(error+KRB5KDC_ERR_NONE));
503 if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) {
504 free(errpkt.text.data);
507 if (e_data && e_data->data) {
508 errpkt.e_data = *e_data;
510 errpkt.e_data.length = 0;
511 errpkt.e_data.data = 0;
514 retval = krb5_mk_error(kdc_context, &errpkt, scratch);
515 free(errpkt.text.data);