#include "kdc_locl.h"
-RCSID("$Id$");
-
/*
* return the realm of a krbtgt-ticket or NULL
*/
krb5_kdc_configuration *config,
hdb_entry_ex *krbtgt,
krb5_enctype enctype,
+ krb5_principal client,
krb5_const_principal server,
- KRB5SignedPathPrincipals *principals,
+ krb5_principals principals,
EncTicketPart *tkt)
{
krb5_error_code ret;
size_t size;
if (server && principals) {
- ret = add_KRB5SignedPathPrincipals(principals, server);
+ ret = add_Principals(principals, server);
if (ret)
return ret;
}
{
KRB5SignedPathData spd;
- spd.encticket = *tkt;
+ spd.client = client;
+ spd.authtime = tkt->authtime;
spd.delegated = principals;
+ spd.method_data = NULL;
ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
&spd, &size, ret);
sp.etype = enctype;
sp.delegated = principals;
+ sp.method_data = NULL;
ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0,
data.data, data.length, &sp.cksum);
check_KRB5SignedPath(krb5_context context,
krb5_kdc_configuration *config,
hdb_entry_ex *krbtgt,
+ krb5_principal cp,
EncTicketPart *tkt,
- KRB5SignedPathPrincipals **delegated,
+ krb5_principals *delegated,
int *signedpath)
{
krb5_error_code ret;
if (ret == 0) {
KRB5SignedPathData spd;
KRB5SignedPath sp;
- AuthorizationData *ad;
size_t size;
ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL);
if (ret)
return ret;
- spd.encticket = *tkt;
- /* the KRB5SignedPath is the last entry */
- ad = spd.encticket.authorization_data;
- if (--ad->len == 0)
- spd.encticket.authorization_data = NULL;
+ spd.client = cp;
+ spd.authtime = tkt->authtime;
spd.delegated = sp.delegated;
+ spd.method_data = sp.method_data;
ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
&spd, &size, ret);
- ad->len++;
- spd.encticket.authorization_data = ad;
if (ret) {
free_KRB5SignedPath(&sp);
return ret;
free(data.data);
if (ret) {
free_KRB5SignedPath(&sp);
- return ret;
+ kdc_log(context, config, 5,
+ "KRB5SignedPath not signed correctly, not marking as signed");
+ return 0;
}
if (delegated && sp.delegated) {
return ENOMEM;
}
- ret = copy_KRB5SignedPathPrincipals(*delegated, sp.delegated);
+ ret = copy_Principals(*delegated, sp.delegated);
if (ret) {
free_KRB5SignedPath(&sp);
free(*delegated);
const krb5_principal client_principal,
hdb_entry_ex *client,
hdb_entry_ex *server,
+ hdb_entry_ex *krbtgt,
const EncryptionKey *server_key,
- const EncryptionKey *krbtgt_key,
+ const EncryptionKey *krbtgt_check_key,
+ const EncryptionKey *krbtgt_sign_key,
EncTicketPart *tkt,
krb5_data *rspac,
int *signedpath)
for (j = 0; j < child.len; j++) {
if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
+ int signed_pac = 0;
krb5_pac pac;
/* Found PAC */
ret = krb5_pac_verify(context, pac, tkt->authtime,
client_principal,
- krbtgt_key, NULL);
+ krbtgt_check_key, NULL);
if (ret) {
krb5_pac_free(context, pac);
return ret;
}
ret = _kdc_pac_verify(context, client_principal,
- client, server, &pac);
+ client, server, krbtgt, &pac, &signed_pac);
if (ret) {
krb5_pac_free(context, pac);
return ret;
}
- *signedpath = 1;
-
- ret = _krb5_pac_sign(context, pac, tkt->authtime,
- client_principal,
- server_key, krbtgt_key, rspac);
+ /*
+ * Only re-sign PAC if we could verify it with the PAC
+ * function. The no-verify case happens when we get in
+ * a PAC from cross realm from a Windows domain and
+ * that there is no PAC verification function.
+ */
+ if (signed_pac) {
+ *signedpath = 1;
+ ret = _krb5_pac_sign(context, pac, tkt->authtime,
+ client_principal,
+ server_key, krbtgt_sign_key, rspac);
+ }
krb5_pac_free(context, pac);
-
+
return ret;
}
}
}
if(f.renewable){
- if(!tgt->flags.renewable){
+ if(!tgt->flags.renewable || tgt->renew_till == NULL){
kdc_log(context, config, 0,
"Bad request for renewable ticket");
return KRB5KDC_ERR_BADOPTION;
}
/*
- *
+ * Determine if constrained delegation is allowed from this client to this server
*/
static krb5_error_code
check_constrained_delegation(krb5_context context,
krb5_kdc_configuration *config,
+ HDB *clientdb,
hdb_entry_ex *client,
krb5_const_principal server)
{
krb5_error_code ret;
int i;
- ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
- if (ret) {
- krb5_clear_error_message(context);
- return ret;
- }
+ /* if client delegates to itself, that ok */
+ if (krb5_principal_compare(context, client->entry.principal, server) == TRUE)
+ return 0;
- if (acl) {
- for (i = 0; i < acl->len; i++) {
- if (krb5_principal_compare(context, server, &acl->val[i]) == TRUE)
- return 0;
+ if (clientdb->hdb_check_constrained_delegation) {
+ ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, server);
+ if (ret == 0)
+ return 0;
+ } else {
+ ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
+ if (ret) {
+ krb5_clear_error_message(context);
+ return ret;
}
+
+ if (acl) {
+ for (i = 0; i < acl->len; i++) {
+ if (krb5_principal_compare(context, server, &acl->val[i]) == TRUE)
+ return 0;
+ }
+ }
+ ret = KRB5KDC_ERR_BADOPTION;
}
kdc_log(context, config, 0,
"Bad request for constrained delegation");
- return KRB5KDC_ERR_BADOPTION;
+ return ret;
+}
+
+/*
+ * Determine if s4u2self is allowed from this client to this server
+ *
+ * For example, regardless of the principal being impersonated, if the
+ * 'client' and 'server' are the same, then it's safe.
+ */
+
+static krb5_error_code
+check_s4u2self(krb5_context context,
+ krb5_kdc_configuration *config,
+ HDB *clientdb,
+ hdb_entry_ex *client,
+ krb5_const_principal server)
+{
+ krb5_error_code ret;
+
+ /* if client does a s4u2self to itself, that ok */
+ if (krb5_principal_compare(context, client->entry.principal, server) == TRUE)
+ return 0;
+
+ if (clientdb->hdb_check_s4u2self) {
+ ret = clientdb->hdb_check_s4u2self(context, clientdb, client, server);
+ if (ret == 0)
+ return 0;
+ } else {
+ ret = KRB5KDC_ERR_BADOPTION;
+ }
+ return ret;
}
/*
KDC_REQ_BODY *b,
krb5_const_principal tgt_name,
const EncTicketPart *tgt,
+ const krb5_keyblock *replykey,
+ int rk_is_subkey,
const EncryptionKey *serverkey,
const krb5_keyblock *sessionkey,
krb5_kvno kvno,
krb5_principal client_principal,
hdb_entry_ex *krbtgt,
krb5_enctype krbtgt_etype,
- KRB5SignedPathPrincipals *spp,
+ krb5_principals spp,
const krb5_data *rspac,
const METHOD_DATA *enc_pa_data,
const char **e_text,
PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
&tgt->transited, &et,
- *krb5_princ_realm(context, client_principal),
- *krb5_princ_realm(context, server->entry.principal),
- *krb5_princ_realm(context, krbtgt->entry.principal));
+ krb5_principal_get_realm(context, client_principal),
+ krb5_principal_get_realm(context, server->entry.principal),
+ krb5_principal_get_realm(context, krbtgt->entry.principal));
if(ret)
goto out;
- copy_Realm(krb5_princ_realm(context, server_principal),
- &rep.ticket.realm);
+ copy_Realm(&server_principal->realm, &rep.ticket.realm);
_krb5_principal2principalname(&rep.ticket.sname, server_principal);
copy_Realm(&tgt_name->realm, &rep.crealm);
/*
et.endtime = *et.starttime + life;
}
if(f.renewable_ok && tgt->flags.renewable &&
- et.renew_till == NULL && et.endtime < *b->till){
+ et.renew_till == NULL && et.endtime < *b->till &&
+ tgt->renew_till != NULL)
+ {
et.flags.renewable = 1;
ALLOC(et.renew_till);
*et.renew_till = *b->till;
et.flags.hw_authent = tgt->flags.hw_authent;
et.flags.anonymous = tgt->flags.anonymous;
et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
+
+ if(rspac->length) {
+ /*
+ * No not need to filter out the any PAC from the
+ * auth_data since it's signed by the KDC.
+ */
+ ret = _kdc_tkt_add_if_relevant_ad(context, &et,
+ KRB5_AUTHDATA_WIN2K_PAC, rspac);
+ if (ret)
+ goto out;
+ }
if (auth_data) {
- /* XXX Check enc-authorization-data */
- et.authorization_data = calloc(1, sizeof(*et.authorization_data));
+ unsigned int i = 0;
+
+ /* XXX check authdata */
+
if (et.authorization_data == NULL) {
- ret = ENOMEM;
- goto out;
+ et.authorization_data = calloc(1, sizeof(*et.authorization_data));
+ if (et.authorization_data == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ }
+ for(i = 0; i < auth_data->len ; i++) {
+ ret = add_AuthorizationData(et.authorization_data, &auth_data->val[i]);
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
}
- ret = copy_AuthorizationData(auth_data, et.authorization_data);
- if (ret)
- goto out;
/* Filter out type KRB5SignedPath */
ret = find_KRB5SignedPath(context, et.authorization_data, NULL);
}
}
- if(rspac->length) {
- /*
- * No not need to filter out the any PAC from the
- * auth_data since it's signed by the KDC.
- */
- ret = _kdc_tkt_add_if_relevant_ad(context, &et,
- KRB5_AUTHDATA_WIN2K_PAC,
- rspac);
- if (ret)
- goto out;
- }
-
ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
if (ret)
goto out;
config,
krbtgt,
krbtgt_etype,
+ client_principal,
NULL,
spp,
&et);
}
if (krb5_enctype_valid(context, et.key.keytype) != 0
- && _kdc_is_weak_expection(server->entry.principal, et.key.keytype))
+ && _kdc_is_weak_exception(server->entry.principal, et.key.keytype))
{
krb5_enctype_enable(context, et.key.keytype);
is_weak = 1;
ret = _kdc_encode_reply(context, config,
&rep, &et, &ek, et.key.keytype,
kvno,
- serverkey, 0, &tgt->key, e_text, reply);
+ serverkey, 0, replykey, rk_is_subkey,
+ e_text, reply);
if (is_weak)
krb5_enctype_disable(context, et.key.keytype);
/* XXX should not re-encode this */
ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
if(ret){
- kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s",
- krb5_get_err_text(context, ret));
+ const char *msg = krb5_get_error_message(context, ret);
+ kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s", msg);
+ krb5_free_error_message(context, msg);
goto out;
}
if(buf_size != len) {
}
ret = krb5_crypto_init(context, key, 0, &crypto);
if (ret) {
+ const char *msg = krb5_get_error_message(context, ret);
free(buf);
- kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
- krb5_get_err_text(context, ret));
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
+ krb5_free_error_message(context, msg);
goto out;
}
ret = krb5_verify_checksum(context,
free(buf);
krb5_crypto_destroy(context, crypto);
if(ret){
+ const char *msg = krb5_get_error_message(context, ret);
kdc_log(context, config, 0,
- "Failed to verify authenticator checksum: %s",
- krb5_get_err_text(context, ret));
+ "Failed to verify authenticator checksum: %s", msg);
+ krb5_free_error_message(context, msg);
}
out:
free_Authenticator(auth);
if (server->name.name_string.len == 1)
name = server->name.name_string.val[0];
- if (server->name.name_string.len > 1)
+ else if (server->name.name_string.len > 1)
name = server->name.name_string.val[1];
else
return FALSE;
const struct sockaddr *from_addr,
time_t **csec,
int **cusec,
- AuthorizationData **auth_data)
+ AuthorizationData **auth_data,
+ krb5_keyblock **replykey,
+ int *rk_is_subkey)
{
krb5_ap_req ap_req;
krb5_error_code ret;
krb5_flags verify_ap_req_flags;
krb5_crypto crypto;
Key *tkey;
+ krb5_keyblock *subkey = NULL;
+ unsigned usage;
*auth_data = NULL;
*csec = NULL;
*cusec = NULL;
+ *replykey = NULL;
memset(&ap_req, 0, sizeof(ap_req));
ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
if(ret){
- kdc_log(context, config, 0, "Failed to decode AP-REQ: %s",
- krb5_get_err_text(context, ret));
+ const char *msg = krb5_get_error_message(context, ret);
+ kdc_log(context, config, 0, "Failed to decode AP-REQ: %s", msg);
+ krb5_free_error_message(context, msg);
goto out;
}
ap_req.ticket.sname,
ap_req.ticket.realm);
- ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt);
+ ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, ap_req.ticket.enc_part.kvno, NULL, krbtgt);
- if(ret) {
+ if(ret == HDB_ERR_NOT_FOUND_HERE) {
+ char *p;
+ ret = krb5_unparse_name(context, princ, &p);
+ if (ret != 0)
+ p = "<unparse_name failed>";
+ krb5_free_principal(context, princ);
+ kdc_log(context, config, 5, "Ticket-granting ticket account %s does not have secrets at this KDC, need to proxy", p);
+ if (ret == 0)
+ free(p);
+ goto out;
+ } else if(ret){
+ const char *msg = krb5_get_error_message(context, ret);
char *p;
ret = krb5_unparse_name(context, princ, &p);
if (ret != 0)
p = "<unparse_name failed>";
krb5_free_principal(context, princ);
kdc_log(context, config, 0,
- "Ticket-granting ticket not found in database: %s: %s",
- p, krb5_get_err_text(context, ret));
+ "Ticket-granting ticket not found in database: %s", msg);
+ krb5_free_error_message(context, msg);
if (ret == 0)
free(p);
ret = KRB5KRB_AP_ERR_NOT_US;
krb5_free_principal(context, princ);
if(ret) {
- kdc_log(context, config, 0, "Failed to verify AP-REQ: %s",
- krb5_get_err_text(context, ret));
+ const char *msg = krb5_get_error_message(context, ret);
+ kdc_log(context, config, 0, "Failed to verify AP-REQ: %s", msg);
+ krb5_free_error_message(context, msg);
goto out;
}
goto out;
}
- if (b->enc_authorization_data) {
- unsigned usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
- krb5_keyblock *subkey;
- krb5_data ad;
+ usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
+ *rk_is_subkey = 1;
- ret = krb5_auth_con_getremotesubkey(context,
- ac,
- &subkey);
- if(ret){
- krb5_auth_con_free(context, ac);
- kdc_log(context, config, 0, "Failed to get remote subkey: %s",
- krb5_get_err_text(context, ret));
- goto out;
- }
- if(subkey == NULL){
- usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
- ret = krb5_auth_con_getkey(context, ac, &subkey);
- if(ret) {
- krb5_auth_con_free(context, ac);
- kdc_log(context, config, 0, "Failed to get session key: %s",
- krb5_get_err_text(context, ret));
- goto out;
- }
- }
- if(subkey == NULL){
+ ret = krb5_auth_con_getremotesubkey(context, ac, &subkey);
+ if(ret){
+ const char *msg = krb5_get_error_message(context, ret);
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0, "Failed to get remote subkey: %s", msg);
+ krb5_free_error_message(context, msg);
+ goto out;
+ }
+ if(subkey == NULL){
+ usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
+ *rk_is_subkey = 0;
+
+ ret = krb5_auth_con_getkey(context, ac, &subkey);
+ if(ret) {
+ const char *msg = krb5_get_error_message(context, ret);
krb5_auth_con_free(context, ac);
- kdc_log(context, config, 0,
- "Failed to get key for enc-authorization-data");
- ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ kdc_log(context, config, 0, "Failed to get session key: %s", msg);
+ krb5_free_error_message(context, msg);
goto out;
}
+ }
+ if(subkey == NULL){
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0,
+ "Failed to get key for enc-authorization-data");
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+
+ *replykey = subkey;
+
+ if (b->enc_authorization_data) {
+ krb5_data ad;
+
ret = krb5_crypto_init(context, subkey, 0, &crypto);
if (ret) {
+ const char *msg = krb5_get_error_message(context, ret);
krb5_auth_con_free(context, ac);
- kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
- krb5_get_err_text(context, ret));
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
+ krb5_free_error_message(context, msg);
goto out;
}
ret = krb5_decrypt_EncryptedData (context,
ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
goto out;
}
- krb5_free_keyblock(context, subkey);
ALLOC(*auth_data);
if (*auth_data == NULL) {
krb5_auth_con_free(context, ac);
KDC_REQ_BODY *b,
hdb_entry_ex *krbtgt,
krb5_enctype krbtgt_etype,
+ const krb5_keyblock *replykey,
+ int rk_is_subkey,
krb5_ticket *ticket,
krb5_data *reply,
const char *from,
const char **e_text,
AuthorizationData **auth_data,
- const struct sockaddr *from_addr,
- int datagram_reply)
+ const struct sockaddr *from_addr)
{
krb5_error_code ret;
krb5_principal cp = NULL, sp = NULL;
krb5_principal client_principal = NULL;
+ krb5_principal krbtgt_principal = NULL;
char *spn = NULL, *cpn = NULL;
- hdb_entry_ex *server = NULL, *client = NULL;
+ hdb_entry_ex *server = NULL, *client = NULL, *s4u2self_impersonated_client = NULL;
+ HDB *clientdb, *s4u2self_impersonated_clientdb;
krb5_realm ref_realm = NULL;
EncTicketPart *tgt = &ticket->ticket;
- KRB5SignedPathPrincipals *spp = NULL;
+ krb5_principals spp = NULL;
const EncryptionKey *ekey;
krb5_keyblock sessionkey;
krb5_kvno kvno;
krb5_data rspac;
- int cross_realm = 0;
+
+ hdb_entry_ex *krbtgt_out = NULL;
METHOD_DATA enc_pa_data;
char opt_str[128];
int signedpath = 0;
+ Key *tkey_check;
+ Key *tkey_sign;
+
memset(&sessionkey, 0, sizeof(sessionkey));
memset(&adtkt, 0, sizeof(adtkt));
krb5_data_zero(&rspac);
}
_krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
ret = _kdc_db_fetch(context, config, p,
- HDB_F_GET_CLIENT|HDB_F_GET_SERVER,
+ HDB_F_GET_KRBTGT, t->enc_part.kvno,
NULL, &uu);
krb5_free_principal(context, p);
if(ret){
server_lookup:
ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER | HDB_F_CANON,
- NULL, &server);
+ NULL, NULL, &server);
- if(ret){
- const char *new_rlm;
+ if(ret == HDB_ERR_NOT_FOUND_HERE) {
+ kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", sp);
+ goto out;
+ } else if(ret){
+ const char *new_rlm, *msg;
Realm req_rlm;
krb5_realm *realms;
}
krb5_free_host_realm(context, realms);
}
+ msg = krb5_get_error_message(context, ret);
kdc_log(context, config, 0,
- "Server not found in database: %s: %s", spn,
- krb5_get_err_text(context, ret));
+ "Server not found in database: %s: %s", spn, msg);
+ krb5_free_error_message(context, msg);
if (ret == HDB_ERR_NOENTRY)
ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
goto out;
}
ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | HDB_F_CANON,
- NULL, &client);
- if(ret) {
- const char *krbtgt_realm;
+ NULL, &clientdb, &client);
+ if(ret == HDB_ERR_NOT_FOUND_HERE) {
+ kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", cp);
+ goto out;
+ } else if(ret){
+ const char *krbtgt_realm, *msg;
/*
* If the client belongs to the same realm as our krbtgt, it
goto out;
}
- kdc_log(context, config, 1, "Client not found in database: %s: %s",
- cpn, krb5_get_err_text(context, ret));
-
- cross_realm = 1;
+ msg = krb5_get_error_message(context, ret);
+ kdc_log(context, config, 1, "Client not found in database: %s", msg);
+ krb5_free_error_message(context, msg);
}
/*
break;
if(i == b->etype.len) {
kdc_log(context, config, 0,
- "Addition ticket have not matching etypes", spp);
+ "Addition ticket have not matching etypes");
krb5_clear_error_message(context);
- return KRB5KDC_ERR_ETYPE_NOSUPP;
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP;
+ goto out;
}
etype = b->etype.val[i];
kvno = 0;
} else {
Key *skey;
- ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len,
- &skey, &etype);
+ ret = _kdc_find_etype(context, server,
+ b->etype.val, b->etype.len, &skey);
if(ret) {
kdc_log(context, config, 0,
"Server (%s) has no support for etypes", spn);
- return ret;
+ goto out;
}
ekey = &skey->key;
+ etype = skey->key.keytype;
kvno = server->entry.kvno;
}
goto out;
}
- /*
- * Validate authoriation data
- */
-
/*
* Check that service is in the same realm as the krbtgt. If it's
* not the same, it's someone that is using a uni-directional trust
* backward.
*/
- if (strcmp(krb5_principal_get_realm(context, sp),
- krb5_principal_get_comp_string(context,
- krbtgt->entry.principal,
- 1)) != 0) {
- char *tpn;
+ /*
+ * Validate authoriation data
+ */
+
+ ret = hdb_enctype2key(context, &krbtgt->entry,
+ krbtgt_etype, &tkey_check);
+ if(ret) {
+ kdc_log(context, config, 0,
+ "Failed to find key for krbtgt PAC check");
+ goto out;
+ }
+
+ /* Now refetch the primary krbtgt, and get the current kvno (the
+ * sign check may have been on an old kvno, and the server may
+ * have been an incoming trust) */
+ ret = krb5_make_principal(context, &krbtgt_principal,
+ krb5_principal_get_comp_string(context,
+ krbtgt->entry.principal,
+ 1),
+ KRB5_TGS_NAME,
+ krb5_principal_get_comp_string(context,
+ krbtgt->entry.principal,
+ 1), NULL);
+ if(ret) {
+ kdc_log(context, config, 0,
+ "Failed to generate krbtgt principal");
+ goto out;
+ }
+
+ ret = _kdc_db_fetch(context, config, krbtgt_principal, HDB_F_GET_KRBTGT, NULL, NULL, &krbtgt_out);
+ krb5_free_principal(context, krbtgt_principal);
+ if (ret) {
+ krb5_error_code ret2;
+ char *tpn, *tpn2;
ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn);
+ ret2 = krb5_unparse_name(context, krbtgt->entry.principal, &tpn2);
kdc_log(context, config, 0,
- "Request with wrong krbtgt: %s",
- (ret == 0) ? tpn : "<unknown>");
+ "Request with wrong krbtgt: %s, %s not found in our database",
+ (ret == 0) ? tpn : "<unknown>", (ret2 == 0) ? tpn2 : "<unknown>");
if(ret == 0)
free(tpn);
+ if(ret2 == 0)
+ free(tpn2);
ret = KRB5KRB_AP_ERR_NOT_US;
goto out;
}
- /* check PAC if not cross realm and if there is one */
- if (!cross_realm) {
- Key *tkey;
+ /* The first realm is the realm of the service, the second is
+ * krbtgt/<this>/@REALM component of the krbtgt DN the request was
+ * encrypted to. The redirection via the krbtgt_out entry allows
+ * the DB to possibly correct the case of the realm (Samba4 does
+ * this) before the strcmp() */
+ if (strcmp(krb5_principal_get_realm(context, server->entry.principal),
+ krb5_principal_get_realm(context, krbtgt_out->entry.principal)) != 0) {
+ char *tpn;
+ ret = krb5_unparse_name(context, krbtgt_out->entry.principal, &tpn);
+ kdc_log(context, config, 0,
+ "Request with wrong krbtgt: %s",
+ (ret == 0) ? tpn : "<unknown>");
+ if(ret == 0)
+ free(tpn);
+ ret = KRB5KRB_AP_ERR_NOT_US;
+ }
- ret = hdb_enctype2key(context, &krbtgt->entry,
- krbtgt_etype, &tkey);
- if(ret) {
- kdc_log(context, config, 0,
- "Failed to find key for krbtgt PAC check");
- goto out;
- }
+ ret = hdb_enctype2key(context, &krbtgt_out->entry,
+ krbtgt_etype, &tkey_sign);
+ if(ret) {
+ kdc_log(context, config, 0,
+ "Failed to find key for krbtgt PAC signature");
+ goto out;
+ }
- ret = check_PAC(context, config, cp,
- client, server, ekey, &tkey->key,
- tgt, &rspac, &signedpath);
- if (ret) {
- kdc_log(context, config, 0,
- "Verify PAC failed for %s (%s) from %s with %s",
- spn, cpn, from, krb5_get_err_text(context, ret));
- goto out;
- }
+ ret = check_PAC(context, config, cp,
+ client, server, krbtgt, ekey, &tkey_check->key, &tkey_sign->key,
+ tgt, &rspac, &signedpath);
+ if (ret) {
+ const char *msg = krb5_get_error_message(context, ret);
+ kdc_log(context, config, 0,
+ "Verify PAC failed for %s (%s) from %s with %s",
+ spn, cpn, from, msg);
+ krb5_free_error_message(context, msg);
+ goto out;
}
/* also check the krbtgt for signature */
ret = check_KRB5SignedPath(context,
config,
krbtgt,
+ cp,
tgt,
&spp,
&signedpath);
if (ret) {
+ const char *msg = krb5_get_error_message(context, ret);
kdc_log(context, config, 0,
"KRB5SignedPath check failed for %s (%s) from %s with %s",
- spn, cpn, from, krb5_get_err_text(context, ret));
+ spn, cpn, from, msg);
+ krb5_free_error_message(context, msg);
goto out;
}
const PA_DATA *sdata;
int i = 0;
- sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF);
+ sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER);
if (sdata) {
krb5_crypto crypto;
krb5_data datack;
ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
if (ret) {
+ const char *msg = krb5_get_error_message(context, ret);
free_PA_S4U2Self(&self);
krb5_data_free(&datack);
- kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
- krb5_get_err_text(context, ret));
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
+ krb5_free_error_message(context, msg);
goto out;
}
krb5_data_free(&datack);
krb5_crypto_destroy(context, crypto);
if (ret) {
+ const char *msg = krb5_get_error_message(context, ret);
free_PA_S4U2Self(&self);
kdc_log(context, config, 0,
- "krb5_verify_checksum failed for S4U2Self: %s",
- krb5_get_err_text(context, ret));
+ "krb5_verify_checksum failed for S4U2Self: %s", msg);
+ krb5_free_error_message(context, msg);
goto out;
}
if (ret)
goto out;
+ /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */
+ if(rspac.data) {
+ krb5_pac p = NULL;
+ krb5_data_free(&rspac);
+ ret = _kdc_db_fetch(context, config, client_principal, HDB_F_GET_CLIENT | HDB_F_CANON,
+ NULL, &s4u2self_impersonated_clientdb, &s4u2self_impersonated_client);
+ if (ret) {
+ const char *msg;
+
+ /*
+ * If the client belongs to the same realm as our krbtgt, it
+ * should exist in the local database.
+ *
+ */
+
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ msg = krb5_get_error_message(context, ret);
+ kdc_log(context, config, 1, "S2U4Self principal to impersonate %s not found in database: %s", cpn, msg);
+ krb5_free_error_message(context, msg);
+ goto out;
+ }
+ ret = _kdc_pac_generate(context, s4u2self_impersonated_client, &p);
+ if (ret) {
+ kdc_log(context, config, 0, "PAC generation failed for -- %s",
+ selfcpn);
+ goto out;
+ }
+ if (p != NULL) {
+ ret = _krb5_pac_sign(context, p, ticket->ticket.authtime,
+ s4u2self_impersonated_client->entry.principal,
+ ekey, &tkey_sign->key,
+ &rspac);
+ krb5_pac_free(context, p);
+ if (ret) {
+ kdc_log(context, config, 0, "PAC signing failed for -- %s",
+ selfcpn);
+ goto out;
+ }
+ }
+ }
+
/*
* Check that service doing the impersonating is
* requesting a ticket to it-self.
*/
- if (krb5_principal_compare(context, cp, sp) != TRUE) {
+ ret = check_s4u2self(context, config, clientdb, client, sp);
+ if (ret) {
kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
- "to impersonate some other user "
+ "to impersonate to service "
"(tried for user %s to service %s)",
cpn, selfcpn, spn);
free(selfcpn);
- ret = KRB5KDC_ERR_BADOPTION; /* ? */
goto out;
}
if (ret) {
kdc_log(context, config, 0,
"failed to decrypt ticket for "
- "constrained delegation from %s to %s ", spn, cpn);
+ "constrained delegation from %s to %s ", cpn, spn);
goto out;
}
if (adtkt.flags.forwardable == 0) {
kdc_log(context, config, 0,
"Missing forwardable flag on ticket for "
- "constrained delegation from %s to %s ", spn, cpn);
+ "constrained delegation from %s to %s ", cpn, spn);
ret = KRB5KDC_ERR_BADOPTION;
goto out;
}
- ret = check_constrained_delegation(context, config, client, sp);
+ ret = check_constrained_delegation(context, config, clientdb,
+ client, sp);
if (ret) {
kdc_log(context, config, 0,
"constrained delegation from %s to %s not allowed",
- spn, cpn);
+ cpn, spn);
goto out;
}
ret = check_KRB5SignedPath(context,
config,
krbtgt,
+ cp,
&adtkt,
NULL,
&ad_signedpath);
if (ret == 0 && !ad_signedpath)
ret = KRB5KDC_ERR_BADOPTION;
if (ret) {
+ const char *msg = krb5_get_error_message(context, ret);
kdc_log(context, config, 0,
"KRB5SignedPath check from service %s failed "
"for delegation to %s for client %s "
"from %s failed with %s",
- spn, str, cpn, from, krb5_get_err_text(context, ret));
+ spn, str, cpn, from, msg);
+ krb5_free_error_message(context, msg);
free(str);
goto out;
}
* Check flags
*/
- ret = _kdc_check_flags(context, config,
- client, cpn,
- server, spn,
- FALSE);
+ ret = kdc_check_flags(context, config,
+ client, cpn,
+ server, spn,
+ FALSE);
if(ret)
goto out;
b,
client_principal,
tgt,
+ replykey,
+ rk_is_subkey,
ekey,
&sessionkey,
kvno,
spn,
client,
cp,
- krbtgt,
+ krbtgt_out,
krbtgt_etype,
spp,
&rspac,
krb5_data_free(&rspac);
krb5_free_keyblock_contents(context, &sessionkey);
+ if(krbtgt_out)
+ _kdc_free_ent(context, krbtgt_out);
if(server)
_kdc_free_ent(context, server);
if(client)
_kdc_free_ent(context, client);
+ if(s4u2self_impersonated_client)
+ _kdc_free_ent(context, s4u2self_impersonated_client);
if (client_principal && client_principal != cp)
krb5_free_principal(context, client_principal);
const char *e_text = NULL;
krb5_enctype krbtgt_etype = ETYPE_NULL;
+ krb5_keyblock *replykey = NULL;
+ int rk_is_subkey = 0;
time_t *csec = NULL;
int *cusec = NULL;
&e_text,
from, from_addr,
&csec, &cusec,
- &auth_data);
+ &auth_data,
+ &replykey,
+ &rk_is_subkey);
if (ret) {
kdc_log(context, config, 0,
"Failed parsing TGS-REQ from %s", from);
&req->req_body,
krbtgt,
krbtgt_etype,
+ replykey,
+ rk_is_subkey,
ticket,
data,
from,
&e_text,
&auth_data,
- from_addr,
- datagram_reply);
+ from_addr);
if (ret) {
kdc_log(context, config, 0,
"Failed building TGS-REP to %s", from);
}
out:
- if(ret && data->data == NULL){
+ if (replykey)
+ krb5_free_keyblock(context, replykey);
+ if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){
krb5_mk_error(context,
ret,
NULL,
csec,
cusec,
data);
+ ret = 0;
}
free(csec);
free(cusec);
free(auth_data);
}
- return 0;
+ return ret;
}