Return new error codes KRB5_IN_TKT_REALM_MISTCH and KRB5_KDCREP_SKEW
[krb5.git] / src / lib / krb5 / krb / get_in_tkt.c
1 /*
2  * lib/krb5/krb/get_in_tkt.c
3  *
4  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
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.
11  * 
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.
22  * 
23  *
24  * krb5_get_in_tkt()
25  */
26
27
28 #include <krb5/krb5.h>
29 #include <krb5/asn1.h>
30 #include <krb5/los-proto.h>
31 #include <krb5/ext-proto.h>
32
33 /*
34  All-purpose initial ticket routine, usually called via
35  krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey.
36
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.
43
44  key_proc is called to fill in the key to be used for decryption.
45  keyseed is passed on to key_proc.
46
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.
51
52  If addrs is non-NULL, it is used for the addresses requested.  If it is
53  null, the system standard addresses are used.
54
55  A succesful call will place the ticket in the credentials cache ccache
56  and fill in creds with the ticket information used/returned..
57
58  returns system errors, encryption errors
59
60  */
61
62
63 extern krb5_deltat krb5_clockskew;
64 #define in_clock_skew(date) (abs((date)-request.nonce) < krb5_clockskew)
65
66 /* some typedef's for the function args to make things look a bit cleaner */
67
68 /* widen this prototype if need be */
69 #include <krb5/widen.h>
70 typedef krb5_error_code (*git_key_proc) PROTOTYPE((const krb5_keytype,
71                                                    krb5_keyblock **,
72                                                    krb5_const_pointer,
73                                                    krb5_pa_data **));
74 #include <krb5/narrow.h>
75
76 typedef krb5_error_code (*git_decrypt_proc) PROTOTYPE((const krb5_keyblock *,
77                                                        krb5_const_pointer,
78                                                        krb5_kdc_rep * ));
79 krb5_error_code
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)
104 {
105     krb5_kdc_req request;
106     krb5_kdc_rep *as_reply = 0;
107     krb5_error *err_reply;
108     krb5_error_code retval;
109     krb5_data *packet;
110     krb5_data reply;
111     krb5_keyblock *decrypt_key = 0;
112     krb5_enctype etypes[1];
113     krb5_timestamp time_now;
114     krb5_pa_data        *padata;
115
116     if (! krb5_realm_compare(creds->client, creds->server))
117         return KRB5_IN_TKT_REALM_MISMATCH;
118
119     if (ret_as_reply)
120         *ret_as_reply = 0;
121     
122     request.msg_type = KRB5_AS_REQ;
123     if (!addrs)
124         krb5_os_localaddr(&request.addresses);
125     else
126         request.addresses = (krb5_address **) addrs;
127
128     reply.data = 0;
129
130     if (pre_auth_type == KRB5_PADATA_NONE) {
131             decrypt_key = 0;
132             request.padata = 0;
133     } else {
134             /*
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.
138              *
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.
144              */
145             retval = (*key_proc)(keytype, &decrypt_key, keyseed, 0);
146             if (retval)
147                     return retval;
148             request.padata = (krb5_pa_data **) malloc(sizeof(krb5_pa_data *)
149                                                       * 2);
150             if (!request.padata) {
151                 retval = ENOMEM;
152                 goto cleanup;
153             }
154             
155             retval = krb5_obtain_padata(pre_auth_type, creds->client,
156                                         request.addresses, decrypt_key,
157                                         &padata);
158             if (retval)
159                 goto cleanup;
160             request.padata[0] = padata;
161             request.padata[1] = 0;
162     }
163     
164     request.kdc_options = options;
165     request.client = creds->client;
166     request.server = creds->server;
167
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))
172         goto cleanup;
173
174     /* XXX we know they are the same size... */
175     request.nonce = (krb5_int32) time_now;
176
177     etypes[0] = etype;
178     request.etype = etypes;
179     request.netypes = 1;
180     request.second_ticket = 0;
181     request.authorization_data.ciphertext.length = 0;
182     request.authorization_data.ciphertext.data = 0;
183     request.unenc_authdata = 0;
184
185     /* encode & send to KDC */
186     if (retval = encode_krb5_as_req(&request, &packet))
187         goto cleanup;
188
189     retval = krb5_sendto_kdc(packet, krb5_princ_realm(creds->client), &reply);
190     krb5_free_data(packet);
191     if (retval)
192         goto cleanup;
193
194     /* now decode the reply...could be error or as_rep */
195
196     if (krb5_is_krb_error(&reply)) {
197         if (retval = decode_krb5_error(&reply, &err_reply))
198             /* some other error code--??? */        
199             goto cleanup;
200     
201         /* it was an error */
202
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;
207         else
208             retval = err_reply->error + ERROR_TABLE_BASE_krb5;
209
210         /* XXX somehow make error msg text available to application? */
211
212         krb5_free_error(err_reply);
213         goto cleanup;
214     }
215
216     if (!krb5_is_as_rep(&reply)) {
217         retval = KRB5KRB_AP_ERR_MSG_TYPE;
218         goto cleanup;
219     }
220     if (retval = decode_krb5_as_rep(&reply, &as_reply))
221         /* some other error code ??? */
222         goto cleanup;
223
224     if (as_reply->msg_type != KRB5_AS_REP) {
225         retval = KRB5KRB_AP_ERR_MSG_TYPE;
226         goto cleanup;
227     }
228
229     /* it was a kdc_rep--decrypt & check */
230
231      /* Generate the key, if we haven't done so already. */
232     if (!decrypt_key) {
233             if (retval = (*key_proc)(keytype, &decrypt_key, keyseed,
234                                      as_reply->padata))
235                 goto cleanup;
236     }
237     
238     if (retval = (*decrypt_proc)(decrypt_key, decryptarg, as_reply))
239         goto cleanup;
240
241     krb5_free_keyblock(decrypt_key);
242     decrypt_key = 0;
243     
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))
265         ) {
266         retval = KRB5_KDCREP_MODIFIED;
267         goto cleanup;
268     }
269     if ((request.from == 0) &&
270         !in_clock_skew(as_reply->enc_part2->times.starttime)) {
271         retval = KRB5_KDCREP_MODIFIED;
272         goto cleanup;
273     }
274     
275
276     /* XXX issue warning if as_reply->enc_part2->key_exp is nearby */
277         
278     /* fill in the credentials */
279     if (retval = krb5_copy_keyblock_contents(as_reply->enc_part2->session,
280                                              &creds->keyblock))
281         goto cleanup;
282
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,
288                                      &creds->addresses))
289         goto cred_cleanup;
290
291     creds->second_ticket.length = 0;
292     creds->second_ticket.data = 0;
293
294     if (retval = encode_krb5_ticket(as_reply->ticket, &packet))
295         goto cred_cleanup;
296
297     creds->ticket = *packet;
298     krb5_xfree(packet);
299
300     /* store it in the ccache! */
301     if (retval = krb5_cc_store_cred(ccache, creds))
302         goto cred_cleanup;
303
304     if (ret_as_reply) {
305         *ret_as_reply = as_reply;
306         as_reply = 0;
307     }
308
309     retval = 0;
310     
311 cleanup:
312     if (as_reply)
313         krb5_free_kdc_rep(as_reply);
314     if (reply.data)
315         free(reply.data);
316     if (decrypt_key)
317         krb5_free_keyblock(decrypt_key);
318     if (request.padata)
319         free(request.padata);
320     if (!addrs)
321         krb5_free_addresses(request.addresses);
322     return retval;
323     
324     /*
325      * Clean up left over mess in credentials structure, in case of 
326      * error
327      */
328 cred_cleanup:
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;
334     }
335     if (creds->ticket.data) {
336         krb5_xfree(creds->ticket.data);
337         creds->ticket.data = 0;
338     }
339     if (creds->addresses) {
340         krb5_free_addresses(creds->addresses);
341         creds->addresses = 0;
342     }
343     goto cleanup;
344 }
345