kdc: Modernize kx509 logging too

This commit is contained in:
Nicolas Williams
2019-12-11 11:44:26 -06:00
parent 608c2876d4
commit 1d5062b167
9 changed files with 329 additions and 233 deletions

View File

@@ -219,7 +219,7 @@ characterize(krb5_context context,
*/
static const krb5_config_binding *
get_cf(krb5_context context,
const char *toplevel,
krb5_kdc_configuration *config,
hx509_request req,
krb5_principal cprinc)
{
@@ -236,6 +236,7 @@ get_cf(krb5_context context,
size_t nsans = 0;
if (ncomp == 0) {
kdc_log(context, config, 5, "Client principal has no components!");
krb5_set_error_message(context, ENOTSUP,
"Client principal has no components!");
return NULL;
@@ -243,8 +244,8 @@ get_cf(krb5_context context,
if ((ret = count_sans(req, &nsans)) ||
(certtype = characterize(context, cprinc, req)) == CERT_NOTSUP) {
krb5_set_error_message(context, ret,
"Could not characterize CSR");
kdc_log(context, config, 5, "Could not characterize CSR");
krb5_set_error_message(context, ret, "Could not characterize CSR");
return NULL;
}
@@ -274,19 +275,26 @@ get_cf(krb5_context context,
}
}
if (strcmp(toplevel, "kdc") == 0)
cf = krb5_config_get_list(context, NULL, toplevel, "realms", realm,
if (strcmp(config->app, "kdc") == 0)
cf = krb5_config_get_list(context, NULL, config->app, "realms", realm,
"kx509", label, svc, NULL);
else
cf = krb5_config_get_list(context, NULL, toplevel, "realms", realm,
cf = krb5_config_get_list(context, NULL, config->app, "realms", realm,
label, svc, NULL);
if (cf == NULL)
krb5_set_error_message(context, ENOTSUP,
if (cf == NULL) {
kdc_log(context, config, 3,
"No %s configuration for %s %s certificates [%s] realm "
"-> %s -> kx509 -> %s%s%s",
strcmp(toplevel, "bx509") == 0 ? "bx509" : "kx509",
def, label, toplevel, realm, label,
strcmp(config->app, "bx509") == 0 ? "bx509" : "kx509",
def, label, config->app, realm, label,
svc ? " -> " : "", svc ? svc : "");
krb5_set_error_message(context, KRB5KDC_ERR_POLICY,
"No %s configuration for %s %s certificates [%s] realm "
"-> %s -> kx509 -> %s%s%s",
strcmp(config->app, "bx509") == 0 ? "bx509" : "kx509",
def, label, config->app, realm, label,
svc ? " -> " : "", svc ? svc : "");
}
return cf;
}
@@ -304,6 +312,7 @@ get_cf(krb5_context context,
*/
static krb5_error_code
set_template(krb5_context context,
krb5_kdc_configuration *config,
const krb5_config_binding *cf,
hx509_ca_tbs tbs)
{
@@ -329,6 +338,9 @@ set_template(krb5_context context,
ret = hx509_get_one_cert(context->hx509ctx, certs, &template);
hx509_certs_free(&certs);
if (ret) {
kdc_log(context, config, 1,
"Failed to load certificate template from %s",
cert_template);
krb5_set_error_message(context, KRB5KDC_ERR_POLICY,
"Failed to load certificate template from "
"%s", cert_template);
@@ -406,6 +418,7 @@ set_template(krb5_context context,
*/
static krb5_error_code
set_tbs(krb5_context context,
krb5_kdc_configuration *config,
const krb5_config_binding *cf,
hx509_request req,
krb5_principal cprinc,
@@ -438,7 +451,7 @@ set_tbs(krb5_context context,
/* Populate requested certificate extensions from CSR/CSRPlus if allowed */
ret = hx509_ca_tbs_set_from_csr(context->hx509ctx, tbs, req);
if (ret == 0)
ret = set_template(context, cf, tbs);
ret = set_template(context, config, cf, tbs);
/*
* Optionally add PKINIT SAN.
@@ -520,17 +533,23 @@ set_tbs(krb5_context context,
}
}
} else {
kdc_log(context, config, 5, "kx509/bx509 client %s has too many "
"components!", princ);
krb5_set_error_message(context, ret = KRB5KDC_ERR_POLICY,
"kx509/bx509 client %s has too many "
"components!", princ);
}
out:
if (ret == ENOMEM)
goto enomem;
krb5_xfree(princ_no_realm);
krb5_xfree(princ);
return ret;
enomem:
kdc_log(context, config, 0,
"Could not set up TBSCertificate: Out of memory");
ret = krb5_enomem(context);
goto out;
}
@@ -572,7 +591,7 @@ tbs_set_times(krb5_context context,
*/
krb5_error_code
kdc_issue_certificate(krb5_context context,
const krb5_kdc_configuration *config,
krb5_kdc_configuration *config,
hx509_request req,
krb5_principal cprinc,
krb5_times *auth_times,
@@ -596,21 +615,25 @@ kdc_issue_certificate(krb5_context context,
hx509_request_authorize_ku(req, ku);
/* Get configuration */
if ((cf = get_cf(context, config->app, req, cprinc)) == NULL)
if ((cf = get_cf(context, config, req, cprinc)) == NULL)
return KRB5KDC_ERR_POLICY;
if ((ca = krb5_config_get_string(context, cf, "ca", NULL)) == NULL) {
kdc_log(context, config, 3, "No kx509 CA issuer credential specified");
krb5_set_error_message(context, ret = KRB5KDC_ERR_POLICY,
"No kx509 CA issuer credential specified");
return ret;
}
ret = hx509_ca_tbs_init(context->hx509ctx, &tbs);
if (ret)
if (ret) {
kdc_log(context, config, 0,
"Failed to create certificate: Out of memory");
return ret;
}
/* Lookup a template and set things in `env' and `tbs' as appropriate */
if (ret == 0)
ret = set_tbs(context, cf, req, cprinc, &env, tbs);
ret = set_tbs(context, config, cf, req, cprinc, &env, tbs);
/* Populate generic template "env" variables */
@@ -622,10 +645,13 @@ kdc_issue_certificate(krb5_context context,
* issue a certificate now.
*/
if (ret == 0 && hx509_name_is_null_p(hx509_ca_tbs_get_name(tbs)) &&
!has_sans(req))
!has_sans(req)) {
kdc_log(context, config, 3,
"Not issuing certificate because it would have no names");
krb5_set_error_message(context, ret = KRB5KDC_ERR_POLICY,
"Not issuing certificate because it "
"would have no names");
}
if (ret)
goto out;
@@ -646,7 +672,10 @@ kdc_issue_certificate(krb5_context context,
ret = hx509_certs_init(context->hx509ctx, ca, 0, NULL, &certs);
if (ret) {
krb5_set_error_message(context, ret, "Failed to load CA %s", ca);
kdc_log(context, config, 1,
"Failed to load CA certificate and private key %s", ca);
krb5_set_error_message(context, ret, "Failed to load CA "
"certificate and private key %s", ca);
goto out;
}
ret = hx509_query_alloc(context->hx509ctx, &q);
@@ -662,7 +691,11 @@ kdc_issue_certificate(krb5_context context,
hx509_query_free(context->hx509ctx, q);
hx509_certs_free(&certs);
if (ret) {
krb5_set_error_message(context, ret, "Failed to find a CA in %s", ca);
kdc_log(context, config, 1,
"Failed to find a CA certificate in %s", ca);
krb5_set_error_message(context, ret,
"Failed to find a CA certificate in %s",
ca);
goto out;
}
}

View File

@@ -93,12 +93,8 @@
#include <parse_units.h>
#include <krb5.h>
#include <krb5_locl.h>
#ifdef DIGEST
#include <digest_asn1.h>
#endif
#ifdef KX509
#include <kx509_asn1.h>
#endif
#include <hdb.h>
#include <hdb_err.h>
#include <der.h>

View File

@@ -43,6 +43,7 @@
#include <hdb.h>
#include <krb5.h>
#include <kx509_asn1.h>
enum krb5_kdc_trpolicy {
TRPOLICY_ALWAYS_CHECK,
@@ -103,6 +104,7 @@ typedef struct krb5_kdc_configuration {
typedef struct kdc_request_desc *kdc_request_t;
typedef struct astgs_request_desc *astgs_request_t;
typedef struct kx509_req_context_desc *kx509_req_context;
struct krb5_kdc_service {
unsigned int flags;

View File

@@ -41,8 +41,6 @@
#include "headers.h"
typedef struct pk_client_params pk_client_params;
struct DigestREQ;
struct Kx509Request;
#include <kdc-private.h>
@@ -117,6 +115,21 @@ struct astgs_request_desc {
KDCFastState fast;
};
typedef struct kx509_req_context_desc {
KDC_REQUEST_DESC_COMMON_ELEMENTS;
struct Kx509Request req;
Kx509CSRPlus csr_plus;
krb5_auth_context ac;
const char *realm; /* XXX Confusion: is this crealm or srealm? */
krb5_keyblock *key;
hx509_request csr;
krb5_times ticket_times;
unsigned int send_chain:1; /* Client expects a full chain */
unsigned int have_csr:1; /* Client sent a CSR */
} *kx509_req_context;
extern sig_atomic_t exit_flag;
extern size_t max_request_udp;
extern size_t max_request_tcp;

View File

@@ -100,31 +100,14 @@
static const unsigned char version_2_0[4] = {0 , 0, 2, 0};
typedef struct kx509_req_context {
krb5_kdc_configuration *config;
const struct Kx509Request *req;
Kx509CSRPlus csr_plus;
krb5_auth_context ac;
const char *realm; /* XXX Confusion: is this crealm or srealm? */
char *sname;
char *cname;
struct sockaddr *addr;
const char *from;
krb5_keyblock *key;
hx509_request csr;
krb5_data *reply;
krb5_times ticket_times;
unsigned int send_chain:1; /* Client expects a full chain */
unsigned int have_csr:1; /* Client sent a CSR */
} *kx509_req_context;
/*
* Taste the request to see if it's a kx509 request.
*/
krb5_error_code
_kdc_try_kx509_request(void *ptr, size_t len, struct Kx509Request *req)
_kdc_try_kx509_request(kx509_req_context r)
{
const unsigned char *p = (const void *)(uintptr_t)ptr;
const unsigned char *p = (const void *)(uintptr_t)r->request.data;
size_t len = r->request.length;
size_t sz;
if (len < sizeof(version_2_0))
@@ -135,7 +118,8 @@ _kdc_try_kx509_request(void *ptr, size_t len, struct Kx509Request *req)
len -= sizeof(version_2_0);
if (len == 0)
return -1;
return decode_Kx509Request(p, len, req, &sz);
memset(&r->req, 0, sizeof(r->req));
return decode_Kx509Request(p, len, &r->req, &sz);
}
static krb5_boolean
@@ -194,37 +178,6 @@ verify_req_hash(krb5_context context,
return 0;
}
/* Wrapper around kdc_log() that adds contextual information */
static void
kx509_log(krb5_context context,
kx509_req_context reqctx,
int level,
const char *fmt,
...)
{
va_list ap;
char *msg;
va_start(ap, fmt);
if (vasprintf(&msg, fmt, ap) == -1 || msg == NULL) {
kdc_log(context, reqctx->config, level,
"Out of memory while formatting log message");
va_end(ap);
va_start(ap, fmt);
kdc_vlog(context, reqctx->config, level, fmt, ap);
va_end(ap);
return;
}
va_end(ap);
kdc_log(context, reqctx->config, level,
"kx509 %s (from %s for %s, service %s)", msg,
reqctx->from ? reqctx->from : "<unknown>",
reqctx->cname ? reqctx->cname : "<unknown-client-principal>",
reqctx->sname ? reqctx->sname : "<unknown-service-principal>");
free(msg);
}
/*
* Set the HMAC in the response.
*/
@@ -243,8 +196,7 @@ calculate_reply_hash(krb5_context context,
ret = krb5_data_alloc(rep->hash, HMAC_size(&ctx));
if (ret) {
HMAC_CTX_cleanup(&ctx);
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
return krb5_enomem(context);
}
HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
@@ -347,7 +299,8 @@ kdc_kx509_verify_service_principal(krb5_context context,
KRB5_TGS_NAME) == 0) {
const char *r = krb5_principal_get_comp_string(context, sprincipal, 1);
if ((ret = is_local_realm(context, reqctx, r)))
kx509_log(context, reqctx, 2, "client used wrong krbtgt for kx509");
_kdc_audit_addreason((kdc_request_t)reqctx,
"Client used wrong krbtgt for kx509");
goto out;
}
@@ -355,8 +308,9 @@ kdc_kx509_verify_service_principal(krb5_context context,
ret = gethostname(localhost, sizeof(localhost) - 1);
if (ret != 0) {
ret = errno;
krb5_set_error_message(context, ret,
N_("Failed to get local hostname", ""));
kdc_log(context, reqctx->config, 0, "Failed to get local hostname");
_kdc_audit_addreason((kdc_request_t)reqctx,
"Failed to get local hostname");
return ret;
}
localhost[sizeof(localhost) - 1] = '\0';
@@ -375,8 +329,8 @@ err:
goto out;
ret = KRB5KDC_ERR_SERVER_NOMATCH;
kx509_log(context, reqctx, 2, "client used wrong kx509 service principal "
"(expected %s)", expected);
_kdc_audit_addreason((kdc_request_t)reqctx, "Client used wrong kx509 "
"service principal (expected %s)", expected);
out:
krb5_xfree(expected);
@@ -397,10 +351,8 @@ encode_reply(krb5_context context,
reqctx->reply->data = NULL;
reqctx->reply->length = 0;
ASN1_MALLOC_ENCODE(Kx509Response, data.data, data.length, r, &size, ret);
if (ret) {
kdc_log(context, reqctx->config, 1, "Failed to encode kx509 reply");
if (ret)
return ret;
}
if (size != data.length)
krb5_abortx(context, "ASN1 internal error");
@@ -418,6 +370,7 @@ encode_reply(krb5_context context,
static krb5_error_code
mk_error_response(krb5_context context,
kx509_req_context reqctx,
int level,
int32_t code,
const char *fmt,
...)
@@ -430,6 +383,21 @@ mk_error_response(krb5_context context,
char *freeme1 = NULL;
va_list ap;
if (code != 0) {
/* Log errors where _kdc_audit_trail() is not enough */
if (code == ENOMEM)
level = 0;
if (level < 3) {
va_start(ap, fmt);
kdc_vlog(context, reqctx->config, level, fmt, ap);
va_end(ap);
}
va_start(ap, fmt);
_kdc_audit_vaddreason((kdc_request_t)reqctx, fmt, ap);
va_end(ap);
}
if (!reqctx->config->enable_kx509)
code = KRB5KDC_ERR_POLICY;
@@ -460,8 +428,6 @@ mk_error_response(krb5_context context,
msg = freeme1;
}
kdc_log(context, reqctx->config, 1, "%s", msg);
rep.hash = NULL;
rep.certificate = NULL;
rep.error_code = code;
@@ -569,9 +535,14 @@ update_csr(krb5_context context, kx509_req_context reqctx, Extensions *exts)
free_GeneralNames(&san);
}
}
if (ret)
kx509_log(context, reqctx, 2,
"request has bad desired certificate extensions");
if (ret) {
kdc_log(context, reqctx->config, 1,
"Error handling requested extensions: %s",
krb5_get_error_message(context, ret));
_kdc_audit_addreason((kdc_request_t)reqctx,
"Error handling requested extensions: %s",
krb5_get_error_message(context, ret));
}
return ret;
}
@@ -585,7 +556,7 @@ get_csr(krb5_context context, kx509_req_context reqctx)
{
krb5_error_code ret;
RSAPublicKey rsapkey;
heim_octet_string pk_key = reqctx->req->pk_key;
heim_octet_string pk_key = reqctx->req.pk_key;
size_t size;
ret = decode_Kx509CSRPlus(pk_key.data, pk_key.length, &reqctx->csr_plus,
@@ -597,38 +568,50 @@ get_csr(krb5_context context, kx509_req_context reqctx)
/* Parse CSR */
ret = hx509_request_parse_der(context->hx509ctx, &reqctx->csr_plus.csr,
&reqctx->csr);
if (ret)
kx509_log(context, reqctx, 2, "invalid CSR");
/*
* Handle any additional Certificate Extensions requested out of band
* of the CSR.
*/
if (ret == 0)
return update_csr(context, reqctx, reqctx->csr_plus.exts);
_kdc_audit_addreason((kdc_request_t)reqctx, "Invalid CSR");
return ret;
}
reqctx->send_chain = 0;
reqctx->have_csr = 0;
/* Check if proof of possession is required by configuration */
if (!get_bool_param(context, FALSE, reqctx->realm, "require_csr"))
return mk_error_response(context, reqctx, KX509_STATUS_CLIENT_USE_CSR,
if (!get_bool_param(context, FALSE, reqctx->realm, "require_csr")) {
_kdc_audit_addreason((kdc_request_t)reqctx,
"CSRs required but client did not send one");
krb5_set_error_message(context, KX509_STATUS_CLIENT_USE_CSR,
"CSRs required but kx509 client did not send "
"one");
return KX509_STATUS_CLIENT_USE_CSR;
}
/* Attempt to decode pk_key as RSAPublicKey */
ret = decode_RSAPublicKey(reqctx->req->pk_key.data,
reqctx->req->pk_key.length,
ret = decode_RSAPublicKey(reqctx->req.pk_key.data,
reqctx->req.pk_key.length,
&rsapkey, &size);
free_RSAPublicKey(&rsapkey);
if (ret == 0 && size == reqctx->req->pk_key.length)
if (ret == 0 && size == reqctx->req.pk_key.length)
return make_csr(context, reqctx, &pk_key); /* Make pretend CSR */
/* Not an RSAPublicKey or garbage follows it */
if (ret == 0)
kx509_log(context, reqctx, 2, "request has garbage after key");
return mk_error_response(context, reqctx, KRB5KDC_ERR_NULL_KEY,
if (ret == 0) {
ret = KRB5KDC_ERR_NULL_KEY;
_kdc_audit_addreason((kdc_request_t)reqctx,
"Request has garbage after key");
krb5_set_error_message(context, ret, "Request has garbage after key");
return ret;
}
_kdc_audit_addreason((kdc_request_t)reqctx,
"Could not decode CSR or RSA subject public key");
krb5_set_error_message(context, ret,
"Could not decode CSR or RSA subject public key");
return ret;
}
/*
@@ -664,6 +647,7 @@ check_authz(krb5_context context,
const char *comp0 = krb5_principal_get_comp_string(context, cprincipal, 0);
const char *comp1 = krb5_principal_get_comp_string(context, cprincipal, 1);
unsigned int ncomp = krb5_principal_get_num_comp(context, cprincipal);
hx509_san_type san_type;
KeyUsage ku, ku_allowed;
size_t i;
const heim_oid *eku_whitelist[] = {
@@ -683,23 +667,46 @@ check_authz(krb5_context context,
return 0;
ret = kdc_authorize_csr(context, reqctx->config, reqctx->csr, cprincipal);
if (ret == 0) {
kx509_log(context, reqctx, 0, "Requested extensions authorized "
"by plugin");
_kdc_audit_addkv((kdc_request_t)reqctx, 0, "authorized", "true");
ret = hx509_request_get_san(reqctx->csr, 0, &san_type, &s);
if (ret == 0) {
const char *san_type_s;
/* This should be an hx509 function... */
switch (san_type) {
case HX509_SAN_TYPE_EMAIL: san_type_s = "rfc822Name"; break;
case HX509_SAN_TYPE_DNSNAME: san_type_s = "dNSName"; break;
case HX509_SAN_TYPE_DN: san_type_s = "DN"; break;
case HX509_SAN_TYPE_REGISTERED_ID: san_type_s = "registeredID"; break;
case HX509_SAN_TYPE_XMPP: san_type_s = "xMPPName"; break;
case HX509_SAN_TYPE_PKINIT: san_type_s = "krb5PrincipalName"; break;
case HX509_SAN_TYPE_MS_UPN: san_type_s = "ms-UPN"; break;
default: san_type_s = "unknown"; break;
}
_kdc_audit_addkv((kdc_request_t)reqctx, 0, "san0_type", "%s",
san_type_s);
_kdc_audit_addkv((kdc_request_t)reqctx, 0, "san0", "%s", s);
free(s);
}
ret = hx509_request_get_eku(reqctx->csr, 0, &s);
if (ret == 0) {
_kdc_audit_addkv((kdc_request_t)reqctx, 0, "eku0", "%s", s);
free(s);
}
return 0;
}
if (ret != KRB5_PLUGIN_NO_HANDLE) {
kx509_log(context, reqctx, 0, "Requested extensions rejected "
"by plugin");
_kdc_audit_addreason((kdc_request_t)reqctx,
"Requested extensions rejected by plugin");
return ret;
}
/* Default authz */
if ((ret = krb5_unparse_name(context, cprincipal, &cprinc)))
return ret;
for (i = 0; ret == 0; i++) {
hx509_san_type san_type;
frees(&s);
ret = hx509_request_get_san(reqctx->csr, i, &san_type, &s);
@@ -710,22 +717,28 @@ check_authz(krb5_context context,
if (ncomp != 2 || strcasecmp(comp1, s) != 0 ||
strchr(s, '.') == NULL ||
!check_authz_svc_ok(context, comp0)) {
kx509_log(context, reqctx, 0, "Requested extensions rejected "
"by default policy (dNSName SAN %s does not match "
"client %s)", s, cprinc);
_kdc_audit_addreason((kdc_request_t)reqctx,
"Requested extensions rejected by "
"default policy (dNSName SAN "
"does not match client)");
goto eacces;
}
break;
case HX509_SAN_TYPE_PKINIT:
if (strcmp(cprinc, s) != 0) {
kx509_log(context, reqctx, 0, "Requested extensions rejected "
"by default policy (PKINIT SAN %s does not match "
"client %s)", s, cprinc);
_kdc_audit_addreason((kdc_request_t)reqctx,
"Requested extensions rejected by "
"default policy (PKINIT SAN "
"does not match client)");
goto eacces;
}
break;
default:
ret = ENOTSUP;
_kdc_audit_addreason((kdc_request_t)reqctx,
"Requested extensions rejected by "
"default policy (non-default SAN "
"requested)");
goto eacces;
}
}
frees(&s);
@@ -753,9 +766,12 @@ check_authz(krb5_context context,
break;
}
der_free_oid(&oid);
if (k == sizeof(eku_whitelist)/sizeof(eku_whitelist[0]))
if (k == sizeof(eku_whitelist)/sizeof(eku_whitelist[0])) {
_kdc_audit_addreason((kdc_request_t)reqctx,
"Requested EKU rejected by default policy");
goto eacces;
}
}
if (ret == HX509_NO_ITEM)
ret = 0;
if (ret)
@@ -770,11 +786,18 @@ check_authz(krb5_context context,
if (KeyUsage2int(ku) != (KeyUsage2int(ku) & KeyUsage2int(ku_allowed)))
goto eacces;
_kdc_audit_addkv((kdc_request_t)reqctx, 0, "authorized", "true");
return 0;
eacces:
ret = EACCES;
goto out2;
out:
/* XXX Display error code */
_kdc_audit_addreason((kdc_request_t)reqctx,
"Error handling requested extensions");
out2:
free(cprinc);
free(s);
return ret;
@@ -825,10 +848,7 @@ encode_cert_and_chain(hx509_context hx509ctx,
*/
krb5_error_code
_kdc_do_kx509(krb5_context context,
krb5_kdc_configuration *config,
const struct Kx509Request *req, krb5_data *reply,
const char *from, struct sockaddr *addr)
_kdc_do_kx509(kx509_req_context r)
{
krb5_error_code ret = 0;
krb5_ticket *ticket = NULL;
@@ -838,33 +858,26 @@ _kdc_do_kx509(krb5_context context,
krb5_keytab id = NULL;
Kx509Response rep;
hx509_certs certs = NULL;
struct kx509_req_context reqctx;
int is_probe = 0;
memset(&reqctx, 0, sizeof(reqctx));
reqctx.csr_plus.csr.data = NULL;
reqctx.csr_plus.exts = NULL;
reqctx.config = config;
reqctx.sname = NULL;
reqctx.cname = NULL;
reqctx.realm = NULL;
reqctx.reply = reply;
reqctx.from = from;
reqctx.addr = addr;
reqctx.key = NULL;
reqctx.csr = NULL;
reqctx.req = req;
reqctx.ac = NULL;
r->csr_plus.csr.data = NULL;
r->csr_plus.exts = NULL;
r->sname = NULL;
r->cname = NULL;
r->realm = NULL;
r->key = NULL;
r->csr = NULL;
r->ac = NULL;
/*
* In order to support authenticated error messages we defer checking
* whether the kx509 service is enabled until after accepting the AP-REQ.
*/
krb5_data_zero(reply);
krb5_data_zero(r->reply);
memset(&rep, 0, sizeof(rep));
if (req->authenticator.length == 0) {
if (r->req.authenticator.length == 0) {
/*
* Unauthenticated kx509 service availability probe.
*
@@ -872,77 +885,79 @@ _kdc_do_kx509(krb5_context context,
* possibly change the error code and message.
*/
is_probe = 1;
kx509_log(context, &reqctx, 4, "unauthenticated probe request");
ret = mk_error_response(context, &reqctx, KRB5KDC_ERR_NULL_KEY,
_kdc_audit_addkv((kdc_request_t)r, 0, "probe", "unauthenticated");
ret = mk_error_response(r->context, r, 4, 0,
"kx509 service is available");
goto out;
}
/* Authenticate the request (consume the AP-REQ) */
ret = krb5_kt_resolve(context, "HDBGET:", &id);
ret = krb5_kt_resolve(r->context, "HDBGET:", &id);
if (ret) {
ret = mk_error_response(context, &reqctx,
ret = mk_error_response(r->context, r, 1,
KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
"Can't open HDB/keytab for kx509");
"Can't open HDB/keytab for kx509: %s",
krb5_get_error_message(r->context, ret));
goto out;
}
ret = krb5_rd_req(context,
&reqctx.ac,
&req->authenticator,
ret = krb5_rd_req(r->context,
&r->ac,
&r->req.authenticator,
NULL,
id,
&ap_req_options,
&ticket);
if (ret == 0)
ret = krb5_auth_con_getkey(context, reqctx.ac, &reqctx.key);
if (ret == 0 && reqctx.key == NULL)
ret = krb5_auth_con_getkey(r->context, r->ac, &r->key);
if (ret == 0 && r->key == NULL)
ret = KRB5KDC_ERR_NULL_KEY;
/*
* Provided we got the session key, errors past this point will be
* authenticated.
*/
if (ret == 0)
ret = krb5_ticket_get_client(context, ticket, &cprincipal);
ret = krb5_ticket_get_client(r->context, ticket, &cprincipal);
/* Optional: check if Ticket is INITIAL */
if (ret == 0 &&
!ticket->ticket.flags.initial &&
!get_bool_param(context, TRUE,
krb5_principal_get_realm(context, cprincipal),
!get_bool_param(r->context, TRUE,
krb5_principal_get_realm(r->context, cprincipal),
"require_initial_kca_tickets")) {
ret = mk_error_response(context, &reqctx, KRB5KDC_ERR_POLICY,
"client used non-INITIAL tickets, but kx509"
"kx509 service is configured to require "
"INITIAL tickets");
ret = mk_error_response(r->context, r, 4, KRB5KDC_ERR_POLICY,
"Client used non-INITIAL tickets, but kx509 "
"service is configured to require INITIAL "
"tickets");
goto out;
}
ret = krb5_unparse_name(context, cprincipal, &reqctx.cname);
ret = krb5_unparse_name(r->context, cprincipal, &r->cname);
/* Check that the service name is a valid kx509 service name */
if (ret == 0)
ret = krb5_ticket_get_server(context, ticket, &sprincipal);
ret = krb5_ticket_get_server(r->context, ticket, &sprincipal);
if (ret == 0)
reqctx.realm = krb5_principal_get_realm(context, sprincipal);
r->realm = krb5_principal_get_realm(r->context, sprincipal);
if (ret == 0)
ret = krb5_unparse_name(context, sprincipal, &reqctx.sname);
ret = krb5_unparse_name(r->context, sprincipal, &r->sname);
if (ret == 0)
ret = kdc_kx509_verify_service_principal(context, &reqctx, sprincipal);
ret = kdc_kx509_verify_service_principal(r->context, r, sprincipal);
if (ret) {
mk_error_response(context, &reqctx, ret,
"client used incorrect service name");
ret = mk_error_response(r->context, r, 4, ret,
"kx509 client used incorrect service name");
goto out;
}
/* Authenticate the rest of the request */
ret = verify_req_hash(context, req, reqctx.key);
ret = verify_req_hash(r->context, &r->req, r->key);
if (ret) {
mk_error_response(context, &reqctx, ret, "Incorrect request HMAC");
ret = mk_error_response(r->context, r, 4, ret,
"Incorrect request HMAC on kx509 request");
goto out;
}
if (req->pk_key.length == 0) {
if (r->req.pk_key.length == 0) {
/*
* The request is an authenticated kx509 service availability probe.
*
@@ -950,20 +965,27 @@ _kdc_do_kx509(krb5_context context,
* possibly change the error code and message.
*/
is_probe = 1;
ret = mk_error_response(context, &reqctx, 0,
_kdc_audit_addkv((kdc_request_t)r, 0, "probe", "authenticated");
ret = mk_error_response(r->context, r, 4, 0,
"kx509 authenticated probe request");
goto out;
}
/* Extract and parse CSR or a DER-encoded RSA public key */
ret = get_csr(context, &reqctx);
if (ret)
ret = get_csr(r->context, r);
if (ret) {
ret = mk_error_response(r->context, r, 3, ret,
"Failed to parse CSR: %s",
krb5_get_error_message(r->context, ret));
goto out;
}
/* Authorize the request */
ret = check_authz(context, &reqctx, cprincipal);
ret = check_authz(r->context, r, cprincipal);
if (ret) {
ret = mk_error_response(context, &reqctx, ret, "rejected by policy");
ret = mk_error_response(r->context, r, 3, ret,
"Rejected by policy: %s",
krb5_get_error_message(r->context, ret));
goto out;
}
@@ -971,68 +993,71 @@ _kdc_do_kx509(krb5_context context,
ALLOC(rep.hash);
ALLOC(rep.certificate);
if (rep.certificate == NULL || rep.hash == NULL) {
ret = mk_error_response(context, &reqctx, ENOMEM,
"could allocate memory for response");
ret = mk_error_response(r->context, r, 0, ENOMEM,
"Could allocate memory for response");
goto out;
}
krb5_data_zero(rep.hash);
krb5_data_zero(rep.certificate);
krb5_ticket_get_times(context, ticket, &reqctx.ticket_times);
ret = kdc_issue_certificate(context, reqctx.config, reqctx.csr, cprincipal,
&reqctx.ticket_times, reqctx.send_chain,
&certs);
krb5_ticket_get_times(r->context, ticket, &r->ticket_times);
ret = kdc_issue_certificate(r->context, r->config, r->csr, cprincipal,
&r->ticket_times, r->send_chain, &certs);
if (ret) {
mk_error_response(context, &reqctx, ret, "Certificate isuance failed");
int level = 1;
if (ret == KRB5KDC_ERR_POLICY)
level = 4; /* _kdc_audit_trail() logs at level 3 */
ret = mk_error_response(r->context, r, level, ret,
"Certificate isuance failed: %s",
krb5_get_error_message(r->context, ret));
goto out;
}
ret = encode_cert_and_chain(context->hx509ctx, certs, rep.certificate);
ret = encode_cert_and_chain(r->context->hx509ctx, certs, rep.certificate);
if (ret) {
mk_error_response(context, &reqctx, ret,
"Could not encode certificate and chain");
ret = mk_error_response(r->context, r, 1, ret,
"Could not encode certificate and chain: %s",
krb5_get_error_message(r->context, ret));
goto out;
}
/* Authenticate the response */
ret = calculate_reply_hash(context, reqctx.key, &rep);
ret = calculate_reply_hash(r->context, r->key, &rep);
if (ret) {
mk_error_response(context, &reqctx, ret,
ret = mk_error_response(r->context, r, 1, ret,
"Failed to compute response HMAC");
goto out;
}
/* Encode and output reply */
ret = encode_reply(context, &reqctx, &rep);
ret = encode_reply(r->context, r, &rep);
if (ret)
/* Can't send an error message either in this case, surely */
kx509_log(context, &reqctx, 1, "Could not encode response");
_kdc_audit_addreason((kdc_request_t)r, "Could not encode response");
out:
hx509_certs_free(&certs);
if (ret == 0 && !is_probe)
kx509_log(context, &reqctx, 3, "Issued certificate");
_kdc_audit_addkv((kdc_request_t)r, 0, "cert_issued", "true");
else
kx509_log(context, &reqctx, 2, "Did not issue certificate");
if (reqctx.ac)
krb5_auth_con_free(context, reqctx.ac);
_kdc_audit_addkv((kdc_request_t)r, 0, "cert_issued", "false");
if (r->ac)
krb5_auth_con_free(r->context, r->ac);
if (ticket)
krb5_free_ticket(context, ticket);
krb5_free_ticket(r->context, ticket);
if (id)
krb5_kt_close(context, id);
krb5_kt_close(r->context, id);
if (sprincipal)
krb5_free_principal(context, sprincipal);
krb5_free_principal(r->context, sprincipal);
if (cprincipal)
krb5_free_principal(context, cprincipal);
if (reqctx.key)
krb5_free_keyblock (context, reqctx.key);
if (reqctx.sname)
free(reqctx.sname);
if (reqctx.cname)
free(reqctx.cname);
hx509_request_free(&reqctx.csr);
free_Kx509CSRPlus(&reqctx.csr_plus);
krb5_free_principal(r->context, cprincipal);
if (r->key)
krb5_free_keyblock (r->context, r->key);
hx509_request_free(&r->csr);
free_Kx509CSRPlus(&r->csr_plus);
free_Kx509Response(&rep);
free_Kx509Request(&r->req);
return ret;
}

View File

@@ -18,4 +18,6 @@ EXPORTS
krb5_kdc_pk_initialize
_kdc_audit_addkv
_kdc_audit_addreason
_kdc_audit_vaddkv
_kdc_audit_vaddreason
_kdc_audit_trail

View File

@@ -87,24 +87,44 @@ fmtkv(int flags, const char *k, const char *fmt, va_list ap)
}
void
_kdc_audit_addreason(kdc_request_t r, const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 2, 3)))
_kdc_audit_vaddreason(kdc_request_t r, const char *fmt, va_list ap)
__attribute__ ((__format__ (__printf__, 2, 0)))
{
va_list ap;
heim_string_t str;
va_start(ap, fmt);
str = fmtkv(KDC_AUDIT_VISLAST, "reason", fmt, ap);
va_end(ap);
if (!str) {
kdc_log(r->context, r->config, 1, "failed to add reason");
return;
}
kdc_log(r->context, r->config, 7, "_kdc_audit_addkv(): adding "
"kv pair %s", heim_string_get_utf8(str));
kdc_log(r->context, r->config, 7, "_kdc_audit_addreason(): adding "
"reason %s", heim_string_get_utf8(str));
if (r->reason) {
heim_string_t str2;
str2 = heim_string_create_with_format("%s: %s",
heim_string_get_utf8(str),
heim_string_get_utf8(r->reason));
if (str2) {
heim_release(r->reason);
heim_release(str);
r->reason = str;
} /* else the earlier reason is likely better than the newer one */
return;
}
r->reason = str;
}
void
_kdc_audit_addreason(kdc_request_t r, const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 2, 3)))
{
va_list ap;
va_start(ap, fmt);
_kdc_audit_vaddreason(r, fmt, ap);
va_end(ap);
}
/*
@@ -114,16 +134,13 @@ _kdc_audit_addreason(kdc_request_t r, const char *fmt, ...)
*/
void
_kdc_audit_addkv(kdc_request_t r, int flags, const char *k,
const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 4, 5)))
_kdc_audit_vaddkv(kdc_request_t r, int flags, const char *k,
const char *fmt, va_list ap)
__attribute__ ((__format__ (__printf__, 4, 0)))
{
va_list ap;
heim_string_t str;
va_start(ap, fmt);
str = fmtkv(flags, k, fmt, ap);
va_end(ap);
if (!str) {
kdc_log(r->context, r->config, 1, "failed to add kv pair");
return;
@@ -135,6 +152,18 @@ _kdc_audit_addkv(kdc_request_t r, int flags, const char *k,
heim_release(str);
}
void
_kdc_audit_addkv(kdc_request_t r, int flags, const char *k,
const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 4, 5)))
{
va_list ap;
va_start(ap, fmt);
_kdc_audit_vaddkv(r, flags, k, fmt, ap);
va_end(ap);
}
void
_kdc_audit_addkv_timediff(kdc_request_t r, const char *k,
const struct timeval *start,
@@ -347,27 +376,21 @@ kdc_digest(kdc_request_t *rptr, int *claim)
static krb5_error_code
kdc_kx509(kdc_request_t *rptr, int *claim)
{
kdc_request_t r = *rptr;
krb5_context context = r->context;
krb5_kdc_configuration *config = r->config;
krb5_data *req_buffer = &r->request;
krb5_data *reply = r->reply;
const char *from = r->from;
struct sockaddr *addr = r->addr;
Kx509Request kx509req;
kx509_req_context r;
krb5_error_code ret;
ret = _kdc_try_kx509_request(req_buffer->data, req_buffer->length,
&kx509req);
/* We must free things in the extensions */
EXTEND_REQUEST_T(*rptr, r);
ret = _kdc_try_kx509_request(r);
if (ret)
return ret;
r->use_request_t = 0;
r->use_request_t = 1;
r->reqtype = "KX509";
*claim = 1;
ret = _kdc_do_kx509(context, config, &kx509req, reply, from, addr);
free_Kx509Request(&kx509req);
return ret;
return _kdc_do_kx509(r); /* Must clean up the req struct extensions */
}
#endif

View File

@@ -22,6 +22,8 @@ HEIMDAL_KDC_1.0 {
krb5_kdc_pk_initialize;
_kdc_audit_addkv;
_kdc_audit_addreason;
_kdc_audit_vaddkv;
_kdc_audit_vaddreason;
_kdc_audit_trail;
# needed for digest-service

View File

@@ -272,12 +272,12 @@ $hxtool acert --expr="%{certificate.subject} == \"OU=Users,CN=KDC,$DCs\"" \
if ! which curl; then
echo "curl is not available -- not testing bx509d"
exit 0
exit 77
fi
if ! test -x ${objdir}/../../kdc/bx509d; then
echo "Configured w/o libmicrohttpd -- not testing bx509d"
exit 0
exit 77
fi
echo "Creating database"