bx509d: Allow requesting longer cert lifetimes
Add a `lifetime=NUMunit` query parameter. Also add a krb5.conf parameter to indicate whether this is allowed. We already have a max lifetime configuration parameter.
This commit is contained in:
41
kdc/bx509d.8
41
kdc/bx509d.8
@@ -113,6 +113,45 @@ Uses a thread per-client instead of as many threads as there are CPUs.
|
|||||||
.Xc
|
.Xc
|
||||||
verbose
|
verbose
|
||||||
.El
|
.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 <base64-encoded-DER-encoded-CSR>
|
||||||
|
.It Li dNSName = Va <hostname>
|
||||||
|
.It Li rfc822Name = Va <email-address>
|
||||||
|
.It Li xMPPName = Va <XMPP-address>
|
||||||
|
.It Li krb5PrincipalName = Va <Kerberos-principal-name>
|
||||||
|
.It Li ms-upn = Va <UPN>
|
||||||
|
.It Li eku = Va <OID>
|
||||||
|
.It Li lifetime = Va <lifetime>
|
||||||
|
.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
|
.Sh ENVIRONMENT
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Ev KRB5_CONFIG
|
.It Ev KRB5_CONFIG
|
||||||
@@ -122,6 +161,8 @@ the default being
|
|||||||
.Pa /etc/krb5.conf .
|
.Pa /etc/krb5.conf .
|
||||||
.El
|
.El
|
||||||
.Sh FILES
|
.Sh FILES
|
||||||
|
Configuration parameters are specified in
|
||||||
|
.Ar /etc/krb5.conf .
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Pa /etc/krb5.conf
|
.It Pa /etc/krb5.conf
|
||||||
.El
|
.El
|
||||||
|
11
kdc/bx509d.c
11
kdc/bx509d.c
@@ -117,6 +117,7 @@ typedef struct bx509_request_desc {
|
|||||||
|
|
||||||
struct MHD_Connection *connection;
|
struct MHD_Connection *connection;
|
||||||
krb5_times token_times;
|
krb5_times token_times;
|
||||||
|
time_t req_life;
|
||||||
hx509_request req;
|
hx509_request req;
|
||||||
const char *target;
|
const char *target;
|
||||||
const char *redir;
|
const char *redir;
|
||||||
@@ -667,6 +668,8 @@ bx509_param_cb(void *d,
|
|||||||
} else if (strcmp(key, "csr") == 0 && val) {
|
} else if (strcmp(key, "csr") == 0 && val) {
|
||||||
heim_audit_addkv((heim_svc_req_desc)a->r, 0, "requested_csr", "true");
|
heim_audit_addkv((heim_svc_req_desc)a->r, 0, "requested_csr", "true");
|
||||||
a->ret = 0; /* Handled upstairs */
|
a->ret = 0; /* Handled upstairs */
|
||||||
|
} else if (strcmp(key, "lifetime") == 0 && val) {
|
||||||
|
a->r->req_life = parse_time(val, "day");
|
||||||
} else {
|
} else {
|
||||||
/* Produce error for unknown params */
|
/* Produce error for unknown params */
|
||||||
heim_audit_addkv((heim_svc_req_desc)a->r, 0, "requested_unknown", "true");
|
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 */
|
/* Issue the certificate */
|
||||||
ret = kdc_issue_certificate(r->context, "bx509", logfac, r->req, p,
|
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);
|
krb5_free_principal(r->context, p);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (ret == KRB5KDC_ERR_POLICY || ret == EACCES)
|
if (ret == KRB5KDC_ERR_POLICY || ret == EACCES)
|
||||||
@@ -872,6 +876,7 @@ set_req_desc(struct MHD_Connection *connection,
|
|||||||
r->cname = NULL;
|
r->cname = NULL;
|
||||||
r->addr = NULL;
|
r->addr = NULL;
|
||||||
r->req = NULL;
|
r->req = NULL;
|
||||||
|
r->req_life = 0;
|
||||||
r->kv = heim_array_create();
|
r->kv = heim_array_create();
|
||||||
ci = MHD_get_connection_info(connection,
|
ci = MHD_get_connection_info(connection,
|
||||||
MHD_CONNECTION_INFO_CLIENT_ADDRESS);
|
MHD_CONNECTION_INFO_CLIENT_ADDRESS);
|
||||||
@@ -1268,8 +1273,8 @@ bnegotiate_do_CA(struct bx509_request_desc *r)
|
|||||||
/* Issue the certificate */
|
/* Issue the certificate */
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
ret = kdc_issue_certificate(r->context, "bx509", logfac, req, p,
|
ret = kdc_issue_certificate(r->context, "bx509", logfac, req, p,
|
||||||
&r->token_times, 1 /* send_chain */,
|
&r->token_times, 0,
|
||||||
&certs);
|
1 /* send_chain */, &certs);
|
||||||
krb5_free_principal(r->context, p);
|
krb5_free_principal(r->context, p);
|
||||||
hx509_request_free(&req);
|
hx509_request_free(&req);
|
||||||
p = NULL;
|
p = NULL;
|
||||||
|
5
kdc/ca.c
5
kdc/ca.c
@@ -104,6 +104,7 @@ kdc_issue_certificate(krb5_context context,
|
|||||||
hx509_request req,
|
hx509_request req,
|
||||||
krb5_principal cprinc,
|
krb5_principal cprinc,
|
||||||
krb5_times *auth_times,
|
krb5_times *auth_times,
|
||||||
|
time_t req_life,
|
||||||
int send_chain,
|
int send_chain,
|
||||||
hx509_certs *out)
|
hx509_certs *out)
|
||||||
{
|
{
|
||||||
@@ -122,7 +123,9 @@ kdc_issue_certificate(krb5_context context,
|
|||||||
(const heim_config_binding *)cf,
|
(const heim_config_binding *)cf,
|
||||||
logf, req, &cprinc2,
|
logf, req, &cprinc2,
|
||||||
auth_times->starttime,
|
auth_times->starttime,
|
||||||
auth_times->endtime, send_chain,
|
auth_times->endtime,
|
||||||
|
req_life,
|
||||||
|
send_chain,
|
||||||
out);
|
out);
|
||||||
if (ret == EACCES)
|
if (ret == EACCES)
|
||||||
ret = KRB5KDC_ERR_POLICY;
|
ret = KRB5KDC_ERR_POLICY;
|
||||||
|
@@ -1005,8 +1005,8 @@ _kdc_do_kx509(kx509_req_context r)
|
|||||||
krb5_data_zero(rep.certificate);
|
krb5_data_zero(rep.certificate);
|
||||||
krb5_ticket_get_times(r->context, ticket, &r->ticket_times);
|
krb5_ticket_get_times(r->context, ticket, &r->ticket_times);
|
||||||
ret = kdc_issue_certificate(r->context, r->config->app, r->logf, r->csr,
|
ret = kdc_issue_certificate(r->context, r->config->app, r->logf, r->csr,
|
||||||
cprincipal, &r->ticket_times, r->send_chain,
|
cprincipal, &r->ticket_times, 0 /*req_life*/,
|
||||||
&certs);
|
r->send_chain, &certs);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
int level = 1;
|
int level = 1;
|
||||||
const char *msg = krb5_get_error_message(r->context, ret);
|
const char *msg = krb5_get_error_message(r->context, ret);
|
||||||
|
@@ -2,12 +2,15 @@
|
|||||||
|
|
||||||
static int authorized_flag;
|
static int authorized_flag;
|
||||||
static int help_flag;
|
static int help_flag;
|
||||||
|
static char *lifetime_string;
|
||||||
static const char *app_string = "kdc";
|
static const char *app_string = "kdc";
|
||||||
static int version_flag;
|
static int version_flag;
|
||||||
|
|
||||||
struct getargs args[] = {
|
struct getargs args[] = {
|
||||||
{ "authorized", 'A', arg_flag, &authorized_flag,
|
{ "authorized", 'A', arg_flag, &authorized_flag,
|
||||||
"Assume CSR is authorized", NULL },
|
"Assume CSR is authorized", NULL },
|
||||||
|
{ "lifetime", 'l', arg_string, &lifetime_string,
|
||||||
|
"Certificate lifetime desired", "TIME" },
|
||||||
{ "help", 'h', arg_flag, &help_flag,
|
{ "help", 'h', arg_flag, &help_flag,
|
||||||
"Print usage message", NULL },
|
"Print usage message", NULL },
|
||||||
{ "app", 'a', arg_string, &app_string,
|
{ "app", 'a', arg_string, &app_string,
|
||||||
@@ -78,6 +81,7 @@ main(int argc, char **argv)
|
|||||||
hx509_certs certs = NULL;
|
hx509_certs certs = NULL;
|
||||||
const char *argv0 = argv[0];
|
const char *argv0 = argv[0];
|
||||||
const char *out = "MEMORY:junk-it";
|
const char *out = "MEMORY:junk-it";
|
||||||
|
time_t req_life = 0;
|
||||||
int optidx = 0;
|
int optidx = 0;
|
||||||
|
|
||||||
setprogname(argv[0]);
|
setprogname(argv[0]);
|
||||||
@@ -143,8 +147,9 @@ main(int argc, char **argv)
|
|||||||
memset(&t, 0, sizeof(t));
|
memset(&t, 0, sizeof(t));
|
||||||
t.starttime = time(NULL);
|
t.starttime = time(NULL);
|
||||||
t.endtime = t.starttime + 3600;
|
t.endtime = t.starttime + 3600;
|
||||||
if ((ret = kdc_issue_certificate(context, app_string, logf, req, p, &t, 1,
|
req_life = lifetime_string ? parse_time(lifetime_string, "day") : 0;
|
||||||
&certs)))
|
if ((ret = kdc_issue_certificate(context, app_string, logf, req, p, &t,
|
||||||
|
req_life, 1, &certs)))
|
||||||
krb5_err(context, 1, ret, "Certificate issuance failed");
|
krb5_err(context, 1, ret, "Certificate issuance failed");
|
||||||
|
|
||||||
if (argv[2])
|
if (argv[2])
|
||||||
|
@@ -2835,6 +2835,7 @@ enomem:
|
|||||||
static heim_error_code
|
static heim_error_code
|
||||||
tbs_set_times(hx509_context context,
|
tbs_set_times(hx509_context context,
|
||||||
const heim_config_binding *cf,
|
const heim_config_binding *cf,
|
||||||
|
heim_log_facility *logf,
|
||||||
time_t starttime,
|
time_t starttime,
|
||||||
time_t endtime,
|
time_t endtime,
|
||||||
time_t req_life,
|
time_t req_life,
|
||||||
@@ -2847,15 +2848,29 @@ tbs_set_times(hx509_context context,
|
|||||||
time_t clamp =
|
time_t clamp =
|
||||||
heim_config_get_time_default(context->hcontext, cf, 0,
|
heim_config_get_time_default(context->hcontext, cf, 0,
|
||||||
"max_cert_lifetime", NULL);
|
"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;
|
starttime = starttime ? starttime : now - 5 * 60;
|
||||||
if (fudge && now + fudge > endtime)
|
if (fudge && now + fudge > endtime)
|
||||||
endtime = now + fudge;
|
endtime = now + fudge;
|
||||||
if (req_life && req_life < endtime - now)
|
if (req_life > 0 && req_life < endtime - now)
|
||||||
endtime = now + req_life;
|
endtime = now + req_life;
|
||||||
if (clamp && clamp < endtime - now)
|
if (clamp && clamp < endtime - now)
|
||||||
endtime = now + clamp;
|
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_notAfter(context, tbs, endtime);
|
||||||
hx509_ca_tbs_set_notBefore(context, tbs, starttime);
|
hx509_ca_tbs_set_notBefore(context, tbs, starttime);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -2874,6 +2889,7 @@ _hx509_ca_issue_certificate(hx509_context context,
|
|||||||
KRB5PrincipalName *cprinc,
|
KRB5PrincipalName *cprinc,
|
||||||
time_t starttime,
|
time_t starttime,
|
||||||
time_t endtime,
|
time_t endtime,
|
||||||
|
time_t req_life,
|
||||||
int send_chain,
|
int send_chain,
|
||||||
hx509_certs *out)
|
hx509_certs *out)
|
||||||
{
|
{
|
||||||
@@ -2995,8 +3011,8 @@ _hx509_ca_issue_certificate(hx509_context context,
|
|||||||
|
|
||||||
/* Work out cert expiration */
|
/* Work out cert expiration */
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
ret = tbs_set_times(context, cf, starttime, endtime,
|
ret = tbs_set_times(context, cf, logf, starttime, endtime, req_life,
|
||||||
0 /* XXX req_life */, tbs);
|
tbs);
|
||||||
|
|
||||||
/* Expand the subjectName template in the TBS using the env */
|
/* Expand the subjectName template in the TBS using the env */
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
@@ -827,9 +827,30 @@ for non-default client and server certificates.
|
|||||||
and where the parameters are as follows:
|
and where the parameters are as follows:
|
||||||
.Bl -tag -width "xxx" -offset indent
|
.Bl -tag -width "xxx" -offset indent
|
||||||
.It Li ca = Va file
|
.It Li ca = Va file
|
||||||
Specifies the PEM credentials for the kx509 certification
|
Specifies the PEM credentials for the kx509 / bx509d certification
|
||||||
authority. If not specified for any specific use-case, then that
|
authority.
|
||||||
use-case will be disabled.
|
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
|
.It Li require_initial_kca_tickets = Va boolean
|
||||||
Specified whether to require that tickets for the
|
Specified whether to require that tickets for the
|
||||||
.Li kca_service
|
.Li kca_service
|
||||||
|
Reference in New Issue
Block a user