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
;
160 if (HMAC_Init_ex(&ctx
, key
->keyvalue
.data
, key
->keyvalue
.length
,
161 EVP_sha1(), NULL
) == 0) {
162 HMAC_CTX_cleanup(&ctx
);
163 return krb5_enomem(context
);
165 if (sizeof(digest
) != HMAC_size(&ctx
))
166 krb5_abortx(context
, "runtime error, hmac buffer wrong size in kx509");
167 HMAC_Update(&ctx
, version_2_0
, sizeof(version_2_0
));
168 if (req
->pk_key
.length
)
169 HMAC_Update(&ctx
, req
->pk_key
.data
, req
->pk_key
.length
);
171 HMAC_Update(&ctx
, req
->authenticator
.data
, req
->authenticator
.length
);
172 HMAC_Final(&ctx
, digest
, 0);
173 HMAC_CTX_cleanup(&ctx
);
175 if (ct_memcmp(req
->pk_hash
.data
, digest
, sizeof(digest
)) != 0) {
176 krb5_set_error_message(context
, KRB5KDC_ERR_PREAUTH_FAILED
,
177 "kx509 request MAC mismatch");
178 return KRB5KDC_ERR_PREAUTH_FAILED
;
184 * Set the HMAC in the response.
186 static krb5_error_code
187 calculate_reply_hash(krb5_context context
,
191 krb5_error_code ret
= 0;
196 if (HMAC_Init_ex(&ctx
, key
->keyvalue
.data
, key
->keyvalue
.length
,
197 EVP_sha1(), NULL
) == 0)
198 ret
= krb5_enomem(context
);
201 ret
= krb5_data_alloc(rep
->hash
, HMAC_size(&ctx
));
203 HMAC_CTX_cleanup(&ctx
);
204 return krb5_enomem(context
);
207 HMAC_Update(&ctx
, version_2_0
, sizeof(version_2_0
));
209 int32_t t
= rep
->error_code
;
210 unsigned char encint
[sizeof(t
) + 1];
214 * RFC6717 says this about how the error-code is included in the HMAC:
216 * o DER representation of the error-code exclusive of the tag and
217 * length, if it is present.
219 * So we use der_put_integer(), which encodes from the right.
221 * RFC6717 does not constrain the error-code's range. We assume it to
222 * be a 32-bit, signed integer, for which we'll need no more than 5
225 ret
= der_put_integer(&encint
[sizeof(encint
) - 1],
226 sizeof(encint
), &t
, &k
);
228 HMAC_Update(&ctx
, &encint
[sizeof(encint
)] - k
, k
);
230 if (rep
->certificate
)
231 HMAC_Update(&ctx
, rep
->certificate
->data
, rep
->certificate
->length
);
233 HMAC_Update(&ctx
, (unsigned char *)*rep
->e_text
, strlen(*rep
->e_text
));
235 HMAC_Final(&ctx
, rep
->hash
->data
, 0);
236 HMAC_CTX_cleanup(&ctx
);
248 /* Check that a krbtgt's second component is a local realm */
249 static krb5_error_code
250 is_local_realm(krb5_context context
,
251 kx509_req_context reqctx
,
257 hdb_entry
*ent
= NULL
;
259 ret
= krb5_make_principal(context
, &tgs
, realm
, KRB5_TGS_NAME
, realm
,
264 ret
= _kdc_db_fetch(context
, reqctx
->config
, tgs
, HDB_F_GET_KRBTGT
,
267 _kdc_free_ent(context
, db
, ent
);
268 krb5_free_principal(context
, tgs
);
269 if (ret
== HDB_ERR_NOENTRY
|| ret
== HDB_ERR_NOT_FOUND_HERE
)
270 return KRB5KRB_AP_ERR_NOT_US
;
275 * Since we're using the HDB as a keytab we have to check that the client used
276 * an acceptable name for the kx509 service.
278 * We accept two names: kca_service/hostname and krbtgt/REALM.
280 * We allow cross-realm requests.
282 * Maybe x-realm support should be configurable. Requiring INITIAL tickets
283 * does NOT preclude x-realm support! (Cross-realm TGTs can be INITIAL.)
285 * Support for specific client realms is configurable by configuring issuer
286 * credentials and TBS templates on a per-realm basis and configuring no
287 * default. But maybe we should have an explicit configuration parameter
288 * to enable support for clients from different realms than the service.
290 static krb5_error_code
291 kdc_kx509_verify_service_principal(krb5_context context
,
292 kx509_req_context reqctx
,
293 krb5_principal sprincipal
)
295 krb5_error_code ret
= 0;
296 krb5_principal principal
= NULL
;
297 char *expected
= NULL
;
298 char localhost
[MAXHOSTNAMELEN
];
300 if (krb5_principal_get_num_comp(context
, sprincipal
) != 2)
303 /* Check if sprincipal is a krbtgt/REALM name */
304 if (strcmp(krb5_principal_get_comp_string(context
, sprincipal
, 0),
305 KRB5_TGS_NAME
) == 0) {
306 const char *r
= krb5_principal_get_comp_string(context
, sprincipal
, 1);
307 if ((ret
= is_local_realm(context
, reqctx
, r
)))
308 kdc_audit_addreason((kdc_request_t
)reqctx
,
309 "Client used wrong krbtgt for kx509");
313 /* Must be hostbased kca_service name then */
314 ret
= gethostname(localhost
, sizeof(localhost
) - 1);
317 kdc_log(context
, reqctx
->config
, 0, "Failed to get local hostname");
318 kdc_audit_addreason((kdc_request_t
)reqctx
,
319 "Failed to get local hostname");
322 localhost
[sizeof(localhost
) - 1] = '\0';
324 ret
= krb5_make_principal(context
, &principal
, "", "kca_service",
329 if (krb5_principal_compare_any_realm(context
, sprincipal
, principal
))
330 goto out
; /* found a match */
333 ret
= krb5_unparse_name(context
, sprincipal
, &expected
);
337 ret
= KRB5KDC_ERR_SERVER_NOMATCH
;
338 kdc_audit_addreason((kdc_request_t
)reqctx
, "Client used wrong kx509 "
339 "service principal (expected %s)", expected
);
342 krb5_xfree(expected
);
343 krb5_free_principal(context
, principal
);
348 static krb5_error_code
349 encode_reply(krb5_context context
,
350 kx509_req_context reqctx
,
357 reqctx
->reply
->data
= NULL
;
358 reqctx
->reply
->length
= 0;
359 ASN1_MALLOC_ENCODE(Kx509Response
, data
.data
, data
.length
, r
, &size
, ret
);
362 if (size
!= data
.length
)
363 krb5_abortx(context
, "ASN1 internal error");
365 ret
= krb5_data_alloc(reqctx
->reply
, data
.length
+ sizeof(version_2_0
));
367 memcpy(reqctx
->reply
->data
, version_2_0
, sizeof(version_2_0
));
368 memcpy(((unsigned char *)reqctx
->reply
->data
) + sizeof(version_2_0
),
369 data
.data
, data
.length
);
375 /* Make an error response, and log the error message as well */
376 static krb5_error_code
377 mk_error_response(krb5_context context
,
378 kx509_req_context reqctx
,
384 krb5_error_code ret
= code
;
385 krb5_error_code ret2
;
388 char *freeme0
= NULL
;
389 char *freeme1
= NULL
;
393 /* Log errors where _kdc_audit_trail() is not enough */
398 kdc_vlog(context
, reqctx
->config
, level
, fmt
, ap
);
403 kdc_audit_vaddreason((kdc_request_t
)reqctx
, fmt
, ap
);
407 if (!reqctx
->config
->enable_kx509
)
408 code
= KRB5KDC_ERR_POLICY
;
410 /* Make sure we only send RFC4120 and friends wire protocol error codes */
412 if (code
== KX509_ERR_NONE
) {
414 } else if (code
> KX509_ERR_NONE
&& code
<= KX509_ERR_SRV_OVERLOADED
) {
415 code
-= KX509_ERR_NONE
;
417 if (code
< KRB5KDC_ERR_NONE
|| code
>= KRB5_ERR_RCSID
)
418 code
= KRB5KRB_ERR_GENERIC
;
419 code
-= KRB5KDC_ERR_NONE
;
420 code
+= kx509_krb5_error_base
;
425 if (vasprintf(&freeme0
, fmt
, ap
) == -1 || freeme0
== NULL
)
426 msg
= "Could not format error message (out of memory)";
431 if (!reqctx
->config
->enable_kx509
&&
432 asprintf(&freeme1
, "kx509 service is disabled (%s)", msg
) > -1 &&
438 rep
.certificate
= NULL
;
439 rep
.error_code
= code
;
440 if (ALLOC(rep
.e_text
))
441 *rep
.e_text
= (void *)(uintptr_t)msg
;
444 if (ALLOC(rep
.hash
) != NULL
&&
445 calculate_reply_hash(context
, reqctx
->key
, &rep
)) {
451 if ((ret2
= encode_reply(context
, reqctx
, &rep
)))
454 krb5_data_free(rep
.hash
);
462 /* Wrap a bare public (RSA) key with a CSR (not signed it, since we can't) */
463 static krb5_error_code
464 make_csr(krb5_context context
, kx509_req_context reqctx
, krb5_data
*key
)
467 SubjectPublicKeyInfo spki
;
470 ret
= hx509_request_init(context
->hx509ctx
, &reqctx
->csr
);
474 memset(&spki
, 0, sizeof(spki
));
475 spki
.subjectPublicKey
.data
= key
->data
;
476 spki
.subjectPublicKey
.length
= key
->length
* 8;
478 ret
= der_copy_oid(&asn1_oid_id_pkcs1_rsaEncryption
,
479 &spki
.algorithm
.algorithm
);
481 any
.data
= "\x05\x00";
483 spki
.algorithm
.parameters
= &any
;
486 ret
= hx509_request_set_SubjectPublicKeyInfo(context
->hx509ctx
,
488 der_free_oid(&spki
.algorithm
.algorithm
);
490 hx509_request_free(&reqctx
->csr
);
493 * TODO: Move a lot of the templating stuff here so we can let clients
494 * leave out extensions they don't want.
499 /* Update a CSR with desired Certificate Extensions */
500 static krb5_error_code
501 update_csr(krb5_context context
, kx509_req_context reqctx
, Extensions
*exts
)
503 krb5_error_code ret
= 0;
509 for (i
= 0; ret
== 0 && i
< exts
->len
; i
++) {
510 Extension
*e
= &exts
->val
[i
];
512 if (der_heim_oid_cmp(&e
->extnID
, &asn1_oid_id_x509_ce_keyUsage
) == 0) {
515 ret
= decode_KeyUsage(e
->extnValue
.data
, e
->extnValue
.length
, &ku
,
519 ret
= hx509_request_set_ku(context
->hx509ctx
, reqctx
->csr
, ku
);
520 } else if (der_heim_oid_cmp(&e
->extnID
,
521 &asn1_oid_id_x509_ce_extKeyUsage
) == 0) {
524 ret
= decode_ExtKeyUsage(e
->extnValue
.data
, e
->extnValue
.length
,
526 for (k
= 0; ret
== 0 && k
< eku
.len
; k
++) {
527 ret
= hx509_request_add_eku(context
->hx509ctx
, reqctx
->csr
,
530 free_ExtKeyUsage(&eku
);
531 } else if (der_heim_oid_cmp(&e
->extnID
,
532 &asn1_oid_id_x509_ce_subjectAltName
) == 0) {
535 ret
= decode_GeneralNames(e
->extnValue
.data
, e
->extnValue
.length
,
537 for (k
= 0; ret
== 0 && k
< san
.len
; k
++) {
538 ret
= hx509_request_add_GeneralName(context
->hx509ctx
,
539 reqctx
->csr
, &san
.val
[k
]);
541 free_GeneralNames(&san
);
545 const char *emsg
= krb5_get_error_message(context
, ret
);
546 kdc_log(context
, reqctx
->config
, 1,
547 "Error handling requested extensions: %s", emsg
);
548 kdc_audit_addreason((kdc_request_t
)reqctx
,
549 "Error handling requested extensions: %s",
551 krb5_free_error_message(context
, emsg
);
558 * Parse the `pk_key' from the request as a CSR or raw public key, and if the
559 * latter, wrap it in a non-signed CSR.
561 static krb5_error_code
562 get_csr(krb5_context context
, kx509_req_context reqctx
)
565 RSAPublicKey rsapkey
;
566 heim_octet_string pk_key
= reqctx
->req
.pk_key
;
569 ret
= decode_Kx509CSRPlus(pk_key
.data
, pk_key
.length
, &reqctx
->csr_plus
,
572 reqctx
->have_csr
= 1;
573 reqctx
->send_chain
= 1;
576 ret
= hx509_request_parse_der(context
->hx509ctx
, &reqctx
->csr_plus
.csr
,
579 * Handle any additional Certificate Extensions requested out of band
583 return update_csr(context
, reqctx
, reqctx
->csr_plus
.exts
);
584 kdc_audit_addreason((kdc_request_t
)reqctx
, "Invalid CSR");
587 reqctx
->send_chain
= 0;
588 reqctx
->have_csr
= 0;
590 /* Check if proof of possession is required by configuration */
591 if (!get_bool_param(context
, FALSE
, reqctx
->realm
, "require_csr")) {
592 kdc_audit_addreason((kdc_request_t
)reqctx
,
593 "CSRs required but client did not send one");
594 krb5_set_error_message(context
, KX509_STATUS_CLIENT_USE_CSR
,
595 "CSRs required but kx509 client did not send "
597 return KX509_STATUS_CLIENT_USE_CSR
;
600 /* Attempt to decode pk_key as RSAPublicKey */
601 ret
= decode_RSAPublicKey(reqctx
->req
.pk_key
.data
,
602 reqctx
->req
.pk_key
.length
,
604 free_RSAPublicKey(&rsapkey
);
605 if (ret
== 0 && size
== reqctx
->req
.pk_key
.length
)
606 return make_csr(context
, reqctx
, &pk_key
); /* Make pretend CSR */
608 /* Not an RSAPublicKey or garbage follows it */
610 ret
= KRB5KDC_ERR_NULL_KEY
;
611 kdc_audit_addreason((kdc_request_t
)reqctx
,
612 "Request has garbage after key");
613 krb5_set_error_message(context
, ret
, "Request has garbage after key");
617 kdc_audit_addreason((kdc_request_t
)reqctx
,
618 "Could not decode CSR or RSA subject public key");
619 krb5_set_error_message(context
, ret
,
620 "Could not decode CSR or RSA subject public key");
625 * Host-based principal _clients_ might ask for a cert for their host -- but
626 * which services are permitted to do that? This function answers that
630 check_authz_svc_ok(krb5_context context
, const char *svc
)
632 const char *def
[] = { "host", "HTTP", 0 };
633 const char * const *svcs
;
636 strs
= krb5_config_get_strings(context
, NULL
, "kdc",
637 "kx509_permitted_hostbased_services", NULL
);
638 for (svcs
= strs
? (const char * const *)strs
: def
; svcs
[0]; svcs
++) {
639 if (strcmp(svcs
[0], svc
) == 0) {
640 krb5_config_free_strings(strs
);
644 krb5_config_free_strings(strs
);
648 static krb5_error_code
649 check_authz(krb5_context context
,
650 kx509_req_context reqctx
,
651 krb5_principal cprincipal
)
654 const char *comp0
= krb5_principal_get_comp_string(context
, cprincipal
, 0);
655 const char *comp1
= krb5_principal_get_comp_string(context
, cprincipal
, 1);
656 unsigned int ncomp
= krb5_principal_get_num_comp(context
, cprincipal
);
657 hx509_san_type san_type
;
658 KeyUsage ku
, ku_allowed
;
660 const heim_oid
*eku_whitelist
[] = {
661 &asn1_oid_id_pkix_kp_serverAuth
,
662 &asn1_oid_id_pkix_kp_clientAuth
,
663 &asn1_oid_id_pkekuoid
,
664 &asn1_oid_id_pkinit_ms_eku
670 * In the no-CSR case we'll derive cert contents from client name and its
671 * HDB entry -- authorization is implied.
673 if (!reqctx
->have_csr
)
675 ret
= kdc_authorize_csr(context
, reqctx
->config
->app
, reqctx
->csr
,
678 kdc_audit_setkv_bool((kdc_request_t
)reqctx
, "authorized", TRUE
);
680 ret
= hx509_request_get_san(reqctx
->csr
, 0, &san_type
, &s
);
682 const char *san_type_s
;
684 /* This should be an hx509 function... */
686 case HX509_SAN_TYPE_EMAIL
: san_type_s
= "rfc822Name"; break;
687 case HX509_SAN_TYPE_DNSNAME
: san_type_s
= "dNSName"; break;
688 case HX509_SAN_TYPE_DN
: san_type_s
= "DN"; break;
689 case HX509_SAN_TYPE_REGISTERED_ID
: san_type_s
= "registeredID"; break;
690 case HX509_SAN_TYPE_XMPP
: san_type_s
= "xMPPName"; break;
691 case HX509_SAN_TYPE_PKINIT
: san_type_s
= "krb5PrincipalName"; break;
692 case HX509_SAN_TYPE_MS_UPN
: san_type_s
= "ms-UPN"; break;
693 default: san_type_s
= "unknown"; break;
695 kdc_audit_addkv((kdc_request_t
)reqctx
, 0, "san0_type", "%s",
697 kdc_audit_addkv((kdc_request_t
)reqctx
, 0, "san0", "%s", s
);
700 ret
= hx509_request_get_eku(reqctx
->csr
, 0, &s
);
702 kdc_audit_addkv((kdc_request_t
)reqctx
, 0, "eku0", "%s", s
);
706 if (ret
!= KRB5_PLUGIN_NO_HANDLE
) {
707 kdc_audit_addreason((kdc_request_t
)reqctx
,
708 "Requested extensions rejected by plugin");
713 if ((ret
= krb5_unparse_name(context
, cprincipal
, &cprinc
)))
716 for (i
= 0; ret
== 0; i
++) {
719 ret
= hx509_request_get_san(reqctx
->csr
, i
, &san_type
, &s
);
723 case HX509_SAN_TYPE_DNSNAME
:
724 if (ncomp
!= 2 || strcasecmp(comp1
, s
) != 0 ||
725 strchr(s
, '.') == NULL
||
726 !check_authz_svc_ok(context
, comp0
)) {
727 kdc_audit_addreason((kdc_request_t
)reqctx
,
728 "Requested extensions rejected by "
729 "default policy (dNSName SAN "
730 "does not match client)");
734 case HX509_SAN_TYPE_PKINIT
:
735 if (strcmp(cprinc
, s
) != 0) {
736 kdc_audit_addreason((kdc_request_t
)reqctx
,
737 "Requested extensions rejected by "
738 "default policy (PKINIT SAN "
739 "does not match client)");
744 kdc_audit_addreason((kdc_request_t
)reqctx
,
745 "Requested extensions rejected by "
746 "default policy (non-default SAN "
752 if (ret
== HX509_NO_ITEM
)
757 for (i
= 0; ret
== 0; i
++) {
762 ret
= hx509_request_get_eku(reqctx
->csr
, i
, &s
);
766 if ((ret
= der_parse_heim_oid(s
, ".", &oid
))) {
769 for (k
= 0; k
< sizeof(eku_whitelist
)/sizeof(eku_whitelist
[0]); k
++) {
770 if (der_heim_oid_cmp(eku_whitelist
[k
], &oid
) == 0)
774 if (k
== sizeof(eku_whitelist
)/sizeof(eku_whitelist
[0])) {
775 kdc_audit_addreason((kdc_request_t
)reqctx
,
776 "Requested EKU rejected by default policy");
780 if (ret
== HX509_NO_ITEM
)
785 memset(&ku_allowed
, 0, sizeof(ku_allowed
));
786 ku_allowed
.digitalSignature
= 1;
787 ku_allowed
.nonRepudiation
= 1;
788 ret
= hx509_request_get_ku(context
->hx509ctx
, reqctx
->csr
, &ku
);
791 if (KeyUsage2int(ku
) != (KeyUsage2int(ku
) & KeyUsage2int(ku_allowed
)))
794 kdc_audit_setkv_bool((kdc_request_t
)reqctx
, "authorized", TRUE
);
803 /* XXX Display error code */
804 kdc_audit_addreason((kdc_request_t
)reqctx
,
805 "Error handling requested extensions");
813 chain_add1_func(hx509_context context
, void *d
, hx509_cert c
)
815 heim_octet_string os
;
816 Certificates
*cs
= d
;
820 ret
= hx509_cert_binary(context
, c
, &os
);
823 ret
= decode_Certificate(os
.data
, os
.length
, &c2
, NULL
);
824 der_free_octet_string(&os
);
827 ret
= add_Certificates(cs
, &c2
);
828 free_Certificate(&c2
);
832 static krb5_error_code
833 encode_cert_and_chain(hx509_context hx509ctx
,
844 ret
= hx509_certs_iter_f(hx509ctx
, certs
, chain_add1_func
, &cs
);
846 ASN1_MALLOC_ENCODE(Certificates
, out
->data
, out
->length
,
848 free_Certificates(&cs
);
853 * Process a request, produce a reply.
857 _kdc_do_kx509(kx509_req_context r
)
859 krb5_error_code ret
= 0;
860 krb5_ticket
*ticket
= NULL
;
861 krb5_flags ap_req_options
;
862 krb5_principal cprincipal
= NULL
;
863 krb5_principal sprincipal
= NULL
;
864 krb5_keytab id
= NULL
;
866 hx509_certs certs
= NULL
;
869 r
->csr_plus
.csr
.data
= NULL
;
870 r
->csr_plus
.exts
= NULL
;
879 * In order to support authenticated error messages we defer checking
880 * whether the kx509 service is enabled until after accepting the AP-REQ.
883 krb5_data_zero(r
->reply
);
884 memset(&rep
, 0, sizeof(rep
));
886 if (r
->req
.authenticator
.length
== 0) {
888 * Unauthenticated kx509 service availability probe.
890 * mk_error_response() will check whether the service is enabled and
891 * possibly change the error code and message.
894 kdc_audit_addkv((kdc_request_t
)r
, 0, "probe", "unauthenticated");
895 ret
= mk_error_response(r
->context
, r
, 4, 0,
896 "kx509 service is available");
900 /* Authenticate the request (consume the AP-REQ) */
901 ret
= krb5_kt_resolve(r
->context
, "HDBGET:", &id
);
903 const char *msg
= krb5_get_error_message(r
->context
, ret
);
904 ret
= mk_error_response(r
->context
, r
, 1,
905 KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
,
906 "Can't open HDB/keytab for kx509: %s",
908 krb5_free_error_message(r
->context
, msg
);
912 ret
= krb5_rd_req(r
->context
,
914 &r
->req
.authenticator
,
920 ret
= krb5_auth_con_getkey(r
->context
, r
->ac
, &r
->key
);
921 if (ret
== 0 && r
->key
== NULL
)
922 ret
= KRB5KDC_ERR_NULL_KEY
;
924 * Provided we got the session key, errors past this point will be
928 ret
= krb5_ticket_get_client(r
->context
, ticket
, &cprincipal
);
930 /* Optional: check if Ticket is INITIAL */
932 !ticket
->ticket
.flags
.initial
&&
933 !get_bool_param(r
->context
, TRUE
,
934 krb5_principal_get_realm(r
->context
, cprincipal
),
935 "require_initial_kca_tickets")) {
936 ret
= mk_error_response(r
->context
, r
, 4, KRB5KDC_ERR_POLICY
,
937 "Client used non-INITIAL tickets, but kx509 "
938 "service is configured to require INITIAL "
944 ret
= krb5_unparse_name(r
->context
, cprincipal
, &r
->cname
);
946 /* Check that the service name is a valid kx509 service name */
948 ret
= krb5_ticket_get_server(r
->context
, ticket
, &sprincipal
);
950 r
->realm
= krb5_principal_get_realm(r
->context
, sprincipal
);
952 ret
= krb5_unparse_name(r
->context
, sprincipal
, &r
->sname
);
954 ret
= kdc_kx509_verify_service_principal(r
->context
, r
, sprincipal
);
956 ret
= mk_error_response(r
->context
, r
, 4, ret
,
957 "kx509 client used incorrect service name");
961 /* Authenticate the rest of the request */
962 ret
= verify_req_hash(r
->context
, &r
->req
, r
->key
);
964 ret
= mk_error_response(r
->context
, r
, 4, ret
,
965 "Incorrect request HMAC on kx509 request");
969 if (r
->req
.pk_key
.length
== 0) {
971 * The request is an authenticated kx509 service availability probe.
973 * mk_error_response() will check whether the service is enabled and
974 * possibly change the error code and message.
977 kdc_audit_addkv((kdc_request_t
)r
, 0, "probe", "authenticated");
978 ret
= mk_error_response(r
->context
, r
, 4, 0,
979 "kx509 authenticated probe request");
983 /* Extract and parse CSR or a DER-encoded RSA public key */
984 ret
= get_csr(r
->context
, r
);
986 const char *msg
= krb5_get_error_message(r
->context
, ret
);
987 ret
= mk_error_response(r
->context
, r
, 3, ret
,
988 "Failed to parse CSR: %s", msg
);
989 krb5_free_error_message(r
->context
, msg
);
993 /* Authorize the request */
994 ret
= check_authz(r
->context
, r
, cprincipal
);
996 const char *msg
= krb5_get_error_message(r
->context
, ret
);
997 ret
= mk_error_response(r
->context
, r
, 3, ret
,
998 "Rejected by policy: %s", msg
);
999 krb5_free_error_message(r
->context
, msg
);
1003 /* Issue the certificate */
1005 ALLOC(rep
.certificate
);
1006 if (rep
.certificate
== NULL
|| rep
.hash
== NULL
) {
1007 ret
= mk_error_response(r
->context
, r
, 0, ENOMEM
,
1008 "Could allocate memory for response");
1012 krb5_data_zero(rep
.hash
);
1013 krb5_data_zero(rep
.certificate
);
1014 krb5_ticket_get_times(r
->context
, ticket
, &r
->ticket_times
);
1015 ret
= kdc_issue_certificate(r
->context
, r
->config
->app
, r
->logf
, r
->csr
,
1016 cprincipal
, &r
->ticket_times
, 0 /*req_life*/,
1017 r
->send_chain
, &certs
);
1020 const char *msg
= krb5_get_error_message(r
->context
, ret
);
1022 if (ret
== KRB5KDC_ERR_POLICY
)
1023 level
= 4; /* _kdc_audit_trail() logs at level 3 */
1024 ret
= mk_error_response(r
->context
, r
, level
, ret
,
1025 "Certificate isuance failed: %s", msg
);
1026 krb5_free_error_message(r
->context
, msg
);
1030 ret
= encode_cert_and_chain(r
->context
->hx509ctx
, certs
, rep
.certificate
);
1032 const char *msg
= krb5_get_error_message(r
->context
, ret
);
1033 ret
= mk_error_response(r
->context
, r
, 1, ret
,
1034 "Could not encode certificate and chain: %s",
1036 krb5_free_error_message(r
->context
, msg
);
1040 /* Authenticate the response */
1041 ret
= calculate_reply_hash(r
->context
, r
->key
, &rep
);
1043 ret
= mk_error_response(r
->context
, r
, 1, ret
,
1044 "Failed to compute response HMAC");
1048 /* Encode and output reply */
1049 ret
= encode_reply(r
->context
, r
, &rep
);
1051 /* Can't send an error message either in this case, surely */
1052 kdc_audit_addreason((kdc_request_t
)r
, "Could not encode response");
1055 hx509_certs_free(&certs
);
1056 if (ret
== 0 && !is_probe
)
1057 kdc_audit_setkv_bool((kdc_request_t
)r
, "cert_issued", TRUE
);
1059 kdc_audit_setkv_bool((kdc_request_t
)r
, "cert_issued", FALSE
);
1061 krb5_auth_con_free(r
->context
, r
->ac
);
1063 krb5_free_ticket(r
->context
, ticket
);
1065 krb5_kt_close(r
->context
, id
);
1067 krb5_free_principal(r
->context
, sprincipal
);
1069 krb5_free_principal(r
->context
, cprincipal
);
1071 krb5_free_keyblock (r
->context
, r
->key
);
1072 hx509_request_free(&r
->csr
);
1073 free_Kx509CSRPlus(&r
->csr_plus
);
1074 free_Kx509Response(&rep
);
1075 free_Kx509Request(&r
->req
);