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, requesting encryption type etype, and using
40 creds->times.starttime, creds->times.endtime, creds->times.renew_till
41 as from, till, and rtime. creds->times.renew_till is ignored unless
42 the RENEWABLE option is requested.
44 key_proc is called to fill in the key to be used for decryption.
45 keyseed is passed on to key_proc.
47 decrypt_proc is called to perform the decryption of the response (the
48 encrypted part is in dec_rep->enc_part; the decrypted part should be
49 allocated and filled into dec_rep->enc_part2
50 arg is passed on to decrypt_proc.
52 If addrs is non-NULL, it is used for the addresses requested. If it is
53 null, the system standard addresses are used.
55 A succesful call will place the ticket in the credentials cache ccache
56 and fill in creds with the ticket information used/returned..
58 returns system errors, encryption errors
63 extern krb5_deltat krb5_clockskew;
64 #define in_clock_skew(date) (abs((date)-request.nonce) < krb5_clockskew)
66 /* some typedef's for the function args to make things look a bit cleaner */
68 /* widen this prototype if need be */
69 #include <krb5/widen.h>
70 typedef krb5_error_code (*git_key_proc) PROTOTYPE((const krb5_keytype,
74 #include <krb5/narrow.h>
76 typedef krb5_error_code (*git_decrypt_proc) PROTOTYPE((const krb5_keyblock *,
80 krb5_get_in_tkt(DECLARG(const krb5_flags, options),
81 DECLARG(krb5_address * const *, addrs),
82 DECLARG(const krb5_preauthtype, pre_auth_type),
83 DECLARG(const krb5_enctype, etype),
84 DECLARG(const krb5_keytype, keytype),
85 DECLARG(git_key_proc, key_proc),
86 DECLARG(krb5_const_pointer, keyseed),
87 DECLARG(git_decrypt_proc, decrypt_proc),
88 DECLARG(krb5_const_pointer, decryptarg),
89 DECLARG(krb5_creds *, creds),
90 DECLARG(krb5_ccache, ccache),
91 DECLARG(krb5_kdc_rep **, ret_as_reply))
92 OLDDECLARG(const krb5_flags, options)
93 OLDDECLARG(krb5_address * const *, addrs)
94 OLDDECLARG(const krb5_preauthtype, pre_auth_type)
95 OLDDECLARG(const krb5_enctype, etype)
96 OLDDECLARG(const krb5_keytype, keytype)
97 OLDDECLARG(git_key_proc, key_proc)
98 OLDDECLARG(krb5_const_pointer, keyseed)
99 OLDDECLARG(git_decrypt_proc, decrypt_proc)
100 OLDDECLARG(krb5_const_pointer, decryptarg)
101 OLDDECLARG(krb5_creds *, creds)
102 OLDDECLARG(krb5_ccache, ccache)
103 OLDDECLARG(krb5_kdc_rep **, ret_as_reply)
105 krb5_kdc_req request;
106 krb5_kdc_rep *as_reply = 0;
107 krb5_error *err_reply;
108 krb5_error_code retval;
111 krb5_keyblock *decrypt_key = 0;
112 krb5_enctype etypes[1];
113 krb5_timestamp time_now;
114 krb5_pa_data *padata;
116 if (! krb5_realm_compare(creds->client, creds->server))
117 return KRB5_IN_TKT_REALM_MISMATCH;
122 request.msg_type = KRB5_AS_REQ;
124 krb5_os_localaddr(&request.addresses);
126 request.addresses = (krb5_address **) addrs;
130 if (pre_auth_type == KRB5_PADATA_NONE) {
135 * First, we get the user's key. We assume we will need
136 * it for the pre-authentication. Actually, this could
137 * possibly not be the case, but it's usually true.
139 * XXX Problem here: if we're doing preauthentication,
140 * we're getting the key before we get the KDC hit as to
141 * which salting algorithm to use; hence, we're using the
142 * default. But if we're changing salts, because of a
143 * realm renaming, or some such, this won't work.
145 retval = (*key_proc)(keytype, &decrypt_key, keyseed, 0);
148 request.padata = (krb5_pa_data **) malloc(sizeof(krb5_pa_data *)
150 if (!request.padata) {
155 retval = krb5_obtain_padata(pre_auth_type, creds->client,
156 request.addresses, decrypt_key,
160 request.padata[0] = padata;
161 request.padata[1] = 0;
164 request.kdc_options = options;
165 request.client = creds->client;
166 request.server = creds->server;
168 request.from = creds->times.starttime;
169 request.till = creds->times.endtime;
170 request.rtime = creds->times.renew_till;
171 if (retval = krb5_timeofday(&time_now))
174 /* XXX we know they are the same size... */
175 request.nonce = (krb5_int32) time_now;
178 request.etype = etypes;
180 request.second_ticket = 0;
181 request.authorization_data.ciphertext.length = 0;
182 request.authorization_data.ciphertext.data = 0;
183 request.unenc_authdata = 0;
185 /* encode & send to KDC */
186 if (retval = encode_krb5_as_req(&request, &packet))
189 retval = krb5_sendto_kdc(packet, krb5_princ_realm(creds->client), &reply);
190 krb5_free_data(packet);
194 /* now decode the reply...could be error or as_rep */
196 if (krb5_is_krb_error(&reply)) {
197 if (retval = decode_krb5_error(&reply, &err_reply))
198 /* some other error code--??? */
201 /* it was an error */
203 if ((err_reply->ctime != request.nonce) ||
204 !krb5_principal_compare(err_reply->server, request.server) ||
205 !krb5_principal_compare(err_reply->client, request.client))
206 retval = KRB5_KDCREP_MODIFIED;
208 retval = err_reply->error + ERROR_TABLE_BASE_krb5;
210 /* XXX somehow make error msg text available to application? */
212 krb5_free_error(err_reply);
216 if (!krb5_is_as_rep(&reply)) {
217 retval = KRB5KRB_AP_ERR_MSG_TYPE;
220 if (retval = decode_krb5_as_rep(&reply, &as_reply))
221 /* some other error code ??? */
224 if (as_reply->msg_type != KRB5_AS_REP) {
225 retval = KRB5KRB_AP_ERR_MSG_TYPE;
229 /* it was a kdc_rep--decrypt & check */
231 /* Generate the key, if we haven't done so already. */
233 if (retval = (*key_proc)(keytype, &decrypt_key, keyseed,
238 if (retval = (*decrypt_proc)(decrypt_key, decryptarg, as_reply))
241 krb5_free_keyblock(decrypt_key);
244 /* check the contents for sanity: */
245 if (!as_reply->enc_part2->times.starttime)
246 as_reply->enc_part2->times.starttime =
247 as_reply->enc_part2->times.authtime;
248 if (!krb5_principal_compare(as_reply->client, request.client)
249 || !krb5_principal_compare(as_reply->enc_part2->server, request.server)
250 || !krb5_principal_compare(as_reply->ticket->server, request.server)
251 || (request.nonce != as_reply->enc_part2->nonce)
252 /* XXX check for extraneous flags */
253 /* XXX || (!krb5_addresses_compare(addrs, as_reply->enc_part2->caddrs)) */
254 || ((request.from != 0) &&
255 (request.from != as_reply->enc_part2->times.starttime))
256 || ((request.till != 0) &&
257 (as_reply->enc_part2->times.endtime > request.till))
258 || ((request.kdc_options & KDC_OPT_RENEWABLE) &&
259 (request.rtime != 0) &&
260 (as_reply->enc_part2->times.renew_till > request.rtime))
261 || ((request.kdc_options & KDC_OPT_RENEWABLE_OK) &&
262 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
263 (request.till != 0) &&
264 (as_reply->enc_part2->times.renew_till > request.till))
266 retval = KRB5_KDCREP_MODIFIED;
269 if ((request.from == 0) &&
270 !in_clock_skew(as_reply->enc_part2->times.starttime)) {
271 retval = KRB5_KDCREP_MODIFIED;
276 /* XXX issue warning if as_reply->enc_part2->key_exp is nearby */
278 /* fill in the credentials */
279 if (retval = krb5_copy_keyblock_contents(as_reply->enc_part2->session,
283 creds->times = as_reply->enc_part2->times;
284 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot
285 be encrypted in skey */
286 creds->ticket_flags = as_reply->enc_part2->flags;
287 if (retval = krb5_copy_addresses(as_reply->enc_part2->caddrs,
291 creds->second_ticket.length = 0;
292 creds->second_ticket.data = 0;
294 if (retval = encode_krb5_ticket(as_reply->ticket, &packet))
297 creds->ticket = *packet;
300 /* store it in the ccache! */
301 if (retval = krb5_cc_store_cred(ccache, creds))
305 *ret_as_reply = as_reply;
313 krb5_free_kdc_rep(as_reply);
317 krb5_free_keyblock(decrypt_key);
319 free(request.padata);
321 krb5_free_addresses(request.addresses);
325 * Clean up left over mess in credentials structure, in case of
329 if (creds->keyblock.contents) {
330 memset((char *)creds->keyblock.contents, 0, creds->keyblock.length);
331 krb5_xfree(creds->keyblock.contents);
332 creds->keyblock.contents = 0;
333 creds->keyblock.length = 0;
335 if (creds->ticket.data) {
336 krb5_xfree(creds->ticket.data);
337 creds->ticket.data = 0;
339 if (creds->addresses) {
340 krb5_free_addresses(creds->addresses);
341 creds->addresses = 0;