155682Smarkm/*
2178825Sdfr * Copyright (c) 1997-2005 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kdc_locl.h"
3555682Smarkm
36178825SdfrRCSID("$Id: 524.c 18270 2006-10-06 17:06:30Z lha $");
3755682Smarkm
38120945Snectar#include <krb5-v4compat.h>
3955682Smarkm
4072445Sassar/*
4172445Sassar * fetch the server from `t', returning the name in malloced memory in
4272445Sassar * `spn' and the entry itself in `server'
4372445Sassar */
4472445Sassar
4572445Sassarstatic krb5_error_code
46178825Sdfrfetch_server (krb5_context context,
47178825Sdfr	      krb5_kdc_configuration *config,
48178825Sdfr	      const Ticket *t,
4972445Sassar	      char **spn,
50178825Sdfr	      hdb_entry_ex **server,
5172445Sassar	      const char *from)
5272445Sassar{
5372445Sassar    krb5_error_code ret;
5472445Sassar    krb5_principal sprinc;
5572445Sassar
56178825Sdfr    ret = _krb5_principalname2krb5_principal(context, &sprinc,
57178825Sdfr					     t->sname, t->realm);
5872445Sassar    if (ret) {
59178825Sdfr	kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s",
6072445Sassar		krb5_get_err_text(context, ret));
6172445Sassar	return ret;
6272445Sassar    }
6372445Sassar    ret = krb5_unparse_name(context, sprinc, spn);
6472445Sassar    if (ret) {
6572445Sassar	krb5_free_principal(context, sprinc);
66178825Sdfr	kdc_log(context, config, 0, "krb5_unparse_name: %s",
67178825Sdfr		krb5_get_err_text(context, ret));
6872445Sassar	return ret;
6972445Sassar    }
70178825Sdfr    ret = _kdc_db_fetch(context, config, sprinc, HDB_F_GET_SERVER,
71178825Sdfr			NULL, server);
7272445Sassar    krb5_free_principal(context, sprinc);
7372445Sassar    if (ret) {
74178825Sdfr	kdc_log(context, config, 0,
7572445Sassar	"Request to convert ticket from %s for unknown principal %s: %s",
7672445Sassar		from, *spn, krb5_get_err_text(context, ret));
77102644Snectar	if (ret == HDB_ERR_NOENTRY)
7872445Sassar	    ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
7972445Sassar	return ret;
8072445Sassar    }
8172445Sassar    return 0;
8272445Sassar}
8372445Sassar
8472445Sassarstatic krb5_error_code
85178825Sdfrlog_524 (krb5_context context,
86178825Sdfr	 krb5_kdc_configuration *config,
87178825Sdfr	 const EncTicketPart *et,
8872445Sassar	 const char *from,
8972445Sassar	 const char *spn)
9072445Sassar{
9172445Sassar    krb5_principal client;
9272445Sassar    char *cpn;
9372445Sassar    krb5_error_code ret;
9472445Sassar
95178825Sdfr    ret = _krb5_principalname2krb5_principal(context, &client,
96178825Sdfr					     et->cname, et->crealm);
9772445Sassar    if (ret) {
98178825Sdfr	kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s",
9972445Sassar		krb5_get_err_text (context, ret));
10072445Sassar	return ret;
10172445Sassar    }
10272445Sassar    ret = krb5_unparse_name(context, client, &cpn);
10372445Sassar    if (ret) {
10472445Sassar	krb5_free_principal(context, client);
105178825Sdfr	kdc_log(context, config, 0, "krb5_unparse_name: %s",
10672445Sassar		krb5_get_err_text (context, ret));
10772445Sassar	return ret;
10872445Sassar    }
109178825Sdfr    kdc_log(context, config, 1, "524-REQ %s from %s for %s", cpn, from, spn);
11072445Sassar    free(cpn);
11172445Sassar    krb5_free_principal(context, client);
11272445Sassar    return 0;
11372445Sassar}
11472445Sassar
11572445Sassarstatic krb5_error_code
116178825Sdfrverify_flags (krb5_context context,
117178825Sdfr	      krb5_kdc_configuration *config,
118178825Sdfr	      const EncTicketPart *et,
11972445Sassar	      const char *spn)
12072445Sassar{
12172445Sassar    if(et->endtime < kdc_time){
122178825Sdfr	kdc_log(context, config, 0, "Ticket expired (%s)", spn);
12372445Sassar	return KRB5KRB_AP_ERR_TKT_EXPIRED;
12472445Sassar    }
12572445Sassar    if(et->flags.invalid){
126178825Sdfr	kdc_log(context, config, 0, "Ticket not valid (%s)", spn);
12772445Sassar	return KRB5KRB_AP_ERR_TKT_NYV;
12872445Sassar    }
12972445Sassar    return 0;
13072445Sassar}
13172445Sassar
13272445Sassar/*
13372445Sassar * set the `et->caddr' to the most appropriate address to use, where
13472445Sassar * `addr' is the address the request was received from.
13572445Sassar */
13672445Sassar
13772445Sassarstatic krb5_error_code
138178825Sdfrset_address (krb5_context context,
139178825Sdfr	     krb5_kdc_configuration *config,
140178825Sdfr	     EncTicketPart *et,
14172445Sassar	     struct sockaddr *addr,
14272445Sassar	     const char *from)
14372445Sassar{
14472445Sassar    krb5_error_code ret;
14572445Sassar    krb5_address *v4_addr;
14672445Sassar
14772445Sassar    v4_addr = malloc (sizeof(*v4_addr));
14872445Sassar    if (v4_addr == NULL)
14972445Sassar	return ENOMEM;
15072445Sassar
15178527Sassar    ret = krb5_sockaddr2address(context, addr, v4_addr);
15272445Sassar    if(ret) {
15372445Sassar	free (v4_addr);
154178825Sdfr	kdc_log(context, config, 0, "Failed to convert address (%s)", from);
15572445Sassar	return ret;
15672445Sassar    }
15772445Sassar
15872445Sassar    if (et->caddr && !krb5_address_search (context, v4_addr, et->caddr)) {
159178825Sdfr	kdc_log(context, config, 0, "Incorrect network address (%s)", from);
16072445Sassar	krb5_free_address(context, v4_addr);
16172445Sassar	free (v4_addr);
16272445Sassar	return KRB5KRB_AP_ERR_BADADDR;
16372445Sassar    }
16472445Sassar    if(v4_addr->addr_type == KRB5_ADDRESS_INET) {
16572445Sassar	/* we need to collapse the addresses in the ticket to a
16672445Sassar	   single address; best guess is to use the address the
16772445Sassar	   connection came from */
16872445Sassar
16972445Sassar	if (et->caddr != NULL) {
17072445Sassar	    free_HostAddresses(et->caddr);
17172445Sassar	} else {
17272445Sassar	    et->caddr = malloc (sizeof (*et->caddr));
17372445Sassar	    if (et->caddr == NULL) {
17472445Sassar		krb5_free_address(context, v4_addr);
17572445Sassar		free(v4_addr);
17672445Sassar		return ENOMEM;
17772445Sassar	    }
17872445Sassar	}
17972445Sassar	et->caddr->val = v4_addr;
18072445Sassar	et->caddr->len = 1;
18172445Sassar    } else {
18272445Sassar	krb5_free_address(context, v4_addr);
18372445Sassar	free(v4_addr);
18472445Sassar    }
18572445Sassar    return 0;
18672445Sassar}
18772445Sassar
188120945Snectar
189120945Snectarstatic krb5_error_code
190178825Sdfrencrypt_v4_ticket(krb5_context context,
191178825Sdfr		  krb5_kdc_configuration *config,
192178825Sdfr		  void *buf,
193120945Snectar		  size_t len,
194120945Snectar		  krb5_keyblock *skey,
195120945Snectar		  EncryptedData *reply)
196120945Snectar{
197120945Snectar    krb5_crypto crypto;
198120945Snectar    krb5_error_code ret;
199120945Snectar    ret = krb5_crypto_init(context, skey, ETYPE_DES_PCBC_NONE, &crypto);
200120945Snectar    if (ret) {
201120945Snectar	free(buf);
202178825Sdfr	kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
203120945Snectar		krb5_get_err_text(context, ret));
204120945Snectar	return ret;
205120945Snectar    }
206120945Snectar
207120945Snectar    ret = krb5_encrypt_EncryptedData(context,
208120945Snectar				     crypto,
209120945Snectar				     KRB5_KU_TICKET,
210120945Snectar				     buf,
211120945Snectar				     len,
212120945Snectar				     0,
213120945Snectar				     reply);
214120945Snectar    krb5_crypto_destroy(context, crypto);
215120945Snectar    if(ret) {
216178825Sdfr	kdc_log(context, config, 0, "Failed to encrypt data: %s",
217120945Snectar		krb5_get_err_text(context, ret));
218120945Snectar	return ret;
219120945Snectar    }
220120945Snectar    return 0;
221120945Snectar}
222120945Snectar
223120945Snectarstatic krb5_error_code
224178825Sdfrencode_524_response(krb5_context context,
225178825Sdfr		    krb5_kdc_configuration *config,
226178825Sdfr		    const char *spn, const EncTicketPart et,
227178825Sdfr		    const Ticket *t, hdb_entry_ex *server,
228178825Sdfr		    EncryptedData *ticket, int *kvno)
229120945Snectar{
230120945Snectar    krb5_error_code ret;
231120945Snectar    int use_2b;
232120945Snectar    size_t len;
233120945Snectar
234120945Snectar    use_2b = krb5_config_get_bool(context, NULL, "kdc", "use_2b", spn, NULL);
235120945Snectar    if(use_2b) {
236120945Snectar	ASN1_MALLOC_ENCODE(EncryptedData,
237120945Snectar			   ticket->cipher.data, ticket->cipher.length,
238120945Snectar			   &t->enc_part, &len, ret);
239120945Snectar
240120945Snectar	if (ret) {
241178825Sdfr	    kdc_log(context, config, 0,
242178825Sdfr		    "Failed to encode v4 (2b) ticket (%s)", spn);
243120945Snectar	    return ret;
244120945Snectar	}
245120945Snectar
246120945Snectar	ticket->etype = 0;
247120945Snectar	ticket->kvno = NULL;
248120945Snectar	*kvno = 213; /* 2b's use this magic kvno */
249120945Snectar    } else {
250120945Snectar	unsigned char buf[MAX_KTXT_LEN + 4 * 4];
251120945Snectar	Key *skey;
252120945Snectar
253178825Sdfr	if (!config->enable_v4_cross_realm && strcmp (et.crealm, t->realm) != 0) {
254178825Sdfr	    kdc_log(context, config, 0, "524 cross-realm %s -> %s disabled", et.crealm,
255120945Snectar		    t->realm);
256120945Snectar	    return KRB5KDC_ERR_POLICY;
257120945Snectar	}
258120945Snectar
259178825Sdfr	ret = _kdc_encode_v4_ticket(context, config,
260178825Sdfr				    buf + sizeof(buf) - 1, sizeof(buf),
261178825Sdfr				    &et, &t->sname, &len);
262120945Snectar	if(ret){
263178825Sdfr	    kdc_log(context, config, 0,
264178825Sdfr		    "Failed to encode v4 ticket (%s)", spn);
265120945Snectar	    return ret;
266120945Snectar	}
267178825Sdfr	ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey);
268120945Snectar	if(ret){
269178825Sdfr	    kdc_log(context, config, 0,
270178825Sdfr		    "no suitable DES key for server (%s)", spn);
271120945Snectar	    return ret;
272120945Snectar	}
273178825Sdfr	ret = encrypt_v4_ticket(context, config, buf + sizeof(buf) - len, len,
274120945Snectar				&skey->key, ticket);
275120945Snectar	if(ret){
276178825Sdfr	    kdc_log(context, config, 0,
277178825Sdfr		    "Failed to encrypt v4 ticket (%s)", spn);
278120945Snectar	    return ret;
279120945Snectar	}
280178825Sdfr	*kvno = server->entry.kvno;
281120945Snectar    }
282120945Snectar
283120945Snectar    return 0;
284120945Snectar}
285120945Snectar
28672445Sassar/*
28772445Sassar * process a 5->4 request, based on `t', and received `from, addr',
28872445Sassar * returning the reply in `reply'
28972445Sassar */
29072445Sassar
29155682Smarkmkrb5_error_code
292178825Sdfr_kdc_do_524(krb5_context context,
293178825Sdfr	    krb5_kdc_configuration *config,
294178825Sdfr	    const Ticket *t, krb5_data *reply,
295178825Sdfr	    const char *from, struct sockaddr *addr)
29655682Smarkm{
29755682Smarkm    krb5_error_code ret = 0;
29855682Smarkm    krb5_crypto crypto;
299178825Sdfr    hdb_entry_ex *server = NULL;
30055682Smarkm    Key *skey;
30155682Smarkm    krb5_data et_data;
30255682Smarkm    EncTicketPart et;
30355682Smarkm    EncryptedData ticket;
30455682Smarkm    krb5_storage *sp;
30555682Smarkm    char *spn = NULL;
30655682Smarkm    unsigned char buf[MAX_KTXT_LEN + 4 * 4];
30755682Smarkm    size_t len;
308178825Sdfr    int kvno = 0;
30955682Smarkm
310178825Sdfr    if(!config->enable_524) {
31172445Sassar	ret = KRB5KDC_ERR_POLICY;
312178825Sdfr	kdc_log(context, config, 0,
313178825Sdfr		"Rejected ticket conversion request from %s", from);
31455682Smarkm	goto out;
31555682Smarkm    }
31672445Sassar
317178825Sdfr    ret = fetch_server (context, config, t, &spn, &server, from);
31872445Sassar    if (ret) {
31972445Sassar	goto out;
32072445Sassar    }
32172445Sassar
322178825Sdfr    ret = hdb_enctype2key(context, &server->entry, t->enc_part.etype, &skey);
32355682Smarkm    if(ret){
324178825Sdfr	kdc_log(context, config, 0,
325178825Sdfr		"No suitable key found for server (%s) from %s", spn, from);
32655682Smarkm	goto out;
32755682Smarkm    }
32872445Sassar    ret = krb5_crypto_init(context, &skey->key, 0, &crypto);
32972445Sassar    if (ret) {
330178825Sdfr	kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
33172445Sassar		krb5_get_err_text(context, ret));
33272445Sassar	goto out;
33372445Sassar    }
33455682Smarkm    ret = krb5_decrypt_EncryptedData (context,
33555682Smarkm				      crypto,
33655682Smarkm				      KRB5_KU_TICKET,
33755682Smarkm				      &t->enc_part,
33855682Smarkm				      &et_data);
33955682Smarkm    krb5_crypto_destroy(context, crypto);
34055682Smarkm    if(ret){
341178825Sdfr	kdc_log(context, config, 0,
342178825Sdfr		"Failed to decrypt ticket from %s for %s", from, spn);
34355682Smarkm	goto out;
34455682Smarkm    }
34555682Smarkm    ret = krb5_decode_EncTicketPart(context, et_data.data, et_data.length,
34655682Smarkm				    &et, &len);
34755682Smarkm    krb5_data_free(&et_data);
34855682Smarkm    if(ret){
349178825Sdfr	kdc_log(context, config, 0,
350178825Sdfr		"Failed to decode ticket from %s for %s", from, spn);
35155682Smarkm	goto out;
35255682Smarkm    }
35355682Smarkm
354178825Sdfr    ret = log_524 (context, config, &et, from, spn);
35572445Sassar    if (ret) {
35655682Smarkm	free_EncTicketPart(&et);
35755682Smarkm	goto out;
35855682Smarkm    }
35972445Sassar
360178825Sdfr    ret = verify_flags (context, config, &et, spn);
36172445Sassar    if (ret) {
36255682Smarkm	free_EncTicketPart(&et);
36355682Smarkm	goto out;
36455682Smarkm    }
36555682Smarkm
366178825Sdfr    ret = set_address (context, config, &et, addr, from);
36772445Sassar    if (ret) {
36872445Sassar	free_EncTicketPart(&et);
36972445Sassar	goto out;
37055682Smarkm    }
371120945Snectar
372178825Sdfr    ret = encode_524_response(context, config, spn, et, t,
373178825Sdfr			      server, &ticket, &kvno);
37455682Smarkm    free_EncTicketPart(&et);
375120945Snectar
376178825Sdfr out:
37755682Smarkm    /* make reply */
37855682Smarkm    memset(buf, 0, sizeof(buf));
37955682Smarkm    sp = krb5_storage_from_mem(buf, sizeof(buf));
380178825Sdfr    if (sp) {
381178825Sdfr	krb5_store_int32(sp, ret);
382178825Sdfr	if(ret == 0){
383178825Sdfr	    krb5_store_int32(sp, kvno);
384178825Sdfr	    krb5_store_data(sp, ticket.cipher);
385178825Sdfr	    /* Aargh! This is coded as a KTEXT_ST. */
386178825Sdfr	    krb5_storage_seek(sp, MAX_KTXT_LEN - ticket.cipher.length, SEEK_CUR);
387178825Sdfr	    krb5_store_int32(sp, 0); /* mbz */
388178825Sdfr	    free_EncryptedData(&ticket);
389178825Sdfr	}
390178825Sdfr	ret = krb5_storage_to_data(sp, reply);
391178825Sdfr	reply->length = krb5_storage_seek(sp, 0, SEEK_CUR);
392178825Sdfr	krb5_storage_free(sp);
393178825Sdfr    } else
394178825Sdfr	krb5_data_zero(reply);
39555682Smarkm    if(spn)
39655682Smarkm	free(spn);
39772445Sassar    if(server)
398178825Sdfr	_kdc_free_ent (context, server);
39955682Smarkm    return ret;
40055682Smarkm}
401