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 extern krb5_deltat krb5_clockskew;
59 #define in_clock_skew(date) (labs((date)-request.nonce) < krb5_clockskew)
61 /* some typedef's for the function args to make things look a bit cleaner */
63 typedef krb5_error_code (*git_key_proc) PROTOTYPE((krb5_context,
69 typedef krb5_error_code (*git_decrypt_proc) PROTOTYPE((krb5_context,
70 const krb5_keyblock *,
74 krb5_get_in_tkt(context, options, addrs, ktypes, ptypes, key_proc, keyseed,
75 decrypt_proc, decryptarg, creds, ccache, ret_as_reply)
77 const krb5_flags options;
78 krb5_address * const * addrs;
79 krb5_enctype * ktypes;
80 krb5_preauthtype * ptypes;
81 git_key_proc key_proc;
82 krb5_const_pointer keyseed;
83 git_decrypt_proc decrypt_proc;
84 krb5_const_pointer decryptarg;
87 krb5_kdc_rep ** ret_as_reply;
89 krb5_enctype enctype, ktype;
91 krb5_kdc_rep *as_reply = 0;
92 krb5_error *err_reply;
93 krb5_error_code retval;
96 krb5_keyblock *decrypt_key = 0;
97 krb5_timestamp time_now;
98 /* krb5_pa_data *padata; */
99 krb5_pa_data **preauth_to_use = 0;
100 int f_salt = 0, use_salt = 0;
102 char k4_version; /* same type as *(krb5_data::data) */
104 if (! krb5_realm_compare(context, creds->client, creds->server))
105 return KRB5_IN_TKT_REALM_MISMATCH;
110 request.msg_type = KRB5_AS_REQ;
112 krb5_os_localaddr(context, &request.addresses);
114 request.addresses = (krb5_address **) addrs;
122 /* Pre authentication is not yet supported */
125 * First, we get the user's key. We assume we will need
126 * it for the pre-authentication. Actually, this could
127 * possibly not be the case, but it's usually true.
129 * XXX Problem here: if we're doing preauthentication,
130 * we're getting the key before we get the KDC hit as to
131 * which salting algorithm to use; hence, we're using the
132 * default. But if we're changing salts, because of a
133 * realm renaming, or some such, this won't work.
135 /* retval = (*key_proc)(context, enctype, &decrypt_key, keyseed, 0); */
138 request.padata = (krb5_pa_data **) malloc(sizeof(krb5_pa_data *)
140 if (!request.padata) {
145 /* retval = krb5_obtain_padata(context, ptypes[0], creds->client,
146 request.addresses, decrypt_key,
150 /* request.padata[0] = padata; */
151 request.padata[1] = 0;
154 request.kdc_options = options;
155 request.client = creds->client;
156 request.server = creds->server;
158 request.from = creds->times.starttime;
159 request.till = creds->times.endtime;
160 request.rtime = creds->times.renew_till;
162 if ((retval = krb5_timeofday(context, &time_now)))
165 /* XXX we know they are the same size... */
166 request.nonce = (krb5_int32) time_now;
169 request.ktype = ktypes;
171 krb5_get_default_in_tkt_ktypes(context, &request.ktype);
172 for (request.nktypes = 0;request.ktype[request.nktypes];request.nktypes++);
173 request.authorization_data.ciphertext.length = 0;
174 request.authorization_data.ciphertext.data = 0;
175 request.unenc_authdata = 0;
176 request.second_ticket = 0;
178 if ((retval = krb5_timeofday(context, &time_now)))
181 /* XXX we know they are the same size... */
182 request.nonce = (krb5_int32) time_now;
184 /* encode & send to KDC */
185 retval = encode_krb5_as_req(&request, &packet);
191 k4_version = packet->data[0];
192 retval = krb5_sendto_kdc(context, packet,
193 krb5_princ_realm(context, creds->client), &reply);
194 krb5_free_data(context, packet);
198 /* now decode the reply...could be error or as_rep */
200 if (krb5_is_krb_error(&reply)) {
201 if ((retval = decode_krb5_error(&reply, &err_reply)))
202 /* some other error code--??? */
205 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
206 err_reply->e_data.length > 0) {
207 retval = decode_krb5_padata_sequence(&err_reply->e_data,
209 /* XXX we need to actually do something with the info */
210 krb5_free_pa_data(context, preauth_to_use);
213 retval = err_reply->error + ERROR_TABLE_BASE_krb5;
215 /* XXX somehow make error msg text available to application? */
217 krb5_free_error(context, err_reply);
221 if (!krb5_is_as_rep(&reply)) {
222 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
223 #define V4_KRB_PROT_VERSION 4
224 #define V4_AUTH_MSG_ERR_REPLY (5<<1)
225 /* check here for V4 reply */
226 unsigned int t_switch;
228 /* From v4 g_in_tkt.c: This used to be
229 switch (pkt_msg_type(rpkt) & ~1) {
230 but SCO 3.2v4 cc compiled that incorrectly. */
231 t_switch = reply.data[1];
234 if (t_switch == V4_AUTH_MSG_ERR_REPLY
235 && (reply.data[0] == V4_KRB_PROT_VERSION
236 || reply.data[0] == k4_version)) {
237 retval = KRB5KRB_AP_ERR_V4_REPLY;
239 retval = KRB5KRB_AP_ERR_MSG_TYPE;
243 if ((retval = decode_krb5_as_rep(&reply, &as_reply)))
244 /* some other error code ??? */
247 if (as_reply->msg_type != KRB5_AS_REP) {
248 retval = KRB5KRB_AP_ERR_MSG_TYPE;
252 /* Encryption type, enctype, */
253 enctype = as_reply->ticket->enc_part.enctype;
256 if (as_reply->padata) {
259 for (ptr = as_reply->padata; *ptr; ptr++) {
260 if ((*ptr)->pa_type == KRB5_PADATA_PW_SALT) {
261 /* use KDC-supplied salt, instead of default */
262 salt.data = (char *)(*ptr)->contents;
263 salt.length = (*ptr)->length;
270 /* need to use flattened principal */
271 if ((retval = krb5_principal2salt(context, creds->client, &salt)))
276 /* it was a kdc_rep--decrypt & check */
277 /* Generate the key, if we haven't done so already. */
279 if ((retval = (*key_proc)(context, enctype, & salt, keyseed,
284 if ((retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply)))
287 krb5_free_keyblock(context, decrypt_key);
290 /* check the contents for sanity: */
291 if (!as_reply->enc_part2->times.starttime)
292 as_reply->enc_part2->times.starttime =
293 as_reply->enc_part2->times.authtime;
294 if (!krb5_principal_compare(context, as_reply->client, request.client)
295 || !krb5_principal_compare(context, as_reply->enc_part2->server, request.server)
296 || !krb5_principal_compare(context, as_reply->ticket->server, request.server)
297 || (request.nonce != as_reply->enc_part2->nonce)
298 /* XXX check for extraneous flags */
299 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
300 || ((request.from != 0) &&
301 (request.from != as_reply->enc_part2->times.starttime))
302 || ((request.till != 0) &&
303 (as_reply->enc_part2->times.endtime > request.till))
304 || ((request.kdc_options & KDC_OPT_RENEWABLE) &&
305 (request.rtime != 0) &&
306 (as_reply->enc_part2->times.renew_till > request.rtime))
307 || ((request.kdc_options & KDC_OPT_RENEWABLE_OK) &&
308 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
309 (request.till != 0) &&
310 (as_reply->enc_part2->times.renew_till > request.till))
312 retval = KRB5_KDCREP_MODIFIED;
315 if ((request.from == 0) &&
316 !in_clock_skew(as_reply->enc_part2->times.starttime)) {
317 retval = KRB5_KDCREP_SKEW;
321 if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME)
322 krb5_set_time_offsets(context,
323 as_reply->enc_part2->times.authtime - time_now,
326 /* XXX issue warning if as_reply->enc_part2->key_exp is nearby */
328 /* fill in the credentials */
329 if ((retval = krb5_copy_keyblock_contents(context,
330 as_reply->enc_part2->session,
334 creds->times = as_reply->enc_part2->times;
335 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot
336 be encrypted in skey */
337 creds->ticket_flags = as_reply->enc_part2->flags;
338 if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
342 creds->second_ticket.length = 0;
343 creds->second_ticket.data = 0;
345 if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
348 creds->ticket = *packet;
351 /* store it in the ccache! */
353 if ((retval = krb5_cc_store_cred(context, ccache, creds)))
358 *ret_as_reply = as_reply;
366 krb5_xfree(salt.data);
368 krb5_free_kdc_rep(context, as_reply);
372 krb5_free_keyblock(context, decrypt_key);
374 free(request.padata);
376 krb5_free_addresses(context, request.addresses);
380 * Clean up left over mess in credentials structure, in case of
384 if (creds->keyblock.contents) {
385 memset((char *)creds->keyblock.contents, 0, creds->keyblock.length);
386 krb5_xfree(creds->keyblock.contents);
387 creds->keyblock.contents = 0;
388 creds->keyblock.length = 0;
390 if (creds->ticket.data) {
391 krb5_xfree(creds->ticket.data);
392 creds->ticket.data = 0;
394 if (creds->addresses) {
395 krb5_free_addresses(context, creds->addresses);
396 creds->addresses = 0;