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 (INTERFACE *git_key_proc) PROTOTYPE((krb5_context,
69 typedef krb5_error_code (INTERFACE *git_decrypt_proc) PROTOTYPE((krb5_context,
70 const krb5_keyblock *,
73 krb5_error_code INTERFACE
74 krb5_get_in_tkt(context, options, addrs, etypes, 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 * etypes;
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;
92 krb5_kdc_rep *as_reply = 0;
93 krb5_error *err_reply;
94 krb5_error_code retval;
97 krb5_keyblock *decrypt_key = 0;
98 krb5_timestamp time_now;
99 /* krb5_pa_data *padata; */
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(&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, keytype, &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;
161 if (retval = krb5_timeofday(context, &time_now))
164 /* XXX we know they are the same size... */
165 request.nonce = (krb5_int32) time_now;
168 request.etype = etypes;
170 krb5_get_default_in_tkt_etypes(context, &request.etype);
171 for (request.netypes = 0;request.etype[request.netypes];request.netypes++);
172 request.authorization_data.ciphertext.length = 0;
173 request.authorization_data.ciphertext.data = 0;
174 request.unenc_authdata = 0;
175 request.second_ticket = 0;
177 /* encode & send to KDC */
178 retval = encode_krb5_as_req(&request, &packet);
184 k4_version = packet->data[0];
185 retval = krb5_sendto_kdc(context, packet,
186 krb5_princ_realm(context, creds->client), &reply);
187 krb5_free_data(context, packet);
191 /* now decode the reply...could be error or as_rep */
193 if (krb5_is_krb_error(&reply)) {
194 if (retval = decode_krb5_error(&reply, &err_reply))
195 /* some other error code--??? */
198 /* it was an error */
200 if ((err_reply->ctime != request.nonce) ||
201 !krb5_principal_compare(context, err_reply->server, request.server) ||
202 !krb5_principal_compare(context, err_reply->client, request.client))
203 retval = KRB5_KDCREP_MODIFIED;
205 retval = err_reply->error + ERROR_TABLE_BASE_krb5;
207 /* XXX somehow make error msg text available to application? */
209 krb5_free_error(context, err_reply);
213 if (!krb5_is_as_rep(&reply)) {
214 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
215 #define V4_KRB_PROT_VERSION 4
216 #define V4_AUTH_MSG_ERR_REPLY (5<<1)
217 /* check here for V4 reply */
218 unsigned int t_switch;
220 /* From v4 g_in_tkt.c: This used to be
221 switch (pkt_msg_type(rpkt) & ~1) {
222 but SCO 3.2v4 cc compiled that incorrectly. */
223 t_switch = reply.data[1];
226 if (t_switch == V4_AUTH_MSG_ERR_REPLY
227 && (reply.data[0] == V4_KRB_PROT_VERSION
228 || reply.data[0] == k4_version)) {
229 retval = KRB5KRB_AP_ERR_V4_REPLY;
231 retval = KRB5KRB_AP_ERR_MSG_TYPE;
235 if (retval = decode_krb5_as_rep(&reply, &as_reply))
236 /* some other error code ??? */
239 if (as_reply->msg_type != KRB5_AS_REP) {
240 retval = KRB5KRB_AP_ERR_MSG_TYPE;
244 /* Encryption type, keytype, */
245 etype = as_reply->ticket->enc_part.etype;
246 keytype = krb5_csarray[etype]->system->proto_keytype;
249 if (as_reply->padata) {
252 for (ptr = as_reply->padata; *ptr; ptr++) {
253 if ((*ptr)->pa_type == KRB5_PADATA_PW_SALT) {
254 /* use KDC-supplied salt, instead of default */
255 salt.data = (char *)(*ptr)->contents;
256 salt.length = (*ptr)->length;
263 /* need to use flattened principal */
264 if (retval = krb5_principal2salt(context, creds->client, &salt))
269 /* it was a kdc_rep--decrypt & check */
270 /* Generate the key, if we haven't done so already. */
272 if (retval = (*key_proc)(context, keytype, & salt, keyseed,
277 if (retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply))
280 krb5_free_keyblock(context, decrypt_key);
283 /* check the contents for sanity: */
284 if (!as_reply->enc_part2->times.starttime)
285 as_reply->enc_part2->times.starttime =
286 as_reply->enc_part2->times.authtime;
287 if (!krb5_principal_compare(context, as_reply->client, request.client)
288 || !krb5_principal_compare(context, as_reply->enc_part2->server, request.server)
289 || !krb5_principal_compare(context, as_reply->ticket->server, request.server)
290 || (request.nonce != as_reply->enc_part2->nonce)
291 /* XXX check for extraneous flags */
292 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
293 || ((request.from != 0) &&
294 (request.from != as_reply->enc_part2->times.starttime))
295 || ((request.till != 0) &&
296 (as_reply->enc_part2->times.endtime > request.till))
297 || ((request.kdc_options & KDC_OPT_RENEWABLE) &&
298 (request.rtime != 0) &&
299 (as_reply->enc_part2->times.renew_till > request.rtime))
300 || ((request.kdc_options & KDC_OPT_RENEWABLE_OK) &&
301 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
302 (request.till != 0) &&
303 (as_reply->enc_part2->times.renew_till > request.till))
305 retval = KRB5_KDCREP_MODIFIED;
308 if ((request.from == 0) &&
309 !in_clock_skew(as_reply->enc_part2->times.starttime)) {
310 retval = KRB5_KDCREP_SKEW;
315 /* XXX issue warning if as_reply->enc_part2->key_exp is nearby */
317 /* fill in the credentials */
318 if (retval = krb5_copy_keyblock_contents(context,
319 as_reply->enc_part2->session,
322 creds->keyblock.etype = as_reply->ticket->enc_part.etype;
324 creds->times = as_reply->enc_part2->times;
325 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot
326 be encrypted in skey */
327 creds->ticket_flags = as_reply->enc_part2->flags;
328 if (retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
332 creds->second_ticket.length = 0;
333 creds->second_ticket.data = 0;
335 if (retval = encode_krb5_ticket(as_reply->ticket, &packet))
338 creds->ticket = *packet;
341 /* store it in the ccache! */
342 if (retval = krb5_cc_store_cred(context, ccache, creds))
346 *ret_as_reply = as_reply;
354 krb5_xfree(salt.data);
356 krb5_free_kdc_rep(context, as_reply);
360 krb5_free_keyblock(context, decrypt_key);
362 free(request.padata);
364 krb5_free_addresses(context, request.addresses);
368 * Clean up left over mess in credentials structure, in case of
372 if (creds->keyblock.contents) {
373 memset((char *)creds->keyblock.contents, 0, creds->keyblock.length);
374 krb5_xfree(creds->keyblock.contents);
375 creds->keyblock.contents = 0;
376 creds->keyblock.length = 0;
378 if (creds->ticket.data) {
379 krb5_xfree(creds->ticket.data);
380 creds->ticket.data = 0;
382 if (creds->addresses) {
383 krb5_free_addresses(context, creds->addresses);
384 creds->addresses = 0;