* auth_con.c, decrypt_tk.c, encode_kdc.c, encrypt_tk.c,
[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 #include "k5-int.h"
28
29 /*
30  All-purpose initial ticket routine, usually called via
31  krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey.
32
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.
38
39  key_proc is called to fill in the key to be used for decryption.
40  keyseed is passed on to key_proc.
41
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.
46
47  If addrs is non-NULL, it is used for the addresses requested.  If it is
48  null, the system standard addresses are used.
49
50  A succesful call will place the ticket in the credentials cache ccache
51  and fill in creds with the ticket information used/returned..
52
53  returns system errors, encryption errors
54
55  */
56
57
58 extern krb5_deltat krb5_clockskew;
59 #define in_clock_skew(date) (labs((date)-request.nonce) < krb5_clockskew)
60
61 /* some typedef's for the function args to make things look a bit cleaner */
62
63 typedef krb5_error_code (*git_key_proc) PROTOTYPE((krb5_context,
64                                                    const krb5_enctype,
65                                                    krb5_data *,
66                                                    krb5_const_pointer,
67                                                    krb5_keyblock **));
68
69 typedef krb5_error_code (*git_decrypt_proc) PROTOTYPE((krb5_context,
70                                                        const krb5_keyblock *,
71                                                        krb5_const_pointer,
72                                                        krb5_kdc_rep * ));
73 krb5_error_code
74 krb5_get_in_tkt(context, options, addrs, ktypes, ptypes, key_proc, keyseed,
75                 decrypt_proc, decryptarg, creds, ccache, ret_as_reply)
76     krb5_context context;
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;
85     krb5_creds * creds;
86     krb5_ccache ccache;
87     krb5_kdc_rep ** ret_as_reply;
88 {
89     krb5_enctype enctype, ktype;
90     krb5_kdc_req request;
91     krb5_kdc_rep *as_reply = 0;
92     krb5_error *err_reply;
93     krb5_error_code retval;
94     krb5_data *packet;
95     krb5_data reply;
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;
101     krb5_data salt;
102     char k4_version;            /* same type as *(krb5_data::data) */
103
104     if (! krb5_realm_compare(context, creds->client, creds->server))
105         return KRB5_IN_TKT_REALM_MISMATCH;
106
107     if (ret_as_reply)
108         *ret_as_reply = 0;
109     
110     request.msg_type = KRB5_AS_REQ;
111     if (!addrs)
112         krb5_os_localaddr(context, &request.addresses);
113     else
114         request.addresses = (krb5_address **) addrs;
115
116     reply.data = 0;
117
118     if (1) {
119             decrypt_key = 0;
120             request.padata = 0;
121     } else {
122             /* Pre authentication is not yet supported */
123
124             /*
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.
128              *
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.
134              */
135 /*    retval = (*key_proc)(context, enctype, &decrypt_key, keyseed, 0); */
136             if (retval)
137                     return retval;
138             request.padata = (krb5_pa_data **) malloc(sizeof(krb5_pa_data *)
139                                                       * 2);
140             if (!request.padata) {
141                 retval = ENOMEM;
142                 goto cleanup;
143             }
144             
145           /*  retval = krb5_obtain_padata(context, ptypes[0], creds->client,
146                                         request.addresses, decrypt_key,
147                                         &padata); */
148             if (retval)
149                 goto cleanup;
150 /*          request.padata[0] = padata; */
151             request.padata[1] = 0;
152     }
153     
154     request.kdc_options = options;
155     request.client = creds->client;
156     request.server = creds->server;
157
158     request.from = creds->times.starttime;
159     request.till = creds->times.endtime;
160     request.rtime = creds->times.renew_till;
161
162     if ((retval = krb5_timeofday(context, &time_now)))
163         goto cleanup;
164
165     /* XXX we know they are the same size... */
166     request.nonce = (krb5_int32) time_now;
167
168     if (ktypes) 
169         request.ktype = ktypes;
170     else 
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;
177
178     if ((retval = krb5_timeofday(context, &time_now)))
179         goto cleanup;
180
181     /* XXX we know they are the same size... */
182     request.nonce = (krb5_int32) time_now;
183
184     /* encode & send to KDC */
185     retval = encode_krb5_as_req(&request, &packet);
186     if (!ktypes)
187       free(request.ktype);
188     if (retval)
189       goto cleanup;
190
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);
195     if (retval)
196         goto cleanup;
197
198     /* now decode the reply...could be error or as_rep */
199
200     if (krb5_is_krb_error(&reply)) {
201         if ((retval = decode_krb5_error(&reply, &err_reply)))
202             /* some other error code--??? */        
203             goto cleanup;
204     
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,
208                                                  &preauth_to_use);
209             /* XXX we need to actually do something with the info */
210             krb5_free_pa_data(context, preauth_to_use);
211         }
212
213         retval = err_reply->error + ERROR_TABLE_BASE_krb5;
214
215         /* XXX somehow make error msg text available to application? */
216
217         krb5_free_error(context, err_reply);
218         goto cleanup;
219     }
220
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;
227
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];
232         t_switch &= ~1;
233
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;
238         } else {
239             retval = KRB5KRB_AP_ERR_MSG_TYPE;
240         }
241         goto cleanup;
242     }
243     if ((retval = decode_krb5_as_rep(&reply, &as_reply)))
244         /* some other error code ??? */
245         goto cleanup;
246
247     if (as_reply->msg_type != KRB5_AS_REP) {
248         retval = KRB5KRB_AP_ERR_MSG_TYPE;
249         goto cleanup;
250     }
251
252     /* Encryption type, enctype, */
253     enctype = as_reply->ticket->enc_part.enctype;
254
255     /* and salt */
256     if (as_reply->padata) {
257         krb5_pa_data **ptr;
258
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;
264                 use_salt = 1;
265                 break;
266             }
267         }
268     } 
269     if (!use_salt) {
270         /* need to use flattened principal */
271         if ((retval = krb5_principal2salt(context, creds->client, &salt)))
272             return(retval);
273         f_salt = 1;
274     }
275
276     /* it was a kdc_rep--decrypt & check */
277     /* Generate the key, if we haven't done so already. */
278     if (!decrypt_key) {
279             if ((retval = (*key_proc)(context, enctype, & salt, keyseed,
280                                       &decrypt_key)))
281                 goto cleanup;
282     }
283     
284     if ((retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply)))
285         goto cleanup;
286
287     krb5_free_keyblock(context, decrypt_key);
288     decrypt_key = 0;
289     
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))
311         ) {
312         retval = KRB5_KDCREP_MODIFIED;
313         goto cleanup;
314     }
315     if ((request.from == 0) &&
316         !in_clock_skew(as_reply->enc_part2->times.starttime)) {
317         retval = KRB5_KDCREP_SKEW;
318         goto cleanup;
319     }
320  
321    if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME)
322        krb5_set_time_offsets(context,
323                              as_reply->enc_part2->times.authtime - time_now,
324                              0);
325
326     /* XXX issue warning if as_reply->enc_part2->key_exp is nearby */
327         
328     /* fill in the credentials */
329     if ((retval = krb5_copy_keyblock_contents(context, 
330                                               as_reply->enc_part2->session,
331                                               &creds->keyblock)))
332         goto cleanup;
333
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,
339                                       &creds->addresses)))
340         goto cred_cleanup;
341
342     creds->second_ticket.length = 0;
343     creds->second_ticket.data = 0;
344
345     if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
346         goto cred_cleanup;
347
348     creds->ticket = *packet;
349     krb5_xfree(packet);
350
351     /* store it in the ccache! */
352     if (ccache) {
353       if ((retval = krb5_cc_store_cred(context, ccache, creds)))
354         goto cred_cleanup;
355     }
356
357     if (ret_as_reply) {
358         *ret_as_reply = as_reply;
359         as_reply = 0;
360     }
361
362     retval = 0;
363     
364 cleanup:
365     if (f_salt)
366         krb5_xfree(salt.data);
367     if (as_reply)
368         krb5_free_kdc_rep(context, as_reply);
369     if (reply.data)
370         free(reply.data);
371     if (decrypt_key)
372         krb5_free_keyblock(context, decrypt_key);
373     if (request.padata)
374         free(request.padata);
375     if (!addrs)
376         krb5_free_addresses(context, request.addresses);
377     return retval;
378     
379     /*
380      * Clean up left over mess in credentials structure, in case of 
381      * error
382      */
383 cred_cleanup:
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;
389     }
390     if (creds->ticket.data) {
391         krb5_xfree(creds->ticket.data);
392         creds->ticket.data = 0;
393     }
394     if (creds->addresses) {
395         krb5_free_addresses(context, creds->addresses);
396         creds->addresses = 0;
397     }
398     goto cleanup;
399 }
400