root/source4/heimdal/kdc/kx509.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _kdc_try_kx509_request
  2. verify_req_hash
  3. calculate_reply_hash
  4. build_certificate
  5. _kdc_do_kx509

   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 RCSID("$Id$");
  40 
  41 /*
  42  *
  43  */
  44 
  45 krb5_error_code
  46 _kdc_try_kx509_request(void *ptr, size_t len, Kx509Request *req, size_t *size)
     /* [<][>][^][v][top][bottom][index][help] */
  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 
  59 static const unsigned char version_2_0[4] = {0 , 0, 2, 0};
  60 
  61 static krb5_error_code
  62 verify_req_hash(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
  63                 const Kx509Request *req,
  64                 krb5_keyblock *key)
  65 {
  66     unsigned char digest[SHA_DIGEST_LENGTH];
  67     HMAC_CTX 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     HMAC_CTX_init(&ctx);
  77     HMAC_Init_ex(&ctx,
  78                  key->keyvalue.data, key->keyvalue.length,
  79                  EVP_sha1(), NULL);
  80     if (sizeof(digest) != HMAC_size(&ctx))
  81         krb5_abortx(context, "runtime error, hmac buffer wrong size in kx509");
  82     HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
  83     HMAC_Update(&ctx, req->pk_key.data, req->pk_key.length);
  84     HMAC_Final(&ctx, digest, 0);
  85     HMAC_CTX_cleanup(&ctx);
  86 
  87     if (memcmp(req->pk_hash.data, digest, sizeof(digest)) != 0) {
  88         krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
  89                                "pk-hash is not correct");
  90         return KRB5KDC_ERR_PREAUTH_FAILED;
  91     }
  92     return 0;
  93 }
  94 
  95 static krb5_error_code
  96 calculate_reply_hash(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
  97                      krb5_keyblock *key,
  98                      Kx509Response *rep)
  99 {
 100     HMAC_CTX ctx;
 101 
 102     HMAC_CTX_init(&ctx);
 103 
 104     HMAC_Init_ex(&ctx,
 105                  key->keyvalue.data, key->keyvalue.length,
 106                  EVP_sha1(), NULL);
 107     rep->hash->length = HMAC_size(&ctx);
 108     rep->hash->data = malloc(rep->hash->length);
 109     if (rep->hash->data == NULL) {
 110         HMAC_CTX_cleanup(&ctx);
 111         krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
 112         return ENOMEM;
 113     }
 114 
 115     HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
 116     if (rep->error_code) {
 117         int32_t t = *rep->error_code;
 118         do {
 119             unsigned char p = (t & 0xff);
 120             HMAC_Update(&ctx, &p, 1);
 121             t >>= 8;
 122         } while (t);
 123     }
 124     if (rep->certificate)
 125         HMAC_Update(&ctx, rep->certificate->data, rep->certificate->length);
 126     if (rep->e_text)
 127         HMAC_Update(&ctx, (unsigned char *)*rep->e_text, strlen(*rep->e_text));
 128 
 129     HMAC_Final(&ctx, rep->hash->data, 0);
 130     HMAC_CTX_cleanup(&ctx);
 131 
 132     return 0;
 133 }
 134 
 135 /*
 136  * Build a certifate for `principal´ that will expire at `endtime´.
 137  */
 138 
 139 static krb5_error_code
 140 build_certificate(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 141                   krb5_kdc_configuration *config,
 142                   const krb5_data *key,
 143                   time_t endtime,
 144                   krb5_principal principal,
 145                   krb5_data *certificate)
 146 {
 147     hx509_context hxctx = NULL;
 148     hx509_ca_tbs tbs = NULL;
 149     hx509_env env = NULL;
 150     hx509_cert cert = NULL;
 151     hx509_cert signer = NULL;
 152     int ret;
 153 
 154     if (krb5_principal_get_comp_string(context, principal, 1) != NULL) {
 155         kdc_log(context, config, 0, "Principal is not a user");
 156         return EINVAL;
 157     }
 158 
 159     ret = hx509_context_init(&hxctx);
 160     if (ret)
 161         goto out;
 162 
 163     ret = hx509_env_add(hxctx, &env, "principal-name",
 164                         krb5_principal_get_comp_string(context, principal, 0));
 165     if (ret)
 166         goto out;
 167 
 168     {
 169         hx509_certs certs;
 170         hx509_query *q;
 171 
 172         ret = hx509_certs_init(hxctx, config->kx509_ca, 0,
 173                                NULL, &certs);
 174         if (ret) {
 175             kdc_log(context, config, 0, "Failed to load CA %s",
 176                     config->kx509_ca);
 177             goto out;
 178         }
 179         ret = hx509_query_alloc(hxctx, &q);
 180         if (ret) {
 181             hx509_certs_free(&certs);
 182             goto out;
 183         }
 184 
 185         hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
 186         hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN);
 187 
 188         ret = hx509_certs_find(hxctx, certs, q, &signer);
 189         hx509_query_free(hxctx, q);
 190         hx509_certs_free(&certs);
 191         if (ret) {
 192             kdc_log(context, config, 0, "Failed to find a CA in %s",
 193                     config->kx509_ca);
 194             goto out;
 195         }
 196     }
 197 
 198     ret = hx509_ca_tbs_init(hxctx, &tbs);
 199     if (ret)
 200         goto out;
 201 
 202     {
 203         SubjectPublicKeyInfo spki;
 204         heim_any any;
 205 
 206         memset(&spki, 0, sizeof(spki));
 207 
 208         spki.subjectPublicKey.data = key->data;
 209         spki.subjectPublicKey.length = key->length * 8;
 210 
 211         ret = der_copy_oid(oid_id_pkcs1_rsaEncryption(),
 212                            &spki.algorithm.algorithm);
 213 
 214         any.data = "\x05\x00";
 215         any.length = 2;
 216         spki.algorithm.parameters = &any;
 217 
 218         ret = hx509_ca_tbs_set_spki(hxctx, tbs, &spki);
 219         der_free_oid(&spki.algorithm.algorithm);
 220         if (ret)
 221             goto out;
 222     }
 223 
 224     {
 225         hx509_certs certs;
 226         hx509_cert template;
 227 
 228         ret = hx509_certs_init(hxctx, config->kx509_template, 0,
 229                                NULL, &certs);
 230         if (ret) {
 231             kdc_log(context, config, 0, "Failed to load template %s",
 232                     config->kx509_template);
 233             goto out;
 234         }
 235         ret = hx509_get_one_cert(hxctx, certs, &template);
 236         hx509_certs_free(&certs);
 237         if (ret) {
 238             kdc_log(context, config, 0, "Failed to find template in %s",
 239                     config->kx509_template);
 240             goto out;
 241         }
 242         ret = hx509_ca_tbs_set_template(hxctx, tbs,
 243                                         HX509_CA_TEMPLATE_SUBJECT|
 244                                         HX509_CA_TEMPLATE_KU|
 245                                         HX509_CA_TEMPLATE_EKU,
 246                                         template);
 247         hx509_cert_free(template);
 248         if (ret)
 249             goto out;
 250     }
 251 
 252     hx509_ca_tbs_set_notAfter(hxctx, tbs, endtime);
 253 
 254     hx509_ca_tbs_subject_expand(hxctx, tbs, env);
 255     hx509_env_free(&env);
 256 
 257     ret = hx509_ca_sign(hxctx, tbs, signer, &cert);
 258     hx509_cert_free(signer);
 259     if (ret)
 260         goto out;
 261 
 262     hx509_ca_tbs_free(&tbs);
 263 
 264     ret = hx509_cert_binary(hxctx, cert, certificate);
 265     hx509_cert_free(cert);
 266     if (ret)
 267         goto out;
 268                 
 269     hx509_context_free(&hxctx);
 270 
 271     return 0;
 272 out:
 273     if (env)
 274         hx509_env_free(&env);
 275     if (tbs)
 276         hx509_ca_tbs_free(&tbs);
 277     if (signer)
 278         hx509_cert_free(signer);
 279     if (hxctx)
 280         hx509_context_free(&hxctx);
 281     krb5_set_error_message(context, ret, "cert creation failed");
 282     return ret;
 283 }
 284 
 285 /*
 286  *
 287  */
 288 
 289 krb5_error_code
 290 _kdc_do_kx509(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 291               krb5_kdc_configuration *config,
 292               const Kx509Request *req, krb5_data *reply,
 293               const char *from, struct sockaddr *addr)
 294 {
 295     krb5_error_code ret;
 296     krb5_ticket *ticket = NULL;
 297     krb5_flags ap_req_options;
 298     krb5_auth_context ac = NULL;
 299     krb5_keytab id = NULL;
 300     krb5_principal sprincipal = NULL, cprincipal = NULL;
 301     char *cname = NULL;
 302     Kx509Response rep;
 303     size_t size;
 304     krb5_keyblock *key = NULL;
 305 
 306     krb5_data_zero(reply);
 307     memset(&rep, 0, sizeof(rep));
 308 
 309     if(!config->enable_kx509) {
 310         kdc_log(context, config, 0,
 311                 "Rejected kx509 request (disabled) from %s", from);
 312         return KRB5KDC_ERR_POLICY;
 313     }
 314 
 315     kdc_log(context, config, 0, "Kx509 request from %s", from);
 316 
 317     ret = krb5_kt_resolve(context, "HDB:", &id);
 318     if (ret) {
 319         kdc_log(context, config, 0, "Can't open database for digest");
 320         goto out;
 321     }
 322 
 323     ret = krb5_rd_req(context,
 324                       &ac,
 325                       &req->authenticator,
 326                       NULL,
 327                       id,
 328                       &ap_req_options,
 329                       &ticket);
 330     if (ret)
 331         goto out;
 332 
 333     ret = krb5_ticket_get_client(context, ticket, &cprincipal);
 334     if (ret)
 335         goto out;
 336 
 337     ret = krb5_unparse_name(context, cprincipal, &cname);
 338     if (ret)
 339         goto out;
 340 
 341     /* verify server principal */
 342 
 343     ret = krb5_sname_to_principal(context, NULL, "kca_service",
 344                                   KRB5_NT_UNKNOWN, &sprincipal);
 345     if (ret)
 346         goto out;
 347 
 348     {
 349         krb5_principal principal = NULL;
 350 
 351         ret = krb5_ticket_get_server(context, ticket, &principal);
 352         if (ret)
 353             goto out;
 354 
 355         ret = krb5_principal_compare(context, sprincipal, principal);
 356         krb5_free_principal(context, principal);
 357         if (ret != TRUE) {
 358             ret = KRB5KDC_ERR_SERVER_NOMATCH;
 359             krb5_set_error_message(context, ret,
 360                                    "User %s used wrong Kx509 service principal",
 361                                    cname);
 362             goto out;
 363         }
 364     }
 365 
 366     ret = krb5_auth_con_getkey(context, ac, &key);
 367     if (ret == 0 && key == NULL)
 368         ret = KRB5KDC_ERR_NULL_KEY;
 369     if (ret) {
 370         krb5_set_error_message(context, ret, "Kx509 can't get session key");
 371         goto out;
 372     }
 373 
 374     ret = verify_req_hash(context, req, key);
 375     if (ret)
 376         goto out;
 377 
 378     /* Verify that the key is encoded RSA key */
 379     {
 380         RSAPublicKey key;
 381         size_t size;
 382 
 383         ret = decode_RSAPublicKey(req->pk_key.data, req->pk_key.length,
 384                                   &key, &size);
 385         if (ret)
 386             goto out;
 387         free_RSAPublicKey(&key);
 388         if (size != req->pk_key.length)
 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 
 440 out:
 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 }

/* [<][>][^][v][top][bottom][index][help] */