diff --git a/kdc/bx509d.8 b/kdc/bx509d.8 index 18970b169..faf915a63 100644 --- a/kdc/bx509d.8 +++ b/kdc/bx509d.8 @@ -113,6 +113,45 @@ Uses a thread per-client instead of as many threads as there are CPUs. .Xc verbose .El +.Sh API +This service provides an HTTP-based Certification Authority (CA). +The protocol consists of a +.Ar GET +of +.Ar /bx509 +with the base-63 encoding of a DER encoding of a PKCS#10 +.Ar CertificationRequest +(Certificate Signing Request, or CSR) in a +.Ar csr +required query parameter. +In a successful query, the response body will contain a PEM +encoded end entity certificate and certification chain. +.Pp +Authentication is required. +Unauthenticated requests will elicit a 401 response. +.Pp +Subject Alternative Names (SANs) and Extended Key Usage values +may be requested, both in-band in the CSR as a requested +extensions attribute, and/or via optional query parameters. +.Pp +Supported query parameters (separated by ampersands) +.Bl -tag -width Ds -offset indent +.It Li csr = Va +.It Li dNSName = Va +.It Li rfc822Name = Va +.It Li xMPPName = Va +.It Li krb5PrincipalName = Va +.It Li ms-upn = Va +.It Li eku = Va +.It Li lifetime = Va +.El +.Pp +More than one name or EKU may be requested. +.Pp +Certificate lifetimes are expressed as a decimal number and +an optional unit (which defaults to +.Dq day +). .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev KRB5_CONFIG @@ -122,6 +161,8 @@ the default being .Pa /etc/krb5.conf . .El .Sh FILES +Configuration parameters are specified in +.Ar /etc/krb5.conf . .Bl -tag -width Ds .It Pa /etc/krb5.conf .El diff --git a/kdc/bx509d.c b/kdc/bx509d.c index 777efc14e..f0ec639d5 100644 --- a/kdc/bx509d.c +++ b/kdc/bx509d.c @@ -117,6 +117,7 @@ typedef struct bx509_request_desc { struct MHD_Connection *connection; krb5_times token_times; + time_t req_life; hx509_request req; const char *target; const char *redir; @@ -667,6 +668,8 @@ bx509_param_cb(void *d, } else if (strcmp(key, "csr") == 0 && val) { heim_audit_addkv((heim_svc_req_desc)a->r, 0, "requested_csr", "true"); a->ret = 0; /* Handled upstairs */ + } else if (strcmp(key, "lifetime") == 0 && val) { + a->r->req_life = parse_time(val, "day"); } else { /* Produce error for unknown params */ heim_audit_addkv((heim_svc_req_desc)a->r, 0, "requested_unknown", "true"); @@ -798,7 +801,8 @@ do_CA(struct bx509_request_desc *r, const char *csr) /* Issue the certificate */ ret = kdc_issue_certificate(r->context, "bx509", logfac, r->req, p, - &r->token_times, 1 /* send_chain */, &certs); + &r->token_times, r->req_life, + 1 /* send_chain */, &certs); krb5_free_principal(r->context, p); if (ret) { if (ret == KRB5KDC_ERR_POLICY || ret == EACCES) @@ -872,6 +876,7 @@ set_req_desc(struct MHD_Connection *connection, r->cname = NULL; r->addr = NULL; r->req = NULL; + r->req_life = 0; r->kv = heim_array_create(); ci = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); @@ -1268,8 +1273,8 @@ bnegotiate_do_CA(struct bx509_request_desc *r) /* Issue the certificate */ if (ret == 0) ret = kdc_issue_certificate(r->context, "bx509", logfac, req, p, - &r->token_times, 1 /* send_chain */, - &certs); + &r->token_times, 0, + 1 /* send_chain */, &certs); krb5_free_principal(r->context, p); hx509_request_free(&req); p = NULL; diff --git a/kdc/ca.c b/kdc/ca.c index af7848c3c..0d92ca7fc 100644 --- a/kdc/ca.c +++ b/kdc/ca.c @@ -104,6 +104,7 @@ kdc_issue_certificate(krb5_context context, hx509_request req, krb5_principal cprinc, krb5_times *auth_times, + time_t req_life, int send_chain, hx509_certs *out) { @@ -122,7 +123,9 @@ kdc_issue_certificate(krb5_context context, (const heim_config_binding *)cf, logf, req, &cprinc2, auth_times->starttime, - auth_times->endtime, send_chain, + auth_times->endtime, + req_life, + send_chain, out); if (ret == EACCES) ret = KRB5KDC_ERR_POLICY; diff --git a/kdc/kx509.c b/kdc/kx509.c index 5e86bfc83..bc3ca9dec 100644 --- a/kdc/kx509.c +++ b/kdc/kx509.c @@ -1005,8 +1005,8 @@ _kdc_do_kx509(kx509_req_context r) krb5_data_zero(rep.certificate); krb5_ticket_get_times(r->context, ticket, &r->ticket_times); ret = kdc_issue_certificate(r->context, r->config->app, r->logf, r->csr, - cprincipal, &r->ticket_times, r->send_chain, - &certs); + cprincipal, &r->ticket_times, 0 /*req_life*/, + r->send_chain, &certs); if (ret) { int level = 1; const char *msg = krb5_get_error_message(r->context, ret); diff --git a/kdc/test_kdc_ca.c b/kdc/test_kdc_ca.c index cbf936716..12b873c03 100644 --- a/kdc/test_kdc_ca.c +++ b/kdc/test_kdc_ca.c @@ -2,12 +2,15 @@ static int authorized_flag; static int help_flag; +static char *lifetime_string; static const char *app_string = "kdc"; static int version_flag; struct getargs args[] = { { "authorized", 'A', arg_flag, &authorized_flag, "Assume CSR is authorized", NULL }, + { "lifetime", 'l', arg_string, &lifetime_string, + "Certificate lifetime desired", "TIME" }, { "help", 'h', arg_flag, &help_flag, "Print usage message", NULL }, { "app", 'a', arg_string, &app_string, @@ -78,6 +81,7 @@ main(int argc, char **argv) hx509_certs certs = NULL; const char *argv0 = argv[0]; const char *out = "MEMORY:junk-it"; + time_t req_life = 0; int optidx = 0; setprogname(argv[0]); @@ -143,8 +147,9 @@ main(int argc, char **argv) memset(&t, 0, sizeof(t)); t.starttime = time(NULL); t.endtime = t.starttime + 3600; - if ((ret = kdc_issue_certificate(context, app_string, logf, req, p, &t, 1, - &certs))) + req_life = lifetime_string ? parse_time(lifetime_string, "day") : 0; + if ((ret = kdc_issue_certificate(context, app_string, logf, req, p, &t, + req_life, 1, &certs))) krb5_err(context, 1, ret, "Certificate issuance failed"); if (argv[2]) diff --git a/lib/hx509/ca.c b/lib/hx509/ca.c index 75c273c01..1c43853b9 100644 --- a/lib/hx509/ca.c +++ b/lib/hx509/ca.c @@ -2835,6 +2835,7 @@ enomem: static heim_error_code tbs_set_times(hx509_context context, const heim_config_binding *cf, + heim_log_facility *logf, time_t starttime, time_t endtime, time_t req_life, @@ -2847,15 +2848,29 @@ tbs_set_times(hx509_context context, time_t clamp = heim_config_get_time_default(context->hcontext, cf, 0, "max_cert_lifetime", NULL); + int allow_more = + heim_config_get_bool_default(context->hcontext, cf, FALSE, + "allow_extra_lifetime", NULL); + + if (!allow_more && fudge && now + fudge > endtime) + allow_more = 1; starttime = starttime ? starttime : now - 5 * 60; if (fudge && now + fudge > endtime) endtime = now + fudge; - if (req_life && req_life < endtime - now) + if (req_life > 0 && req_life < endtime - now) endtime = now + req_life; if (clamp && clamp < endtime - now) endtime = now + clamp; + if (endtime < now) { + heim_log_msg(context->hcontext, logf, 3, NULL, + "Endtime would be in the past"); + hx509_set_error_string(context, 0, ERANGE, + "Endtime would be in the past"); + return ERANGE; + } + hx509_ca_tbs_set_notAfter(context, tbs, endtime); hx509_ca_tbs_set_notBefore(context, tbs, starttime); return 0; @@ -2874,6 +2889,7 @@ _hx509_ca_issue_certificate(hx509_context context, KRB5PrincipalName *cprinc, time_t starttime, time_t endtime, + time_t req_life, int send_chain, hx509_certs *out) { @@ -2995,8 +3011,8 @@ _hx509_ca_issue_certificate(hx509_context context, /* Work out cert expiration */ if (ret == 0) - ret = tbs_set_times(context, cf, starttime, endtime, - 0 /* XXX req_life */, tbs); + ret = tbs_set_times(context, cf, logf, starttime, endtime, req_life, + tbs); /* Expand the subjectName template in the TBS using the env */ if (ret == 0) diff --git a/lib/krb5/krb5.conf.5 b/lib/krb5/krb5.conf.5 index 1e1cc5f2f..759a90686 100644 --- a/lib/krb5/krb5.conf.5 +++ b/lib/krb5/krb5.conf.5 @@ -827,9 +827,30 @@ for non-default client and server certificates. and where the parameters are as follows: .Bl -tag -width "xxx" -offset indent .It Li ca = Va file -Specifies the PEM credentials for the kx509 certification -authority. If not specified for any specific use-case, then that -use-case will be disabled. +Specifies the PEM credentials for the kx509 / bx509d certification +authority. +If not specified for any specific use-case, then that use-case +will be disabled. +.It Li max_cert_lifetime = Va NUMunit +Specifies the maximum certificate lifetime as a decimal number +and an optional unit (the default unit is +.Dq day +). +.It Li force_cert_lifetime = Va NUMunit +Specifies a minimum certificate lifetime as a decimal number and +an optional unit (the default unit is +.Dq day +). +.It Li allow_extra_lifetime = Va boolean +Indicates whether a client may request longer lifetimes than +their authentication credentials. +Defaults to false. +If a +.Li force_cert_lifetime +is specified, then +.Li allow_extra_lifetime +is implicitly forced to +.Va true . .It Li require_initial_kca_tickets = Va boolean Specified whether to require that tickets for the .Li kca_service