1/* 2 * Copyright (c) 1997-2005 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 36RCSID("$Id: 524.c 18270 2006-10-06 17:06:30Z lha $"); 37 38#include <krb5-v4compat.h> 39 40/* 41 * fetch the server from `t', returning the name in malloced memory in 42 * `spn' and the entry itself in `server' 43 */ 44 45static krb5_error_code 46fetch_server (krb5_context context, 47 krb5_kdc_configuration *config, 48 const Ticket *t, 49 char **spn, 50 hdb_entry_ex **server, 51 const char *from) 52{ 53 krb5_error_code ret; 54 krb5_principal sprinc; 55 56 ret = _krb5_principalname2krb5_principal(context, &sprinc, 57 t->sname, t->realm); 58 if (ret) { 59 kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s", 60 krb5_get_err_text(context, ret)); 61 return ret; 62 } 63 ret = krb5_unparse_name(context, sprinc, spn); 64 if (ret) { 65 krb5_free_principal(context, sprinc); 66 kdc_log(context, config, 0, "krb5_unparse_name: %s", 67 krb5_get_err_text(context, ret)); 68 return ret; 69 } 70 ret = _kdc_db_fetch(context, config, sprinc, HDB_F_GET_SERVER, 71 NULL, server); 72 krb5_free_principal(context, sprinc); 73 if (ret) { 74 kdc_log(context, config, 0, 75 "Request to convert ticket from %s for unknown principal %s: %s", 76 from, *spn, krb5_get_err_text(context, ret)); 77 if (ret == HDB_ERR_NOENTRY) 78 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 79 return ret; 80 } 81 return 0; 82} 83 84static krb5_error_code 85log_524 (krb5_context context, 86 krb5_kdc_configuration *config, 87 const EncTicketPart *et, 88 const char *from, 89 const char *spn) 90{ 91 krb5_principal client; 92 char *cpn; 93 krb5_error_code ret; 94 95 ret = _krb5_principalname2krb5_principal(context, &client, 96 et->cname, et->crealm); 97 if (ret) { 98 kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s", 99 krb5_get_err_text (context, ret)); 100 return ret; 101 } 102 ret = krb5_unparse_name(context, client, &cpn); 103 if (ret) { 104 krb5_free_principal(context, client); 105 kdc_log(context, config, 0, "krb5_unparse_name: %s", 106 krb5_get_err_text (context, ret)); 107 return ret; 108 } 109 kdc_log(context, config, 1, "524-REQ %s from %s for %s", cpn, from, spn); 110 free(cpn); 111 krb5_free_principal(context, client); 112 return 0; 113} 114 115static krb5_error_code 116verify_flags (krb5_context context, 117 krb5_kdc_configuration *config, 118 const EncTicketPart *et, 119 const char *spn) 120{ 121 if(et->endtime < kdc_time){ 122 kdc_log(context, config, 0, "Ticket expired (%s)", spn); 123 return KRB5KRB_AP_ERR_TKT_EXPIRED; 124 } 125 if(et->flags.invalid){ 126 kdc_log(context, config, 0, "Ticket not valid (%s)", spn); 127 return KRB5KRB_AP_ERR_TKT_NYV; 128 } 129 return 0; 130} 131 132/* 133 * set the `et->caddr' to the most appropriate address to use, where 134 * `addr' is the address the request was received from. 135 */ 136 137static krb5_error_code 138set_address (krb5_context context, 139 krb5_kdc_configuration *config, 140 EncTicketPart *et, 141 struct sockaddr *addr, 142 const char *from) 143{ 144 krb5_error_code ret; 145 krb5_address *v4_addr; 146 147 v4_addr = malloc (sizeof(*v4_addr)); 148 if (v4_addr == NULL) 149 return ENOMEM; 150 151 ret = krb5_sockaddr2address(context, addr, v4_addr); 152 if(ret) { 153 free (v4_addr); 154 kdc_log(context, config, 0, "Failed to convert address (%s)", from); 155 return ret; 156 } 157 158 if (et->caddr && !krb5_address_search (context, v4_addr, et->caddr)) { 159 kdc_log(context, config, 0, "Incorrect network address (%s)", from); 160 krb5_free_address(context, v4_addr); 161 free (v4_addr); 162 return KRB5KRB_AP_ERR_BADADDR; 163 } 164 if(v4_addr->addr_type == KRB5_ADDRESS_INET) { 165 /* we need to collapse the addresses in the ticket to a 166 single address; best guess is to use the address the 167 connection came from */ 168 169 if (et->caddr != NULL) { 170 free_HostAddresses(et->caddr); 171 } else { 172 et->caddr = malloc (sizeof (*et->caddr)); 173 if (et->caddr == NULL) { 174 krb5_free_address(context, v4_addr); 175 free(v4_addr); 176 return ENOMEM; 177 } 178 } 179 et->caddr->val = v4_addr; 180 et->caddr->len = 1; 181 } else { 182 krb5_free_address(context, v4_addr); 183 free(v4_addr); 184 } 185 return 0; 186} 187 188 189static krb5_error_code 190encrypt_v4_ticket(krb5_context context, 191 krb5_kdc_configuration *config, 192 void *buf, 193 size_t len, 194 krb5_keyblock *skey, 195 EncryptedData *reply) 196{ 197 krb5_crypto crypto; 198 krb5_error_code ret; 199 ret = krb5_crypto_init(context, skey, ETYPE_DES_PCBC_NONE, &crypto); 200 if (ret) { 201 free(buf); 202 kdc_log(context, config, 0, "krb5_crypto_init failed: %s", 203 krb5_get_err_text(context, ret)); 204 return ret; 205 } 206 207 ret = krb5_encrypt_EncryptedData(context, 208 crypto, 209 KRB5_KU_TICKET, 210 buf, 211 len, 212 0, 213 reply); 214 krb5_crypto_destroy(context, crypto); 215 if(ret) { 216 kdc_log(context, config, 0, "Failed to encrypt data: %s", 217 krb5_get_err_text(context, ret)); 218 return ret; 219 } 220 return 0; 221} 222 223static krb5_error_code 224encode_524_response(krb5_context context, 225 krb5_kdc_configuration *config, 226 const char *spn, const EncTicketPart et, 227 const Ticket *t, hdb_entry_ex *server, 228 EncryptedData *ticket, int *kvno) 229{ 230 krb5_error_code ret; 231 int use_2b; 232 size_t len; 233 234 use_2b = krb5_config_get_bool(context, NULL, "kdc", "use_2b", spn, NULL); 235 if(use_2b) { 236 ASN1_MALLOC_ENCODE(EncryptedData, 237 ticket->cipher.data, ticket->cipher.length, 238 &t->enc_part, &len, ret); 239 240 if (ret) { 241 kdc_log(context, config, 0, 242 "Failed to encode v4 (2b) ticket (%s)", spn); 243 return ret; 244 } 245 246 ticket->etype = 0; 247 ticket->kvno = NULL; 248 *kvno = 213; /* 2b's use this magic kvno */ 249 } else { 250 unsigned char buf[MAX_KTXT_LEN + 4 * 4]; 251 Key *skey; 252 253 if (!config->enable_v4_cross_realm && strcmp (et.crealm, t->realm) != 0) { 254 kdc_log(context, config, 0, "524 cross-realm %s -> %s disabled", et.crealm, 255 t->realm); 256 return KRB5KDC_ERR_POLICY; 257 } 258 259 ret = _kdc_encode_v4_ticket(context, config, 260 buf + sizeof(buf) - 1, sizeof(buf), 261 &et, &t->sname, &len); 262 if(ret){ 263 kdc_log(context, config, 0, 264 "Failed to encode v4 ticket (%s)", spn); 265 return ret; 266 } 267 ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey); 268 if(ret){ 269 kdc_log(context, config, 0, 270 "no suitable DES key for server (%s)", spn); 271 return ret; 272 } 273 ret = encrypt_v4_ticket(context, config, buf + sizeof(buf) - len, len, 274 &skey->key, ticket); 275 if(ret){ 276 kdc_log(context, config, 0, 277 "Failed to encrypt v4 ticket (%s)", spn); 278 return ret; 279 } 280 *kvno = server->entry.kvno; 281 } 282 283 return 0; 284} 285 286/* 287 * process a 5->4 request, based on `t', and received `from, addr', 288 * returning the reply in `reply' 289 */ 290 291krb5_error_code 292_kdc_do_524(krb5_context context, 293 krb5_kdc_configuration *config, 294 const Ticket *t, krb5_data *reply, 295 const char *from, struct sockaddr *addr) 296{ 297 krb5_error_code ret = 0; 298 krb5_crypto crypto; 299 hdb_entry_ex *server = NULL; 300 Key *skey; 301 krb5_data et_data; 302 EncTicketPart et; 303 EncryptedData ticket; 304 krb5_storage *sp; 305 char *spn = NULL; 306 unsigned char buf[MAX_KTXT_LEN + 4 * 4]; 307 size_t len; 308 int kvno = 0; 309 310 if(!config->enable_524) { 311 ret = KRB5KDC_ERR_POLICY; 312 kdc_log(context, config, 0, 313 "Rejected ticket conversion request from %s", from); 314 goto out; 315 } 316 317 ret = fetch_server (context, config, t, &spn, &server, from); 318 if (ret) { 319 goto out; 320 } 321 322 ret = hdb_enctype2key(context, &server->entry, t->enc_part.etype, &skey); 323 if(ret){ 324 kdc_log(context, config, 0, 325 "No suitable key found for server (%s) from %s", spn, from); 326 goto out; 327 } 328 ret = krb5_crypto_init(context, &skey->key, 0, &crypto); 329 if (ret) { 330 kdc_log(context, config, 0, "krb5_crypto_init failed: %s", 331 krb5_get_err_text(context, ret)); 332 goto out; 333 } 334 ret = krb5_decrypt_EncryptedData (context, 335 crypto, 336 KRB5_KU_TICKET, 337 &t->enc_part, 338 &et_data); 339 krb5_crypto_destroy(context, crypto); 340 if(ret){ 341 kdc_log(context, config, 0, 342 "Failed to decrypt ticket from %s for %s", from, spn); 343 goto out; 344 } 345 ret = krb5_decode_EncTicketPart(context, et_data.data, et_data.length, 346 &et, &len); 347 krb5_data_free(&et_data); 348 if(ret){ 349 kdc_log(context, config, 0, 350 "Failed to decode ticket from %s for %s", from, spn); 351 goto out; 352 } 353 354 ret = log_524 (context, config, &et, from, spn); 355 if (ret) { 356 free_EncTicketPart(&et); 357 goto out; 358 } 359 360 ret = verify_flags (context, config, &et, spn); 361 if (ret) { 362 free_EncTicketPart(&et); 363 goto out; 364 } 365 366 ret = set_address (context, config, &et, addr, from); 367 if (ret) { 368 free_EncTicketPart(&et); 369 goto out; 370 } 371 372 ret = encode_524_response(context, config, spn, et, t, 373 server, &ticket, &kvno); 374 free_EncTicketPart(&et); 375 376 out: 377 /* make reply */ 378 memset(buf, 0, sizeof(buf)); 379 sp = krb5_storage_from_mem(buf, sizeof(buf)); 380 if (sp) { 381 krb5_store_int32(sp, ret); 382 if(ret == 0){ 383 krb5_store_int32(sp, kvno); 384 krb5_store_data(sp, ticket.cipher); 385 /* Aargh! This is coded as a KTEXT_ST. */ 386 krb5_storage_seek(sp, MAX_KTXT_LEN - ticket.cipher.length, SEEK_CUR); 387 krb5_store_int32(sp, 0); /* mbz */ 388 free_EncryptedData(&ticket); 389 } 390 ret = krb5_storage_to_data(sp, reply); 391 reply->length = krb5_storage_seek(sp, 0, SEEK_CUR); 392 krb5_storage_free(sp); 393 } else 394 krb5_data_zero(reply); 395 if(spn) 396 free(spn); 397 if(server) 398 _kdc_free_ent (context, server); 399 return ret; 400} 401