2 * lib/krb5/krb/get_in_tkt.c
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.
30 All-purpose initial ticket routine, usually called via
31 krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey.
33 Attempts to get an initial ticket for creds->client to use server
34 creds->server, (realm is taken from creds->client), with options
35 options, and using creds->times.starttime, creds->times.endtime,
36 creds->times.renew_till as from, till, and rtime.
37 creds->times.renew_till is ignored unless the RENEWABLE option is requested.
39 key_proc is called to fill in the key to be used for decryption.
40 keyseed is passed on to key_proc.
42 decrypt_proc is called to perform the decryption of the response (the
43 encrypted part is in dec_rep->enc_part; the decrypted part should be
44 allocated and filled into dec_rep->enc_part2
45 arg is passed on to decrypt_proc.
47 If addrs is non-NULL, it is used for the addresses requested. If it is
48 null, the system standard addresses are used.
50 A succesful call will place the ticket in the credentials cache ccache
51 and fill in creds with the ticket information used/returned..
53 returns system errors, encryption errors
58 /* some typedef's for the function args to make things look a bit cleaner */
60 typedef krb5_error_code (*git_key_proc) PROTOTYPE((krb5_context,
66 typedef krb5_error_code (*git_decrypt_proc) PROTOTYPE((krb5_context,
67 const krb5_keyblock *,
71 * This function sends a request to the KDC, and gets back a response;
72 * the response is parsed into ret_err_reply or ret_as_reply if the
73 * reponse is a KRB_ERROR or a KRB_AS_REP packet. If it is some other
74 * unexpected response, an error is returned.
76 static krb5_error_code
77 send_as_request(context, request, time_now, ret_err_reply, ret_as_reply)
79 krb5_kdc_req *request;
80 krb5_timestamp *time_now;
81 krb5_error ** ret_err_reply;
82 krb5_kdc_rep ** ret_as_reply;
84 krb5_kdc_rep *as_reply = 0;
85 krb5_error_code retval;
88 char k4_version; /* same type as *(krb5_data::data) */
92 if ((retval = krb5_timeofday(context, time_now)))
96 * XXX we know they are the same size... and we should do
97 * something better than just the current time
99 request->nonce = (krb5_int32) *time_now;
101 /* encode & send to KDC */
102 if ((retval = encode_krb5_as_req(request, &packet)) != 0)
105 k4_version = packet->data[0];
106 retval = krb5_sendto_kdc(context, packet,
107 krb5_princ_realm(context, request->client), &reply);
108 krb5_free_data(context, packet);
112 /* now decode the reply...could be error or as_rep */
113 if (krb5_is_krb_error(&reply)) {
114 krb5_error *err_reply;
116 if ((retval = decode_krb5_error(&reply, &err_reply)))
117 /* some other error code--??? */
121 *ret_err_reply = err_reply;
123 krb5_free_error(context, err_reply);
128 * Check to make sure it isn't a V4 reply.
130 if (!krb5_is_as_rep(&reply)) {
131 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
132 #define V4_KRB_PROT_VERSION 4
133 #define V4_AUTH_MSG_ERR_REPLY (5<<1)
134 /* check here for V4 reply */
135 unsigned int t_switch;
137 /* From v4 g_in_tkt.c: This used to be
138 switch (pkt_msg_type(rpkt) & ~1) {
139 but SCO 3.2v4 cc compiled that incorrectly. */
140 t_switch = reply.data[1];
143 if (t_switch == V4_AUTH_MSG_ERR_REPLY
144 && (reply.data[0] == V4_KRB_PROT_VERSION
145 || reply.data[0] == k4_version)) {
146 retval = KRB5KRB_AP_ERR_V4_REPLY;
148 retval = KRB5KRB_AP_ERR_MSG_TYPE;
153 /* It must be a KRB_AS_REP message, or an bad returned packet */
154 if ((retval = decode_krb5_as_rep(&reply, &as_reply)))
155 /* some other error code ??? */
158 if (as_reply->msg_type != KRB5_AS_REP) {
159 retval = KRB5KRB_AP_ERR_MSG_TYPE;
160 krb5_free_kdc_rep(context, as_reply);
165 *ret_as_reply = as_reply;
167 krb5_free_kdc_rep(context, as_reply);
175 static krb5_error_code
176 decrypt_as_reply(context, request, as_reply, key_proc, keyseed,
177 decrypt_proc, decryptarg)
178 krb5_context context;
179 krb5_kdc_req *request;
180 krb5_kdc_rep *as_reply;
181 git_key_proc key_proc;
182 krb5_const_pointer keyseed;
183 git_decrypt_proc decrypt_proc;
184 krb5_const_pointer decryptarg;
186 krb5_error_code retval;
187 krb5_keyblock * decrypt_key = 0;
192 if (as_reply->enc_part2)
195 /* Temp kludge to get salt */
196 if (as_reply->padata) {
199 for (ptr = as_reply->padata; *ptr; ptr++) {
200 if ((*ptr)->pa_type == KRB5_PADATA_PW_SALT) {
201 /* use KDC-supplied salt, instead of default */
202 salt.data = (char *)(*ptr)->contents;
203 salt.length = (*ptr)->length;
211 if ((retval = krb5_principal2salt(context, request->client, &salt)))
216 if ((retval = (*key_proc)(context, as_reply->ticket->enc_part.enctype,
217 &salt, keyseed, &decrypt_key)))
220 if ((retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply)))
225 krb5_xfree(salt.data);
227 krb5_free_keyblock(context, decrypt_key);
231 static krb5_error_code
232 verify_as_reply(context, time_now, request, as_reply)
233 krb5_context context;
234 krb5_timestamp time_now;
235 krb5_kdc_req *request;
236 krb5_kdc_rep *as_reply;
238 /* check the contents for sanity: */
239 if (!as_reply->enc_part2->times.starttime)
240 as_reply->enc_part2->times.starttime =
241 as_reply->enc_part2->times.authtime;
243 if (!krb5_principal_compare(context, as_reply->client, request->client)
244 || !krb5_principal_compare(context, as_reply->enc_part2->server, request->server)
245 || !krb5_principal_compare(context, as_reply->ticket->server, request->server)
246 || (request->nonce != as_reply->enc_part2->nonce)
247 /* XXX check for extraneous flags */
248 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
249 || ((request->from != 0) &&
250 (request->from != as_reply->enc_part2->times.starttime))
251 || ((request->till != 0) &&
252 (as_reply->enc_part2->times.endtime > request->till))
253 || ((request->kdc_options & KDC_OPT_RENEWABLE) &&
254 (request->rtime != 0) &&
255 (as_reply->enc_part2->times.renew_till > request->rtime))
256 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
257 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
258 (request->till != 0) &&
259 (as_reply->enc_part2->times.renew_till > request->till))
261 return KRB5_KDCREP_MODIFIED;
263 if ((request->from == 0) &&
264 (labs(as_reply->enc_part2->times.starttime - time_now)
265 > context->clockskew))
266 return (KRB5_KDCREP_SKEW);
271 static krb5_error_code
272 stash_as_reply(context, time_now, request, as_reply, creds, ccache)
273 krb5_context context;
274 krb5_timestamp time_now;
275 krb5_kdc_req *request;
276 krb5_kdc_rep *as_reply;
280 krb5_error_code retval;
283 if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME)
284 krb5_set_time_offsets(context,
285 (as_reply->enc_part2->times.authtime -
289 /* XXX issue warning if as_reply->enc_part2->key_exp is nearby */
291 /* fill in the credentials */
292 if ((retval = krb5_copy_keyblock_contents(context,
293 as_reply->enc_part2->session,
297 creds->times = as_reply->enc_part2->times;
298 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot
299 be encrypted in skey */
300 creds->ticket_flags = as_reply->enc_part2->flags;
301 if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
305 creds->second_ticket.length = 0;
306 creds->second_ticket.data = 0;
308 if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
311 creds->ticket = *packet;
314 /* store it in the ccache! */
316 if ((retval = krb5_cc_store_cred(context, ccache, creds)))
322 if (creds->keyblock.contents) {
323 memset((char *)creds->keyblock.contents, 0,
324 creds->keyblock.length);
325 krb5_xfree(creds->keyblock.contents);
326 creds->keyblock.contents = 0;
327 creds->keyblock.length = 0;
329 if (creds->ticket.data) {
330 krb5_xfree(creds->ticket.data);
331 creds->ticket.data = 0;
333 if (creds->addresses) {
334 krb5_free_addresses(context, creds->addresses);
335 creds->addresses = 0;
341 static krb5_error_code
342 make_preauth_list(context, ptypes, ret_list)
343 krb5_context context;
344 krb5_preauthtype * ptypes;
345 krb5_pa_data *** ret_list;
347 krb5_preauthtype * ptypep;
348 krb5_pa_data ** preauthp;
349 krb5_pa_data ** preauth_to_use;
352 for (i=1, ptypep = ptypes; *ptypep; ptypep++, i++)
354 preauth_to_use = malloc(i * sizeof(krb5_pa_data *));
355 if (preauth_to_use == NULL)
357 for (preauthp = preauth_to_use, ptypep = ptypes;
359 preauthp++, ptypep++) {
360 *preauthp = malloc(sizeof(krb5_pa_data));
361 if (*preauthp == NULL) {
362 krb5_free_pa_data(context, preauth_to_use);
365 (*preauthp)->magic = KV5M_PA_DATA;
366 (*preauthp)->pa_type = *ptypep;
367 (*preauthp)->length = 0;
368 (*preauthp)->contents = 0;
370 *ret_list = preauth_to_use;
374 #define MAX_IN_TKT_LOOPS 16
377 krb5_get_in_tkt(context, options, addrs, ktypes, ptypes, key_proc, keyseed,
378 decrypt_proc, decryptarg, creds, ccache, ret_as_reply)
379 krb5_context context;
380 const krb5_flags options;
381 krb5_address * const * addrs;
382 krb5_enctype * ktypes;
383 krb5_preauthtype * ptypes;
384 git_key_proc key_proc;
385 krb5_const_pointer keyseed;
386 git_decrypt_proc decrypt_proc;
387 krb5_const_pointer decryptarg;
390 krb5_kdc_rep ** ret_as_reply;
392 krb5_error_code retval;
393 krb5_timestamp time_now;
394 krb5_kdc_req request;
395 krb5_pa_data **padata = 0;
396 krb5_error * err_reply;
397 krb5_kdc_rep * as_reply;
398 krb5_pa_data ** preauth_to_use = 0;
402 if (! krb5_realm_compare(context, creds->client, creds->server))
403 return KRB5_IN_TKT_REALM_MISMATCH;
409 * Set up the basic request structure
411 request.magic = KV5M_KDC_REQ;
412 request.msg_type = KRB5_AS_REQ;
413 request.addresses = 0;
416 request.addresses = (krb5_address **) addrs;
418 if ((retval = krb5_os_localaddr(context, &request.addresses)))
421 request.kdc_options = options;
422 request.client = creds->client;
423 request.server = creds->server;
424 request.from = creds->times.starttime;
425 request.till = creds->times.endtime;
426 request.rtime = creds->times.renew_till;
428 request.ktype = ktypes;
430 if ((retval = krb5_get_default_in_tkt_ktypes(context, &request.ktype)))
432 for (request.nktypes = 0;request.ktype[request.nktypes];request.nktypes++);
433 request.authorization_data.ciphertext.length = 0;
434 request.authorization_data.ciphertext.data = 0;
435 request.unenc_authdata = 0;
436 request.second_ticket = 0;
439 * If a list of preauth types are passed in, convert it to a
440 * preauth_to_use list.
443 retval = make_preauth_list(context, ptypes, &preauth_to_use);
449 if (loopcount++ > MAX_IN_TKT_LOOPS) {
450 retval = KRB5_GET_IN_TKT_LOOP;
454 if ((retval = krb5_obtain_padata(context, preauth_to_use, 0, key_proc,
455 keyseed, creds, &request)) != 0)
458 krb5_free_pa_data(context, preauth_to_use);
463 if ((retval = send_as_request(context, &request, &time_now, &err_reply,
468 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
469 err_reply->e_data.length > 0) {
470 retval = decode_krb5_padata_sequence(&err_reply->e_data,
472 krb5_free_error(context, err_reply);
477 retval = err_reply->error + ERROR_TABLE_BASE_krb5;
478 krb5_free_error(context, err_reply);
481 } else if (!as_reply) {
482 retval = KRB5KRB_AP_ERR_MSG_TYPE;
485 if ((retval = krb5_process_padata(context, &request, as_reply,
486 key_proc, keyseed, creds,
494 if ((retval = decrypt_as_reply(context, &request, as_reply, key_proc,
495 keyseed, decrypt_proc, decryptarg)))
498 if ((retval = verify_as_reply(context, time_now, &request, as_reply)))
501 if ((retval = stash_as_reply(context, time_now, &request, as_reply,
506 if (!ktypes && request.ktype)
508 if (!addrs && request.addresses)
509 krb5_free_addresses(context, request.addresses);
511 krb5_free_pa_data(context, request.padata);
513 krb5_free_pa_data(context, padata);
515 krb5_free_pa_data(context, preauth_to_use);
518 *ret_as_reply = as_reply;
520 krb5_free_kdc_rep(context, as_reply);