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