LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/krb5 - kx509.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 0 545 0.0 %
Date: 2024-04-21 15:09:00 Functions: 0 27 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2019 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 "krb5_locl.h"
      35             : #include <kx509_asn1.h>
      36             : #include <kx509_err.h>
      37             : #include "../hx509/hx_locl.h" /* XXX find a better way */
      38             : #include "hx509-private.h"
      39             : 
      40             : /*
      41             :  * This file implements a client for the kx509 protocol -- a Kerberized online
      42             :  * CA that can issue a Certificate to a client that authenticates using
      43             :  * Kerberos.
      44             :  *
      45             :  * The kx509 protocol is the inverse of PKINIT.  Whereas PKINIT allows users
      46             :  * with PKIX credentials to acquire Kerberos credentials, the kx509 protocol
      47             :  * allows users with Kerberos credentials to acquire PKIX credentials.
      48             :  *
      49             :  * I.e., kx509 is a bridge, just like PKINIT.
      50             :  *
      51             :  * The kx509 protocol is very simple, and very limited.
      52             :  *
      53             :  * A request consists of a DER-encoded Kx509Request message prefixed with four
      54             :  * bytes identifying the protocol (see `version_2_0' below).
      55             :  *
      56             :  * A Kx509Request message contains an AP-REQ, a public key, and an HMAC of the
      57             :  * public key made with the session key of the AP-REQ's ticket.
      58             :  *
      59             :  * The service principal can be either kca_service/hostname.fqdn or
      60             :  * krbtgt/REALM (a Heimdal innovation).
      61             :  *
      62             :  * If a request is missing a public key, then the request is a probe intended
      63             :  * to discover whether the service is enabled, thus helping the client avoid
      64             :  * a possibly-slow private key generation operation.
      65             :  *
      66             :  * The response is a DER-encoded Kx509Response also prefixed with
      67             :  * `version_2_0', and contains: an optional error code and error text, an
      68             :  * optional certificate (for the success case), and an optional HMAC of those
      69             :  * fields that is present when the service was able to verify the AP-REQ.
      70             :  *
      71             :  * Limitations:
      72             :  *
      73             :  *  - no proof of possession for the public key
      74             :  *  - only RSA keys are supported
      75             :  *  - no way to express options (e.g., what KUs, EKUs, or SANs are desired)
      76             :  *  - no sub-session key usage
      77             :  *  - no reflection protection other than the HMAC's forgery protection and the
      78             :  *    fact that the client could tell that a reflected attack isn't success
      79             :  *
      80             :  * Future directions:
      81             :  *
      82             :  *  - Since the public key field of the request is an OCTET STRING, we could
      83             :  *    send a CSR, or even an expired certificate (possibly self-signed,
      84             :  *    possibly one issued earlier) that can serve as a template.
      85             :  *
      86             :  *    This solves the first three limitations, as it allows the client to
      87             :  *    demonstrate proof of possession, allows arbitrary public key types, and
      88             :  *    allows the client to express desires about the to-be-issued certificate.
      89             :  *
      90             :  *  - Use the AP-REQ's Authenticator's sub-session key for the HMAC, and derive
      91             :  *    per-direction sub-sub-keys.
      92             :  *
      93             :  *  - We might design a new protocol that better fits the RFC4120 KDC message
      94             :  *    framework.
      95             :  */
      96             : 
      97             : static const unsigned char version_2_0[4] = {0 , 0, 2, 0};
      98             : 
      99             : struct krb5_kx509_req_ctx_data {
     100             :     krb5_auth_context   ac;
     101             :     krb5_data           given_csr;
     102             :     hx509_request       csr;
     103             :     Kx509CSRPlus        csr_plus;
     104             :     char                *realm;     /* Realm to which to send request */
     105             :     krb5_keyblock       *hmac_key;  /* For HMAC validation */
     106             :     hx509_private_key   *keys;
     107             :     hx509_private_key   priv_key;
     108             :     unsigned int        expect_chain;
     109             : };
     110             : 
     111             : /**
     112             :  * Create a kx509 request context.
     113             :  *
     114             :  * @param context The Kerberos library context
     115             :  * @param out Where to place the kx509 request context
     116             :  *
     117             :  * @return A krb5 error code.
     118             :  */
     119             : krb5_error_code
     120           0 : krb5_kx509_ctx_init(krb5_context context, krb5_kx509_req_ctx *out)
     121             : {
     122           0 :     krb5_kx509_req_ctx ctx;
     123           0 :     krb5_error_code ret;
     124           0 :     hx509_name name = NULL;
     125             : 
     126           0 :     ALLOC(ctx, 1);
     127           0 :     if (ctx == NULL)
     128           0 :         return krb5_enomem(context);
     129           0 :     ctx->given_csr.data = NULL;
     130           0 :     ctx->priv_key = NULL;
     131           0 :     ctx->hmac_key = NULL;
     132           0 :     ctx->realm = NULL;
     133           0 :     ctx->keys = NULL;
     134           0 :     ctx->csr = NULL;
     135           0 :     ret = hx509_request_init(context->hx509ctx, &ctx->csr);
     136           0 :     if (ret == 0)
     137           0 :         ret = hx509_parse_name(context->hx509ctx, "", &name);
     138           0 :     if (ret == 0)
     139           0 :         ret = hx509_request_set_name(context->hx509ctx, ctx->csr, name);
     140           0 :     if (ret == 0)
     141           0 :         ret = krb5_auth_con_init(context, &ctx->ac);
     142           0 :     if (name)
     143           0 :         hx509_name_free(&name);
     144           0 :     if (ret == 0)
     145           0 :         *out = ctx;
     146             :     else
     147           0 :         krb5_kx509_ctx_free(context, &ctx);
     148           0 :     return ret;
     149             : }
     150             : 
     151             : /**
     152             :  * Free a kx509 request context.
     153             :  *
     154             :  * @param context The Kerberos library context
     155             :  * @param ctxp Pointer to krb5 request context to free
     156             :  *
     157             :  * @return A krb5 error code.
     158             :  */
     159             : void
     160           0 : krb5_kx509_ctx_free(krb5_context context, krb5_kx509_req_ctx *ctxp)
     161             : {
     162           0 :     krb5_kx509_req_ctx ctx = *ctxp;
     163             : 
     164           0 :     *ctxp = NULL;
     165           0 :     if (ctx == NULL)
     166           0 :         return;
     167           0 :     krb5_free_keyblock(context, ctx->hmac_key);
     168           0 :     krb5_auth_con_free(context, ctx->ac);
     169           0 :     free_Kx509CSRPlus(&ctx->csr_plus);
     170           0 :     free(ctx->realm);
     171           0 :     hx509_request_free(&ctx->csr);
     172           0 :     krb5_data_free(&ctx->given_csr);
     173           0 :     hx509_private_key_free(&ctx->priv_key);
     174           0 :     _hx509_certs_keys_free(context->hx509ctx, ctx->keys);
     175           0 :     free(ctx);
     176             : }
     177             : 
     178             : /**
     179             :  * Set a realm to send kx509 request to, if different from the client's.
     180             :  *
     181             :  * @param context The Kerberos library context
     182             :  * @param ctx The kx509 request context
     183             :  * @param realm Realm name
     184             :  *
     185             :  * @return A krb5 error code.
     186             :  */
     187             : krb5_error_code
     188           0 : krb5_kx509_ctx_set_realm(krb5_context context,
     189             :                          krb5_kx509_req_ctx kx509_ctx,
     190             :                          const char *realm)
     191             : {
     192           0 :     return ((kx509_ctx->realm = strdup(realm)) == NULL) ?
     193           0 :         krb5_enomem(context) : 0;
     194             : }
     195             : 
     196             : /**
     197             :  * Sets a CSR for a kx509 request.
     198             :  *
     199             :  * Normally kx509 will generate a CSR (and even a private key for it)
     200             :  * automatically.  If a CSR is given then kx509 will use it instead of
     201             :  * generating one.
     202             :  *
     203             :  * @param context The Kerberos library context
     204             :  * @param ctx The kx509 request context
     205             :  * @param csr_der A DER-encoded PKCS#10 CSR
     206             :  *
     207             :  * @return A krb5 error code.
     208             :  */
     209             : krb5_error_code
     210           0 : krb5_kx509_ctx_set_csr_der(krb5_context context,
     211             :                            krb5_kx509_req_ctx ctx,
     212             :                            krb5_data *csr_der)
     213             : {
     214           0 :     krb5_data_free(&ctx->given_csr);
     215           0 :     return krb5_data_copy(&ctx->given_csr, csr_der->data, csr_der->length);
     216             : }
     217             : 
     218             : /**
     219             :  * Adds an EKU as an additional desired Certificate Extension or in the CSR if
     220             :  * the caller does not set a CSR.
     221             :  *
     222             :  * @param context The Kerberos library context
     223             :  * @param ctx The kx509 request context
     224             :  * @param oids A string representation of an OID
     225             :  *
     226             :  * @return A krb5 error code.
     227             :  */
     228             : krb5_error_code
     229           0 : krb5_kx509_ctx_add_eku(krb5_context context,
     230             :                        krb5_kx509_req_ctx kx509_ctx,
     231             :                        const char *oids)
     232             : {
     233           0 :     krb5_error_code ret;
     234           0 :     heim_oid oid;
     235             : 
     236           0 :     ret = der_parse_heim_oid(oids, NULL, &oid);
     237           0 :     if (ret == 0)
     238           0 :         hx509_request_add_eku(context->hx509ctx, kx509_ctx->csr, &oid);
     239           0 :     der_free_oid(&oid);
     240           0 :     return ret;
     241             : }
     242             : 
     243             : /**
     244             :  * Adds a dNSName SAN (domainname, hostname) as an additional desired
     245             :  * Certificate Extension or in the CSR if the caller does not set a CSR.
     246             :  *
     247             :  * @param context The Kerberos library context
     248             :  * @param ctx The kx509 request context
     249             :  * @param dname A string containing a DNS domainname
     250             :  *
     251             :  * @return A krb5 error code.
     252             :  */
     253             : krb5_error_code
     254           0 : krb5_kx509_ctx_add_san_dns_name(krb5_context context,
     255             :                                 krb5_kx509_req_ctx kx509_ctx,
     256             :                                 const char *dname)
     257             : {
     258           0 :     return hx509_request_add_dns_name(context->hx509ctx, kx509_ctx->csr,
     259             :                                       dname);
     260             : }
     261             : 
     262             : /**
     263             :  * Adds an xmppAddr SAN (jabber address) as an additional desired Certificate
     264             :  * Extension or in the CSR if the caller does not set a CSR.
     265             :  *
     266             :  * @param context The Kerberos library context
     267             :  * @param ctx The kx509 request context
     268             :  * @param jid A string containing a Jabber address
     269             :  *
     270             :  * @return A krb5 error code.
     271             :  */
     272             : krb5_error_code
     273           0 : krb5_kx509_ctx_add_san_xmpp(krb5_context context,
     274             :                             krb5_kx509_req_ctx kx509_ctx,
     275             :                             const char *jid)
     276             : {
     277           0 :     return hx509_request_add_xmpp_name(context->hx509ctx, kx509_ctx->csr, jid);
     278             : }
     279             : 
     280             : /**
     281             :  * Adds an rfc822Name SAN (e-mail address) as an additional desired Certificate
     282             :  * Extension or in the CSR if the caller does not set a CSR.
     283             :  *
     284             :  * @param context The Kerberos library context
     285             :  * @param ctx The kx509 request context
     286             :  * @param email A string containing an e-mail address
     287             :  *
     288             :  * @return A krb5 error code.
     289             :  */
     290             : krb5_error_code
     291           0 : krb5_kx509_ctx_add_san_rfc822Name(krb5_context context,
     292             :                                   krb5_kx509_req_ctx kx509_ctx,
     293             :                                   const char *email)
     294             : {
     295           0 :     return hx509_request_add_email(context->hx509ctx, kx509_ctx->csr, email);
     296             : }
     297             : 
     298             : /**
     299             :  * Adds an pkinit SAN (Kerberos principal name) as an additional desired
     300             :  * Certificate Extension or in the CSR if the caller does not set a CSR.
     301             :  *
     302             :  * @param context The Kerberos library context
     303             :  * @param ctx The kx509 request context
     304             :  * @param pname A string containing a representation of a Kerberos principal
     305             :  *              name
     306             :  *
     307             :  * @return A krb5 error code.
     308             :  */
     309             : krb5_error_code
     310           0 : krb5_kx509_ctx_add_san_pkinit(krb5_context context,
     311             :                               krb5_kx509_req_ctx kx509_ctx,
     312             :                               const char *pname)
     313             : {
     314           0 :     return hx509_request_add_pkinit(context->hx509ctx, kx509_ctx->csr, pname);
     315             : }
     316             : 
     317             : /**
     318             :  * Adds a Microsoft-style UPN (user principal name) as an additional desired
     319             :  * Certificate Extension or in the CSR if the caller does not set a CSR.
     320             :  *
     321             :  * @param context The Kerberos library context
     322             :  * @param ctx The kx509 request context
     323             :  * @param upn A string containing a representation of a UPN
     324             :  *
     325             :  * @return A krb5 error code.
     326             :  */
     327             : krb5_error_code
     328           0 : krb5_kx509_ctx_add_san_ms_upn(krb5_context context,
     329             :                               krb5_kx509_req_ctx kx509_ctx,
     330             :                               const char *upn)
     331             : {
     332           0 :     return hx509_request_add_ms_upn_name(context->hx509ctx, kx509_ctx->csr,
     333             :                                          upn);
     334             : }
     335             : 
     336             : /**
     337             :  * Adds an registeredID SAN (OID) as an additional desired Certificate
     338             :  * Extension or in the CSR if the caller does not set a CSR.
     339             :  *
     340             :  * @param context The Kerberos library context
     341             :  * @param ctx The kx509 request context
     342             :  * @param oids A string representation of an OID
     343             :  *
     344             :  * @return A krb5 error code.
     345             :  */
     346             : krb5_error_code
     347           0 : krb5_kx509_ctx_add_san_registeredID(krb5_context context,
     348             :                                     krb5_kx509_req_ctx kx509_ctx,
     349             :                                     const char *oids)
     350             : {
     351           0 :     krb5_error_code ret;
     352           0 :     heim_oid oid;
     353             : 
     354           0 :     ret = der_parse_heim_oid(oids, NULL, &oid);
     355           0 :     if (ret == 0)
     356           0 :         hx509_request_add_registered(context->hx509ctx, kx509_ctx->csr, &oid);
     357           0 :     der_free_oid(&oid);
     358           0 :     return ret;
     359             : }
     360             : 
     361             : static krb5_error_code
     362           0 : load_priv_key(krb5_context context,
     363             :               krb5_kx509_req_ctx kx509_ctx,
     364             :               const char *fn)
     365             : {
     366           0 :     hx509_private_key *keys = NULL;
     367           0 :     hx509_certs certs = NULL;
     368           0 :     krb5_error_code ret;
     369             : 
     370           0 :     ret = hx509_certs_init(context->hx509ctx, fn, 0, NULL, &certs);
     371           0 :     if (ret == ENOENT)
     372           0 :         return 0;
     373           0 :     if (ret == 0)
     374           0 :         ret = _hx509_certs_keys_get(context->hx509ctx, certs, &keys);
     375           0 :     if (ret == 0 && keys[0] == NULL)
     376           0 :         ret = ENOENT;
     377           0 :     if (ret == 0)
     378           0 :         kx509_ctx->priv_key = _hx509_private_key_ref(keys[0]);
     379           0 :     if (ret) {
     380           0 :         char *emsg = hx509_get_error_string(context->hx509ctx, ret);
     381             : 
     382           0 :         krb5_set_error_message(context, ret, "Could not load private key "
     383             :                                "from %s for kx509: %s", fn, emsg);
     384           0 :         hx509_free_error_string(emsg);
     385             :     }
     386           0 :     hx509_certs_free(&certs);
     387           0 :     return ret;
     388             : }
     389             : 
     390             : /**
     391             :  * Set a private key.
     392             :  *
     393             :  * @param context The Kerberos library context
     394             :  * @param ctx The kx509 request context
     395             :  * @param store The name of a PKIX credential store
     396             :  *
     397             :  * @return A krb5 error code.
     398             :  */
     399             : krb5_error_code
     400           0 : krb5_kx509_ctx_set_key(krb5_context context,
     401             :                        krb5_kx509_req_ctx kx509_ctx,
     402             :                        const char *store)
     403             : {
     404           0 :     SubjectPublicKeyInfo key;
     405           0 :     krb5_error_code ret;
     406             : 
     407           0 :     memset(&key, 0, sizeof(key));
     408           0 :     hx509_private_key_free(&kx509_ctx->priv_key);
     409           0 :     _hx509_certs_keys_free(context->hx509ctx, kx509_ctx->keys);
     410           0 :     kx509_ctx->keys = NULL;
     411           0 :     ret = load_priv_key(context, kx509_ctx, store);
     412           0 :     if (ret == 0)
     413           0 :         ret = hx509_private_key2SPKI(context->hx509ctx, kx509_ctx->priv_key,
     414             :                                      &key);
     415           0 :     if (ret == 0)
     416           0 :         ret = hx509_request_set_SubjectPublicKeyInfo(context->hx509ctx,
     417             :                                                      kx509_ctx->csr, &key);
     418           0 :     free_SubjectPublicKeyInfo(&key);
     419           0 :     return ret;
     420             : }
     421             : 
     422             : static krb5_error_code
     423           0 : gen_priv_key(krb5_context context,
     424             :              const char *gen_type,
     425             :              unsigned long gen_bits,
     426             :              hx509_private_key *key)
     427             : {
     428           0 :     struct hx509_generate_private_context *key_gen_ctx = NULL;
     429           0 :     krb5_error_code ret;
     430             : 
     431           0 :     _krb5_debug(context, 1, "kx509: gen priv key");
     432           0 :     if (strcmp(gen_type, "rsa") != 0) {
     433           0 :         krb5_set_error_message(context, ENOTSUP, "Key type %s is not "
     434             :                                "supported for kx509; only \"rsa\" is "
     435             :                                "supported for kx509 at this time",
     436             :                                gen_type);
     437           0 :         return ENOTSUP;
     438             :     }
     439             : 
     440           0 :     ret = _hx509_generate_private_key_init(context->hx509ctx,
     441             :                                            ASN1_OID_ID_PKCS1_RSAENCRYPTION,
     442             :                                            &key_gen_ctx);
     443           0 :     if (ret == 0)
     444           0 :         ret = _hx509_generate_private_key_bits(context->hx509ctx, key_gen_ctx, gen_bits);
     445             : 
     446           0 :     if (ret == 0)
     447           0 :         ret = _hx509_generate_private_key(context->hx509ctx, key_gen_ctx, key);
     448           0 :     _hx509_generate_private_key_free(&key_gen_ctx);
     449           0 :     if (ret) {
     450           0 :         char *emsg = hx509_get_error_string(context->hx509ctx, ret);
     451             : 
     452           0 :         krb5_set_error_message(context, ret,
     453             :                                "Could not generate a private key: %s", emsg);
     454           0 :         hx509_free_error_string(emsg);
     455             :     }
     456           0 :     return ret;
     457             : }
     458             : 
     459             : /**
     460             :  * Generate a private key.
     461             :  *
     462             :  * @param context The Kerberos library context
     463             :  * @param ctx The kx509 request context
     464             :  * @param gen_type The type of key (default: rsa)
     465             :  * @param gen_bits The size of the key (for non-ECC, really, for RSA)
     466             :  *
     467             :  * @return A krb5 error code.
     468             :  */
     469             : krb5_error_code
     470           0 : krb5_kx509_ctx_gen_key(krb5_context context,
     471             :                        krb5_kx509_req_ctx kx509_ctx,
     472             :                        const char *gen_type,
     473             :                        int gen_bits)
     474             : {
     475           0 :     SubjectPublicKeyInfo key;
     476           0 :     krb5_error_code ret;
     477             : 
     478           0 :     memset(&key, 0, sizeof(key));
     479             : 
     480           0 :     if (gen_type == NULL) {
     481           0 :         gen_type = krb5_config_get_string_default(context, NULL, "rsa",
     482             :                                                   "libdefaults",
     483             :                                                   "kx509_gen_key_type", NULL);
     484             :     }
     485           0 :     if (gen_bits == 0) {
     486             :         /*
     487             :          * The key size is really only for non-ECC, of which we'll only support
     488             :          * RSA.  For ECC key sizes will either be implied by the `key_type' or
     489             :          * will have to be a magic value that allows us to pick from some small
     490             :          * set of curves (e.g., 255 == Curve25519).
     491             :          */
     492           0 :         gen_bits = krb5_config_get_int_default(context, NULL, 2048,
     493             :                                                "libdefaults",
     494             :                                                "kx509_gen_rsa_key_size", NULL);
     495             :     }
     496           0 :     hx509_private_key_free(&kx509_ctx->priv_key);
     497           0 :     _hx509_certs_keys_free(context->hx509ctx, kx509_ctx->keys);
     498           0 :     kx509_ctx->keys = NULL;
     499             : 
     500           0 :     ret = gen_priv_key(context, gen_type, gen_bits, &kx509_ctx->priv_key);
     501           0 :     if (ret == 0)
     502           0 :         ret = hx509_private_key2SPKI(context->hx509ctx, kx509_ctx->priv_key,
     503             :                                      &key);
     504           0 :     if (ret == 0)
     505           0 :         ret = hx509_request_set_SubjectPublicKeyInfo(context->hx509ctx,
     506             :                                                      kx509_ctx->csr, &key);
     507           0 :     free_SubjectPublicKeyInfo(&key);
     508           0 :     return ret;
     509             : }
     510             : 
     511             : /* Set a cc config entry indicating that the kx509 service is not available */
     512             : static void
     513           0 : store_kx509_disabled(krb5_context context, const char *realm, krb5_ccache cc)
     514             : {
     515           0 :     krb5_data data;
     516             : 
     517           0 :     if (!cc)
     518           0 :         return;
     519             : 
     520           0 :     data.data = (void *)(uintptr_t)realm;
     521           0 :     data.length = strlen(realm);
     522           0 :     krb5_cc_set_config(context, cc, NULL, "kx509_service_realm", &data);
     523           0 :     data.data = "disabled";
     524           0 :     data.length = strlen(data.data);
     525           0 :     krb5_cc_set_config(context, cc, NULL, "kx509_service_status", &data);
     526             : }
     527             : 
     528             : static int KRB5_CALLCONV
     529           0 : certs_export_func(hx509_context context, void *d, hx509_cert c)
     530             : {
     531           0 :     heim_octet_string os;
     532           0 :     Certificates *cs = d;
     533           0 :     Certificate c2;
     534           0 :     int ret;
     535             : 
     536           0 :     ret = hx509_cert_binary(context, c, &os);
     537           0 :     if (ret)
     538           0 :         return ret;
     539           0 :     ret = decode_Certificate(os.data, os.length, &c2, NULL);
     540           0 :     der_free_octet_string(&os);
     541           0 :     if (ret)
     542           0 :         return ret;
     543           0 :     ret = add_Certificates(cs, &c2);
     544           0 :     free_Certificate(&c2);
     545           0 :     return ret;
     546             : }
     547             : 
     548             : static krb5_error_code
     549           0 : certs_export(hx509_context context, hx509_certs certs, heim_octet_string *out)
     550             : {
     551           0 :     Certificates cs;
     552           0 :     size_t len;
     553           0 :     int ret;
     554             : 
     555           0 :     cs.len = 0;
     556           0 :     cs.val = 0;
     557           0 :     ret = hx509_certs_iter_f(context, certs, certs_export_func, &cs);
     558           0 :     if (ret == 0)
     559           0 :         ASN1_MALLOC_ENCODE(Certificates, out->data, out->length, &cs, &len, ret);
     560           0 :     free_Certificates(&cs);
     561           0 :     return ret;
     562             : }
     563             : 
     564             : /* Store the private key and certificate where requested */
     565             : static krb5_error_code
     566           0 : store(krb5_context context,
     567             :       const char *hx509_store,
     568             :       const char *realm,
     569             :       krb5_ccache cc,
     570             :       hx509_private_key key,
     571             :       hx509_cert cert,
     572             :       hx509_certs chain)
     573             : {
     574           0 :     heim_octet_string hdata;
     575           0 :     krb5_error_code ret = 0;
     576           0 :     krb5_data data;
     577             : 
     578           0 :     krb5_clear_error_message(context);
     579             : 
     580           0 :     if (cc) {
     581             :         /* Record the realm we used */
     582           0 :         data.data = (void *)(uintptr_t)realm;
     583           0 :         data.length = strlen(realm);
     584           0 :         krb5_cc_set_config(context, cc, NULL, "kx509_service_realm", &data);
     585             : 
     586             :         /* Serialize and store the certificate in the ccache */
     587           0 :         ret = hx509_cert_binary(context->hx509ctx, cert, &hdata);
     588           0 :         if (ret == 0)
     589           0 :             ret = krb5_cc_set_config(context, cc, NULL, "kx509cert", &hdata);
     590           0 :         der_free_octet_string(&hdata);
     591             : 
     592           0 :         if (ret == 0 && key) {
     593             :             /*
     594             :              * Serialized and store the key in the ccache.  Use PKCS#8 so that we
     595             :              * store the algorithm OID too, which is needed in order to be able to
     596             :              * read the private key back.
     597             :              */
     598           0 :             if (ret == 0)
     599           0 :                 ret = _hx509_private_key_export(context->hx509ctx, key,
     600             :                                                 HX509_KEY_FORMAT_PKCS8, &hdata);
     601           0 :             if (ret == 0)
     602           0 :                 ret = krb5_cc_set_config(context, cc, NULL, "kx509key", &hdata);
     603           0 :             der_free_octet_string(&hdata);
     604           0 :             if (ret)
     605           0 :                 krb5_set_error_message(context, ret, "Could not store kx509 "
     606             :                                        "private key and certificate in ccache %s",
     607             :                                        krb5_cc_get_name(context, cc));
     608             :         }
     609             : 
     610           0 :         if (ret == 0 && chain) {
     611           0 :             ret = certs_export(context->hx509ctx, chain, &hdata);
     612           0 :             if (ret == 0)
     613           0 :                 ret = krb5_cc_set_config(context, cc, NULL, "kx509cert-chain",
     614             :                                          &hdata);
     615           0 :             der_free_octet_string(&hdata);
     616             :         }
     617             :     }
     618             : 
     619             :     /* Store the private key and cert in an hx509 store */
     620           0 :     if (hx509_store != NULL) {
     621           0 :         hx509_certs certs;
     622             : 
     623           0 :         if (key)
     624           0 :             _hx509_cert_assign_key(cert, key); /* store both in the same store */
     625             : 
     626           0 :         ret = hx509_certs_init(context->hx509ctx, hx509_store,
     627             :                                HX509_CERTS_CREATE, NULL, &certs);
     628           0 :         if (ret == 0)
     629           0 :             ret = hx509_certs_add(context->hx509ctx, certs, cert);
     630           0 :         if (ret == 0 && chain != NULL)
     631           0 :             ret = hx509_certs_merge(context->hx509ctx, certs, chain);
     632           0 :         if (ret == 0)
     633           0 :             ret = hx509_certs_store(context->hx509ctx, certs, 0, NULL);
     634           0 :         hx509_certs_free(&certs);
     635           0 :         if (ret)
     636           0 :             krb5_prepend_error_message(context, ret, "Could not store kx509 "
     637             :                                        "private key and certificate in key "
     638             :                                        "store %s", hx509_store);
     639             :     }
     640             : 
     641             :     /* Store the name of the hx509 store in the ccache too */
     642           0 :     if (cc && hx509_store) {
     643           0 :         data.data = (void *)(uintptr_t)hx509_store;
     644           0 :         data.length = strlen(hx509_store);
     645           0 :         (void) krb5_cc_set_config(context, cc, NULL, "kx509store", &data);
     646             :     }
     647           0 :     return ret;
     648             : }
     649             : 
     650             : /* Make a Kx509CSRPlus or a raw SPKI */
     651             : static krb5_error_code
     652           0 : mk_kx509_req_body(krb5_context context,
     653             :                   krb5_kx509_req_ctx kx509_ctx,
     654             :                   krb5_data *out)
     655             : {
     656           0 :     krb5_error_code ret;
     657           0 :     size_t len;
     658             : 
     659           0 :     if (krb5_config_get_bool_default(context, NULL, FALSE,
     660             :                                      "realms", kx509_ctx->realm,
     661             :                                      "kx509_req_use_raw_spki", NULL)) {
     662           0 :         SubjectPublicKeyInfo spki;
     663             : 
     664             :         /* Interop with old kx509 servers, send a raw SPKI, not a CSR */
     665           0 :         out->data = NULL;
     666           0 :         out->length = 0;
     667           0 :         memset(&spki, 0, sizeof(spki));
     668           0 :         ret = hx509_private_key2SPKI(context->hx509ctx,
     669             :                                      kx509_ctx->priv_key, &spki);
     670           0 :         if (ret == 0) {
     671           0 :             out->length = spki.subjectPublicKey.length >> 3;
     672           0 :             out->data = spki.subjectPublicKey.data;
     673             :         }
     674           0 :         kx509_ctx->expect_chain = 0;
     675           0 :         return ret;
     676             :     }
     677             : 
     678             :     /*
     679             :      * New kx509 servers use a CSR for proof of possession, and send back a
     680             :      * chain of certificates, with the issued certificate first.
     681             :      */
     682           0 :     kx509_ctx->expect_chain = 1;
     683             : 
     684           0 :     if (kx509_ctx->given_csr.length) {
     685           0 :         krb5_data exts_der;
     686             : 
     687           0 :         exts_der.data = NULL;
     688           0 :         exts_der.length = 0;
     689             : 
     690             :         /* Use the given CSR */
     691           0 :         ret = der_copy_octet_string(&kx509_ctx->given_csr,
     692             :                                     &kx509_ctx->csr_plus.csr);
     693             : 
     694             :         /*
     695             :          * Extract the desired Certificate Extensions from our internal
     696             :          * as-yet-unsigned CSR, then decode them into place in the
     697             :          * Kx509CSRPlus.
     698             :          */
     699           0 :         if (ret == 0)
     700           0 :             ret = hx509_request_get_exts(context->hx509ctx,
     701             :                                          kx509_ctx->csr,
     702             :                                          &exts_der);
     703           0 :         if (ret == 0 && exts_der.data && exts_der.length &&
     704           0 :             (kx509_ctx->csr_plus.exts =
     705           0 :              calloc(1, sizeof (kx509_ctx->csr_plus.exts[0]))) == NULL)
     706           0 :             ret = krb5_enomem(context);
     707           0 :         if (ret == 0 && exts_der.data && exts_der.length)
     708           0 :             ret = decode_Extensions(exts_der.data, exts_der.length,
     709             :                                     kx509_ctx->csr_plus.exts, NULL);
     710           0 :         krb5_data_free(&exts_der);
     711             :     } else {
     712             :         /*
     713             :          * Sign and use our internal CSR, which will carry all our desired
     714             :          * Certificate Extensions as an extReq CSR Attribute.
     715             :          */
     716           0 :         ret = hx509_request_to_pkcs10(context->hx509ctx,
     717             :                                       kx509_ctx->csr,
     718             :                                       kx509_ctx->priv_key,
     719             :                                       &kx509_ctx->csr_plus.csr);
     720             :     }
     721           0 :     if (ret == 0)
     722           0 :         ASN1_MALLOC_ENCODE(Kx509CSRPlus, out->data, out->length,
     723             :                            &kx509_ctx->csr_plus, &len, ret);
     724           0 :     return ret;
     725             : }
     726             : 
     727             : static krb5_error_code
     728           0 : get_start_realm(krb5_context context,
     729             :                 krb5_ccache cc,
     730             :                 krb5_const_principal princ,
     731             :                 char **out)
     732             : {
     733           0 :     krb5_error_code ret;
     734           0 :     krb5_data d;
     735             : 
     736           0 :     ret = krb5_cc_get_config(context, cc, NULL, "start_realm", &d);
     737           0 :     if (ret == 0) {
     738           0 :         *out = strndup(d.data, d.length);
     739           0 :         krb5_data_free(&d);
     740           0 :     } else if (princ) {
     741           0 :         *out = strdup(krb5_principal_get_realm(context, princ));
     742             :     } else {
     743           0 :         krb5_principal ccprinc = NULL;
     744             : 
     745           0 :         ret = krb5_cc_get_principal(context, cc, &ccprinc);
     746           0 :         if (ret)
     747           0 :             return ret;
     748           0 :         *out = strdup(krb5_principal_get_realm(context, ccprinc));
     749           0 :         krb5_free_principal(context, ccprinc);
     750             :     }
     751           0 :     return (*out) ? 0 : krb5_enomem(context);
     752             : }
     753             : 
     754             : /*
     755             :  * Make a request, which is a DER-encoded Kx509Request with version_2_0
     756             :  * prefixed to it.
     757             :  *
     758             :  * If no private key is given, then a probe request will be made.
     759             :  */
     760             : static krb5_error_code
     761           0 : mk_kx509_req(krb5_context context,
     762             :              krb5_kx509_req_ctx kx509_ctx,
     763             :              krb5_ccache incc,
     764             :              hx509_private_key private_key,
     765             :              krb5_data *req)
     766             : {
     767           0 :     unsigned char digest[SHA_DIGEST_LENGTH];
     768           0 :     SubjectPublicKeyInfo spki;
     769           0 :     struct Kx509Request kx509_req;
     770           0 :     krb5_data pre_req;
     771           0 :     krb5_error_code ret = 0;
     772           0 :     krb5_creds this_cred;
     773           0 :     krb5_creds *cred = NULL;
     774           0 :     HMAC_CTX ctx;
     775           0 :     const char *hostname;
     776           0 :     char *start_realm = NULL;
     777           0 :     size_t len = 0;
     778             : 
     779           0 :     krb5_data_zero(&pre_req);
     780           0 :     memset(&spki, 0, sizeof(spki));
     781           0 :     memset(&this_cred, 0, sizeof(this_cred));
     782           0 :     memset(&kx509_req, 0, sizeof(kx509_req));
     783           0 :     kx509_req.pk_hash.data = digest;
     784           0 :     kx509_req.pk_hash.length = SHA_DIGEST_LENGTH;
     785             : 
     786           0 :     if (private_key || kx509_ctx->given_csr.data) {
     787             :         /* Encode the CSR or public key for use in the request */
     788           0 :         ret = mk_kx509_req_body(context, kx509_ctx, &kx509_req.pk_key);
     789             :     } else {
     790             :         /* Probe */
     791           0 :         kx509_req.pk_key.data = NULL;
     792           0 :         kx509_req.pk_key.length = 0;
     793             :     }
     794             : 
     795           0 :     if (ret == 0)
     796           0 :         ret = krb5_cc_get_principal(context, incc, &this_cred.client);
     797           0 :     if (ret == 0)
     798           0 :         ret = get_start_realm(context, incc, this_cred.client, &start_realm);
     799           0 :     if (ret == 0 && kx509_ctx->realm == NULL)
     800           0 :         ret = krb5_kx509_ctx_set_realm(context, kx509_ctx, start_realm);
     801           0 :     if (ret == 0) {
     802             :         /*
     803             :          * The kx509 protocol as deployed uses kca_service/kdc_hostname, but
     804             :          * this is inconvenient in libkrb5: we want to be able to use the
     805             :          * send_to_kdc machinery, and since the Heimdal KDC is also the kx509
     806             :          * service, we want not to have to specify kx509 hosts separately from
     807             :          * KDCs.
     808             :          *
     809             :          * We'd much rather use krbtgt/CLIENT_REALM@REQUESTED_REALM.  What
     810             :          * we do is assume all KDCs for `realm' support the kx509 service and
     811             :          * then sendto the KDCs for that realm while using a hostbased service
     812             :          * if still desired.
     813             :          *
     814             :          * Note that upstairs we try to get the start_realm cc config, so if
     815             :          * realm wasn't given to krb5_kx509_ext(), then it should be set to
     816             :          * that already unless there's no start_realm cc config, in which case
     817             :          * we'll use the ccache's default client principal's realm.
     818             :          */
     819           0 :         hostname = krb5_config_get_string(context, NULL, "realms",
     820             :                                           kx509_ctx->realm, "kx509_hostname",
     821             :                                           NULL);
     822           0 :         if (hostname == NULL)
     823           0 :             hostname = krb5_config_get_string(context, NULL, "libdefaults",
     824             :                                               "kx509_hostname", NULL);
     825           0 :         if (hostname) {
     826           0 :             ret = krb5_sname_to_principal(context, hostname, "kca_service",
     827             :                                           KRB5_NT_SRV_HST, &this_cred.server);
     828           0 :             if (ret == 0)
     829           0 :                 ret = krb5_principal_set_realm(context, this_cred.server,
     830           0 :                                                kx509_ctx->realm);
     831             :         } else {
     832           0 :             ret = krb5_make_principal(context, &this_cred.server,
     833             :                                       start_realm,
     834             :                                       KRB5_TGS_NAME,
     835             :                                       kx509_ctx->realm,
     836             :                                       NULL);
     837             :         }
     838             :     }
     839             : 
     840             :     /* Make the AP-REQ and extract the HMAC key */
     841           0 :     if (ret == 0)
     842           0 :         ret = krb5_get_credentials(context, 0, incc, &this_cred, &cred);
     843           0 :     if (ret == 0)
     844           0 :         ret = krb5_mk_req_extended(context, &kx509_ctx->ac, AP_OPTS_USE_SUBKEY,
     845             :                                    NULL, cred, &kx509_req.authenticator);
     846           0 :     krb5_free_keyblock(context, kx509_ctx->hmac_key);
     847           0 :     kx509_ctx->hmac_key = NULL;
     848           0 :     if (ret == 0)
     849           0 :         ret = krb5_auth_con_getkey(context, kx509_ctx->ac,
     850             :                                    &kx509_ctx->hmac_key);
     851             : 
     852           0 :     if (ret)
     853           0 :         goto out;
     854             : 
     855             :     /* Add the the key and HMAC to the message */
     856           0 :     HMAC_CTX_init(&ctx);
     857           0 :     if (HMAC_Init_ex(&ctx, kx509_ctx->hmac_key->keyvalue.data,
     858           0 :                      kx509_ctx->hmac_key->keyvalue.length,
     859             :                      EVP_sha1(), NULL) == 0) {
     860           0 :         HMAC_CTX_cleanup(&ctx);
     861           0 :         ret = krb5_enomem(context);
     862             :     } else {
     863           0 :         HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
     864           0 :         if (private_key || kx509_ctx->given_csr.data) {
     865           0 :             HMAC_Update(&ctx, kx509_req.pk_key.data, kx509_req.pk_key.length);
     866             :         } else {
     867             :             /* Probe */
     868           0 :             HMAC_Update(&ctx, kx509_req.authenticator.data, kx509_req.authenticator.length);
     869             :         }
     870           0 :         HMAC_Final(&ctx, kx509_req.pk_hash.data, 0);
     871           0 :         HMAC_CTX_cleanup(&ctx);
     872             :     }
     873             : 
     874             :     /* Encode the message, prefix `version_2_0', output the result */
     875           0 :     if (ret == 0)
     876           0 :         ASN1_MALLOC_ENCODE(Kx509Request, pre_req.data, pre_req.length, &kx509_req, &len, ret);
     877           0 :     if (ret == 0)
     878           0 :         ret = krb5_data_alloc(req, pre_req.length + sizeof(version_2_0));
     879           0 :     if (ret == 0) {
     880           0 :         memcpy(req->data, version_2_0, sizeof(version_2_0));
     881           0 :         memcpy(((unsigned char *)req->data) + sizeof(version_2_0),
     882           0 :                pre_req.data, pre_req.length);
     883             :     }
     884             : 
     885           0 : out:
     886           0 :     free(start_realm);
     887           0 :     free(pre_req.data);
     888           0 :     krb5_free_creds(context, cred);
     889           0 :     kx509_req.pk_hash.data = NULL;
     890           0 :     kx509_req.pk_hash.length = 0;
     891           0 :     free_Kx509Request(&kx509_req);
     892           0 :     free_SubjectPublicKeyInfo(&spki);
     893           0 :     krb5_free_cred_contents(context, &this_cred);
     894           0 :     if (ret == 0 && req->length != len + sizeof(version_2_0)) {
     895           0 :         krb5_data_free(req);
     896           0 :         krb5_set_error_message(context, ret = ERANGE,
     897             :                                "Could not make a kx509 request");
     898             :     }
     899           0 :     return ret;
     900             : }
     901             : 
     902             : static krb5_error_code
     903           0 : rd_chain(krb5_context context,
     904             :          heim_octet_string *d,
     905             :          hx509_cert *cert,
     906             :          hx509_certs *chain,
     907             :          heim_error_t *herr)
     908             : {
     909           0 :     krb5_error_code ret;
     910           0 :     Certificates certs;
     911           0 :     size_t i, len;
     912             : 
     913           0 :     *cert = NULL;
     914           0 :     *chain = NULL;
     915             : 
     916           0 :     if ((ret = decode_Certificates(d->data, d->length, &certs, &len)))
     917           0 :         return ret;
     918           0 :     if (certs.len == 0) {
     919           0 :         *herr = heim_error_create(EINVAL, "Server sent empty Certificate list");
     920           0 :         return EINVAL;
     921             :     }
     922           0 :     *cert = hx509_cert_init(context->hx509ctx, &certs.val[0], herr);
     923           0 :     if (*cert == NULL) {
     924           0 :         free_Certificates(&certs);
     925           0 :         return errno;
     926             :     }
     927           0 :     if (certs.len == 1)
     928           0 :         _krb5_debug(context, 1, "kx509 server sent certificate but no chain");
     929             :     else
     930           0 :         _krb5_debug(context, 1, "kx509 server sent %llu certificates",
     931           0 :                     (unsigned long long)certs.len);
     932             : 
     933           0 :     ret = hx509_certs_init(context->hx509ctx, "MEMORY:anonymous",
     934             :                            HX509_CERTS_CREATE, NULL, chain);
     935           0 :     if (ret) {
     936           0 :         hx509_cert_free(*cert);
     937           0 :         *cert = NULL;
     938           0 :         free_Certificates(&certs);
     939           0 :         return ret;
     940             :     }
     941             : 
     942           0 :     for (i = 1; ret == 0 && i < certs.len; i++) {
     943           0 :         hx509_cert c = hx509_cert_init(context->hx509ctx, &certs.val[i], herr);
     944             : 
     945           0 :         if (c == NULL)
     946           0 :             ret = errno;
     947             :         else
     948           0 :             ret = hx509_certs_add(context->hx509ctx, *chain, c);
     949           0 :         hx509_cert_free(c);
     950             :     }
     951           0 :     free_Certificates(&certs);
     952           0 :     if (ret) {
     953           0 :         hx509_certs_free(chain);
     954           0 :         hx509_cert_free(*cert);
     955           0 :         *cert = NULL;
     956             :     }
     957           0 :     return ret;
     958             : }
     959             : 
     960             : /* Parse and validate a kx509 reply */
     961             : static krb5_error_code
     962           0 : rd_kx509_resp(krb5_context context,
     963             :               krb5_kx509_req_ctx kx509_ctx,
     964             :               krb5_data *rep,
     965             :               hx509_cert *cert,
     966             :               hx509_certs *chain)
     967             : {
     968           0 :     unsigned char digest[SHA_DIGEST_LENGTH];
     969           0 :     Kx509Response r;
     970           0 :     krb5_error_code code = 0;
     971           0 :     krb5_error_code ret = 0;
     972           0 :     heim_string_t hestr;
     973           0 :     heim_error_t herr = NULL;
     974           0 :     const char *estr;
     975           0 :     HMAC_CTX ctx;
     976           0 :     size_t hdr_len = sizeof(version_2_0);
     977           0 :     size_t len;
     978             : 
     979           0 :     *cert = NULL;
     980           0 :     *chain = NULL;
     981             : 
     982             :     /* Strip `version_2_0' prefix */
     983           0 :     if (rep->length < hdr_len || memcmp(rep->data, version_2_0, hdr_len) != 0) {
     984           0 :         krb5_set_error_message(context, ENOTSUP,
     985             :                                "KDC does not support kx509 protocol");
     986           0 :         return ENOTSUP; /* XXX */
     987             :     }
     988             : 
     989             :     /* Decode */
     990           0 :     ret = decode_Kx509Response(((unsigned char *)rep->data) + 4,
     991           0 :                                rep->length - 4, &r, &len);
     992           0 :     if (ret == 0 && len + hdr_len != rep->length)
     993           0 :         ret = EINVAL; /* XXX */
     994           0 :     if (ret) {
     995           0 :         krb5_set_error_message(context, ret, "kx509 response is not valid");
     996           0 :         return ret;
     997             :     }
     998             : 
     999           0 :     HMAC_CTX_init(&ctx);
    1000           0 :     if (HMAC_Init_ex(&ctx, kx509_ctx->hmac_key->keyvalue.data,
    1001           0 :                      kx509_ctx->hmac_key->keyvalue.length, EVP_sha1(), NULL) == 0) {
    1002           0 :         free_Kx509Response(&r);
    1003           0 :         HMAC_CTX_cleanup(&ctx);
    1004           0 :         return krb5_enomem(context);
    1005             :     }
    1006             : 
    1007           0 :     HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
    1008             : 
    1009             :     {
    1010           0 :         int32_t t = r.error_code;
    1011           0 :         unsigned char encint[sizeof(t) + 1];
    1012           0 :         size_t k;
    1013             : 
    1014             :         /*
    1015             :          * RFC6717 says this about how the error-code is included in the HMAC:
    1016             :          *
    1017             :          *  o DER representation of the error-code exclusive of the tag and
    1018             :          *    length, if it is present.
    1019             :          *
    1020             :          * So we use der_put_integer(), which encodes from the right.
    1021             :          *
    1022             :          * RFC6717 does not constrain the error-code's range.  We assume it to
    1023             :          * be a 32-bit, signed integer, for which we'll need no more than 5
    1024             :          * bytes.
    1025             :          */
    1026           0 :         ret = der_put_integer(&encint[sizeof(encint) - 1],
    1027             :                               sizeof(encint), &t, &k);
    1028           0 :         if (ret == 0)
    1029           0 :             HMAC_Update(&ctx, &encint[sizeof(encint)] - k, k);
    1030             : 
    1031             :         /* Normalize error code */
    1032           0 :         if (r.error_code == 0) {
    1033           0 :             code = 0; /* No error */
    1034           0 :         } else if (r.error_code < 0) {
    1035           0 :             code = KRB5KRB_ERR_GENERIC; /* ??? */
    1036           0 :         } else if (r.error_code <= KX509_ERR_SRV_OVERLOADED - ERROR_TABLE_BASE_kx59) {
    1037             :             /*
    1038             :              * RFC6717 (kx509) error code.  These are actually not used on the
    1039             :              * wire in any existing implementations that we are aware of.  Just
    1040             :              * in case, however, we'll map these.
    1041             :              */
    1042           0 :             code = KX509_ERR_CLNT_FATAL + r.error_code;
    1043           0 :         } else if (r.error_code < kx509_krb5_error_base) {
    1044             :             /* Unknown error codes */
    1045           0 :             code = KRB5KRB_ERR_GENERIC;
    1046             :         } else {
    1047             :             /*
    1048             :              * Heimdal-specific enhancement to RFC6171: Kerberos wire protocol
    1049             :              * error codes.
    1050             :              */
    1051           0 :             code = KRB5KDC_ERR_NONE + r.error_code - kx509_krb5_error_base;
    1052           0 :             if (code >= KRB5_ERR_RCSID)
    1053           0 :                 code = KRB5KRB_ERR_GENERIC;
    1054           0 :             if (code == KRB5KDC_ERR_NONE)
    1055           0 :                 code = 0;
    1056             :         }
    1057             :     }
    1058           0 :     if (r.certificate)
    1059           0 :         HMAC_Update(&ctx, r.certificate->data, r.certificate->length);
    1060           0 :     if (r.e_text)
    1061           0 :         HMAC_Update(&ctx, *r.e_text, strlen(*r.e_text));
    1062           0 :     HMAC_Final(&ctx, &digest, 0);
    1063           0 :     HMAC_CTX_cleanup(&ctx);
    1064             : 
    1065           0 :     if (r.hash == NULL) {
    1066             :         /*
    1067             :          * No HMAC -> unauthenticated [error] response.
    1068             :          *
    1069             :          * Do not output any certificate.
    1070             :          */
    1071           0 :         free_Kx509Response(&r);
    1072           0 :         return code;
    1073             :     }
    1074             : 
    1075             :     /*
    1076             :      * WARNING: We do not validate that `r.certificate' is a DER-encoded
    1077             :      *          Certificate, not here, and we don't use a different HMAC key
    1078             :      *          for the response than for the request.
    1079             :      *
    1080             :      *          If ever we start sending a Certificate as the Kx509Request
    1081             :      *          pk-key field, then we'll have a reflection attack.  As the
    1082             :      *          Certificate we'd send in that case will be expired, the
    1083             :      *          reflection attack would be just a DoS.
    1084             :      */
    1085           0 :     if (r.hash->length != sizeof(digest) ||
    1086           0 :         ct_memcmp(r.hash->data, digest, sizeof(digest)) != 0) {
    1087           0 :         krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
    1088             :                                "kx509 response MAC mismatch");
    1089           0 :         free_Kx509Response(&r);
    1090           0 :         return KRB5KRB_AP_ERR_BAD_INTEGRITY;
    1091             :     }
    1092             : 
    1093           0 :     if (r.certificate == NULL) {
    1094             :         /* Authenticated response, either an error or probe success */
    1095           0 :         free_Kx509Response(&r);
    1096           0 :         if (code != KRB5KDC_ERR_POLICY && kx509_ctx->priv_key == NULL)
    1097           0 :             return 0; /* Probe success */
    1098           0 :         return code ? code : KRB5KDC_ERR_POLICY; /* Not a probe -> must fail */
    1099             :     }
    1100             : 
    1101             :     /* Import the certificate payload */
    1102           0 :     if (kx509_ctx->expect_chain) {
    1103           0 :         ret = rd_chain(context, r.certificate, cert, chain, &herr);
    1104             :     } else {
    1105           0 :         *cert = hx509_cert_init_data(context->hx509ctx, r.certificate->data,
    1106           0 :                                      r.certificate->length, &herr);
    1107           0 :         if (!*cert)
    1108           0 :             ret = errno;
    1109             :     }
    1110           0 :     free_Kx509Response(&r);
    1111           0 :     if (*cert) {
    1112           0 :         heim_release(herr);
    1113           0 :         return 0;
    1114             :     }
    1115             : 
    1116           0 :     hestr = herr ? heim_error_copy_string(herr) : NULL;
    1117           0 :     estr = hestr ? heim_string_get_utf8(hestr) : "(no error message)";
    1118           0 :     krb5_set_error_message(context, ret, "Could not parse certificate "
    1119             :                            "produced by kx509 KDC: %s (%ld)",
    1120             :                            estr,
    1121           0 :                            herr ? (long)heim_error_get_code(herr) : 0L);
    1122             : 
    1123           0 :     heim_release(hestr);
    1124           0 :     heim_release(herr);
    1125           0 :     return HEIM_PKINIT_CERTIFICATE_INVALID; /* XXX */
    1126             : }
    1127             : 
    1128             : /*
    1129             :  * Make a request, send it, get the response, parse it, and store the
    1130             :  * private key and certificate.
    1131             :  */
    1132             : static krb5_error_code
    1133           0 : kx509_core(krb5_context context,
    1134             :            krb5_kx509_req_ctx kx509_ctx,
    1135             :            krb5_ccache incc,
    1136             :            const char *hx509_store,
    1137             :            krb5_ccache outcc)
    1138             : {
    1139           0 :     krb5_error_code ret;
    1140           0 :     hx509_certs chain = NULL;
    1141           0 :     hx509_cert cert = NULL;
    1142           0 :     krb5_data req, resp;
    1143             : 
    1144           0 :     krb5_data_zero(&req);
    1145           0 :     krb5_data_zero(&resp);
    1146             : 
    1147             :     /* Make the kx509 request */
    1148           0 :     ret = mk_kx509_req(context, kx509_ctx, incc, kx509_ctx->priv_key, &req);
    1149             : 
    1150             :     /* Send the kx509 request and get the response */
    1151           0 :     if (ret == 0)
    1152           0 :         ret = krb5_sendto_context(context, NULL, &req,
    1153           0 :                                   kx509_ctx->realm, &resp);
    1154           0 :     if (ret == 0)
    1155           0 :         ret = rd_kx509_resp(context, kx509_ctx, &resp, &cert, &chain);
    1156             : 
    1157             :     /* Store the key and cert! */
    1158           0 :     if (ret == 0 && cert && (kx509_ctx->priv_key || kx509_ctx->given_csr.data))
    1159           0 :         ret = store(context, hx509_store, kx509_ctx->realm, outcc,
    1160             :                     kx509_ctx->priv_key, cert, chain);
    1161           0 :     else if (ret == KRB5KDC_ERR_POLICY)
    1162             :         /* Probe failed -> record that the realm does not support kx509 */
    1163           0 :         store_kx509_disabled(context, kx509_ctx->realm, outcc);
    1164             : 
    1165           0 :     hx509_certs_free(&chain);
    1166           0 :     hx509_cert_free(cert);
    1167           0 :     krb5_data_free(&resp);
    1168           0 :     krb5_data_free(&req);
    1169           0 :     return ret;
    1170             : }
    1171             : 
    1172             : /**
    1173             :  * Use the kx509 v2 protocol to get a certificate for the client principal.
    1174             :  *
    1175             :  * Given a private key this function will get a certificate.  If no private key
    1176             :  * is given, one will be generated.
    1177             :  *
    1178             :  * The private key and certificate will be stored in the given PKIX credential
    1179             :  * store (e.g, "PEM-FILE:/path/to/file.pem") and/or given output ccache.  When
    1180             :  * stored in a ccache, the DER-encoded Certificate will be stored as the data
    1181             :  * payload of a "cc config" named "kx509cert", while the key will be stored as
    1182             :  * a DER-encoded PKCS#8 PrivateKeyInfo in a cc config named "kx509key".
    1183             :  *
    1184             :  * @param context The Kerberos library context
    1185             :  * @param kx509_ctx A kx509 request context
    1186             :  * @param incc A credential cache (if NULL use default ccache)
    1187             :  * @param hx509_store An PKIX credential store into which to store the private
    1188             :  *                    key and certificate (e.g, "PEM-FILE:/path/to/file.pem")
    1189             :  * @param outcc A ccache into which to store the private key and certificate
    1190             :  *              (mandatory)
    1191             :  *
    1192             :  * @return A krb5 error code.
    1193             :  */
    1194             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1195           0 : krb5_kx509_ext(krb5_context context,
    1196             :                krb5_kx509_req_ctx kx509_ctx,
    1197             :                krb5_ccache incc,
    1198             :                const char *hx509_store,
    1199             :                krb5_ccache outcc)
    1200             : {
    1201           0 :     krb5_ccache def_cc = NULL;
    1202           0 :     krb5_error_code ret;
    1203             : 
    1204           0 :     if (incc == NULL) {
    1205           0 :         if ((ret = krb5_cc_default(context, &def_cc)))
    1206           0 :             return ret;
    1207           0 :         incc = def_cc;
    1208             :     }
    1209             : 
    1210           0 :     if (kx509_ctx->realm == NULL &&
    1211           0 :         (ret = get_start_realm(context, incc, NULL, &kx509_ctx->realm))) {
    1212           0 :         if (def_cc)
    1213           0 :             krb5_cc_close(context, def_cc);
    1214           0 :         return ret;
    1215             :     }
    1216             : 
    1217           0 :     if (kx509_ctx->priv_key || kx509_ctx->given_csr.data) {
    1218             :         /* If given a private key, use it */
    1219           0 :         ret = kx509_core(context, kx509_ctx, incc, hx509_store, outcc);
    1220           0 :         if (def_cc)
    1221           0 :             krb5_cc_close(context, def_cc);
    1222           0 :         return ret;
    1223             :     }
    1224             : 
    1225             :     /*
    1226             :      * No private key given, so we generate one.
    1227             :      *
    1228             :      * However, before taking the hit for generating a keypair we probe to see
    1229             :      * if we're likely to succeeed.
    1230             :      */
    1231             : 
    1232             :     /* Probe == call kx509_core() w/o a private key */
    1233           0 :     ret = kx509_core(context, kx509_ctx, incc, NULL, outcc);
    1234           0 :     if (ret == 0 && kx509_ctx->given_csr.data == NULL)
    1235           0 :         ret = krb5_kx509_ctx_gen_key(context, kx509_ctx, NULL, 0);
    1236           0 :     if (ret == 0)
    1237           0 :         ret = kx509_core(context, kx509_ctx, incc, hx509_store, outcc);
    1238             : 
    1239           0 :     if (def_cc)
    1240           0 :         krb5_cc_close(context, def_cc);
    1241           0 :     return ret;
    1242             : }
    1243             : 
    1244             : /**
    1245             :  * Generates a public key and uses the kx509 v2 protocol to get a certificate
    1246             :  * for that key and the client principal's subject name.
    1247             :  *
    1248             :  * The private key and certificate will be stored in the given ccache, and also
    1249             :  * in a corresponding PKIX credential store if one is configured via
    1250             :  * [libdefaults] kx509_store.
    1251             :  *
    1252             :  * XXX NOTE: Dicey feature here...  Review carefully!
    1253             :  *
    1254             :  * @param context The Kerberos library context
    1255             :  * @param cc A credential cache
    1256             :  * @param realm A realm from which to get the certificate (uses the client
    1257             :  *              principal's realm if NULL)
    1258             :  *
    1259             :  * @return A krb5 error code.
    1260             :  */
    1261             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1262           0 : krb5_kx509(krb5_context context, krb5_ccache cc, const char *realm)
    1263             : {
    1264           0 :     krb5_kx509_req_ctx kx509_ctx;
    1265           0 :     krb5_error_code ret;
    1266           0 :     const char *defcc;
    1267           0 :     char *ccache_full_name = NULL;
    1268           0 :     char *store_exp = NULL;
    1269             : 
    1270           0 :     ret = krb5_kx509_ctx_init(context, &kx509_ctx);
    1271           0 :     if (ret)
    1272           0 :         return ret;
    1273           0 :     if (realm)
    1274           0 :         ret = krb5_kx509_ctx_set_realm(context, kx509_ctx, realm);
    1275             : 
    1276             :     /*
    1277             :      * The idea is that IF we are asked to do kx509 w/ creds from a default
    1278             :      * ccache THEN we should store the kx509 certificate (if we get one) and
    1279             :      * private key in the default hx509 store for kx509.
    1280             :      *
    1281             :      * Ideally we could have HTTP user-agents and/or TLS libraries look for
    1282             :      * client certificates and private keys in that default hx509 store.
    1283             :      *
    1284             :      * Of course, those user-agents / libraries should be configured to use
    1285             :      * those credentials with specific hostnames/domainnames, not the entire
    1286             :      * Internet, as the latter leaks the user's identity to the world.
    1287             :      *
    1288             :      * So we check if the full name for `cc' is the same as that of the default
    1289             :      * ccache name, and if so we get the [libdefaults] kx509_store string and
    1290             :      * expand it, then use it.
    1291             :      */
    1292           0 :     if (ret == 0 &&
    1293           0 :         (defcc = krb5_cc_configured_default_name(context)) &&
    1294           0 :         krb5_cc_get_full_name(context, cc, &ccache_full_name) == 0 &&
    1295           0 :         strcmp(defcc, ccache_full_name) == 0) {
    1296             : 
    1297             :         /* Find an hx509 store */
    1298           0 :         const char *store = krb5_config_get_string(context, NULL,
    1299             :                                                    "libdefaults",
    1300             :                                                    "kx509_store", NULL);
    1301           0 :         if (store)
    1302           0 :             ret = _krb5_expand_path_tokens(context, store, 1, &store_exp);
    1303             : 
    1304             :         /*
    1305             :          * If there's a private key in the store already, we'll use it, else
    1306             :          * we'll let krb5_kx509_ext() generate one, so we ignore this return
    1307             :          * value:
    1308             :          */
    1309           0 :         (void) krb5_kx509_ctx_set_key(context, kx509_ctx, store);
    1310             :     }
    1311             : 
    1312             :     /*
    1313             :      * If we did settle on a default hx509 store, we'll use it for reading the
    1314             :      * private key from (if it exists) as well as for storing the certificate
    1315             :      * (and private key) into, which may save us some key generation cycles.
    1316             :      */
    1317           0 :     if (ret == 0)
    1318           0 :         ret = krb5_kx509_ext(context, kx509_ctx, cc, store_exp, cc);
    1319           0 :     krb5_kx509_ctx_free(context, &kx509_ctx);
    1320           0 :     free(ccache_full_name);
    1321           0 :     free(store_exp);
    1322           0 :     return ret;
    1323             : }

Generated by: LCOV version 1.14