const krb5_db_entry *server,
krb5_const_principal proxy);
+krb5_error_code krb5_db_get_s4u_x509_principal(krb5_context kcontext,
+ const krb5_data *client_cert,
+ krb5_const_principal in_princ,
+ unsigned int flags,
+ krb5_db_entry **entry);
+
/**
* Sort an array of @a krb5_key_data keys in descending order by their kvno.
* Key data order within a kvno is preserved.
const krb5_db_entry *server,
krb5_const_principal proxy);
- /* End of minor version 0. */
-
/*
* Optional: Free the e_data pointer of a database entry. If this method
* is not implemented, the e_data pointer in principal entries will be
*/
void (*free_principal_e_data)(krb5_context kcontext, krb5_octet *e_data);
- /* End of minor version 1 for major version 6. */
+ /* End of minor version 0. */
+
+ /*
+ * Optional: get a principal entry for S4U2Self based on X509 certificate.
+ *
+ * If flags include KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY, princ->realm
+ * indicates the request realm, but the data components should be ignored.
+ * The module can return an out-of-realm client referral as it would for
+ * get_principal().
+ *
+ * If flags does not include KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY, princ is
+ * from PA-S4U-X509-USER. If it contains data components (and not just a
+ * realm), the module should verify that it is the same as the lookup
+ * result for client_cert. The module should not return a referral.
+ */
+ krb5_error_code (*get_s4u_x509_principal)(krb5_context kcontext,
+ const krb5_data *client_cert,
+ krb5_const_principal princ,
+ unsigned int flags,
+ krb5_db_entry **entry_out);
+
+ /* End of minor version 1 for major version 7. */
} kdb_vftabl;
#endif /* !defined(_WIN32) */
return 0;
}
+static krb5_error_code
+lookup_client(krb5_context context, krb5_kdc_req *req, unsigned int flags,
+ krb5_db_entry **entry_out)
+{
+ krb5_pa_data *pa;
+ krb5_data cert;
+
+ *entry_out = NULL;
+ pa = krb5int_find_pa_data(context, req->padata, KRB5_PADATA_S4U_X509_USER);
+ if (pa != NULL && pa->length != 0 &&
+ req->client->type == KRB5_NT_X500_PRINCIPAL) {
+ cert = make_data(pa->contents, pa->length);
+ return krb5_db_get_s4u_x509_principal(context, &cert, req->client,
+ flags, entry_out);
+ } else {
+ return krb5_db_get_principal(context, req->client, flags, entry_out);
+ }
+}
+
struct as_req_state {
loop_respond_fn respond;
void *arg;
if (include_pac_p(kdc_context, state->request)) {
setflag(state->c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
}
- errcode = krb5_db_get_principal(kdc_context, state->request->client,
- state->c_flags, &state->client);
+ errcode = lookup_client(kdc_context, state->request, state->c_flags,
+ &state->client);
if (errcode == KRB5_KDB_CANTLOCK_DB)
errcode = KRB5KDC_ERR_SVC_UNAVAILABLE;
if (errcode == KRB5_KDB_NOENTRY) {
return code;
}
- if (krb5_princ_size(context, (*s4u_x509_user)->user_id.user) == 0 ||
- (*s4u_x509_user)->user_id.subject_cert.length != 0) {
+ if (krb5_princ_size(context, (*s4u_x509_user)->user_id.user) == 0 &&
+ (*s4u_x509_user)->user_id.subject_cert.length == 0) {
*status = "INVALID_S4U2SELF_REQUEST";
krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
*s4u_x509_user = NULL;
krb5_pa_data *pa_data;
int flags;
krb5_db_entry *princ;
+ krb5_s4u_userid *id;
*princ_ptr = NULL;
} else
return 0;
}
+ id = &(*s4u_x509_user)->user_id;
/*
* We need to compare the client name in the TGT with the requested
/*
* Do not attempt to lookup principals in foreign realms.
*/
- if (is_local_principal(kdc_active_realm,
- (*s4u_x509_user)->user_id.user)) {
+ if (is_local_principal(kdc_active_realm, id->user)) {
krb5_db_entry no_server;
krb5_pa_data **e_data = NULL;
return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error */
}
- code = krb5_db_get_principal(kdc_context,
- (*s4u_x509_user)->user_id.user,
- KRB5_KDB_FLAG_INCLUDE_PAC, &princ);
+ if (id->subject_cert.length != 0) {
+ code = krb5_db_get_s4u_x509_principal(kdc_context,
+ &id->subject_cert, id->user,
+ KRB5_KDB_FLAG_INCLUDE_PAC,
+ &princ);
+ if (code == 0 && id->user->length == 0) {
+ krb5_free_principal(kdc_context, id->user);
+ code = krb5_copy_principal(kdc_context, princ->princ,
+ &id->user);
+ }
+ } else {
+ code = krb5_db_get_principal(kdc_context, id->user,
+ KRB5_KDB_FLAG_INCLUDE_PAC, &princ);
+ }
if (code == KRB5_KDB_NOENTRY) {
*status = "UNKNOWN_S4U2SELF_PRINCIPAL";
return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
*/
*status = "S4U2SELF_CLIENT_NOT_OURS";
return KRB5KDC_ERR_POLICY; /* match Windows error */
+ } else if (id->user->length == 0) {
+ /*
+ * Only a KDC in the client realm can handle a certificate-only
+ * S4U2Self request. Other KDCs require a principal name and ignore
+ * the subject-certificate field.
+ */
+ *status = "INVALID_XREALM_S4U2SELF_REQUEST";
+ return KRB5KDC_ERR_POLICY; /* match Windows error */
}
return 0;
out->check_allowed_to_delegate = in->check_allowed_to_delegate;
out->free_principal_e_data = in->free_principal_e_data;
+ /* Copy fields for minor version 1 (major version 7). */
+ assert(KRB5_KDB_DAL_MAJOR_VERSION == 7);
+ out->get_s4u_x509_principal = NULL;
+ if (in->min_ver >= 1)
+ out->get_s4u_x509_principal = in->get_s4u_x509_principal;
+
/* Set defaults for optional fields. */
if (out->fetch_master_key == NULL)
out->fetch_master_key = krb5_db_def_fetch_mkey;
return v->check_allowed_to_delegate(kcontext, client, server, proxy);
}
+krb5_error_code
+krb5_db_get_s4u_x509_principal(krb5_context kcontext,
+ const krb5_data *client_cert,
+ krb5_const_principal in_princ,
+ unsigned int flags, krb5_db_entry **entry)
+{
+ krb5_error_code ret;
+ kdb_vftabl *v;
+
+ ret = get_vftabl(kcontext, &v);
+ if (ret)
+ return ret;
+ if (v->get_s4u_x509_principal == NULL)
+ return KRB5_PLUGIN_OP_NOTSUPP;
+ ret = v->get_s4u_x509_principal(kcontext, client_cert, in_princ, flags,
+ entry);
+ if (ret)
+ return ret;
+
+ /* Sort the keys in the db entry, same as get_principal(). */
+ if ((*entry)->key_data != NULL)
+ krb5_dbe_sort_key_data((*entry)->key_data, (*entry)->n_key_data);
+
+ return 0;
+}
+
void
krb5_dbe_sort_key_data(krb5_key_data *key_data, size_t key_data_length)
{
krb5_db_free
krb5_db_audit_as_req
krb5_db_check_allowed_to_delegate
+krb5_db_get_s4u_x509_principal
krb5_db_check_policy_as
krb5_db_check_policy_tgs
krb5_db_check_transited_realms