remove gcc34
[dragonfly.git] / crypto / heimdal-0.6.3 / kdc / 524.c
1 /*
2  * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "kdc_locl.h"
35
36 RCSID("$Id: 524.c,v 1.29 2003/03/17 05:35:47 assar Exp $");
37
38 #ifndef KRB4
39 #include <krb5-v4compat.h>
40 #endif
41
42 /*
43  * fetch the server from `t', returning the name in malloced memory in
44  * `spn' and the entry itself in `server'
45  */
46
47 static krb5_error_code
48 fetch_server (const Ticket *t,
49               char **spn,
50               hdb_entry **server,
51               const char *from)
52 {
53     krb5_error_code ret;
54     krb5_principal sprinc;
55
56     ret = principalname2krb5_principal(&sprinc, t->sname, t->realm);
57     if (ret) {
58         kdc_log(0, "principalname2krb5_principal: %s",
59                 krb5_get_err_text(context, ret));
60         return ret;
61     }
62     ret = krb5_unparse_name(context, sprinc, spn);
63     if (ret) {
64         krb5_free_principal(context, sprinc);
65         kdc_log(0, "krb5_unparse_name: %s", krb5_get_err_text(context, ret));
66         return ret;
67     }
68     ret = db_fetch(sprinc, server);
69     krb5_free_principal(context, sprinc);
70     if (ret) {
71         kdc_log(0,
72         "Request to convert ticket from %s for unknown principal %s: %s",
73                 from, *spn, krb5_get_err_text(context, ret));
74         if (ret == HDB_ERR_NOENTRY)
75             ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
76         return ret;
77     }
78     return 0;
79 }
80
81 static krb5_error_code
82 log_524 (const EncTicketPart *et,
83          const char *from,
84          const char *spn)
85 {
86     krb5_principal client;
87     char *cpn;
88     krb5_error_code ret;
89
90     ret = principalname2krb5_principal(&client, et->cname, et->crealm);
91     if (ret) {
92         kdc_log(0, "principalname2krb5_principal: %s",
93                 krb5_get_err_text (context, ret));
94         return ret;
95     }
96     ret = krb5_unparse_name(context, client, &cpn);
97     if (ret) {
98         krb5_free_principal(context, client);
99         kdc_log(0, "krb5_unparse_name: %s",
100                 krb5_get_err_text (context, ret));
101         return ret;
102     }
103     kdc_log(1, "524-REQ %s from %s for %s", cpn, from, spn);
104     free(cpn);
105     krb5_free_principal(context, client);
106     return 0;
107 }
108
109 static krb5_error_code
110 verify_flags (const EncTicketPart *et,
111               const char *spn)
112 {
113     if(et->endtime < kdc_time){
114         kdc_log(0, "Ticket expired (%s)", spn);
115         return KRB5KRB_AP_ERR_TKT_EXPIRED;
116     }
117     if(et->flags.invalid){
118         kdc_log(0, "Ticket not valid (%s)", spn);
119         return KRB5KRB_AP_ERR_TKT_NYV;
120     }
121     return 0;
122 }
123
124 /*
125  * set the `et->caddr' to the most appropriate address to use, where
126  * `addr' is the address the request was received from.
127  */
128
129 static krb5_error_code
130 set_address (EncTicketPart *et,
131              struct sockaddr *addr,
132              const char *from)
133 {
134     krb5_error_code ret;
135     krb5_address *v4_addr;
136
137     v4_addr = malloc (sizeof(*v4_addr));
138     if (v4_addr == NULL)
139         return ENOMEM;
140
141     ret = krb5_sockaddr2address(context, addr, v4_addr);
142     if(ret) {
143         free (v4_addr);
144         kdc_log(0, "Failed to convert address (%s)", from);
145         return ret;
146     }
147             
148     if (et->caddr && !krb5_address_search (context, v4_addr, et->caddr)) {
149         kdc_log(0, "Incorrect network address (%s)", from);
150         krb5_free_address(context, v4_addr);
151         free (v4_addr);
152         return KRB5KRB_AP_ERR_BADADDR;
153     }
154     if(v4_addr->addr_type == KRB5_ADDRESS_INET) {
155         /* we need to collapse the addresses in the ticket to a
156            single address; best guess is to use the address the
157            connection came from */
158         
159         if (et->caddr != NULL) {
160             free_HostAddresses(et->caddr);
161         } else {
162             et->caddr = malloc (sizeof (*et->caddr));
163             if (et->caddr == NULL) {
164                 krb5_free_address(context, v4_addr);
165                 free(v4_addr);
166                 return ENOMEM;
167             }
168         }
169         et->caddr->val = v4_addr;
170         et->caddr->len = 1;
171     } else {
172         krb5_free_address(context, v4_addr);
173         free(v4_addr);
174     }
175     return 0;
176 }
177
178
179 static krb5_error_code
180 encrypt_v4_ticket(void *buf, 
181                   size_t len, 
182                   krb5_keyblock *skey, 
183                   EncryptedData *reply)
184 {
185     krb5_crypto crypto;
186     krb5_error_code ret;
187     ret = krb5_crypto_init(context, skey, ETYPE_DES_PCBC_NONE, &crypto);
188     if (ret) {
189         free(buf);
190         kdc_log(0, "krb5_crypto_init failed: %s",
191                 krb5_get_err_text(context, ret));
192         return ret;
193     }
194
195     ret = krb5_encrypt_EncryptedData(context, 
196                                      crypto,
197                                      KRB5_KU_TICKET,
198                                      buf,
199                                      len,
200                                      0,
201                                      reply);
202     krb5_crypto_destroy(context, crypto);
203     if(ret) {
204         kdc_log(0, "Failed to encrypt data: %s",
205                 krb5_get_err_text(context, ret));
206         return ret;
207     }
208     return 0;
209 }
210
211 static krb5_error_code
212 encode_524_response(const char *spn, const EncTicketPart et, const Ticket *t,
213                     hdb_entry *server, EncryptedData *ticket, int *kvno)
214 {
215     krb5_error_code ret;
216     int use_2b;
217     size_t len;
218
219     use_2b = krb5_config_get_bool(context, NULL, "kdc", "use_2b", spn, NULL);
220     if(use_2b) {
221         ASN1_MALLOC_ENCODE(EncryptedData, 
222                            ticket->cipher.data, ticket->cipher.length, 
223                            &t->enc_part, &len, ret);
224         
225         if (ret) {
226             kdc_log(0, "Failed to encode v4 (2b) ticket (%s)", spn);
227             return ret;
228         }
229         
230         ticket->etype = 0;
231         ticket->kvno = NULL;
232         *kvno = 213; /* 2b's use this magic kvno */
233     } else {
234         unsigned char buf[MAX_KTXT_LEN + 4 * 4];
235         Key *skey;
236         
237         if (!enable_v4_cross_realm && strcmp (et.crealm, t->realm) != 0) {
238             kdc_log(0, "524 cross-realm %s -> %s disabled", et.crealm,
239                     t->realm);
240             return KRB5KDC_ERR_POLICY;
241         }
242
243         ret = encode_v4_ticket(buf + sizeof(buf) - 1, sizeof(buf),
244                                &et, &t->sname, &len);
245         if(ret){
246             kdc_log(0, "Failed to encode v4 ticket (%s)", spn);
247             return ret;
248         }
249         ret = get_des_key(server, TRUE, FALSE, &skey);
250         if(ret){
251             kdc_log(0, "no suitable DES key for server (%s)", spn);
252             return ret;
253         }
254         ret = encrypt_v4_ticket(buf + sizeof(buf) - len, len, 
255                                 &skey->key, ticket);
256         if(ret){
257             kdc_log(0, "Failed to encrypt v4 ticket (%s)", spn);
258             return ret;
259         }
260         *kvno = server->kvno;
261     }
262
263     return 0;
264 }
265
266 /*
267  * process a 5->4 request, based on `t', and received `from, addr',
268  * returning the reply in `reply'
269  */
270
271 krb5_error_code
272 do_524(const Ticket *t, krb5_data *reply,
273        const char *from, struct sockaddr *addr)
274 {
275     krb5_error_code ret = 0;
276     krb5_crypto crypto;
277     hdb_entry *server = NULL;
278     Key *skey;
279     krb5_data et_data;
280     EncTicketPart et;
281     EncryptedData ticket;
282     krb5_storage *sp;
283     char *spn = NULL;
284     unsigned char buf[MAX_KTXT_LEN + 4 * 4];
285     size_t len;
286     int kvno;
287     
288     if(!enable_524) {
289         ret = KRB5KDC_ERR_POLICY;
290         kdc_log(0, "Rejected ticket conversion request from %s", from);
291         goto out;
292     }
293
294     ret = fetch_server (t, &spn, &server, from);
295     if (ret) {
296         goto out;
297     }
298
299     ret = hdb_enctype2key(context, server, t->enc_part.etype, &skey);
300     if(ret){
301         kdc_log(0, "No suitable key found for server (%s) from %s", spn, from);
302         goto out;
303     }
304     ret = krb5_crypto_init(context, &skey->key, 0, &crypto);
305     if (ret) {
306         kdc_log(0, "krb5_crypto_init failed: %s",
307                 krb5_get_err_text(context, ret));
308         goto out;
309     }
310     ret = krb5_decrypt_EncryptedData (context,
311                                       crypto,
312                                       KRB5_KU_TICKET,
313                                       &t->enc_part,
314                                       &et_data);
315     krb5_crypto_destroy(context, crypto);
316     if(ret){
317         kdc_log(0, "Failed to decrypt ticket from %s for %s", from, spn);
318         goto out;
319     }
320     ret = krb5_decode_EncTicketPart(context, et_data.data, et_data.length, 
321                                     &et, &len);
322     krb5_data_free(&et_data);
323     if(ret){
324         kdc_log(0, "Failed to decode ticket from %s for %s", from, spn);
325         goto out;
326     }
327
328     ret = log_524 (&et, from, spn);
329     if (ret) {
330         free_EncTicketPart(&et);
331         goto out;
332     }
333
334     ret = verify_flags (&et, spn);
335     if (ret) {
336         free_EncTicketPart(&et);
337         goto out;
338     }
339
340     ret = set_address (&et, addr, from);
341     if (ret) {
342         free_EncTicketPart(&et);
343         goto out;
344     }
345
346     ret = encode_524_response(spn, et, t, server, &ticket, &kvno);
347     free_EncTicketPart(&et);
348
349 out:
350     /* make reply */
351     memset(buf, 0, sizeof(buf));
352     sp = krb5_storage_from_mem(buf, sizeof(buf));
353     krb5_store_int32(sp, ret);
354     if(ret == 0){
355         krb5_store_int32(sp, kvno);
356         krb5_store_data(sp, ticket.cipher);
357         /* Aargh! This is coded as a KTEXT_ST. */
358         krb5_storage_seek(sp, MAX_KTXT_LEN - ticket.cipher.length, SEEK_CUR);
359         krb5_store_int32(sp, 0); /* mbz */
360         free_EncryptedData(&ticket);
361     }
362     ret = krb5_storage_to_data(sp, reply);
363     reply->length = krb5_storage_seek(sp, 0, SEEK_CUR);
364     krb5_storage_free(sp);
365     
366     if(spn)
367         free(spn);
368     if(server)
369         free_ent (server);
370     return ret;
371 }