2 * Copyright (c) 2006 - 2019 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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
36 #include <rfc2459_asn1.h>
38 #include <hx509_err.h>
39 #include <kx509_err.h>
44 * This file implements the kx509 service.
46 * The protocol, its shortcomings, and its future are described in
47 * lib/krb5/hx509.c. See also lib/asn1/kx509.asn1.
49 * The service handles requests, decides whether to issue a certificate, and
50 * does so by populating a "template" to generate a TBSCertificate and signing
51 * it with a configured CA issuer certificate and private key. See ca.c for
54 * A "template" is a Certificate that has ${variable} references in its
55 * subjectName, and may have EKUs.
57 * Some SANs may be included in issued certificates. See below.
59 * Besides future protocol improvements described in lib/krb5/hx509.c, here is
60 * a list of KDC functionality we'd like to add:
62 * - support templates as strings (rather than filenames) in configuration?
63 * - lookup an hx509 template for the client principal in its HDB entry?
64 * - lookup subjectName, SANs for a principal in its HDB entry
65 * - lookup a host-based client principal's HDB entry and add its canonical
66 * name / aliases as dNSName SANs
67 * (this would have to be if requested by the client, perhaps; see
68 * commentary about the protocol in lib/krb5/kx509.c)
69 * - add code to build a template on the fly
71 * (just SANs, with empty subjectName?
73 * CN=component0,CN=component1,..,CN=componentN,DC=<from-REALM>
74 * and set KU and EKUs)
76 * Processing begins in _kdc_do_kx509().
78 * The sequence of events in _kdc_do_kx509() is:
80 * - parse outer request
81 * - authenticate request
82 * - extract CSR and AP-REQ Authenticator authz-data elements
83 * - characterize request as one of
84 * - default client cert req (no cert exts requested, client user princ)
85 * - default server cert req (no cert exts requested, client service princ)
86 * - client cert req (cert exts requested denoting client use)
87 * - server cert req (cert exts requested denoting server use)
88 * - mixed cert req (cert exts requested denoting client and server use)
89 * - authorize request based only on the request's details
90 * - there is a default authorizer, and a plugin authorizer
91 * - get configuration sub-tree corresponding to the request as characterized
92 * - missing configuration sub-tree -> reject (we have multiple ways to
94 * - get common config params from that sub-tree
95 * - set TBS template and details from CSR and such
96 * - issue certificate by signing TBS
101 static const unsigned char version_2_0
[4] = {0 , 0, 2, 0};
104 * Taste the request to see if it's a kx509 request.
107 _kdc_try_kx509_request(kx509_req_context r
)
109 const unsigned char *p
= (const void *)(uintptr_t)r
->request
.data
;
110 size_t len
= r
->request
.length
;
113 if (len
< sizeof(version_2_0
))
115 if (memcmp(version_2_0
, p
, sizeof(version_2_0
)) != 0)
117 p
+= sizeof(version_2_0
);
118 len
-= sizeof(version_2_0
);
121 memset(&r
->req
, 0, sizeof(r
->req
));
122 return decode_Kx509Request(p
, len
, &r
->req
, &sz
);
126 get_bool_param(krb5_context context
,
131 krb5_boolean global_default
;
133 global_default
= krb5_config_get_bool_default(context
, NULL
, def
, "kdc",
136 return global_default
;
137 return krb5_config_get_bool_default(context
, NULL
, global_default
,
138 "kdc", "realms", crealm
, name
, NULL
);
142 * Verify the HMAC in the request.
144 static krb5_error_code
145 verify_req_hash(krb5_context context
,
146 const Kx509Request
*req
,
149 unsigned char digest
[SHA_DIGEST_LENGTH
];
152 if (req
->pk_hash
.length
!= sizeof(digest
)) {
153 krb5_set_error_message(context
, KRB5KDC_ERR_PREAUTH_FAILED
,
154 "pk-hash has wrong length: %lu",
155 (unsigned long)req
->pk_hash
.length
);
156 return KRB5KDC_ERR_PREAUTH_FAILED
;
161 key
->keyvalue
.data
, key
->keyvalue
.length
,
163 if (sizeof(digest
) != HMAC_size(&ctx
))
164 krb5_abortx(context
, "runtime error, hmac buffer wrong size in kx509");
165 HMAC_Update(&ctx
, version_2_0
, sizeof(version_2_0
));
166 if (req
->pk_key
.length
)
167 HMAC_Update(&ctx
, req
->pk_key
.data
, req
->pk_key
.length
);
169 HMAC_Update(&ctx
, req
->authenticator
.data
, req
->authenticator
.length
);
170 HMAC_Final(&ctx
, digest
, 0);
171 HMAC_CTX_cleanup(&ctx
);
173 if (ct_memcmp(req
->pk_hash
.data
, digest
, sizeof(digest
)) != 0) {
174 krb5_set_error_message(context
, KRB5KDC_ERR_PREAUTH_FAILED
,
175 "kx509 request MAC mismatch");
176 return KRB5KDC_ERR_PREAUTH_FAILED
;
182 * Set the HMAC in the response.
184 static krb5_error_code
185 calculate_reply_hash(krb5_context context
,
194 HMAC_Init_ex(&ctx
, key
->keyvalue
.data
, key
->keyvalue
.length
,
196 ret
= krb5_data_alloc(rep
->hash
, HMAC_size(&ctx
));
198 HMAC_CTX_cleanup(&ctx
);
199 return krb5_enomem(context
);
202 HMAC_Update(&ctx
, version_2_0
, sizeof(version_2_0
));
204 int32_t t
= rep
->error_code
;
205 unsigned char encint
[sizeof(t
) + 1];
209 * RFC6717 says this about how the error-code is included in the HMAC:
211 * o DER representation of the error-code exclusive of the tag and
212 * length, if it is present.
214 * So we use der_put_integer(), which encodes from the right.
216 * RFC6717 does not constrain the error-code's range. We assume it to
217 * be a 32-bit, signed integer, for which we'll need no more than 5
220 ret
= der_put_integer(&encint
[sizeof(encint
) - 1],
221 sizeof(encint
), &t
, &k
);
223 HMAC_Update(&ctx
, &encint
[sizeof(encint
)] - k
, k
);
225 if (rep
->certificate
)
226 HMAC_Update(&ctx
, rep
->certificate
->data
, rep
->certificate
->length
);
228 HMAC_Update(&ctx
, (unsigned char *)*rep
->e_text
, strlen(*rep
->e_text
));
230 HMAC_Final(&ctx
, rep
->hash
->data
, 0);
231 HMAC_CTX_cleanup(&ctx
);
243 /* Check that a krbtgt's second component is a local realm */
244 static krb5_error_code
245 is_local_realm(krb5_context context
,
246 kx509_req_context reqctx
,
251 hdb_entry_ex
*ent
= NULL
;
253 ret
= krb5_make_principal(context
, &tgs
, realm
, KRB5_TGS_NAME
, realm
,
258 ret
= _kdc_db_fetch(context
, reqctx
->config
, tgs
, HDB_F_GET_KRBTGT
,
261 _kdc_free_ent(context
, ent
);
262 krb5_free_principal(context
, tgs
);
263 if (ret
== HDB_ERR_NOENTRY
|| ret
== HDB_ERR_NOT_FOUND_HERE
)
264 return KRB5KRB_AP_ERR_NOT_US
;
269 * Since we're using the HDB as a keytab we have to check that the client used
270 * an acceptable name for the kx509 service.
272 * We accept two names: kca_service/hostname and krbtgt/REALM.
274 * We allow cross-realm requests.
276 * Maybe x-realm support should be configurable. Requiring INITIAL tickets
277 * does NOT preclude x-realm support! (Cross-realm TGTs can be INITIAL.)
279 * Support for specific client realms is configurable by configuring issuer
280 * credentials and TBS templates on a per-realm basis and configuring no
281 * default. But maybe we should have an explicit configuration parameter
282 * to enable support for clients from different realms than the service.
284 static krb5_error_code
285 kdc_kx509_verify_service_principal(krb5_context context
,
286 kx509_req_context reqctx
,
287 krb5_principal sprincipal
)
289 krb5_error_code ret
= 0;
290 krb5_principal principal
= NULL
;
291 char *expected
= NULL
;
292 char localhost
[MAXHOSTNAMELEN
];
294 if (krb5_principal_get_num_comp(context
, sprincipal
) != 2)
297 /* Check if sprincipal is a krbtgt/REALM name */
298 if (strcmp(krb5_principal_get_comp_string(context
, sprincipal
, 0),
299 KRB5_TGS_NAME
) == 0) {
300 const char *r
= krb5_principal_get_comp_string(context
, sprincipal
, 1);
301 if ((ret
= is_local_realm(context
, reqctx
, r
)))
302 _kdc_audit_addreason((kdc_request_t
)reqctx
,
303 "Client used wrong krbtgt for kx509");
307 /* Must be hostbased kca_service name then */
308 ret
= gethostname(localhost
, sizeof(localhost
) - 1);
311 kdc_log(context
, reqctx
->config
, 0, "Failed to get local hostname");
312 _kdc_audit_addreason((kdc_request_t
)reqctx
,
313 "Failed to get local hostname");
316 localhost
[sizeof(localhost
) - 1] = '\0';
318 ret
= krb5_make_principal(context
, &principal
, "", "kca_service",
323 if (krb5_principal_compare_any_realm(context
, sprincipal
, principal
))
324 goto out
; /* found a match */
327 ret
= krb5_unparse_name(context
, sprincipal
, &expected
);
331 ret
= KRB5KDC_ERR_SERVER_NOMATCH
;
332 _kdc_audit_addreason((kdc_request_t
)reqctx
, "Client used wrong kx509 "
333 "service principal (expected %s)", expected
);
336 krb5_xfree(expected
);
337 krb5_free_principal(context
, principal
);
342 static krb5_error_code
343 encode_reply(krb5_context context
,
344 kx509_req_context reqctx
,
351 reqctx
->reply
->data
= NULL
;
352 reqctx
->reply
->length
= 0;
353 ASN1_MALLOC_ENCODE(Kx509Response
, data
.data
, data
.length
, r
, &size
, ret
);
356 if (size
!= data
.length
)
357 krb5_abortx(context
, "ASN1 internal error");
359 ret
= krb5_data_alloc(reqctx
->reply
, data
.length
+ sizeof(version_2_0
));
361 memcpy(reqctx
->reply
->data
, version_2_0
, sizeof(version_2_0
));
362 memcpy(((unsigned char *)reqctx
->reply
->data
) + sizeof(version_2_0
),
363 data
.data
, data
.length
);
369 /* Make an error response, and log the error message as well */
370 static krb5_error_code
371 mk_error_response(krb5_context context
,
372 kx509_req_context reqctx
,
378 krb5_error_code ret
= code
;
379 krb5_error_code ret2
;
382 char *freeme0
= NULL
;
383 char *freeme1
= NULL
;
387 /* Log errors where _kdc_audit_trail() is not enough */
392 kdc_vlog(context
, reqctx
->config
, level
, fmt
, ap
);
397 _kdc_audit_vaddreason((kdc_request_t
)reqctx
, fmt
, ap
);
401 if (!reqctx
->config
->enable_kx509
)
402 code
= KRB5KDC_ERR_POLICY
;
404 /* Make sure we only send RFC4120 and friends wire protocol error codes */
406 if (code
== KX509_ERR_NONE
) {
408 } else if (code
> KX509_ERR_NONE
&& code
<= KX509_ERR_SRV_OVERLOADED
) {
409 code
-= KX509_ERR_NONE
;
411 if (code
< KRB5KDC_ERR_NONE
|| code
>= KRB5_ERR_RCSID
)
412 code
= KRB5KRB_ERR_GENERIC
;
413 code
-= KRB5KDC_ERR_NONE
;
414 code
+= kx509_krb5_error_base
;
419 if (vasprintf(&freeme0
, fmt
, ap
) == -1 || freeme0
== NULL
)
420 msg
= "Could not format error message (out of memory)";
425 if (!reqctx
->config
->enable_kx509
&&
426 asprintf(&freeme1
, "kx509 service is disabled (%s)", msg
) > -1 &&
432 rep
.certificate
= NULL
;
433 rep
.error_code
= code
;
434 if (ALLOC(rep
.e_text
))
435 *rep
.e_text
= (void *)(uintptr_t)msg
;
438 if (ALLOC(rep
.hash
) != NULL
&&
439 calculate_reply_hash(context
, reqctx
->key
, &rep
)) {
445 if ((ret2
= encode_reply(context
, reqctx
, &rep
)))
448 krb5_data_free(rep
.hash
);
456 /* Wrap a bare public (RSA) key with a CSR (not signed it, since we can't) */
457 static krb5_error_code
458 make_csr(krb5_context context
, kx509_req_context reqctx
, krb5_data
*key
)
461 SubjectPublicKeyInfo spki
;
464 ret
= hx509_request_init(context
->hx509ctx
, &reqctx
->csr
);
468 memset(&spki
, 0, sizeof(spki
));
469 spki
.subjectPublicKey
.data
= key
->data
;
470 spki
.subjectPublicKey
.length
= key
->length
* 8;
472 ret
= der_copy_oid(&asn1_oid_id_pkcs1_rsaEncryption
,
473 &spki
.algorithm
.algorithm
);
475 any
.data
= "\x05\x00";
477 spki
.algorithm
.parameters
= &any
;
480 ret
= hx509_request_set_SubjectPublicKeyInfo(context
->hx509ctx
,
482 der_free_oid(&spki
.algorithm
.algorithm
);
484 hx509_request_free(&reqctx
->csr
);
487 * TODO: Move a lot of the templating stuff here so we can let clients
488 * leave out extensions they don't want.
493 /* Update a CSR with desired Certificate Extensions */
494 static krb5_error_code
495 update_csr(krb5_context context
, kx509_req_context reqctx
, Extensions
*exts
)
497 krb5_error_code ret
= 0;
503 for (i
= 0; ret
== 0 && i
< exts
->len
; i
++) {
504 Extension
*e
= &exts
->val
[i
];
506 if (der_heim_oid_cmp(&e
->extnID
, &asn1_oid_id_x509_ce_keyUsage
) == 0) {
509 ret
= decode_KeyUsage(e
->extnValue
.data
, e
->extnValue
.length
, &ku
,
513 ret
= hx509_request_set_ku(context
->hx509ctx
, reqctx
->csr
, ku
);
514 } else if (der_heim_oid_cmp(&e
->extnID
,
515 &asn1_oid_id_x509_ce_extKeyUsage
) == 0) {
518 ret
= decode_ExtKeyUsage(e
->extnValue
.data
, e
->extnValue
.length
,
520 for (k
= 0; ret
== 0 && k
< eku
.len
; k
++) {
521 ret
= hx509_request_add_eku(context
->hx509ctx
, reqctx
->csr
,
524 free_ExtKeyUsage(&eku
);
525 } else if (der_heim_oid_cmp(&e
->extnID
,
526 &asn1_oid_id_x509_ce_subjectAltName
) == 0) {
529 ret
= decode_GeneralNames(e
->extnValue
.data
, e
->extnValue
.length
,
531 for (k
= 0; ret
== 0 && k
< san
.len
; k
++) {
532 ret
= hx509_request_add_GeneralName(context
->hx509ctx
,
533 reqctx
->csr
, &san
.val
[k
]);
535 free_GeneralNames(&san
);
539 kdc_log(context
, reqctx
->config
, 1,
540 "Error handling requested extensions: %s",
541 krb5_get_error_message(context
, ret
));
542 _kdc_audit_addreason((kdc_request_t
)reqctx
,
543 "Error handling requested extensions: %s",
544 krb5_get_error_message(context
, ret
));
551 * Parse the `pk_key' from the request as a CSR or raw public key, and if the
552 * latter, wrap it in a non-signed CSR.
554 static krb5_error_code
555 get_csr(krb5_context context
, kx509_req_context reqctx
)
558 RSAPublicKey rsapkey
;
559 heim_octet_string pk_key
= reqctx
->req
.pk_key
;
562 ret
= decode_Kx509CSRPlus(pk_key
.data
, pk_key
.length
, &reqctx
->csr_plus
,
565 reqctx
->have_csr
= 1;
566 reqctx
->send_chain
= 1;
569 ret
= hx509_request_parse_der(context
->hx509ctx
, &reqctx
->csr_plus
.csr
,
572 * Handle any additional Certificate Extensions requested out of band
576 return update_csr(context
, reqctx
, reqctx
->csr_plus
.exts
);
577 _kdc_audit_addreason((kdc_request_t
)reqctx
, "Invalid CSR");
580 reqctx
->send_chain
= 0;
581 reqctx
->have_csr
= 0;
583 /* Check if proof of possession is required by configuration */
584 if (!get_bool_param(context
, FALSE
, reqctx
->realm
, "require_csr")) {
585 _kdc_audit_addreason((kdc_request_t
)reqctx
,
586 "CSRs required but client did not send one");
587 krb5_set_error_message(context
, KX509_STATUS_CLIENT_USE_CSR
,
588 "CSRs required but kx509 client did not send "
590 return KX509_STATUS_CLIENT_USE_CSR
;
593 /* Attempt to decode pk_key as RSAPublicKey */
594 ret
= decode_RSAPublicKey(reqctx
->req
.pk_key
.data
,
595 reqctx
->req
.pk_key
.length
,
597 free_RSAPublicKey(&rsapkey
);
598 if (ret
== 0 && size
== reqctx
->req
.pk_key
.length
)
599 return make_csr(context
, reqctx
, &pk_key
); /* Make pretend CSR */
601 /* Not an RSAPublicKey or garbage follows it */
603 ret
= KRB5KDC_ERR_NULL_KEY
;
604 _kdc_audit_addreason((kdc_request_t
)reqctx
,
605 "Request has garbage after key");
606 krb5_set_error_message(context
, ret
, "Request has garbage after key");
610 _kdc_audit_addreason((kdc_request_t
)reqctx
,
611 "Could not decode CSR or RSA subject public key");
612 krb5_set_error_message(context
, ret
,
613 "Could not decode CSR or RSA subject public key");
618 * Host-based principal _clients_ might ask for a cert for their host -- but
619 * which services are permitted to do that? This function answers that
623 check_authz_svc_ok(krb5_context context
, const char *svc
)
625 const char *def
[] = { "host", "HTTP", 0 };
626 const char * const *svcs
;
629 strs
= krb5_config_get_strings(context
, NULL
, "kdc",
630 "kx509_permitted_hostbased_services", NULL
);
631 for (svcs
= strs
? (const char * const *)strs
: def
; svcs
[0]; svcs
++) {
632 if (strcmp(svcs
[0], svc
) == 0) {
633 krb5_config_free_strings(strs
);
637 krb5_config_free_strings(strs
);
641 static krb5_error_code
642 check_authz(krb5_context context
,
643 kx509_req_context reqctx
,
644 krb5_principal cprincipal
)
647 const char *comp0
= krb5_principal_get_comp_string(context
, cprincipal
, 0);
648 const char *comp1
= krb5_principal_get_comp_string(context
, cprincipal
, 1);
649 unsigned int ncomp
= krb5_principal_get_num_comp(context
, cprincipal
);
650 hx509_san_type san_type
;
651 KeyUsage ku
, ku_allowed
;
653 const heim_oid
*eku_whitelist
[] = {
654 &asn1_oid_id_pkix_kp_serverAuth
,
655 &asn1_oid_id_pkix_kp_clientAuth
,
656 &asn1_oid_id_pkekuoid
,
657 &asn1_oid_id_pkinit_ms_eku
663 * In the no-CSR case we'll derive cert contents from client name and its
664 * HDB entry -- authorization is implied.
666 if (!reqctx
->have_csr
)
668 ret
= kdc_authorize_csr(context
, reqctx
->config
->app
, reqctx
->csr
,
671 _kdc_audit_setkv_bool((kdc_request_t
)reqctx
, "authorized", TRUE
);
673 ret
= hx509_request_get_san(reqctx
->csr
, 0, &san_type
, &s
);
675 const char *san_type_s
;
677 /* This should be an hx509 function... */
679 case HX509_SAN_TYPE_EMAIL
: san_type_s
= "rfc822Name"; break;
680 case HX509_SAN_TYPE_DNSNAME
: san_type_s
= "dNSName"; break;
681 case HX509_SAN_TYPE_DN
: san_type_s
= "DN"; break;
682 case HX509_SAN_TYPE_REGISTERED_ID
: san_type_s
= "registeredID"; break;
683 case HX509_SAN_TYPE_XMPP
: san_type_s
= "xMPPName"; break;
684 case HX509_SAN_TYPE_PKINIT
: san_type_s
= "krb5PrincipalName"; break;
685 case HX509_SAN_TYPE_MS_UPN
: san_type_s
= "ms-UPN"; break;
686 default: san_type_s
= "unknown"; break;
688 _kdc_audit_addkv((kdc_request_t
)reqctx
, 0, "san0_type", "%s",
690 _kdc_audit_addkv((kdc_request_t
)reqctx
, 0, "san0", "%s", s
);
693 ret
= hx509_request_get_eku(reqctx
->csr
, 0, &s
);
695 _kdc_audit_addkv((kdc_request_t
)reqctx
, 0, "eku0", "%s", s
);
700 if (ret
!= KRB5_PLUGIN_NO_HANDLE
) {
701 _kdc_audit_addreason((kdc_request_t
)reqctx
,
702 "Requested extensions rejected by plugin");
707 if ((ret
= krb5_unparse_name(context
, cprincipal
, &cprinc
)))
710 for (i
= 0; ret
== 0; i
++) {
713 ret
= hx509_request_get_san(reqctx
->csr
, i
, &san_type
, &s
);
717 case HX509_SAN_TYPE_DNSNAME
:
718 if (ncomp
!= 2 || strcasecmp(comp1
, s
) != 0 ||
719 strchr(s
, '.') == NULL
||
720 !check_authz_svc_ok(context
, comp0
)) {
721 _kdc_audit_addreason((kdc_request_t
)reqctx
,
722 "Requested extensions rejected by "
723 "default policy (dNSName SAN "
724 "does not match client)");
728 case HX509_SAN_TYPE_PKINIT
:
729 if (strcmp(cprinc
, s
) != 0) {
730 _kdc_audit_addreason((kdc_request_t
)reqctx
,
731 "Requested extensions rejected by "
732 "default policy (PKINIT SAN "
733 "does not match client)");
738 _kdc_audit_addreason((kdc_request_t
)reqctx
,
739 "Requested extensions rejected by "
740 "default policy (non-default SAN "
746 if (ret
== HX509_NO_ITEM
)
751 for (i
= 0; ret
== 0; i
++) {
756 ret
= hx509_request_get_eku(reqctx
->csr
, i
, &s
);
760 if ((ret
= der_parse_heim_oid(s
, ".", &oid
))) {
763 for (k
= 0; k
< sizeof(eku_whitelist
)/sizeof(eku_whitelist
[0]); k
++) {
764 if (der_heim_oid_cmp(eku_whitelist
[k
], &oid
) == 0)
768 if (k
== sizeof(eku_whitelist
)/sizeof(eku_whitelist
[0])) {
769 _kdc_audit_addreason((kdc_request_t
)reqctx
,
770 "Requested EKU rejected by default policy");
774 if (ret
== HX509_NO_ITEM
)
779 memset(&ku_allowed
, 0, sizeof(ku_allowed
));
780 ku_allowed
.digitalSignature
= 1;
781 ku_allowed
.nonRepudiation
= 1;
782 ret
= hx509_request_get_ku(context
->hx509ctx
, reqctx
->csr
, &ku
);
785 if (KeyUsage2int(ku
) != (KeyUsage2int(ku
) & KeyUsage2int(ku_allowed
)))
788 _kdc_audit_setkv_bool((kdc_request_t
)reqctx
, "authorized", TRUE
);
796 /* XXX Display error code */
797 _kdc_audit_addreason((kdc_request_t
)reqctx
,
798 "Error handling requested extensions");
806 chain_add1_func(hx509_context context
, void *d
, hx509_cert c
)
808 heim_octet_string os
;
809 Certificates
*cs
= d
;
813 ret
= hx509_cert_binary(context
, c
, &os
);
816 ret
= decode_Certificate(os
.data
, os
.length
, &c2
, NULL
);
817 der_free_octet_string(&os
);
820 ret
= add_Certificates(cs
, &c2
);
821 free_Certificate(&c2
);
825 static krb5_error_code
826 encode_cert_and_chain(hx509_context hx509ctx
,
837 ret
= hx509_certs_iter_f(hx509ctx
, certs
, chain_add1_func
, &cs
);
839 ASN1_MALLOC_ENCODE(Certificates
, out
->data
, out
->length
,
841 free_Certificates(&cs
);
846 * Process a request, produce a reply.
850 _kdc_do_kx509(kx509_req_context r
)
852 krb5_error_code ret
= 0;
853 krb5_ticket
*ticket
= NULL
;
854 krb5_flags ap_req_options
;
855 krb5_principal cprincipal
= NULL
;
856 krb5_principal sprincipal
= NULL
;
857 krb5_keytab id
= NULL
;
859 hx509_certs certs
= NULL
;
862 r
->csr_plus
.csr
.data
= NULL
;
863 r
->csr_plus
.exts
= NULL
;
872 * In order to support authenticated error messages we defer checking
873 * whether the kx509 service is enabled until after accepting the AP-REQ.
876 krb5_data_zero(r
->reply
);
877 memset(&rep
, 0, sizeof(rep
));
879 if (r
->req
.authenticator
.length
== 0) {
881 * Unauthenticated kx509 service availability probe.
883 * mk_error_response() will check whether the service is enabled and
884 * possibly change the error code and message.
887 _kdc_audit_addkv((kdc_request_t
)r
, 0, "probe", "unauthenticated");
888 ret
= mk_error_response(r
->context
, r
, 4, 0,
889 "kx509 service is available");
893 /* Authenticate the request (consume the AP-REQ) */
894 ret
= krb5_kt_resolve(r
->context
, "HDBGET:", &id
);
896 const char *msg
= krb5_get_error_message(r
->context
, ret
);
897 ret
= mk_error_response(r
->context
, r
, 1,
898 KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
,
899 "Can't open HDB/keytab for kx509: %s",
901 krb5_free_error_message(r
->context
, msg
);
905 ret
= krb5_rd_req(r
->context
,
907 &r
->req
.authenticator
,
913 ret
= krb5_auth_con_getkey(r
->context
, r
->ac
, &r
->key
);
914 if (ret
== 0 && r
->key
== NULL
)
915 ret
= KRB5KDC_ERR_NULL_KEY
;
917 * Provided we got the session key, errors past this point will be
921 ret
= krb5_ticket_get_client(r
->context
, ticket
, &cprincipal
);
923 /* Optional: check if Ticket is INITIAL */
925 !ticket
->ticket
.flags
.initial
&&
926 !get_bool_param(r
->context
, TRUE
,
927 krb5_principal_get_realm(r
->context
, cprincipal
),
928 "require_initial_kca_tickets")) {
929 ret
= mk_error_response(r
->context
, r
, 4, KRB5KDC_ERR_POLICY
,
930 "Client used non-INITIAL tickets, but kx509 "
931 "service is configured to require INITIAL "
936 ret
= krb5_unparse_name(r
->context
, cprincipal
, &r
->cname
);
938 /* Check that the service name is a valid kx509 service name */
940 ret
= krb5_ticket_get_server(r
->context
, ticket
, &sprincipal
);
942 r
->realm
= krb5_principal_get_realm(r
->context
, sprincipal
);
944 ret
= krb5_unparse_name(r
->context
, sprincipal
, &r
->sname
);
946 ret
= kdc_kx509_verify_service_principal(r
->context
, r
, sprincipal
);
948 ret
= mk_error_response(r
->context
, r
, 4, ret
,
949 "kx509 client used incorrect service name");
953 /* Authenticate the rest of the request */
954 ret
= verify_req_hash(r
->context
, &r
->req
, r
->key
);
956 ret
= mk_error_response(r
->context
, r
, 4, ret
,
957 "Incorrect request HMAC on kx509 request");
961 if (r
->req
.pk_key
.length
== 0) {
963 * The request is an authenticated kx509 service availability probe.
965 * mk_error_response() will check whether the service is enabled and
966 * possibly change the error code and message.
969 _kdc_audit_addkv((kdc_request_t
)r
, 0, "probe", "authenticated");
970 ret
= mk_error_response(r
->context
, r
, 4, 0,
971 "kx509 authenticated probe request");
975 /* Extract and parse CSR or a DER-encoded RSA public key */
976 ret
= get_csr(r
->context
, r
);
978 const char *msg
= krb5_get_error_message(r
->context
, ret
);
979 ret
= mk_error_response(r
->context
, r
, 3, ret
,
980 "Failed to parse CSR: %s", msg
);
981 krb5_free_error_message(r
->context
, msg
);
985 /* Authorize the request */
986 ret
= check_authz(r
->context
, r
, cprincipal
);
988 const char *msg
= krb5_get_error_message(r
->context
, ret
);
989 ret
= mk_error_response(r
->context
, r
, 3, ret
,
990 "Rejected by policy: %s", msg
);
991 krb5_free_error_message(r
->context
, msg
);
995 /* Issue the certificate */
997 ALLOC(rep
.certificate
);
998 if (rep
.certificate
== NULL
|| rep
.hash
== NULL
) {
999 ret
= mk_error_response(r
->context
, r
, 0, ENOMEM
,
1000 "Could allocate memory for response");
1004 krb5_data_zero(rep
.hash
);
1005 krb5_data_zero(rep
.certificate
);
1006 krb5_ticket_get_times(r
->context
, ticket
, &r
->ticket_times
);
1007 ret
= kdc_issue_certificate(r
->context
, r
->config
->app
, r
->logf
, r
->csr
,
1008 cprincipal
, &r
->ticket_times
, 0 /*req_life*/,
1009 r
->send_chain
, &certs
);
1012 const char *msg
= krb5_get_error_message(r
->context
, ret
);
1014 if (ret
== KRB5KDC_ERR_POLICY
)
1015 level
= 4; /* _kdc_audit_trail() logs at level 3 */
1016 ret
= mk_error_response(r
->context
, r
, level
, ret
,
1017 "Certificate isuance failed: %s", msg
);
1018 krb5_free_error_message(r
->context
, msg
);
1022 ret
= encode_cert_and_chain(r
->context
->hx509ctx
, certs
, rep
.certificate
);
1024 const char *msg
= krb5_get_error_message(r
->context
, ret
);
1025 ret
= mk_error_response(r
->context
, r
, 1, ret
,
1026 "Could not encode certificate and chain: %s",
1028 krb5_free_error_message(r
->context
, msg
);
1032 /* Authenticate the response */
1033 ret
= calculate_reply_hash(r
->context
, r
->key
, &rep
);
1035 ret
= mk_error_response(r
->context
, r
, 1, ret
,
1036 "Failed to compute response HMAC");
1040 /* Encode and output reply */
1041 ret
= encode_reply(r
->context
, r
, &rep
);
1043 /* Can't send an error message either in this case, surely */
1044 _kdc_audit_addreason((kdc_request_t
)r
, "Could not encode response");
1047 hx509_certs_free(&certs
);
1048 if (ret
== 0 && !is_probe
)
1049 _kdc_audit_setkv_bool((kdc_request_t
)r
, "cert_issued", TRUE
);
1051 _kdc_audit_setkv_bool((kdc_request_t
)r
, "cert_issued", FALSE
);
1053 krb5_auth_con_free(r
->context
, r
->ac
);
1055 krb5_free_ticket(r
->context
, ticket
);
1057 krb5_kt_close(r
->context
, id
);
1059 krb5_free_principal(r
->context
, sprincipal
);
1061 krb5_free_principal(r
->context
, cprincipal
);
1063 krb5_free_keyblock (r
->context
, r
->key
);
1064 hx509_request_free(&r
->csr
);
1065 free_Kx509CSRPlus(&r
->csr_plus
);
1066 free_Kx509Response(&rep
);
1067 free_Kx509Request(&r
->req
);