1178825Sdfr/* 2233294Sstas * Copyright (c) 2006 - 2007 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 5178825Sdfr * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 9178825Sdfr * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 20178825Sdfr * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34178825Sdfr#include "kdc_locl.h" 35178825Sdfr#include <hex.h> 36178825Sdfr#include <rfc2459_asn1.h> 37178825Sdfr#include <hx509.h> 38178825Sdfr 39233294Sstas#ifdef KX509 40178825Sdfr 41178825Sdfr/* 42178825Sdfr * 43178825Sdfr */ 44178825Sdfr 45178825Sdfrkrb5_error_code 46233294Sstas_kdc_try_kx509_request(void *ptr, size_t len, struct 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 62233294Sstasverify_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; 68233294Sstas 69178825Sdfr if (req->pk_hash.length != sizeof(digest)) { 70233294Sstas krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, 71233294Sstas "pk-hash have wrong length: %lu", 72233294Sstas (unsigned long)req->pk_hash.length); 73178825Sdfr return KRB5KDC_ERR_PREAUTH_FAILED; 74178825Sdfr } 75178825Sdfr 76178825Sdfr HMAC_CTX_init(&ctx); 77233294Sstas HMAC_Init_ex(&ctx, 78233294Sstas key->keyvalue.data, key->keyvalue.length, 79178825Sdfr EVP_sha1(), NULL); 80178825Sdfr if (sizeof(digest) != HMAC_size(&ctx)) 81178825Sdfr krb5_abortx(context, "runtime error, hmac buffer wrong size in kx509"); 82178825Sdfr HMAC_Update(&ctx, version_2_0, sizeof(version_2_0)); 83178825Sdfr HMAC_Update(&ctx, req->pk_key.data, req->pk_key.length); 84178825Sdfr HMAC_Final(&ctx, digest, 0); 85178825Sdfr HMAC_CTX_cleanup(&ctx); 86178825Sdfr 87178825Sdfr if (memcmp(req->pk_hash.data, digest, sizeof(digest)) != 0) { 88233294Sstas krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, 89233294Sstas "pk-hash is not correct"); 90178825Sdfr return KRB5KDC_ERR_PREAUTH_FAILED; 91178825Sdfr } 92178825Sdfr return 0; 93178825Sdfr} 94178825Sdfr 95178825Sdfrstatic krb5_error_code 96178825Sdfrcalculate_reply_hash(krb5_context context, 97178825Sdfr krb5_keyblock *key, 98178825Sdfr Kx509Response *rep) 99178825Sdfr{ 100233294Sstas krb5_error_code ret; 101178825Sdfr HMAC_CTX ctx; 102233294Sstas 103178825Sdfr HMAC_CTX_init(&ctx); 104178825Sdfr 105233294Sstas HMAC_Init_ex(&ctx, key->keyvalue.data, key->keyvalue.length, 106178825Sdfr EVP_sha1(), NULL); 107233294Sstas ret = krb5_data_alloc(rep->hash, HMAC_size(&ctx)); 108233294Sstas if (ret) { 109178825Sdfr HMAC_CTX_cleanup(&ctx); 110233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 111178825Sdfr return ENOMEM; 112178825Sdfr } 113178825Sdfr 114178825Sdfr HMAC_Update(&ctx, version_2_0, sizeof(version_2_0)); 115178825Sdfr if (rep->error_code) { 116178825Sdfr int32_t t = *rep->error_code; 117178825Sdfr do { 118178825Sdfr unsigned char p = (t & 0xff); 119178825Sdfr HMAC_Update(&ctx, &p, 1); 120178825Sdfr t >>= 8; 121178825Sdfr } while (t); 122178825Sdfr } 123178825Sdfr if (rep->certificate) 124178825Sdfr HMAC_Update(&ctx, rep->certificate->data, rep->certificate->length); 125178825Sdfr if (rep->e_text) 126178825Sdfr HMAC_Update(&ctx, (unsigned char *)*rep->e_text, strlen(*rep->e_text)); 127178825Sdfr 128178825Sdfr HMAC_Final(&ctx, rep->hash->data, 0); 129178825Sdfr HMAC_CTX_cleanup(&ctx); 130178825Sdfr 131178825Sdfr return 0; 132178825Sdfr} 133178825Sdfr 134178825Sdfr/* 135233294Sstas * Build a certifate for `principal�� that will expire at `endtime��. 136178825Sdfr */ 137178825Sdfr 138178825Sdfrstatic krb5_error_code 139233294Sstasbuild_certificate(krb5_context context, 140178825Sdfr krb5_kdc_configuration *config, 141178825Sdfr const krb5_data *key, 142178825Sdfr time_t endtime, 143178825Sdfr krb5_principal principal, 144178825Sdfr krb5_data *certificate) 145178825Sdfr{ 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 157233294Sstas ret = hx509_env_add(context->hx509ctx, &env, "principal-name", 158178825Sdfr krb5_principal_get_comp_string(context, principal, 0)); 159178825Sdfr if (ret) 160178825Sdfr goto out; 161178825Sdfr 162178825Sdfr { 163178825Sdfr hx509_certs certs; 164178825Sdfr hx509_query *q; 165178825Sdfr 166233294Sstas ret = hx509_certs_init(context->hx509ctx, config->kx509_ca, 0, 167178825Sdfr NULL, &certs); 168178825Sdfr if (ret) { 169178825Sdfr kdc_log(context, config, 0, "Failed to load CA %s", 170178825Sdfr config->kx509_ca); 171178825Sdfr goto out; 172178825Sdfr } 173233294Sstas ret = hx509_query_alloc(context->hx509ctx, &q); 174178825Sdfr if (ret) { 175178825Sdfr hx509_certs_free(&certs); 176178825Sdfr goto out; 177178825Sdfr } 178178825Sdfr 179178825Sdfr hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); 180178825Sdfr hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN); 181178825Sdfr 182233294Sstas ret = hx509_certs_find(context->hx509ctx, certs, q, &signer); 183233294Sstas hx509_query_free(context->hx509ctx, q); 184178825Sdfr hx509_certs_free(&certs); 185178825Sdfr if (ret) { 186178825Sdfr kdc_log(context, config, 0, "Failed to find a CA in %s", 187178825Sdfr config->kx509_ca); 188178825Sdfr goto out; 189178825Sdfr } 190178825Sdfr } 191178825Sdfr 192233294Sstas ret = hx509_ca_tbs_init(context->hx509ctx, &tbs); 193178825Sdfr if (ret) 194178825Sdfr goto out; 195178825Sdfr 196178825Sdfr { 197178825Sdfr SubjectPublicKeyInfo spki; 198178825Sdfr heim_any any; 199178825Sdfr 200178825Sdfr memset(&spki, 0, sizeof(spki)); 201178825Sdfr 202178825Sdfr spki.subjectPublicKey.data = key->data; 203178825Sdfr spki.subjectPublicKey.length = key->length * 8; 204178825Sdfr 205233294Sstas ret = der_copy_oid(&asn1_oid_id_pkcs1_rsaEncryption, 206178825Sdfr &spki.algorithm.algorithm); 207178825Sdfr 208178825Sdfr any.data = "\x05\x00"; 209178825Sdfr any.length = 2; 210178825Sdfr spki.algorithm.parameters = &any; 211178825Sdfr 212233294Sstas ret = hx509_ca_tbs_set_spki(context->hx509ctx, tbs, &spki); 213178825Sdfr der_free_oid(&spki.algorithm.algorithm); 214178825Sdfr if (ret) 215178825Sdfr goto out; 216178825Sdfr } 217178825Sdfr 218178825Sdfr { 219178825Sdfr hx509_certs certs; 220178825Sdfr hx509_cert template; 221178825Sdfr 222233294Sstas ret = hx509_certs_init(context->hx509ctx, config->kx509_template, 0, 223178825Sdfr NULL, &certs); 224178825Sdfr if (ret) { 225178825Sdfr kdc_log(context, config, 0, "Failed to load template %s", 226178825Sdfr config->kx509_template); 227178825Sdfr goto out; 228178825Sdfr } 229233294Sstas ret = hx509_get_one_cert(context->hx509ctx, certs, &template); 230178825Sdfr hx509_certs_free(&certs); 231178825Sdfr if (ret) { 232178825Sdfr kdc_log(context, config, 0, "Failed to find template in %s", 233178825Sdfr config->kx509_template); 234178825Sdfr goto out; 235178825Sdfr } 236233294Sstas ret = hx509_ca_tbs_set_template(context->hx509ctx, tbs, 237178825Sdfr HX509_CA_TEMPLATE_SUBJECT| 238178825Sdfr HX509_CA_TEMPLATE_KU| 239178825Sdfr HX509_CA_TEMPLATE_EKU, 240178825Sdfr template); 241178825Sdfr hx509_cert_free(template); 242178825Sdfr if (ret) 243178825Sdfr goto out; 244178825Sdfr } 245178825Sdfr 246233294Sstas hx509_ca_tbs_set_notAfter(context->hx509ctx, tbs, endtime); 247178825Sdfr 248233294Sstas hx509_ca_tbs_subject_expand(context->hx509ctx, tbs, env); 249178825Sdfr hx509_env_free(&env); 250178825Sdfr 251233294Sstas ret = hx509_ca_sign(context->hx509ctx, tbs, signer, &cert); 252178825Sdfr hx509_cert_free(signer); 253178825Sdfr if (ret) 254178825Sdfr goto out; 255178825Sdfr 256178825Sdfr hx509_ca_tbs_free(&tbs); 257178825Sdfr 258233294Sstas ret = hx509_cert_binary(context->hx509ctx, cert, certificate); 259178825Sdfr hx509_cert_free(cert); 260178825Sdfr if (ret) 261178825Sdfr goto out; 262178825Sdfr 263178825Sdfr return 0; 264178825Sdfrout: 265178825Sdfr if (env) 266178825Sdfr hx509_env_free(&env); 267178825Sdfr if (tbs) 268178825Sdfr hx509_ca_tbs_free(&tbs); 269178825Sdfr if (signer) 270178825Sdfr hx509_cert_free(signer); 271233294Sstas krb5_set_error_message(context, ret, "cert creation failed"); 272178825Sdfr return ret; 273178825Sdfr} 274178825Sdfr 275178825Sdfr/* 276178825Sdfr * 277178825Sdfr */ 278178825Sdfr 279178825Sdfrkrb5_error_code 280233294Sstas_kdc_do_kx509(krb5_context context, 281178825Sdfr krb5_kdc_configuration *config, 282233294Sstas const struct Kx509Request *req, krb5_data *reply, 283178825Sdfr const char *from, struct sockaddr *addr) 284178825Sdfr{ 285178825Sdfr krb5_error_code ret; 286178825Sdfr krb5_ticket *ticket = NULL; 287178825Sdfr krb5_flags ap_req_options; 288178825Sdfr krb5_auth_context ac = NULL; 289178825Sdfr krb5_keytab id = NULL; 290178825Sdfr krb5_principal sprincipal = NULL, cprincipal = NULL; 291178825Sdfr char *cname = NULL; 292178825Sdfr Kx509Response rep; 293178825Sdfr size_t size; 294178825Sdfr krb5_keyblock *key = NULL; 295178825Sdfr 296178825Sdfr krb5_data_zero(reply); 297178825Sdfr memset(&rep, 0, sizeof(rep)); 298178825Sdfr 299178825Sdfr if(!config->enable_kx509) { 300233294Sstas kdc_log(context, config, 0, 301178825Sdfr "Rejected kx509 request (disabled) from %s", from); 302178825Sdfr return KRB5KDC_ERR_POLICY; 303178825Sdfr } 304178825Sdfr 305178825Sdfr kdc_log(context, config, 0, "Kx509 request from %s", from); 306178825Sdfr 307178825Sdfr ret = krb5_kt_resolve(context, "HDB:", &id); 308178825Sdfr if (ret) { 309178825Sdfr kdc_log(context, config, 0, "Can't open database for digest"); 310178825Sdfr goto out; 311178825Sdfr } 312178825Sdfr 313233294Sstas ret = krb5_rd_req(context, 314178825Sdfr &ac, 315178825Sdfr &req->authenticator, 316178825Sdfr NULL, 317178825Sdfr id, 318178825Sdfr &ap_req_options, 319178825Sdfr &ticket); 320178825Sdfr if (ret) 321178825Sdfr goto out; 322178825Sdfr 323178825Sdfr ret = krb5_ticket_get_client(context, ticket, &cprincipal); 324178825Sdfr if (ret) 325178825Sdfr goto out; 326178825Sdfr 327178825Sdfr ret = krb5_unparse_name(context, cprincipal, &cname); 328178825Sdfr if (ret) 329178825Sdfr goto out; 330233294Sstas 331178825Sdfr /* verify server principal */ 332178825Sdfr 333178825Sdfr ret = krb5_sname_to_principal(context, NULL, "kca_service", 334178825Sdfr KRB5_NT_UNKNOWN, &sprincipal); 335178825Sdfr if (ret) 336178825Sdfr goto out; 337178825Sdfr 338178825Sdfr { 339178825Sdfr krb5_principal principal = NULL; 340178825Sdfr 341178825Sdfr ret = krb5_ticket_get_server(context, ticket, &principal); 342178825Sdfr if (ret) 343178825Sdfr goto out; 344178825Sdfr 345178825Sdfr ret = krb5_principal_compare(context, sprincipal, principal); 346178825Sdfr krb5_free_principal(context, principal); 347178825Sdfr if (ret != TRUE) { 348233294Sstas char *expected, *used; 349233294Sstas 350233294Sstas ret = krb5_unparse_name(context, sprincipal, &expected); 351233294Sstas if (ret) 352233294Sstas goto out; 353233294Sstas ret = krb5_unparse_name(context, principal, &used); 354233294Sstas if (ret) { 355233294Sstas krb5_xfree(expected); 356233294Sstas goto out; 357233294Sstas } 358233294Sstas 359178825Sdfr ret = KRB5KDC_ERR_SERVER_NOMATCH; 360233294Sstas krb5_set_error_message(context, ret, 361233294Sstas "User %s used wrong Kx509 service " 362233294Sstas "principal, expected: %s, used %s", 363233294Sstas cname, expected, used); 364233294Sstas krb5_xfree(expected); 365233294Sstas krb5_xfree(used); 366178825Sdfr goto out; 367178825Sdfr } 368178825Sdfr } 369233294Sstas 370178825Sdfr ret = krb5_auth_con_getkey(context, ac, &key); 371233294Sstas if (ret == 0 && key == NULL) 372233294Sstas ret = KRB5KDC_ERR_NULL_KEY; 373233294Sstas if (ret) { 374233294Sstas krb5_set_error_message(context, ret, "Kx509 can't get session key"); 375178825Sdfr goto out; 376178825Sdfr } 377233294Sstas 378178825Sdfr ret = verify_req_hash(context, req, key); 379178825Sdfr if (ret) 380178825Sdfr goto out; 381178825Sdfr 382178825Sdfr /* Verify that the key is encoded RSA key */ 383178825Sdfr { 384178825Sdfr RSAPublicKey key; 385178825Sdfr size_t size; 386178825Sdfr 387178825Sdfr ret = decode_RSAPublicKey(req->pk_key.data, req->pk_key.length, 388178825Sdfr &key, &size); 389178825Sdfr if (ret) 390178825Sdfr goto out; 391178825Sdfr free_RSAPublicKey(&key); 392233294Sstas if (size != req->pk_key.length) { 393233294Sstas ret = ASN1_EXTRA_DATA; 394233294Sstas goto out; 395233294Sstas } 396178825Sdfr } 397178825Sdfr 398178825Sdfr ALLOC(rep.certificate); 399178825Sdfr if (rep.certificate == NULL) 400178825Sdfr goto out; 401178825Sdfr krb5_data_zero(rep.certificate); 402178825Sdfr ALLOC(rep.hash); 403178825Sdfr if (rep.hash == NULL) 404178825Sdfr goto out; 405178825Sdfr krb5_data_zero(rep.hash); 406178825Sdfr 407233294Sstas ret = build_certificate(context, config, &req->pk_key, 408178825Sdfr krb5_ticket_get_endtime(context, ticket), 409178825Sdfr cprincipal, rep.certificate); 410178825Sdfr if (ret) 411178825Sdfr goto out; 412178825Sdfr 413178825Sdfr ret = calculate_reply_hash(context, key, &rep); 414178825Sdfr if (ret) 415178825Sdfr goto out; 416178825Sdfr 417178825Sdfr /* 418178825Sdfr * Encode reply, [ version | Kx509Response ] 419178825Sdfr */ 420178825Sdfr 421178825Sdfr { 422178825Sdfr krb5_data data; 423178825Sdfr 424178825Sdfr ASN1_MALLOC_ENCODE(Kx509Response, data.data, data.length, &rep, 425178825Sdfr &size, ret); 426178825Sdfr if (ret) { 427233294Sstas krb5_set_error_message(context, ret, "Failed to encode kx509 reply"); 428178825Sdfr goto out; 429178825Sdfr } 430178825Sdfr if (size != data.length) 431178825Sdfr krb5_abortx(context, "ASN1 internal error"); 432178825Sdfr 433178825Sdfr ret = krb5_data_alloc(reply, data.length + sizeof(version_2_0)); 434178825Sdfr if (ret) { 435178825Sdfr free(data.data); 436178825Sdfr goto out; 437178825Sdfr } 438178825Sdfr memcpy(reply->data, version_2_0, sizeof(version_2_0)); 439178825Sdfr memcpy(((unsigned char *)reply->data) + sizeof(version_2_0), 440178825Sdfr data.data, data.length); 441178825Sdfr free(data.data); 442178825Sdfr } 443178825Sdfr 444178825Sdfr kdc_log(context, config, 0, "Successful Kx509 request for %s", cname); 445178825Sdfr 446178825Sdfrout: 447178825Sdfr if (ac) 448178825Sdfr krb5_auth_con_free(context, ac); 449178825Sdfr if (ret) 450178825Sdfr krb5_warn(context, ret, "Kx509 request from %s failed", from); 451178825Sdfr if (ticket) 452178825Sdfr krb5_free_ticket(context, ticket); 453178825Sdfr if (id) 454178825Sdfr krb5_kt_close(context, id); 455178825Sdfr if (sprincipal) 456178825Sdfr krb5_free_principal(context, sprincipal); 457178825Sdfr if (cprincipal) 458178825Sdfr krb5_free_principal(context, cprincipal); 459178825Sdfr if (key) 460178825Sdfr krb5_free_keyblock (context, key); 461178825Sdfr if (cname) 462178825Sdfr free(cname); 463178825Sdfr free_Kx509Response(&rep); 464178825Sdfr 465178825Sdfr return 0; 466178825Sdfr} 467233294Sstas 468233294Sstas#endif /* KX509 */ 469