diff --git a/kdc/bx509d.8 b/kdc/bx509d.8 index a521f1349..512d0545e 100644 --- a/kdc/bx509d.8 +++ b/kdc/bx509d.8 @@ -115,6 +115,11 @@ verbose .El .Sh ONLINE CERTIFICATION AUTHORITY HTTP API This service provides an HTTP-based Certification Authority (CA). +CA credentials and configuration are specified in the +.Va [bx509] +section of +.Xr krb5.conf 5 . +.Pp The protocol consists of a .Ar GET of @@ -163,6 +168,10 @@ an optional unit (which defaults to ). .Sh NEGOTIATE TOKEN HTTP API This service provides an HTTP-based Negotiate token service. +This service requires a certification authority (CA) issuer +credential as it impersonates client principals to the KDC using +PKINIT client certificates it issues itself. +.Pp The protocol consists of a .Ar GET of @@ -194,15 +203,29 @@ header is included in the request, then the response will be a redirect to that URI with the Negotiate token in an .Va Authorization header that the user-agent should copy to the redirected request. +.Pp +The certification authority configuration is the same as for the +.Va /get-cert +end-point, but as configured in the +.Va [get-tgt] +section of +.Xr krb5.conf 5 . .Sh TGT HTTP API This service provides an HTTP-based "kinit" service. +This service requires a certification authority (CA) issuer +credential as it impersonates client principals to the KDC using +PKINIT client certificates it issues itself. +.Pp The protocol consists of a .Ar GET of -.Ar /get-tgt -with an optional -.Ar cname = Ar principal-name -query parameter. +.Ar /get-tgt . +.Pp +Supported query parameters (separated by ampersands) +.Bl -tag -width Ds -offset indent +.It Li cname = Va principal-name +.It Li address = Va IP-address +.El .Pp In a successful query, the response body will contain a TGT and its session key encoded as a "ccache" file contents. @@ -210,14 +233,6 @@ its session key encoded as a "ccache" file contents. Authentication is required. Unauthenticated requests will elicit a 401 response. .Pp -Supported query parameters (separated by ampersands) -.Bl -tag -width Ds -offset indent -.It Li cname = Va principal -.El -.Pp -Authentication is required -Unauthenticated requests will elicit a 401 response. -.Pp Authorization is required, where the authorization check is the same as for .Va /get-cert @@ -225,6 +240,23 @@ by the authenticated client principal to get a certificate with a PKINIT SAN for itself or the requested principal if a .Va cname query parameter was included. +.Pp +Unauthorized requests will elicit a 403 response. +.Pp +Requested IP addresses will be added to the issued TGT if allowed. +The IP address of the client will be included if address-less TGTs +are not allowed. +See the +.Va [get-tgt] +section of +.Xr krb5.conf 5 . +.Pp +The certification authority configuration is the same as for the +.Va /get-cert +end-point, but as configured in the +.Va [get-tgt] +section of +.Xr krb5.conf 5 . .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev KRB5_CONFIG diff --git a/kdc/bx509d.c b/kdc/bx509d.c index 2fc525fe0..b3fbcecf7 100644 --- a/kdc/bx509d.c +++ b/kdc/bx509d.c @@ -124,6 +124,8 @@ typedef struct bx509_request_desc { char *pkix_store; char *ccname; char *freeme1; + krb5_addresses tgt_addresses; /* For /get-tgt */ + krb5_error_code ret; char frombuf[128]; } *bx509_request_desc; @@ -610,73 +612,65 @@ good_bx509(struct bx509_request_desc *r) return ret; } -struct bx509_param_handler_arg { - struct bx509_request_desc *r; - hx509_request req; - krb5_error_code ret; -}; - static int bx509_param_cb(void *d, enum MHD_ValueKind kind, const char *key, const char *val) { - struct bx509_param_handler_arg *a = d; + struct bx509_request_desc *r = d; heim_oid oid = { 0, 0 }; if (strcmp(key, "eku") == 0 && val) { - heim_audit_addkv((heim_svc_req_desc)a->r, KDC_AUDIT_VIS, "requested_eku", + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, "requested_eku", "%s", val); - a->ret = der_parse_heim_oid(val, ".", &oid); - if (a->ret == 0) - a->ret = hx509_request_add_eku(a->r->context->hx509ctx, a->req, - &oid); + r->ret = der_parse_heim_oid(val, ".", &oid); + if (r->ret == 0) + r->ret = hx509_request_add_eku(r->context->hx509ctx, r->req, &oid); der_free_oid(&oid); } else if (strcmp(key, "dNSName") == 0 && val) { - heim_audit_addkv((heim_svc_req_desc)a->r, KDC_AUDIT_VIS, + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, "requested_dNSName", "%s", val); - a->ret = hx509_request_add_dns_name(a->r->context->hx509ctx, a->req, - val); + r->ret = hx509_request_add_dns_name(r->context->hx509ctx, r->req, val); } else if (strcmp(key, "rfc822Name") == 0 && val) { - heim_audit_addkv((heim_svc_req_desc)a->r, KDC_AUDIT_VIS, + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, "requested_rfc822Name", "%s", val); - a->ret = hx509_request_add_email(a->r->context->hx509ctx, a->req, val); + r->ret = hx509_request_add_email(r->context->hx509ctx, r->req, val); } else if (strcmp(key, "xMPPName") == 0 && val) { - heim_audit_addkv((heim_svc_req_desc)a->r, KDC_AUDIT_VIS, + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, "requested_xMPPName", "%s", val); - a->ret = hx509_request_add_xmpp_name(a->r->context->hx509ctx, a->req, + r->ret = hx509_request_add_xmpp_name(r->context->hx509ctx, r->req, val); } else if (strcmp(key, "krb5PrincipalName") == 0 && val) { - heim_audit_addkv((heim_svc_req_desc)a->r, KDC_AUDIT_VIS, + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, "requested_krb5PrincipalName", "%s", val); - a->ret = hx509_request_add_pkinit(a->r->context->hx509ctx, a->req, + r->ret = hx509_request_add_pkinit(r->context->hx509ctx, r->req, val); } else if (strcmp(key, "ms-upn") == 0 && val) { - heim_audit_addkv((heim_svc_req_desc)a->r, KDC_AUDIT_VIS, + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, "requested_ms_upn", "%s", val); - a->ret = hx509_request_add_ms_upn_name(a->r->context->hx509ctx, a->req, + r->ret = hx509_request_add_ms_upn_name(r->context->hx509ctx, r->req, val); } else if (strcmp(key, "registeredID") == 0 && val) { - heim_audit_addkv((heim_svc_req_desc)a->r, KDC_AUDIT_VIS, + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, "requested_registered_id", "%s", val); - a->ret = der_parse_heim_oid(val, ".", &oid); - if (a->ret == 0) - a->ret = hx509_request_add_registered(a->r->context->hx509ctx, - a->req, &oid); + r->ret = der_parse_heim_oid(val, ".", &oid); + if (r->ret == 0) + r->ret = hx509_request_add_registered(r->context->hx509ctx, r->req, + &oid); der_free_oid(&oid); } 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 */ + heim_audit_addkv((heim_svc_req_desc)r, 0, "requested_csr", "true"); + r->ret = 0; /* Handled upstairs */ } else if (strcmp(key, "lifetime") == 0 && val) { - a->r->req_life = parse_time(val, "day"); + 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"); - krb5_set_error_message(a->r->context, a->ret = ENOTSUP, + heim_audit_addkv((heim_svc_req_desc)r, 0, "requested_unknown", "true"); + krb5_set_error_message(r->context, r->ret = ENOTSUP, "Query parameter %s not supported", key); } - return a->ret == 0 ? MHD_YES : MHD_NO /* Stop iterating */; + return r->ret == 0 ? MHD_YES : MHD_NO /* Stop iterating */; } static krb5_error_code @@ -684,19 +678,16 @@ authorize_CSR(struct bx509_request_desc *r, krb5_data *csr, krb5_const_principal p) { - struct bx509_param_handler_arg cb_data; krb5_error_code ret; ret = hx509_request_parse_der(r->context->hx509ctx, csr, &r->req); if (ret) return bad_req(r, ret, MHD_HTTP_SERVICE_UNAVAILABLE, "Could not parse CSR"); - cb_data.r = r; - cb_data.req = r->req; - cb_data.ret = 0; + r->ret = 0; (void) MHD_get_connection_values(r->connection, MHD_GET_ARGUMENT_KIND, - bx509_param_cb, &cb_data); - ret = cb_data.ret; + bx509_param_cb, r); + ret = r->ret; if (ret) return bad_req(r, ret, MHD_HTTP_SERVICE_UNAVAILABLE, "Could not handle query parameters"); @@ -862,6 +853,8 @@ set_req_desc(struct MHD_Connection *connection, r->request.data = ""; r->request.length = sizeof(""); r->from = r->frombuf; + r->tgt_addresses.len = 0; + r->tgt_addresses.val = 0; r->hcontext = r->context->hcontext; r->config = NULL; r->logf = logfac; @@ -877,6 +870,7 @@ set_req_desc(struct MHD_Connection *connection, r->addr = NULL; r->req = NULL; r->req_life = 0; + r->ret = 0; r->kv = heim_array_create(); ci = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); @@ -915,6 +909,7 @@ clean_req_desc(struct bx509_request_desc *r) return; if (r->pkix_store) (void) unlink(strchr(r->pkix_store, ':') + 1); + krb5_free_addresses(r->context, &r->tgt_addresses); hx509_request_free(&r->req); heim_release(r->reason); heim_release(r->kv); @@ -1188,8 +1183,20 @@ do_pkinit(struct bx509_request_desc *r, enum k5_creds_kind kind) if (ret == 0) krb5_get_init_creds_opt_set_default_flags(r->context, "kinit", crealm, opt); - if (ret == 0) + if (ret == 0 && kind == K5_CREDS_EPHEMERAL && + !krb5_config_get_bool_default(r->context, NULL, TRUE, + "get-tgt", "no_addresses", NULL)) { + krb5_addresses addr; + + ret = _krb5_parse_address_no_lookup(r->context, r->frombuf, &addr); + if (ret == 0) + ret = krb5_append_addresses(r->context, &r->tgt_addresses, + &addr); + } + if (ret == 0 && r->tgt_addresses.len == 0) ret = krb5_get_init_creds_opt_set_addressless(r->context, opt, 1); + else + krb5_get_init_creds_opt_set_address_list(opt, &r->tgt_addresses); if (ret == 0) ret = krb5_get_init_creds_opt_set_pkinit(r->context, opt, p, r->pkix_store, @@ -1306,7 +1313,7 @@ k5_do_CA(struct bx509_request_desc *r) /* Issue the certificate */ if (ret == 0) - ret = kdc_issue_certificate(r->context, "getTGT", logfac, req, p, + ret = kdc_issue_certificate(r->context, "get-tgt", logfac, req, p, &r->token_times, 0, 1 /* send_chain */, &certs); krb5_free_principal(r->context, p); @@ -1667,7 +1674,7 @@ authorize_TGT_REQ(struct bx509_request_desc *r, const char *cname) "requested_krb5PrincipalName", "%s", cname); ret = hx509_request_add_pkinit(r->context->hx509ctx, r->req, cname); if (ret == 0) - ret = kdc_authorize_csr(r->context, "getTGT", r->req, p); + ret = kdc_authorize_csr(r->context, "get-tgt", r->req, p); krb5_free_principal(r->context, p); hx509_request_free(&r->req); if (ret) @@ -1675,6 +1682,44 @@ authorize_TGT_REQ(struct bx509_request_desc *r, const char *cname) return ret; } +static int +get_tgt_param_cb(void *d, + enum MHD_ValueKind kind, + const char *key, + const char *val) +{ + struct bx509_request_desc *r = d; + + if (strcmp(key, "address") == 0 && val) { + if (!krb5_config_get_bool_default(r->context, NULL, + FALSE, + "get-tgt", "allow_addresses", NULL)) { + krb5_set_error_message(r->context, r->ret = ENOTSUP, + "Query parameter %s not allowed", key); + } else { + krb5_addresses addresses; + + r->ret = _krb5_parse_address_no_lookup(r->context, val, + &addresses); + if (r->ret == 0) + r->ret = krb5_append_addresses(r->context, &r->tgt_addresses, + &addresses); + krb5_free_addresses(r->context, &addresses); + } + } else if (strcmp(key, "cname") == 0) { + /* Handled upstairs */ + ; + } else if (strcmp(key, "lifetime") == 0 && val) { + r->req_life = parse_time(val, "day"); + } else { + /* Produce error for unknown params */ + heim_audit_addkv((heim_svc_req_desc)r, 0, "requested_unknown", "true"); + krb5_set_error_message(r->context, r->ret = ENOTSUP, + "Query parameter %s not supported", key); + } + return r->ret == 0 ? MHD_YES : MHD_NO /* Stop iterating */; +} + /* * Implements /get-tgt end-point. * @@ -1701,6 +1746,11 @@ get_tgt(struct bx509_request_desc *r) if (ret) return ret; + r->ret = 0; + (void) MHD_get_connection_values(r->connection, MHD_GET_ARGUMENT_KIND, + get_tgt_param_cb, r); + ret = r->ret; + ret = k5_get_creds(r, K5_CREDS_EPHEMERAL); if (ret) return bad_503(r, ret, "Could not get TGT"); diff --git a/lib/krb5/krb5.conf.5 b/lib/krb5/krb5.conf.5 index 5cf10ddaf..1707a992f 100644 --- a/lib/krb5/krb5.conf.5 +++ b/lib/krb5/krb5.conf.5 @@ -1140,6 +1140,58 @@ service can be configured to have the ok-as-delegate flag while all others do not. .El .Pp +.It Li [bx509] +This section contains online certification authority configuration, much +like +.Li kx509 +in the +.Li [kdc] +section, but with the +.Li kx509 +layer removed. +.Bd -literal -offset indent +[kdc] + realm = { + = { + ... + } + } +.Ed +.It Li [get-tgt] +.Bl -tag -width "xxx" -offset indent +.It Li no_addresses = Va BOOL +If set to +.Va true +then the +.Va /get-tgt +end-point of the +.Xr bx509d 8 +service will issue address-less TGTs. +If set to +.Va false +then the +.Va /get-tgt +end-point of the +.Xr bx509d 8 +service will include the client's IP address in the TGT it issues +it. +Defaults to +.Va true . +.It Li allow_addresses = Va BOOL +If set to +.Va true +then the +.Va /get-tgt +end-point of the +.Xr bx509d 8 +service will add arbitrary addresses requested by clients to the +TGTs it issues them. +Defaults to +.Va false . +.El +.Pp +Certification authority related parameters are as for +.Va bx509 . .It Li [kadmin] .Bl -tag -width "xxx" -offset indent .It Li password_lifetime = Va time