From 87f8c0d2b527d69824caa0a5a2edb1592d1bb48d Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Mon, 20 Dec 2021 16:22:52 -0600 Subject: [PATCH] krb5: Add name attributes to krb5_principal We now have what we need in krb5_principal to implement much of RFC6680. Now we populate those fields so that they can be accessed by GSS-API RFC6680 name attributes functions. The next commit should add much of the GSS-API RFC6680 name attributes functions and functionality. --- lib/asn1/krb5.asn1 | 39 +++++++++++--- lib/krb5/asn1_glue.c | 90 +++++++++++++++++++++++++++++++++ lib/krb5/libkrb5-exports.def.in | 2 + lib/krb5/rd_req.c | 28 ++++++++-- lib/krb5/ticket.c | 21 +++++--- lib/krb5/version-script.map | 2 + 6 files changed, 163 insertions(+), 19 deletions(-) diff --git a/lib/asn1/krb5.asn1 b/lib/asn1/krb5.asn1 index baaec52f5..e604eb027 100644 --- a/lib/asn1/krb5.asn1 +++ b/lib/asn1/krb5.asn1 @@ -18,6 +18,7 @@ EXPORTS CKSUMTYPE, ChangePasswdDataMS, Checksum, + CompositePrincipal, ENCTYPE, ETYPE-INFO, ETYPE-INFO-ENTRY, @@ -440,9 +441,9 @@ Checksum ::= SEQUENCE { -- -- Attributes have three possible sources in Heimdal Kerberos at this time: -- --- - the EncKDCRepPart --- - the EncTicketPart --- - the Authenticator's AuthorizationData (if any) +-- - the EncKDCRepPart (for the client's attributes on the client side) +-- - the EncTicketPart (for the client's attributes on the server side) +-- - the Authenticator's AuthorizationData (if any; server-side) -- -- In principle there can be more: -- @@ -464,15 +465,39 @@ PrincipalNameAttrs ::= SEQUENCE { authenticated [0] BOOLEAN, -- These are compiled from the Ticket and Authenticator: source [1] PrincipalNameAttrSrc OPTIONAL, - authenticator-ad [2] AuthorizationData OPTIONAL + authenticator-ad [2] AuthorizationData OPTIONAL, + -- For the server on the client side we should keep track of the + -- transit path taken to reach it (if absent -> unknown). + -- + -- We don't learn much more about the server from the KDC. + peer-realm [3] Realm OPTIONAL, + transited [4] TransitedEncoding OPTIONAL, + pac-verified [5] BOOLEAN + -- TODO: Add requested attributes, for gss_set_name_attribute(), which + -- should cause corresponding authz-data elements to be added to + -- any TGS-REQ or to the AP-REQ's Authenticator as appropriate. +} +-- This is our type for exported composite name tokens for GSS [RFC6680]. +-- It's the same as Principal (below) as decorated with (see krb5.opt file and +-- asn1_compile usage), except it's not decorated, so the name attributes are +-- encoded/decoded. +CompositePrincipal ::= [APPLICATION 48] SEQUENCE { + name[0] PrincipalName, + realm[1] Realm, + nameattrs[2] PrincipalNameAttrs OPTIONAL } --- this is not part of RFC1510 +-- This is not part of RFC1510/RFC4120. We use this internally as our +-- krb5_principal (which is a typedef of *Principal), and in HDB entries. Principal ::= SEQUENCE { name[0] PrincipalName, realm[1] Realm - -- This will be decorated with a name-attrs field of - -- PrincipalNameAttrs type that doesn't get encoded + -- This will be decorated with an optional nameattrs field of + -- PrincipalNameAttrs type that doesn't get encoded. Same as + -- CompositePrincipal above, except that CompositePrincipal's + -- nameattrs field does get encoded, while Principal's does not: + -- + -- nameattrs[2] PrincipalNameAttrs OPTIONAL } Principals ::= SEQUENCE OF Principal diff --git a/lib/krb5/asn1_glue.c b/lib/krb5/asn1_glue.c index 6df8defbc..bee170b61 100644 --- a/lib/krb5/asn1_glue.c +++ b/lib/krb5/asn1_glue.c @@ -70,3 +70,93 @@ _krb5_principalname2krb5_principal (krb5_context context, *principal = p; return 0; } + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_ticket2krb5_principal(krb5_context context, + krb5_principal *principal, + const EncTicketPart *ticket, + const AuthorizationData *authenticator_ad) +{ + krb5_error_code ret; + krb5_principal p; + + *principal = NULL; + + ret = _krb5_principalname2krb5_principal(context, + &p, + ticket->cname, + ticket->crealm); + if (ret == 0 && + (p->nameattrs = calloc(1, sizeof(p->nameattrs[0]))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) + p->nameattrs->authenticated = 1; + if (ret == 0 && + (p->nameattrs->source = + calloc(1, sizeof(p->nameattrs->source[0]))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) { + p->nameattrs->source->element = + choice_PrincipalNameAttrSrc_enc_ticket_part; + ret = copy_EncTicketPart(ticket, + &p->nameattrs->source->u.enc_ticket_part); + /* NOTE: we don't want to keep a copy of the session key here! */ + if (ret == 0) + der_free_octet_string(&p->nameattrs->source->u.enc_ticket_part.key.keyvalue); + } + if (ret == 0 && authenticator_ad) { + p->nameattrs->authenticator_ad = + calloc(1, sizeof(p->nameattrs->authenticator_ad[0])); + if (p->nameattrs->authenticator_ad == NULL) + ret = krb5_enomem(context); + if (ret == 0) + ret = copy_AuthorizationData(authenticator_ad, + p->nameattrs->authenticator_ad); + } + + if (ret == 0) + *principal = p; + else + krb5_free_principal(context, p); + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_kdcrep2krb5_principal(krb5_context context, + krb5_principal *principal, + const EncKDCRepPart *kdcrep) +{ + krb5_error_code ret; + krb5_principal p; + + *principal = NULL; + + ret = _krb5_principalname2krb5_principal(context, + &p, + kdcrep->sname, + kdcrep->srealm); + if (ret == 0 && + (p->nameattrs = calloc(1, sizeof(p->nameattrs[0]))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) + p->nameattrs->authenticated = 1; + if (ret == 0 && + (p->nameattrs->source = + calloc(1, sizeof(p->nameattrs->source[0]))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) { + p->nameattrs->source->element = + choice_PrincipalNameAttrSrc_enc_kdc_rep_part; + ret = copy_EncKDCRepPart(kdcrep, + &p->nameattrs->source->u.enc_kdc_rep_part); + /* NOTE: we don't want to keep a copy of the session key here! */ + if (ret == 0) + der_free_octet_string(&p->nameattrs->source->u.enc_kdc_rep_part.key.keyvalue); + } + + if (ret == 0) + *principal = p; + else + krb5_free_principal(context, p); + return ret; +} diff --git a/lib/krb5/libkrb5-exports.def.in b/lib/krb5/libkrb5-exports.def.in index e016c3bec..5174d3c94 100644 --- a/lib/krb5/libkrb5-exports.def.in +++ b/lib/krb5/libkrb5-exports.def.in @@ -837,6 +837,8 @@ EXPORTS _krb5_enctype_requires_random_salt _krb5_principal2principalname _krb5_principalname2krb5_principal + _krb5_kdcrep2krb5_principal + _krb5_ticket2krb5_principal _krb5_put_int _krb5_s4u2self_to_checksumdata _krb5_HMAC_MD5_checksum diff --git a/lib/krb5/rd_req.c b/lib/krb5/rd_req.c index bd0b68b9c..ce18d715a 100644 --- a/lib/krb5/rd_req.c +++ b/lib/krb5/rd_req.c @@ -351,11 +351,6 @@ krb5_verify_ap_req2(krb5_context context, ap_req->ticket.sname, ap_req->ticket.realm); if (ret) goto out; - ret = _krb5_principalname2krb5_principal(context, - &t->client, - t->ticket.cname, - t->ticket.crealm); - if (ret) goto out; ret = decrypt_authenticator (context, &t->ticket.key, @@ -387,6 +382,27 @@ krb5_verify_ap_req2(krb5_context context, } } + /* + * The ticket authenticates the client, and conveys naming attributes that + * we want to expose in GSS using RFC6680 APIs. + * + * So we same the ticket enc-part in the client's krb5_principal object + * (note though that the session key will be absent in that copy of the + * ticket enc-part). + */ + ret = _krb5_ticket2krb5_principal(context, &t->client, &t->ticket, + ac->authenticator->authorization_data); + if (ret) goto out; + + t->client->nameattrs->peer_realm = + calloc(1, sizeof(t->client->nameattrs->peer_realm[0])); + if (t->client->nameattrs->peer_realm == NULL) { + ret = krb5_enomem(context); + goto out; + } + ret = copy_Realm(&ap_req->ticket.realm, t->client->nameattrs->peer_realm); + if (ret) goto out; + /* check addresses */ if (t->ticket.caddr @@ -1042,6 +1058,8 @@ krb5_rd_req_ctx(krb5_context context, o->ticket->client, o->keyblock, NULL); + if (ret == 0) + o->ticket->client->nameattrs->pac_verified = 1; if (ret == 0 && (context->flags & KRB5_CTX_F_REPORT_CANONICAL_CLIENT_NAME)) { krb5_error_code ret2; krb5_principal canon_name; diff --git a/lib/krb5/ticket.c b/lib/krb5/ticket.c index 11e4e8496..631fb9480 100644 --- a/lib/krb5/ticket.c +++ b/lib/krb5/ticket.c @@ -752,9 +752,9 @@ _krb5_extract_ticket(krb5_context context, /* compare client and save */ ret = _krb5_principalname2krb5_principal(context, - &tmp_principal, - rep->kdc_rep.cname, - rep->kdc_rep.crealm); + &tmp_principal, + rep->kdc_rep.cname, + rep->kdc_rep.crealm); if (ret) goto out; @@ -785,12 +785,19 @@ _krb5_extract_ticket(krb5_context context, creds->client = tmp_principal; /* check server referral and save principal */ - ret = _krb5_principalname2krb5_principal (context, - &tmp_principal, - rep->enc_part.sname, - rep->enc_part.srealm); + ret = _krb5_kdcrep2krb5_principal(context, &tmp_principal, &rep->enc_part); if (ret) goto out; + + tmp_principal->nameattrs->peer_realm = + calloc(1, sizeof(tmp_principal->nameattrs->peer_realm[0])); + if (tmp_principal->nameattrs->peer_realm == NULL) { + ret = krb5_enomem(context); + goto out; + } + ret = copy_Realm(&creds->client->realm, tmp_principal->nameattrs->peer_realm); + if (ret) goto out; + if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){ ret = check_server_referral(context, rep, diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map index b325c121b..991c4d5ff 100644 --- a/lib/krb5/version-script.map +++ b/lib/krb5/version-script.map @@ -828,6 +828,8 @@ HEIMDAL_KRB5_2.0 { _krb5_plugin_run_f; _krb5_principal2principalname; _krb5_principalname2krb5_principal; + _krb5_kdcrep2krb5_principal; + _krb5_ticket2krb5_principal; _krb5_put_int; _krb5_s4u2self_to_checksumdata; _krb5_HMAC_MD5_checksum;