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.
28 #include <krb5/krb5.h>
29 #include <krb5/asn1.h>
30 #include <krb5/los-proto.h>
31 #include <krb5/ext-proto.h>
34 All-purpose initial ticket routine, usually called via
35 krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey.
37 Attempts to get an initial ticket for creds->client to use server
38 creds->server, (realm is taken from creds->client), with options
39 options, and using creds->times.starttime, creds->times.endtime,
40 creds->times.renew_till as from, till, and rtime.
41 creds->times.renew_till is ignored unless the RENEWABLE option is requested.
43 key_proc is called to fill in the key to be used for decryption.
44 keyseed is passed on to key_proc.
46 decrypt_proc is called to perform the decryption of the response (the
47 encrypted part is in dec_rep->enc_part; the decrypted part should be
48 allocated and filled into dec_rep->enc_part2
49 arg is passed on to decrypt_proc.
51 If addrs is non-NULL, it is used for the addresses requested. If it is
52 null, the system standard addresses are used.
54 A succesful call will place the ticket in the credentials cache ccache
55 and fill in creds with the ticket information used/returned..
57 returns system errors, encryption errors
62 extern krb5_deltat krb5_clockskew;
63 #define in_clock_skew(date) (abs((date)-request.nonce) < krb5_clockskew)
65 /* some typedef's for the function args to make things look a bit cleaner */
67 typedef krb5_error_code (*git_key_proc) PROTOTYPE((krb5_context,
73 typedef krb5_error_code (*git_decrypt_proc) PROTOTYPE((krb5_context,
74 const krb5_keyblock *,
78 krb5_get_in_tkt(context, options, addrs, etypes, ptypes, key_proc, keyseed,
79 decrypt_proc, decryptarg, creds, ccache, ret_as_reply)
81 const krb5_flags options;
82 krb5_address * const * addrs;
83 krb5_enctype * etypes;
84 krb5_preauthtype * ptypes;
85 git_key_proc key_proc;
86 krb5_const_pointer keyseed;
87 git_decrypt_proc decrypt_proc;
88 krb5_const_pointer decryptarg;
91 krb5_kdc_rep ** ret_as_reply;
96 krb5_kdc_rep *as_reply = 0;
97 krb5_error *err_reply;
98 krb5_error_code retval;
101 krb5_keyblock *decrypt_key = 0;
102 krb5_timestamp time_now;
103 krb5_pa_data *padata;
104 int f_salt = 0, use_salt = 0;
106 char k4_version; /* same type as *(krb5_data::data) */
108 if (! krb5_realm_compare(context, creds->client, creds->server))
109 return KRB5_IN_TKT_REALM_MISMATCH;
114 request.msg_type = KRB5_AS_REQ;
116 krb5_os_localaddr(&request.addresses);
118 request.addresses = (krb5_address **) addrs;
126 /* Pre authentication is not yet supported */
129 * First, we get the user's key. We assume we will need
130 * it for the pre-authentication. Actually, this could
131 * possibly not be the case, but it's usually true.
133 * XXX Problem here: if we're doing preauthentication,
134 * we're getting the key before we get the KDC hit as to
135 * which salting algorithm to use; hence, we're using the
136 * default. But if we're changing salts, because of a
137 * realm renaming, or some such, this won't work.
139 /* retval = (*key_proc)(context, keytype, &decrypt_key, keyseed, 0); */
142 request.padata = (krb5_pa_data **) malloc(sizeof(krb5_pa_data *)
144 if (!request.padata) {
149 /* retval = krb5_obtain_padata(context, ptypes[0], creds->client,
150 request.addresses, decrypt_key,
154 /* request.padata[0] = padata; */
155 request.padata[1] = 0;
158 request.kdc_options = options;
159 request.client = creds->client;
160 request.server = creds->server;
162 request.from = creds->times.starttime;
163 request.till = creds->times.endtime;
164 request.rtime = creds->times.renew_till;
165 if (retval = krb5_timeofday(context, &time_now))
168 /* XXX we know they are the same size... */
169 request.nonce = (krb5_int32) time_now;
172 request.etype = etypes;
174 krb5_get_default_in_tkt_etypes(context, &request.etype);
175 for (request.netypes = 0;request.etype[request.netypes];request.netypes++);
176 request.authorization_data.ciphertext.length = 0;
177 request.authorization_data.ciphertext.data = 0;
178 request.unenc_authdata = 0;
179 request.second_ticket = 0;
181 /* encode & send to KDC */
182 if (retval = encode_krb5_as_req(&request, &packet))
185 k4_version = packet->data[0];
186 retval = krb5_sendto_kdc(context, packet,
187 krb5_princ_realm(context, creds->client), &reply);
188 krb5_free_data(context, packet);
192 /* now decode the reply...could be error or as_rep */
194 if (krb5_is_krb_error(&reply)) {
195 if (retval = decode_krb5_error(&reply, &err_reply))
196 /* some other error code--??? */
199 /* it was an error */
201 if ((err_reply->ctime != request.nonce) ||
202 !krb5_principal_compare(context, err_reply->server, request.server) ||
203 !krb5_principal_compare(context, err_reply->client, request.client))
204 retval = KRB5_KDCREP_MODIFIED;
206 retval = err_reply->error + ERROR_TABLE_BASE_krb5;
208 /* XXX somehow make error msg text available to application? */
210 krb5_free_error(context, err_reply);
214 if (!krb5_is_as_rep(&reply)) {
215 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
216 #define V4_KRB_PROT_VERSION 4
217 #define V4_AUTH_MSG_ERR_REPLY (5<<1)
218 /* check here for V4 reply */
219 unsigned int t_switch;
221 /* From v4 g_in_tkt.c: This used to be
222 switch (pkt_msg_type(rpkt) & ~1) {
223 but SCO 3.2v4 cc compiled that incorrectly. */
224 t_switch = reply.data[1];
227 if (t_switch == V4_AUTH_MSG_ERR_REPLY
228 && (reply.data[0] == V4_KRB_PROT_VERSION
229 || reply.data[0] == k4_version)) {
230 retval = KRB5KRB_AP_ERR_V4_REPLY;
232 retval = KRB5KRB_AP_ERR_MSG_TYPE;
236 if (retval = decode_krb5_as_rep(&reply, &as_reply))
237 /* some other error code ??? */
240 if (as_reply->msg_type != KRB5_AS_REP) {
241 retval = KRB5KRB_AP_ERR_MSG_TYPE;
245 /* Encryption type, keytype, */
246 etype = as_reply->ticket->enc_part.etype;
247 keytype = krb5_csarray[etype]->system->proto_keytype;
250 if (as_reply->padata) {
253 for (ptr = as_reply->padata; *ptr; ptr++) {
254 if ((*ptr)->pa_type == KRB5_PADATA_PW_SALT) {
255 /* use KDC-supplied salt, instead of default */
256 salt.data = (char *)(*ptr)->contents;
257 salt.length = (*ptr)->length;
264 /* need to use flattened principal */
265 if (retval = krb5_principal2salt(context, creds->client, &salt))
270 /* it was a kdc_rep--decrypt & check */
271 /* Generate the key, if we haven't done so already. */
273 if (retval = (*key_proc)(context, keytype, & salt, keyseed,
278 if (retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply))
281 krb5_free_keyblock(context, decrypt_key);
284 /* check the contents for sanity: */
285 if (!as_reply->enc_part2->times.starttime)
286 as_reply->enc_part2->times.starttime =
287 as_reply->enc_part2->times.authtime;
288 if (!krb5_principal_compare(context, as_reply->client, request.client)
289 || !krb5_principal_compare(context, as_reply->enc_part2->server, request.server)
290 || !krb5_principal_compare(context, as_reply->ticket->server, request.server)
291 || (request.nonce != as_reply->enc_part2->nonce)
292 /* XXX check for extraneous flags */
293 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
294 || ((request.from != 0) &&
295 (request.from != as_reply->enc_part2->times.starttime))
296 || ((request.till != 0) &&
297 (as_reply->enc_part2->times.endtime > request.till))
298 || ((request.kdc_options & KDC_OPT_RENEWABLE) &&
299 (request.rtime != 0) &&
300 (as_reply->enc_part2->times.renew_till > request.rtime))
301 || ((request.kdc_options & KDC_OPT_RENEWABLE_OK) &&
302 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
303 (request.till != 0) &&
304 (as_reply->enc_part2->times.renew_till > request.till))
306 retval = KRB5_KDCREP_MODIFIED;
309 if ((request.from == 0) &&
310 !in_clock_skew(as_reply->enc_part2->times.starttime)) {
311 retval = KRB5_KDCREP_MODIFIED;
316 /* XXX issue warning if as_reply->enc_part2->key_exp is nearby */
318 /* fill in the credentials */
319 if (retval = krb5_copy_keyblock_contents(context,
320 as_reply->enc_part2->session,
323 creds->keyblock.etype = as_reply->ticket->enc_part.etype;
325 creds->times = as_reply->enc_part2->times;
326 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot
327 be encrypted in skey */
328 creds->ticket_flags = as_reply->enc_part2->flags;
329 if (retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
333 creds->second_ticket.length = 0;
334 creds->second_ticket.data = 0;
336 if (retval = encode_krb5_ticket(as_reply->ticket, &packet))
339 creds->ticket = *packet;
342 /* store it in the ccache! */
343 if (retval = krb5_cc_store_cred(context, ccache, creds))
347 *ret_as_reply = as_reply;
355 krb5_xfree(salt.data);
357 krb5_free_kdc_rep(context, as_reply);
361 krb5_free_keyblock(context, decrypt_key);
363 free(request.padata);
365 krb5_free_addresses(context, request.addresses);
369 * Clean up left over mess in credentials structure, in case of
373 if (creds->keyblock.contents) {
374 memset((char *)creds->keyblock.contents, 0, creds->keyblock.length);
375 krb5_xfree(creds->keyblock.contents);
376 creds->keyblock.contents = 0;
377 creds->keyblock.length = 0;
379 if (creds->ticket.data) {
380 krb5_xfree(creds->ticket.data);
381 creds->ticket.data = 0;
383 if (creds->addresses) {
384 krb5_free_addresses(context, creds->addresses);
385 creds->addresses = 0;