1/* 2 * Copyright (c) 2006 - 2007 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#include <hex.h> 36#include <rfc2459_asn1.h> 37#include <hx509.h> 38 39#ifdef KX509 40 41/* 42 * 43 */ 44 45krb5_error_code 46_kdc_try_kx509_request(void *ptr, size_t len, struct Kx509Request *req, size_t *size) 47{ 48 if (len < 4) 49 return -1; 50 if (memcmp("\x00\x00\x02\x00", ptr, 4) != 0) 51 return -1; 52 return decode_Kx509Request(((unsigned char *)ptr) + 4, len - 4, req, size); 53} 54 55/* 56 * 57 */ 58 59static const unsigned char version_2_0[4] = {0 , 0, 2, 0}; 60 61static krb5_error_code 62verify_req_hash(krb5_context context, 63 const Kx509Request *req, 64 krb5_keyblock *key) 65{ 66 unsigned char digest[CC_SHA_DIGEST_LENGTH]; 67 CCHmacContext ctx; 68 69 if (req->pk_hash.length != sizeof(digest)) { 70 krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, 71 "pk-hash have wrong length: %lu", 72 (unsigned long)req->pk_hash.length); 73 return KRB5KDC_ERR_PREAUTH_FAILED; 74 } 75 76 CCHmacInit(&ctx, kCCHmacAlgSHA1, 77 key->keyvalue.data, key->keyvalue.length) 78 CCHmacUpdate(&ctx, version_2_0, sizeof(version_2_0)); 79 CCHmacUpdate(&ctx, req->pk_key.data, req->pk_key.length); 80 CCHmacFinal(&ctx, digest); 81 memset(&ctx, 0, sizeof(ctx)); 82 83 if (memcmp(req->pk_hash.data, digest, sizeof(digest)) != 0) { 84 krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, 85 "pk-hash is not correct"); 86 return KRB5KDC_ERR_PREAUTH_FAILED; 87 } 88 return 0; 89} 90 91static krb5_error_code 92calculate_reply_hash(krb5_context context, 93 krb5_keyblock *key, 94 Kx509Response *rep) 95{ 96 krb5_error_code ret; 97 CCHmacContext ctx; 98 99 CCHmacInit(&ctx, kCCHmacAlgSHA1, 100 key->keyvalue.data, key->keyvalue.length); 101 ret = krb5_data_alloc(rep->hash, CC_SHA_DIGEST_LENGTH); 102 if (ret) { 103 memset(&ctx, 0, sizeof(ctx)); 104 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 105 return ENOMEM; 106 } 107 108 CCHmacUpdate(&ctx, version_2_0, sizeof(version_2_0)); 109 if (rep->error_code) { 110 int32_t t = *rep->error_code; 111 do { 112 unsigned char p = (t & 0xff); 113 CCHmacUpdate(&ctx, &p, 1); 114 t >>= 8; 115 } while (t); 116 } 117 if (rep->certificate) 118 CCHmacUpdate(&ctx, rep->certificate->data, rep->certificate->length); 119 if (rep->e_text) 120 CCHmacUpdate(&ctx, (unsigned char *)*rep->e_text, strlen(*rep->e_text)); 121 122 CCHmacFinal(&ctx, rep->hash->data); 123 memset(&ctx, 0, sizeof(ctx)); 124 125 return 0; 126} 127 128/* 129 * Build a certifate for `principal´ that will expire at `endtime´. 130 */ 131 132static krb5_error_code 133build_certificate(krb5_context context, 134 krb5_kdc_configuration *config, 135 const krb5_data *key, 136 time_t endtime, 137 krb5_principal principal, 138 krb5_data *certificate) 139{ 140 hx509_ca_tbs tbs = NULL; 141 hx509_env env = NULL; 142 hx509_cert cert = NULL; 143 hx509_cert signer = NULL; 144 int ret; 145 146 if (krb5_principal_get_comp_string(context, principal, 1) != NULL) { 147 kdc_log(context, config, 0, "Principal is not a user"); 148 return EINVAL; 149 } 150 151 ret = hx509_env_add(context->hx509ctx, &env, "principal-name", 152 krb5_principal_get_comp_string(context, principal, 0)); 153 if (ret) 154 goto out; 155 156 { 157 hx509_certs certs; 158 hx509_query *q; 159 160 ret = hx509_certs_init(context->hx509ctx, config->kx509_ca, 0, 161 NULL, &certs); 162 if (ret) { 163 kdc_log(context, config, 0, "Failed to load CA %s", 164 config->kx509_ca); 165 goto out; 166 } 167 ret = hx509_query_alloc(context->hx509ctx, &q); 168 if (ret) { 169 hx509_certs_free(&certs); 170 goto out; 171 } 172 173 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); 174 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN); 175 176 ret = hx509_certs_find(context->hx509ctx, certs, q, &signer); 177 hx509_query_free(context->hx509ctx, q); 178 hx509_certs_free(&certs); 179 if (ret) { 180 kdc_log(context, config, 0, "Failed to find a CA in %s", 181 config->kx509_ca); 182 goto out; 183 } 184 } 185 186 ret = hx509_ca_tbs_init(context->hx509ctx, &tbs); 187 if (ret) 188 goto out; 189 190 { 191 SubjectPublicKeyInfo spki; 192 heim_any any; 193 194 memset(&spki, 0, sizeof(spki)); 195 196 spki.subjectPublicKey.data = key->data; 197 spki.subjectPublicKey.length = key->length * 8; 198 199 ret = der_copy_oid(&asn1_oid_id_pkcs1_rsaEncryption, 200 &spki.algorithm.algorithm); 201 202 any.data = "\x05\x00"; 203 any.length = 2; 204 spki.algorithm.parameters = &any; 205 206 ret = hx509_ca_tbs_set_spki(context->hx509ctx, tbs, &spki); 207 der_free_oid(&spki.algorithm.algorithm); 208 if (ret) 209 goto out; 210 } 211 212 { 213 hx509_certs certs; 214 hx509_cert template; 215 216 ret = hx509_certs_init(context->hx509ctx, config->kx509_template, 0, 217 NULL, &certs); 218 if (ret) { 219 kdc_log(context, config, 0, "Failed to load template %s", 220 config->kx509_template); 221 goto out; 222 } 223 ret = hx509_get_one_cert(context->hx509ctx, certs, &template); 224 hx509_certs_free(&certs); 225 if (ret) { 226 kdc_log(context, config, 0, "Failed to find template in %s", 227 config->kx509_template); 228 goto out; 229 } 230 ret = hx509_ca_tbs_set_template(context->hx509ctx, tbs, 231 HX509_CA_TEMPLATE_SUBJECT| 232 HX509_CA_TEMPLATE_KU| 233 HX509_CA_TEMPLATE_EKU, 234 template); 235 hx509_cert_free(template); 236 if (ret) 237 goto out; 238 } 239 240 hx509_ca_tbs_set_notAfter(context->hx509ctx, tbs, endtime); 241 242 hx509_ca_tbs_subject_expand(context->hx509ctx, tbs, env); 243 hx509_env_free(&env); 244 245 ret = hx509_ca_sign(context->hx509ctx, tbs, signer, &cert); 246 hx509_cert_free(signer); 247 if (ret) 248 goto out; 249 250 hx509_ca_tbs_free(&tbs); 251 252 ret = hx509_cert_binary(context->hx509ctx, cert, certificate); 253 hx509_cert_free(cert); 254 if (ret) 255 goto out; 256 257 return 0; 258out: 259 if (env) 260 hx509_env_free(&env); 261 if (tbs) 262 hx509_ca_tbs_free(&tbs); 263 if (signer) 264 hx509_cert_free(signer); 265 krb5_set_error_message(context, ret, "cert creation failed"); 266 return ret; 267} 268 269/* 270 * 271 */ 272 273krb5_error_code 274_kdc_do_kx509(krb5_context context, 275 krb5_kdc_configuration *config, 276 const struct Kx509Request *req, krb5_data *reply, 277 const char *from, struct sockaddr *addr) 278{ 279 krb5_error_code ret; 280 krb5_ticket *ticket = NULL; 281 krb5_flags ap_req_options; 282 krb5_auth_context ac = NULL; 283 krb5_keytab id = NULL; 284 krb5_principal sprincipal = NULL, cprincipal = NULL; 285 char *cname = NULL; 286 Kx509Response rep; 287 size_t size; 288 krb5_keyblock *key = NULL; 289 290 krb5_data_zero(reply); 291 memset(&rep, 0, sizeof(rep)); 292 293 if(!config->enable_kx509) { 294 kdc_log(context, config, 0, 295 "Rejected kx509 request (disabled) from %s", from); 296 return KRB5KDC_ERR_POLICY; 297 } 298 299 kdc_log(context, config, 0, "Kx509 request from %s", from); 300 301 ret = krb5_kt_resolve(context, "HDB:", &id); 302 if (ret) { 303 kdc_log(context, config, 0, "Can't open database for digest"); 304 goto out; 305 } 306 307 ret = krb5_rd_req(context, 308 &ac, 309 &req->authenticator, 310 NULL, 311 id, 312 &ap_req_options, 313 &ticket); 314 if (ret) 315 goto out; 316 317 ret = krb5_ticket_get_client(context, ticket, &cprincipal); 318 if (ret) 319 goto out; 320 321 ret = krb5_unparse_name(context, cprincipal, &cname); 322 if (ret) 323 goto out; 324 325 /* verify server principal */ 326 327 ret = krb5_sname_to_principal(context, NULL, "kca_service", 328 KRB5_NT_UNKNOWN, &sprincipal); 329 if (ret) 330 goto out; 331 332 { 333 krb5_principal principal = NULL; 334 335 ret = krb5_ticket_get_server(context, ticket, &principal); 336 if (ret) 337 goto out; 338 339 ret = krb5_principal_compare(context, sprincipal, principal); 340 krb5_free_principal(context, principal); 341 if (ret != TRUE) { 342 char *expected, *used; 343 344 ret = krb5_unparse_name(context, sprincipal, &expected); 345 if (ret) 346 goto out; 347 ret = krb5_unparse_name(context, principal, &used); 348 if (ret) { 349 krb5_xfree(expected); 350 goto out; 351 } 352 353 ret = KRB5KDC_ERR_SERVER_NOMATCH; 354 krb5_set_error_message(context, ret, 355 "User %s used wrong Kx509 service " 356 "principal, expected: %s, used %s", 357 cname, expected, used); 358 krb5_xfree(expected); 359 krb5_xfree(used); 360 goto out; 361 } 362 } 363 364 ret = krb5_auth_con_getkey(context, ac, &key); 365 if (ret == 0 && key == NULL) 366 ret = KRB5KDC_ERR_NULL_KEY; 367 if (ret) { 368 krb5_set_error_message(context, ret, "Kx509 can't get session key"); 369 goto out; 370 } 371 372 ret = verify_req_hash(context, req, key); 373 if (ret) 374 goto out; 375 376 /* Verify that the key is encoded RSA key */ 377 { 378 RSAPublicKey key; 379 size_t size; 380 381 ret = decode_RSAPublicKey(req->pk_key.data, req->pk_key.length, 382 &key, &size); 383 if (ret) 384 goto out; 385 free_RSAPublicKey(&key); 386 if (size != req->pk_key.length) { 387 ret = ASN1_EXTRA_DATA; 388 goto out; 389 } 390 } 391 392 ALLOC(rep.certificate); 393 if (rep.certificate == NULL) 394 goto out; 395 krb5_data_zero(rep.certificate); 396 ALLOC(rep.hash); 397 if (rep.hash == NULL) 398 goto out; 399 krb5_data_zero(rep.hash); 400 401 ret = build_certificate(context, config, &req->pk_key, 402 krb5_ticket_get_endtime(context, ticket), 403 cprincipal, rep.certificate); 404 if (ret) 405 goto out; 406 407 ret = calculate_reply_hash(context, key, &rep); 408 if (ret) 409 goto out; 410 411 /* 412 * Encode reply, [ version | Kx509Response ] 413 */ 414 415 { 416 krb5_data data; 417 418 ASN1_MALLOC_ENCODE(Kx509Response, data.data, data.length, &rep, 419 &size, ret); 420 if (ret) { 421 krb5_set_error_message(context, ret, "Failed to encode kx509 reply"); 422 goto out; 423 } 424 if (size != data.length) 425 krb5_abortx(context, "ASN1 internal error"); 426 427 ret = krb5_data_alloc(reply, data.length + sizeof(version_2_0)); 428 if (ret) { 429 free(data.data); 430 goto out; 431 } 432 memcpy(reply->data, version_2_0, sizeof(version_2_0)); 433 memcpy(((unsigned char *)reply->data) + sizeof(version_2_0), 434 data.data, data.length); 435 free(data.data); 436 } 437 438 kdc_log(context, config, 0, "Successful Kx509 request for %s", cname); 439 440out: 441 if (ac) 442 krb5_auth_con_free(context, ac); 443 if (ret) 444 krb5_warn(context, ret, "Kx509 request from %s failed", from); 445 if (ticket) 446 krb5_free_ticket(context, ticket); 447 if (id) 448 krb5_kt_close(context, id); 449 if (sprincipal) 450 krb5_free_principal(context, sprincipal); 451 if (cprincipal) 452 krb5_free_principal(context, cprincipal); 453 if (key) 454 krb5_free_keyblock (context, key); 455 if (cname) 456 free(cname); 457 free_Kx509Response(&rep); 458 459 return 0; 460} 461 462#endif /* KX509 */ 463