Windows global stuff:
[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_keytype,
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, etypes, 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 * 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;
85     krb5_creds * creds;
86     krb5_ccache ccache;
87     krb5_kdc_rep ** ret_as_reply;
88 {
89     krb5_keytype keytype;
90     krb5_enctype etype;
91     krb5_kdc_req request;
92     krb5_kdc_rep *as_reply = 0;
93     krb5_error *err_reply;
94     krb5_error_code retval;
95     krb5_data *packet;
96     krb5_data reply;
97     krb5_keyblock *decrypt_key = 0;
98     krb5_timestamp time_now;
99 /*    krb5_pa_data      *padata; */
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(&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, keytype, &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     if (retval = krb5_timeofday(context, &time_now))
162         goto cleanup;
163
164     /* XXX we know they are the same size... */
165     request.nonce = (krb5_int32) time_now;
166
167     if (etypes) 
168         request.etype = etypes;
169     else 
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;
176
177     /* encode & send to KDC */
178     retval = encode_krb5_as_req(&request, &packet);
179     if (!etypes)
180       free(request.etype);
181     if (retval)
182       goto cleanup;
183
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);
188     if (retval)
189         goto cleanup;
190
191     /* now decode the reply...could be error or as_rep */
192
193     if (krb5_is_krb_error(&reply)) {
194         if (retval = decode_krb5_error(&reply, &err_reply))
195             /* some other error code--??? */        
196             goto cleanup;
197     
198         /* it was an error */
199
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;
204         else
205             retval = err_reply->error + ERROR_TABLE_BASE_krb5;
206
207         /* XXX somehow make error msg text available to application? */
208
209         krb5_free_error(context, err_reply);
210         goto cleanup;
211     }
212
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;
219
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];
224         t_switch &= ~1;
225
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;
230         } else {
231             retval = KRB5KRB_AP_ERR_MSG_TYPE;
232         }
233         goto cleanup;
234     }
235     if (retval = decode_krb5_as_rep(&reply, &as_reply))
236         /* some other error code ??? */
237         goto cleanup;
238
239     if (as_reply->msg_type != KRB5_AS_REP) {
240         retval = KRB5KRB_AP_ERR_MSG_TYPE;
241         goto cleanup;
242     }
243
244     /* Encryption type, keytype, */
245     etype = as_reply->ticket->enc_part.etype;
246     keytype = krb5_csarray[etype]->system->proto_keytype;
247
248     /* and salt */
249     if (as_reply->padata) {
250         krb5_pa_data **ptr;
251
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;
257                 use_salt = 1;
258                 break;
259             }
260         }
261     } 
262     if (!use_salt) {
263         /* need to use flattened principal */
264         if (retval = krb5_principal2salt(context, creds->client, &salt))
265             return(retval);
266         f_salt = 1;
267     }
268
269     /* it was a kdc_rep--decrypt & check */
270     /* Generate the key, if we haven't done so already. */
271     if (!decrypt_key) {
272             if (retval = (*key_proc)(context, keytype, & salt, keyseed,
273                                      &decrypt_key))
274                 goto cleanup;
275     }
276     
277     if (retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply))
278         goto cleanup;
279
280     krb5_free_keyblock(context, decrypt_key);
281     decrypt_key = 0;
282     
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))
304         ) {
305         retval = KRB5_KDCREP_MODIFIED;
306         goto cleanup;
307     }
308     if ((request.from == 0) &&
309         !in_clock_skew(as_reply->enc_part2->times.starttime)) {
310         retval = KRB5_KDCREP_SKEW;
311         goto cleanup;
312     }
313     
314
315     /* XXX issue warning if as_reply->enc_part2->key_exp is nearby */
316         
317     /* fill in the credentials */
318     if (retval = krb5_copy_keyblock_contents(context, 
319                                              as_reply->enc_part2->session,
320                                              &creds->keyblock))
321         goto cleanup;
322     creds->keyblock.etype = as_reply->ticket->enc_part.etype;
323
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,
329                                      &creds->addresses))
330         goto cred_cleanup;
331
332     creds->second_ticket.length = 0;
333     creds->second_ticket.data = 0;
334
335     if (retval = encode_krb5_ticket(as_reply->ticket, &packet))
336         goto cred_cleanup;
337
338     creds->ticket = *packet;
339     krb5_xfree(packet);
340
341     /* store it in the ccache! */
342     if (retval = krb5_cc_store_cred(context, ccache, creds))
343         goto cred_cleanup;
344
345     if (ret_as_reply) {
346         *ret_as_reply = as_reply;
347         as_reply = 0;
348     }
349
350     retval = 0;
351     
352 cleanup:
353     if (f_salt)
354         krb5_xfree(salt.data);
355     if (as_reply)
356         krb5_free_kdc_rep(context, as_reply);
357     if (reply.data)
358         free(reply.data);
359     if (decrypt_key)
360         krb5_free_keyblock(context, decrypt_key);
361     if (request.padata)
362         free(request.padata);
363     if (!addrs)
364         krb5_free_addresses(context, request.addresses);
365     return retval;
366     
367     /*
368      * Clean up left over mess in credentials structure, in case of 
369      * error
370      */
371 cred_cleanup:
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;
377     }
378     if (creds->ticket.data) {
379         krb5_xfree(creds->ticket.data);
380         creds->ticket.data = 0;
381     }
382     if (creds->addresses) {
383         krb5_free_addresses(context, creds->addresses);
384         creds->addresses = 0;
385     }
386     goto cleanup;
387 }
388