1178825Sdfr/* 2178825Sdfr * Copyright (c) 2006 - 2007 Kungliga Tekniska H�gskolan 3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden). 4178825Sdfr * All rights reserved. 5178825Sdfr * 6178825Sdfr * Redistribution and use in source and binary forms, with or without 7178825Sdfr * modification, are permitted provided that the following conditions 8178825Sdfr * are met: 9178825Sdfr * 10178825Sdfr * 1. Redistributions of source code must retain the above copyright 11178825Sdfr * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright 14178825Sdfr * notice, this list of conditions and the following disclaimer in the 15178825Sdfr * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors 18178825Sdfr * may be used to endorse or promote products derived from this software 19178825Sdfr * without specific prior written permission. 20178825Sdfr * 21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24178825Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31178825Sdfr * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34178825Sdfr#include "kdc_locl.h" 35178825Sdfr#include <hex.h> 36178825Sdfr#include <rfc2459_asn1.h> 37178825Sdfr#include <hx509.h> 38178825Sdfr 39178825SdfrRCSID("$Id: kx509.c 21607 2007-07-17 07:04:52Z lha $"); 40178825Sdfr 41178825Sdfr/* 42178825Sdfr * 43178825Sdfr */ 44178825Sdfr 45178825Sdfrkrb5_error_code 46178825Sdfr_kdc_try_kx509_request(void *ptr, size_t len, Kx509Request *req, size_t *size) 47178825Sdfr{ 48178825Sdfr if (len < 4) 49178825Sdfr return -1; 50178825Sdfr if (memcmp("\x00\x00\x02\x00", ptr, 4) != 0) 51178825Sdfr return -1; 52178825Sdfr return decode_Kx509Request(((unsigned char *)ptr) + 4, len - 4, req, size); 53178825Sdfr} 54178825Sdfr 55178825Sdfr/* 56178825Sdfr * 57178825Sdfr */ 58178825Sdfr 59178825Sdfrstatic const unsigned char version_2_0[4] = {0 , 0, 2, 0}; 60178825Sdfr 61178825Sdfrstatic krb5_error_code 62178825Sdfrverify_req_hash(krb5_context context, 63178825Sdfr const Kx509Request *req, 64178825Sdfr krb5_keyblock *key) 65178825Sdfr{ 66178825Sdfr unsigned char digest[SHA_DIGEST_LENGTH]; 67178825Sdfr HMAC_CTX ctx; 68178825Sdfr 69178825Sdfr if (req->pk_hash.length != sizeof(digest)) { 70178825Sdfr krb5_set_error_string(context, "pk-hash have wrong length: %lu", 71178825Sdfr (unsigned long)req->pk_hash.length); 72178825Sdfr return KRB5KDC_ERR_PREAUTH_FAILED; 73178825Sdfr } 74178825Sdfr 75178825Sdfr HMAC_CTX_init(&ctx); 76178825Sdfr HMAC_Init_ex(&ctx, 77178825Sdfr key->keyvalue.data, key->keyvalue.length, 78178825Sdfr EVP_sha1(), NULL); 79178825Sdfr if (sizeof(digest) != HMAC_size(&ctx)) 80178825Sdfr krb5_abortx(context, "runtime error, hmac buffer wrong size in kx509"); 81178825Sdfr HMAC_Update(&ctx, version_2_0, sizeof(version_2_0)); 82178825Sdfr HMAC_Update(&ctx, req->pk_key.data, req->pk_key.length); 83178825Sdfr HMAC_Final(&ctx, digest, 0); 84178825Sdfr HMAC_CTX_cleanup(&ctx); 85178825Sdfr 86178825Sdfr if (memcmp(req->pk_hash.data, digest, sizeof(digest)) != 0) { 87178825Sdfr krb5_set_error_string(context, "pk-hash is not correct"); 88178825Sdfr return KRB5KDC_ERR_PREAUTH_FAILED; 89178825Sdfr } 90178825Sdfr return 0; 91178825Sdfr} 92178825Sdfr 93178825Sdfrstatic krb5_error_code 94178825Sdfrcalculate_reply_hash(krb5_context context, 95178825Sdfr krb5_keyblock *key, 96178825Sdfr Kx509Response *rep) 97178825Sdfr{ 98178825Sdfr HMAC_CTX ctx; 99178825Sdfr 100178825Sdfr HMAC_CTX_init(&ctx); 101178825Sdfr 102178825Sdfr HMAC_Init_ex(&ctx, 103178825Sdfr key->keyvalue.data, key->keyvalue.length, 104178825Sdfr EVP_sha1(), NULL); 105178825Sdfr rep->hash->length = HMAC_size(&ctx); 106178825Sdfr rep->hash->data = malloc(rep->hash->length); 107178825Sdfr if (rep->hash->data == NULL) { 108178825Sdfr HMAC_CTX_cleanup(&ctx); 109178825Sdfr krb5_set_error_string(context, "out of memory"); 110178825Sdfr return ENOMEM; 111178825Sdfr } 112178825Sdfr 113178825Sdfr HMAC_Update(&ctx, version_2_0, sizeof(version_2_0)); 114178825Sdfr if (rep->error_code) { 115178825Sdfr int32_t t = *rep->error_code; 116178825Sdfr do { 117178825Sdfr unsigned char p = (t & 0xff); 118178825Sdfr HMAC_Update(&ctx, &p, 1); 119178825Sdfr t >>= 8; 120178825Sdfr } while (t); 121178825Sdfr } 122178825Sdfr if (rep->certificate) 123178825Sdfr HMAC_Update(&ctx, rep->certificate->data, rep->certificate->length); 124178825Sdfr if (rep->e_text) 125178825Sdfr HMAC_Update(&ctx, (unsigned char *)*rep->e_text, strlen(*rep->e_text)); 126178825Sdfr 127178825Sdfr HMAC_Final(&ctx, rep->hash->data, 0); 128178825Sdfr HMAC_CTX_cleanup(&ctx); 129178825Sdfr 130178825Sdfr return 0; 131178825Sdfr} 132178825Sdfr 133178825Sdfr/* 134178825Sdfr * Build a certifate for `principal� that will expire at `endtime�. 135178825Sdfr */ 136178825Sdfr 137178825Sdfrstatic krb5_error_code 138178825Sdfrbuild_certificate(krb5_context context, 139178825Sdfr krb5_kdc_configuration *config, 140178825Sdfr const krb5_data *key, 141178825Sdfr time_t endtime, 142178825Sdfr krb5_principal principal, 143178825Sdfr krb5_data *certificate) 144178825Sdfr{ 145178825Sdfr hx509_context hxctx = NULL; 146178825Sdfr hx509_ca_tbs tbs = NULL; 147178825Sdfr hx509_env env = NULL; 148178825Sdfr hx509_cert cert = NULL; 149178825Sdfr hx509_cert signer = NULL; 150178825Sdfr int ret; 151178825Sdfr 152178825Sdfr if (krb5_principal_get_comp_string(context, principal, 1) != NULL) { 153178825Sdfr kdc_log(context, config, 0, "Principal is not a user"); 154178825Sdfr return EINVAL; 155178825Sdfr } 156178825Sdfr 157178825Sdfr ret = hx509_context_init(&hxctx); 158178825Sdfr if (ret) 159178825Sdfr goto out; 160178825Sdfr 161178825Sdfr ret = hx509_env_init(hxctx, &env); 162178825Sdfr if (ret) 163178825Sdfr goto out; 164178825Sdfr 165178825Sdfr ret = hx509_env_add(hxctx, env, "principal-name", 166178825Sdfr krb5_principal_get_comp_string(context, principal, 0)); 167178825Sdfr if (ret) 168178825Sdfr goto out; 169178825Sdfr 170178825Sdfr { 171178825Sdfr hx509_certs certs; 172178825Sdfr hx509_query *q; 173178825Sdfr 174178825Sdfr ret = hx509_certs_init(hxctx, config->kx509_ca, 0, 175178825Sdfr NULL, &certs); 176178825Sdfr if (ret) { 177178825Sdfr kdc_log(context, config, 0, "Failed to load CA %s", 178178825Sdfr config->kx509_ca); 179178825Sdfr goto out; 180178825Sdfr } 181178825Sdfr ret = hx509_query_alloc(hxctx, &q); 182178825Sdfr if (ret) { 183178825Sdfr hx509_certs_free(&certs); 184178825Sdfr goto out; 185178825Sdfr } 186178825Sdfr 187178825Sdfr hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); 188178825Sdfr hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN); 189178825Sdfr 190178825Sdfr ret = hx509_certs_find(hxctx, certs, q, &signer); 191178825Sdfr hx509_query_free(hxctx, q); 192178825Sdfr hx509_certs_free(&certs); 193178825Sdfr if (ret) { 194178825Sdfr kdc_log(context, config, 0, "Failed to find a CA in %s", 195178825Sdfr config->kx509_ca); 196178825Sdfr goto out; 197178825Sdfr } 198178825Sdfr } 199178825Sdfr 200178825Sdfr ret = hx509_ca_tbs_init(hxctx, &tbs); 201178825Sdfr if (ret) 202178825Sdfr goto out; 203178825Sdfr 204178825Sdfr { 205178825Sdfr SubjectPublicKeyInfo spki; 206178825Sdfr heim_any any; 207178825Sdfr 208178825Sdfr memset(&spki, 0, sizeof(spki)); 209178825Sdfr 210178825Sdfr spki.subjectPublicKey.data = key->data; 211178825Sdfr spki.subjectPublicKey.length = key->length * 8; 212178825Sdfr 213178825Sdfr ret = der_copy_oid(oid_id_pkcs1_rsaEncryption(), 214178825Sdfr &spki.algorithm.algorithm); 215178825Sdfr 216178825Sdfr any.data = "\x05\x00"; 217178825Sdfr any.length = 2; 218178825Sdfr spki.algorithm.parameters = &any; 219178825Sdfr 220178825Sdfr ret = hx509_ca_tbs_set_spki(hxctx, tbs, &spki); 221178825Sdfr der_free_oid(&spki.algorithm.algorithm); 222178825Sdfr if (ret) 223178825Sdfr goto out; 224178825Sdfr } 225178825Sdfr 226178825Sdfr { 227178825Sdfr hx509_certs certs; 228178825Sdfr hx509_cert template; 229178825Sdfr 230178825Sdfr ret = hx509_certs_init(hxctx, config->kx509_template, 0, 231178825Sdfr NULL, &certs); 232178825Sdfr if (ret) { 233178825Sdfr kdc_log(context, config, 0, "Failed to load template %s", 234178825Sdfr config->kx509_template); 235178825Sdfr goto out; 236178825Sdfr } 237178825Sdfr ret = hx509_get_one_cert(hxctx, certs, &template); 238178825Sdfr hx509_certs_free(&certs); 239178825Sdfr if (ret) { 240178825Sdfr kdc_log(context, config, 0, "Failed to find template in %s", 241178825Sdfr config->kx509_template); 242178825Sdfr goto out; 243178825Sdfr } 244178825Sdfr ret = hx509_ca_tbs_set_template(hxctx, tbs, 245178825Sdfr HX509_CA_TEMPLATE_SUBJECT| 246178825Sdfr HX509_CA_TEMPLATE_KU| 247178825Sdfr HX509_CA_TEMPLATE_EKU, 248178825Sdfr template); 249178825Sdfr hx509_cert_free(template); 250178825Sdfr if (ret) 251178825Sdfr goto out; 252178825Sdfr } 253178825Sdfr 254178825Sdfr hx509_ca_tbs_set_notAfter(hxctx, tbs, endtime); 255178825Sdfr 256178825Sdfr hx509_ca_tbs_subject_expand(hxctx, tbs, env); 257178825Sdfr hx509_env_free(&env); 258178825Sdfr 259178825Sdfr ret = hx509_ca_sign(hxctx, tbs, signer, &cert); 260178825Sdfr hx509_cert_free(signer); 261178825Sdfr if (ret) 262178825Sdfr goto out; 263178825Sdfr 264178825Sdfr hx509_ca_tbs_free(&tbs); 265178825Sdfr 266178825Sdfr ret = hx509_cert_binary(hxctx, cert, certificate); 267178825Sdfr hx509_cert_free(cert); 268178825Sdfr if (ret) 269178825Sdfr goto out; 270178825Sdfr 271178825Sdfr hx509_context_free(&hxctx); 272178825Sdfr 273178825Sdfr return 0; 274178825Sdfrout: 275178825Sdfr if (env) 276178825Sdfr hx509_env_free(&env); 277178825Sdfr if (tbs) 278178825Sdfr hx509_ca_tbs_free(&tbs); 279178825Sdfr if (signer) 280178825Sdfr hx509_cert_free(signer); 281178825Sdfr if (hxctx) 282178825Sdfr hx509_context_free(&hxctx); 283178825Sdfr krb5_set_error_string(context, "cert creation failed"); 284178825Sdfr return ret; 285178825Sdfr} 286178825Sdfr 287178825Sdfr/* 288178825Sdfr * 289178825Sdfr */ 290178825Sdfr 291178825Sdfrkrb5_error_code 292178825Sdfr_kdc_do_kx509(krb5_context context, 293178825Sdfr krb5_kdc_configuration *config, 294178825Sdfr const Kx509Request *req, krb5_data *reply, 295178825Sdfr const char *from, struct sockaddr *addr) 296178825Sdfr{ 297178825Sdfr krb5_error_code ret; 298178825Sdfr krb5_ticket *ticket = NULL; 299178825Sdfr krb5_flags ap_req_options; 300178825Sdfr krb5_auth_context ac = NULL; 301178825Sdfr krb5_keytab id = NULL; 302178825Sdfr krb5_principal sprincipal = NULL, cprincipal = NULL; 303178825Sdfr char *cname = NULL; 304178825Sdfr Kx509Response rep; 305178825Sdfr size_t size; 306178825Sdfr krb5_keyblock *key = NULL; 307178825Sdfr 308178825Sdfr krb5_data_zero(reply); 309178825Sdfr memset(&rep, 0, sizeof(rep)); 310178825Sdfr 311178825Sdfr if(!config->enable_kx509) { 312178825Sdfr kdc_log(context, config, 0, 313178825Sdfr "Rejected kx509 request (disabled) from %s", from); 314178825Sdfr return KRB5KDC_ERR_POLICY; 315178825Sdfr } 316178825Sdfr 317178825Sdfr kdc_log(context, config, 0, "Kx509 request from %s", from); 318178825Sdfr 319178825Sdfr ret = krb5_kt_resolve(context, "HDB:", &id); 320178825Sdfr if (ret) { 321178825Sdfr kdc_log(context, config, 0, "Can't open database for digest"); 322178825Sdfr goto out; 323178825Sdfr } 324178825Sdfr 325178825Sdfr ret = krb5_rd_req(context, 326178825Sdfr &ac, 327178825Sdfr &req->authenticator, 328178825Sdfr NULL, 329178825Sdfr id, 330178825Sdfr &ap_req_options, 331178825Sdfr &ticket); 332178825Sdfr if (ret) 333178825Sdfr goto out; 334178825Sdfr 335178825Sdfr ret = krb5_ticket_get_client(context, ticket, &cprincipal); 336178825Sdfr if (ret) 337178825Sdfr goto out; 338178825Sdfr 339178825Sdfr ret = krb5_unparse_name(context, cprincipal, &cname); 340178825Sdfr if (ret) 341178825Sdfr goto out; 342178825Sdfr 343178825Sdfr /* verify server principal */ 344178825Sdfr 345178825Sdfr ret = krb5_sname_to_principal(context, NULL, "kca_service", 346178825Sdfr KRB5_NT_UNKNOWN, &sprincipal); 347178825Sdfr if (ret) 348178825Sdfr goto out; 349178825Sdfr 350178825Sdfr { 351178825Sdfr krb5_principal principal = NULL; 352178825Sdfr 353178825Sdfr ret = krb5_ticket_get_server(context, ticket, &principal); 354178825Sdfr if (ret) 355178825Sdfr goto out; 356178825Sdfr 357178825Sdfr ret = krb5_principal_compare(context, sprincipal, principal); 358178825Sdfr krb5_free_principal(context, principal); 359178825Sdfr if (ret != TRUE) { 360178825Sdfr ret = KRB5KDC_ERR_SERVER_NOMATCH; 361178825Sdfr krb5_set_error_string(context, 362178825Sdfr "User %s used wrong Kx509 service principal", 363178825Sdfr cname); 364178825Sdfr goto out; 365178825Sdfr } 366178825Sdfr } 367178825Sdfr 368178825Sdfr ret = krb5_auth_con_getkey(context, ac, &key); 369178825Sdfr if (ret || key == NULL) { 370178825Sdfr krb5_set_error_string(context, "Kx509 can't get session key"); 371178825Sdfr goto out; 372178825Sdfr } 373178825Sdfr 374178825Sdfr ret = verify_req_hash(context, req, key); 375178825Sdfr if (ret) 376178825Sdfr goto out; 377178825Sdfr 378178825Sdfr /* Verify that the key is encoded RSA key */ 379178825Sdfr { 380178825Sdfr RSAPublicKey key; 381178825Sdfr size_t size; 382178825Sdfr 383178825Sdfr ret = decode_RSAPublicKey(req->pk_key.data, req->pk_key.length, 384178825Sdfr &key, &size); 385178825Sdfr if (ret) 386178825Sdfr goto out; 387178825Sdfr free_RSAPublicKey(&key); 388178825Sdfr if (size != req->pk_key.length) 389178825Sdfr ; 390178825Sdfr } 391178825Sdfr 392178825Sdfr ALLOC(rep.certificate); 393178825Sdfr if (rep.certificate == NULL) 394178825Sdfr goto out; 395178825Sdfr krb5_data_zero(rep.certificate); 396178825Sdfr ALLOC(rep.hash); 397178825Sdfr if (rep.hash == NULL) 398178825Sdfr goto out; 399178825Sdfr krb5_data_zero(rep.hash); 400178825Sdfr 401178825Sdfr ret = build_certificate(context, config, &req->pk_key, 402178825Sdfr krb5_ticket_get_endtime(context, ticket), 403178825Sdfr cprincipal, rep.certificate); 404178825Sdfr if (ret) 405178825Sdfr goto out; 406178825Sdfr 407178825Sdfr ret = calculate_reply_hash(context, key, &rep); 408178825Sdfr if (ret) 409178825Sdfr goto out; 410178825Sdfr 411178825Sdfr /* 412178825Sdfr * Encode reply, [ version | Kx509Response ] 413178825Sdfr */ 414178825Sdfr 415178825Sdfr { 416178825Sdfr krb5_data data; 417178825Sdfr 418178825Sdfr ASN1_MALLOC_ENCODE(Kx509Response, data.data, data.length, &rep, 419178825Sdfr &size, ret); 420178825Sdfr if (ret) { 421178825Sdfr krb5_set_error_string(context, "Failed to encode kx509 reply"); 422178825Sdfr goto out; 423178825Sdfr } 424178825Sdfr if (size != data.length) 425178825Sdfr krb5_abortx(context, "ASN1 internal error"); 426178825Sdfr 427178825Sdfr ret = krb5_data_alloc(reply, data.length + sizeof(version_2_0)); 428178825Sdfr if (ret) { 429178825Sdfr free(data.data); 430178825Sdfr goto out; 431178825Sdfr } 432178825Sdfr memcpy(reply->data, version_2_0, sizeof(version_2_0)); 433178825Sdfr memcpy(((unsigned char *)reply->data) + sizeof(version_2_0), 434178825Sdfr data.data, data.length); 435178825Sdfr free(data.data); 436178825Sdfr } 437178825Sdfr 438178825Sdfr kdc_log(context, config, 0, "Successful Kx509 request for %s", cname); 439178825Sdfr 440178825Sdfrout: 441178825Sdfr if (ac) 442178825Sdfr krb5_auth_con_free(context, ac); 443178825Sdfr if (ret) 444178825Sdfr krb5_warn(context, ret, "Kx509 request from %s failed", from); 445178825Sdfr if (ticket) 446178825Sdfr krb5_free_ticket(context, ticket); 447178825Sdfr if (id) 448178825Sdfr krb5_kt_close(context, id); 449178825Sdfr if (sprincipal) 450178825Sdfr krb5_free_principal(context, sprincipal); 451178825Sdfr if (cprincipal) 452178825Sdfr krb5_free_principal(context, cprincipal); 453178825Sdfr if (key) 454178825Sdfr krb5_free_keyblock (context, key); 455178825Sdfr if (cname) 456178825Sdfr free(cname); 457178825Sdfr free_Kx509Response(&rep); 458178825Sdfr 459178825Sdfr return 0; 460178825Sdfr} 461