Skip to content

Commit

Permalink
Add KDC support for RBCD requests
Browse files Browse the repository at this point in the history
Add two new KDB methods to support resource-based constrained
delegation.  The get_authdata_info method extracts the client
principal for the authdata (necessary for cross-realm RBCD requests as
the evidence ticket is a cross-realm TGT with the client's authdata),
and also returns an opaque pointer for consumption by other KDB
methods.  The allowed_to_delegate_from method performs a constrained
delegation policy check on the principal entry of the target
principal.

Add the server principal and abstract authdata representation to the
sign_authdata method.  (XXX the DAL major version and KDB API version
need to be bumped; this will be handled in the merge with PR krb5#965.)

Add core KDC code for RBCD requests.  For local RBCD requests
(impersonator and target in the same realm), KDC handling is similar
to existing constrained delegation support.  The evidence ticket is
not required to be forwardable, and allowed_to_delegate_from is used
in preference to check_allowed_to_delegate.

For cross-realm RBCD requests, the KDC could be in the impersonator
realm, the target realm, or in a transit realm between the two.  In
the transit realm case, the request looks like a regular cross-realm
request for a krbtgt service except for the information in the PAC, so
this case is handled by the KDB module sign_authdata() method.

[ghudson@mit.edu: made style and documentation edits, and edited
commit message]
  • Loading branch information
iboukris committed Aug 15, 2019
1 parent 12d8fcb commit 1f8436a
Show file tree
Hide file tree
Showing 9 changed files with 365 additions and 53 deletions.
92 changes: 91 additions & 1 deletion src/include/kdb.h
Expand Up @@ -128,6 +128,8 @@
#define KRB5_KDB_FLAG_CROSS_REALM 0x00001000
/* Allow in-realm aliases */
#define KRB5_KDB_FLAG_ALIAS_OK 0x00002000
/* Issuing referral */
#define KRB5_KDB_FLAG_ISSUING_REFERRAL 0x00004000

#define KRB5_KDB_FLAGS_S4U ( KRB5_KDB_FLAG_PROTOCOL_TRANSITION | \
KRB5_KDB_FLAG_CONSTRAINED_DELEGATION )
Expand Down Expand Up @@ -663,6 +665,7 @@ krb5_db_get_key_data_kvno( krb5_context context,
krb5_error_code krb5_db_sign_authdata(krb5_context kcontext,
unsigned int flags,
krb5_const_principal client_princ,
krb5_const_principal server_princ,
krb5_db_entry *client,
krb5_db_entry *server,
krb5_db_entry *krbtgt,
Expand All @@ -672,6 +675,7 @@ krb5_error_code krb5_db_sign_authdata(krb5_context kcontext,
krb5_keyblock *session_key,
krb5_timestamp authtime,
krb5_authdata **tgt_auth_data,
void *ad_info,
krb5_authdata ***signed_auth_data);

krb5_error_code krb5_db_check_transited_realms(krb5_context kcontext,
Expand Down Expand Up @@ -713,6 +717,26 @@ krb5_error_code krb5_db_get_s4u_x509_principal(krb5_context kcontext,
unsigned int flags,
krb5_db_entry **entry);

krb5_error_code krb5_db_allowed_to_delegate_from(krb5_context context,
krb5_const_principal client,
krb5_const_principal server,
void *server_ad_info,
const krb5_db_entry *proxy);

krb5_error_code krb5_db_get_authdata_info(krb5_context context,
unsigned int flags,
krb5_authdata **in_authdata,
krb5_const_principal client_princ,
krb5_const_principal server_princ,
krb5_keyblock *server_key,
krb5_db_entry *krbtgt,
krb5_timestamp authtime,
void **ad_info,
krb5_principal *authdata_client);

void krb5_db_free_authdata_info(krb5_context context,
void *ad_info);

/**
* Sort an array of @a krb5_key_data keys in descending order by their kvno.
* Key data order within a kvno is preserved.
Expand Down Expand Up @@ -1017,7 +1041,13 @@ typedef struct _kdb_vftabl {
*
* KRB5_KDB_FLAG_CROSS_REALM: Set by the KDC when looking up a client entry
* during a TGS request, if the client principal is not part of the
* realm being served.
* realm being served. It is also set for a final cross-realm S4U2Self
* request, in which a server queries its own realm using a cross-realm
* TGT.
*
* KRB5_KDB_FLAG_ISSUING_REFERRAL: Set by the KDC after looking up a server
* entry during a TGS request, if the server principal is not part of
* the realm being served, and a referral will be issued instead.
*
* KRB5_KDB_FLAG_ALIAS_OK: Set by the KDC for server principal lookups and
* for AS request client principal lookups with canonicalization
Expand Down Expand Up @@ -1268,6 +1298,8 @@ typedef struct _kdb_vftabl {
* principal requested by the service; for regular TGS requests, the
* possibly-canonicalized client principal.
*
* server_princ: The server principal in the request.
*
* client: The DB entry of the client. For S4U2Self, this will be the DB
* entry for the client principal requested by the service).
*
Expand Down Expand Up @@ -1301,10 +1333,15 @@ typedef struct _kdb_vftabl {
*
* tgt_auth_data: For TGS requests, the authorization data present in the
* subject ticket. For AS requests, NULL.
*
* ad_info: For TGS requests, the parsed authorization data if obtained
* by get_authdata_info method from the authorization data present in
* the subject ticket. NULL, otherwise.
*/
krb5_error_code (*sign_authdata)(krb5_context kcontext,
unsigned int flags,
krb5_const_principal client_princ,
krb5_const_principal server_princ,
krb5_db_entry *client,
krb5_db_entry *server,
krb5_db_entry *krbtgt,
Expand All @@ -1314,6 +1351,7 @@ typedef struct _kdb_vftabl {
krb5_keyblock *session_key,
krb5_timestamp authtime,
krb5_authdata **tgt_auth_data,
void *ad_info,
krb5_authdata ***signed_auth_data);

/*
Expand Down Expand Up @@ -1424,6 +1462,58 @@ typedef struct _kdb_vftabl {
krb5_db_entry **entry_out);

/* End of minor version 1 for major version 7. */

/*
* Optional: Perform a policy check on server being allowed to obtain
* tickets from client to proxy. This method is similar to
* check_allowed_to_delegate, but it operates on the target server DB entry
* (called "proxy" here as per Microsoft's protocol documentation) rather
* than the intermediate server entry. server_ad_info represents the
* authdata of the intermediate server, as returned by the
* get_authdata_info method on the header ticket. Return 0 if policy
* allows it, or an appropriate error (such as KRB5KDC_ERR_POLICY) if not.
*
* This method is called for S4U2Proxy requests and implements resource-
* based constrained delegation variant (supporting cross-realm S4U2Proxy).
* If this method is not implemented or if it returns a policy error, the
* KDC will fall back to check_allowed_to_delegate if the intermediate and
* target servers are in the same realm and the evidence ticket is
* forwardable.
*/
krb5_error_code (*allowed_to_delegate_from)(krb5_context context,
krb5_const_principal client,
krb5_const_principal server,
void *server_ad_info,
const krb5_db_entry *proxy);

/*
* Optional: Perform verification and policy checks on authorization data,
* such as a Windows PAC, based on the request client lookup flags. Return
* 0 if all checks have passed. Optionally return a representation of the
* authdata in ad_info. authdata_client is returned if requested - used to
* indicate the reply client principal for a cross-realm S4U2Proxy request.
*
* This method is called for TGS requests on the authorization data from
* the header ticket. For S4U2Proxy requests it is also called on the
* authorization data from the evidence ticket. If the
* KRB5_KDB_FLAG_PROTOCOL_TRANSITION bit is set in flags, the authdata is
* from the header ticket of an S4U2Self referral request, and the supplied
* client_princ will be the requested client.
*/
krb5_error_code (*get_authdata_info)(krb5_context context,
unsigned int flags,
krb5_authdata **in_authdata,
krb5_const_principal client_princ,
krb5_const_principal server_princ,
krb5_keyblock *server_key,
krb5_db_entry *krbtgt,
krb5_timestamp authtime,
void **ad_info,
krb5_principal *authdata_client);

void (*free_authdata_info)(krb5_context context,
void *ad_info);

} kdb_vftabl;

#endif /* !defined(_WIN32) */
Expand Down
3 changes: 2 additions & 1 deletion src/kdc/do_as_req.c
Expand Up @@ -305,7 +305,8 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
NULL,
state->req_pkt,
state->request,
NULL, /* for_user_princ */
NULL, /* altcprinc */
NULL, /* ad_info */
NULL, /* enc_tkt_request */
state->auth_indicators,
&state->enc_tkt_reply);
Expand Down
79 changes: 59 additions & 20 deletions src/kdc/do_tgs_req.c
Expand Up @@ -79,7 +79,7 @@ prepare_error_tgs(struct kdc_request_state *, krb5_kdc_req *,krb5_ticket *,int,

static krb5_error_code
decrypt_2ndtkt(kdc_realm_t *, krb5_kdc_req *, krb5_flags, krb5_db_entry **,
const char **);
krb5_keyblock **, const char **);

static krb5_error_code
gen_session_key(kdc_realm_t *, krb5_kdc_req *, krb5_db_entry *,
Expand All @@ -104,6 +104,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
{
krb5_keyblock * subkey = 0;
krb5_keyblock *header_key = NULL;
krb5_keyblock *stkt_server_key = NULL;
krb5_db_entry *server = NULL;
krb5_db_entry *stkt_server = NULL;
krb5_kdc_rep reply;
Expand All @@ -119,6 +120,8 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
krb5_keyblock *reply_key = NULL;
krb5_key_data *server_key;
krb5_principal cprinc = NULL, sprinc = NULL, altcprinc = NULL;
krb5_const_principal authdata_client;
krb5_principal stkt_authdata_client = NULL;
krb5_last_req_entry *nolrarray[2], nolrentry;
int errcode;
const char *status = 0;
Expand All @@ -138,6 +141,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
krb5_pa_data **e_data = NULL;
krb5_audit_state *au_state = NULL;
krb5_data **auth_indicators = NULL;
void *ad_info = NULL, *stkt_ad_info = NULL;

memset(&reply, 0, sizeof(reply));
memset(&reply_encpart, 0, sizeof(reply_encpart));
Expand Down Expand Up @@ -247,6 +251,8 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
* issuing a referral (or alternate TGT, which we treat similarly). */
is_referral = is_cross_tgs_principal(server->princ) &&
!krb5_principal_compare(kdc_context, request->server, server->princ);
if (is_referral)
setflag(c_flags, KRB5_KDB_FLAG_ISSUING_REFERRAL);

au_state->stage = VALIDATE_POL;

Expand Down Expand Up @@ -290,23 +296,53 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,

if (errcode)
goto cleanup;

if (s4u_x509_user != NULL && client == NULL) {
/*
* For an S4U2Self referral request (the client is following a referral
* back to the server realm), the authdata in the header ticket should
* be for the requested client. Also, the request is cross-realm even
* if the header ticket client is a local principal.
*/
setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM);
authdata_client = s4u_x509_user->user_id.user;
} else {
/* Otherwise (including for initial S4U2Self requests), the authdata
* should be for the header ticket client. */
authdata_client = header_enc_tkt->client;
}
errcode = krb5_db_get_authdata_info(kdc_context, c_flags,
header_enc_tkt->authorization_data,
authdata_client, request->server,
header_key, header_server,
header_enc_tkt->times.authtime,
&ad_info, NULL);
if (errcode && errcode != KRB5_PLUGIN_OP_NOTSUPP)
goto cleanup;

/* Flag all S4U2Self requests now that we have checked the authdata. */
if (s4u_x509_user != NULL)
setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);

/* Deal with user-to-user and constrained delegation */
errcode = decrypt_2ndtkt(kdc_active_realm, request, c_flags,
&stkt_server, &status);
&stkt_server, &stkt_server_key, &status);
if (errcode)
goto cleanup;

if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
/* Do constrained delegation protocol and authorization checks */
errcode = kdc_process_s4u2proxy_req(kdc_active_realm,
request,
setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);

errcode = kdc_process_s4u2proxy_req(kdc_active_realm, c_flags, request,
request->second_ticket[st_idx]->enc_part2,
stkt_server,
header_server, stkt_server,
stkt_server_key,
header_ticket->enc_part2->client,
request->server,
server, request->server, ad_info,
&stkt_ad_info,
&stkt_authdata_client,
&status);
if (errcode == KDC_ERR_POLICY || errcode == KDC_ERR_BADOPTION)
au_state->violation = PROT_CONSTRAINT;
Expand All @@ -323,8 +359,6 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
if (errcode)
goto cleanup;

setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);

assert(krb5_is_tgs_principal(header_ticket->server));

assert(client == NULL); /* assured by kdc_process_s4u2self_req() */
Expand Down Expand Up @@ -476,7 +510,10 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
altcprinc = s4u_x509_user->user_id.user;
} else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
altcprinc = subject_tkt->client;
/* kdc_process_s4u2proxy_req() only allows cross-realm requests if
* stkt_authdata_client is set */
altcprinc = isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM) ?
stkt_authdata_client : subject_tkt->client;
} else {
altcprinc = NULL;
}
Expand Down Expand Up @@ -539,11 +576,10 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
}
}

if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
!isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM))
enc_tkt_reply.client = s4u_x509_user->user_id.user;
if (isflagset(c_flags, KRB5_KDB_FLAGS_S4U) && !is_referral)
enc_tkt_reply.client = altcprinc;
else
enc_tkt_reply.client = subject_tkt->client;
enc_tkt_reply.client = header_enc_tkt->client;

enc_tkt_reply.session = &session_key;
enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
Expand Down Expand Up @@ -621,8 +657,8 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
header_key,
pkt,
request,
s4u_x509_user ?
s4u_x509_user->user_id.user : NULL,
altcprinc,
stkt_ad_info ? stkt_ad_info : ad_info,
subject_tkt,
auth_indicators,
&enc_tkt_reply);
Expand Down Expand Up @@ -759,6 +795,8 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
krb5_free_keyblock_contents(kdc_context, &server_keyblock);
if (reply_key)
krb5_free_keyblock(kdc_context, reply_key);
if (stkt_server_key)
krb5_free_keyblock(kdc_context, stkt_server_key);
if (errcode)
emsg = krb5_get_error_message (kdc_context, errcode);

Expand Down Expand Up @@ -826,6 +864,9 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
krb5_free_authdata(kdc_context, enc_tkt_reply.authorization_data);
krb5_free_pa_data(kdc_context, e_data);
k5_free_data_ptr_list(auth_indicators);
krb5_db_free_authdata_info(kdc_context, ad_info);
krb5_db_free_authdata_info(kdc_context, stkt_ad_info);
krb5_free_principal(kdc_context, stkt_authdata_client);

return retval;
}
Expand Down Expand Up @@ -907,11 +948,10 @@ prepare_error_tgs (struct kdc_request_state *state,
static krb5_error_code
decrypt_2ndtkt(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
krb5_flags flags, krb5_db_entry **server_out,
const char **status)
krb5_keyblock **key, const char **status)
{
krb5_error_code retval;
krb5_db_entry *server = NULL;
krb5_keyblock *key;
krb5_kvno kvno;
krb5_ticket *stkt;

Expand All @@ -923,15 +963,14 @@ decrypt_2ndtkt(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
flags,
TRUE, /* match_enctype */
&server,
&key,
key,
&kvno);
if (retval != 0) {
*status = "2ND_TKT_SERVER";
goto cleanup;
}
retval = krb5_decrypt_tkt_part(kdc_context, key,
retval = krb5_decrypt_tkt_part(kdc_context, *key,
req->second_ticket[0]);
krb5_free_keyblock(kdc_context, key);
if (retval != 0) {
*status = "2ND_TKT_DECRYPT";
goto cleanup;
Expand Down

0 comments on commit 1f8436a

Please sign in to comment.