kdc: update PAC hooks for Samba
Samba includes the user's long-term credentials (encrypted in the AS reply key) to allow legacy authentication protocols such as NTLM to work even if the pre-authentication mechanism replaced the reply key (as PKINIT does). Samba also needs to know whether the client explicitly requested a PAC be included (or excluded), in order to defer PAC exclusion until a service ticket is issued (thereby avoiding a name binding attack if the user is renamed between TGT and service ticket issuance). References: https://bugzilla.samba.org/show_bug.cgi?id=11441 https://bugzilla.samba.org/show_bug.cgi?id=14561 Closes: #864 Original authors: - Joseph Sutton <josephsutton@catalyst.net.nz> - Andrew Bartlett <abartlet@samba.org> - Stefan Metzmacher <metze@samba.org>
This commit is contained in:
@@ -99,6 +99,7 @@ struct astgs_request_desc {
|
||||
/* only valid for tgs-req */
|
||||
unsigned int rk_is_subkey : 1;
|
||||
unsigned int fast_asserted : 1;
|
||||
unsigned int replaced_reply_key : 1;
|
||||
|
||||
krb5_crypto armor_crypto;
|
||||
|
||||
|
@@ -880,6 +880,7 @@ struct kdc_patypes {
|
||||
#define PA_ANNOUNCE 1
|
||||
#define PA_REQ_FAST 2 /* only use inside fast */
|
||||
#define PA_SYNTHETIC_OK 4
|
||||
#define PA_REPLACE_REPLY_KEY 8
|
||||
krb5_error_code (*validate)(astgs_request_t, const PA_DATA *pa);
|
||||
};
|
||||
|
||||
@@ -887,11 +888,11 @@ static const struct kdc_patypes pat[] = {
|
||||
#ifdef PKINIT
|
||||
{
|
||||
KRB5_PADATA_PK_AS_REQ, "PK-INIT(ietf)",
|
||||
PA_ANNOUNCE | PA_SYNTHETIC_OK,
|
||||
PA_ANNOUNCE | PA_SYNTHETIC_OK | PA_REPLACE_REPLY_KEY,
|
||||
pa_pkinit_validate
|
||||
},
|
||||
{
|
||||
KRB5_PADATA_PK_AS_REQ_WIN, "PK-INIT(win2k)", PA_ANNOUNCE,
|
||||
KRB5_PADATA_PK_AS_REQ_WIN, "PK-INIT(win2k)", PA_ANNOUNCE | PA_REPLACE_REPLY_KEY,
|
||||
pa_pkinit_validate
|
||||
},
|
||||
{
|
||||
@@ -920,7 +921,7 @@ static const struct kdc_patypes pat[] = {
|
||||
{ KRB5_PADATA_FX_COOKIE, "FX-COOKIE", 0, NULL },
|
||||
{
|
||||
KRB5_PADATA_GSS , "GSS",
|
||||
PA_ANNOUNCE | PA_SYNTHETIC_OK,
|
||||
PA_ANNOUNCE | PA_SYNTHETIC_OK | PA_REPLACE_REPLY_KEY,
|
||||
pa_gss_validate
|
||||
},
|
||||
};
|
||||
@@ -1731,29 +1732,31 @@ _kdc_check_anon_policy(astgs_request_t r)
|
||||
*
|
||||
*/
|
||||
|
||||
static krb5_boolean
|
||||
send_pac_p(krb5_context context, KDC_REQ *req)
|
||||
static krb5_error_code
|
||||
check_pa_pac_request(krb5_context context,
|
||||
KDC_REQ *req,
|
||||
krb5_boolean *include_pac)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
PA_PAC_REQUEST pacreq;
|
||||
const PA_DATA *pa;
|
||||
int i = 0;
|
||||
|
||||
*include_pac = TRUE;
|
||||
|
||||
pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST);
|
||||
if (pa == NULL)
|
||||
return TRUE;
|
||||
return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
|
||||
|
||||
ret = decode_PA_PAC_REQUEST(pa->padata_value.data,
|
||||
pa->padata_value.length,
|
||||
&pacreq,
|
||||
NULL);
|
||||
if (ret)
|
||||
return TRUE;
|
||||
i = pacreq.include_pac;
|
||||
return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
|
||||
*include_pac = pacreq.include_pac;
|
||||
free_PA_PAC_REQUEST(&pacreq);
|
||||
if (i == 0)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1768,8 +1771,23 @@ generate_pac(astgs_request_t r, const Key *skey, const Key *tkey)
|
||||
krb5_data data;
|
||||
uint16_t rodc_id;
|
||||
krb5_principal client;
|
||||
krb5_boolean client_sent_pac_req, pac_request;
|
||||
|
||||
ret = _kdc_pac_generate(r->context, r->client, &p);
|
||||
client_sent_pac_req =
|
||||
(check_pa_pac_request(r->context, &r->req, &pac_request) == 0);
|
||||
|
||||
/*
|
||||
* When a PA mech replaces the reply key, the PAC may include the
|
||||
* client's long term key (encrypted in the reply key) for use by
|
||||
* other shared secret authentication protocols, e.g. NTLM.
|
||||
*/
|
||||
|
||||
ret = _kdc_pac_generate(r->context,
|
||||
r->client,
|
||||
r->server,
|
||||
r->replaced_reply_key ? &r->reply_key : NULL,
|
||||
client_sent_pac_req ? &pac_request : NULL,
|
||||
&p);
|
||||
if (ret) {
|
||||
_kdc_r_log(r, 4, "PAC generation failed for -- %s",
|
||||
r->cname);
|
||||
@@ -2125,6 +2143,7 @@ _kdc_as_rep(astgs_request_t r)
|
||||
"%s pre-authentication succeeded -- %s",
|
||||
pat[n].name, r->cname);
|
||||
found_pa = 1;
|
||||
r->replaced_reply_key = (pat[n].flags & PA_REPLACE_REPLY_KEY) != 0;
|
||||
r->et.flags.pre_authent = 1;
|
||||
}
|
||||
}
|
||||
@@ -2502,7 +2521,7 @@ _kdc_as_rep(astgs_request_t r)
|
||||
}
|
||||
|
||||
/* Add the PAC */
|
||||
if (send_pac_p(r->context, req) && !r->et.flags.anonymous) {
|
||||
if (!r->et.flags.anonymous) {
|
||||
generate_pac(r, skey, krbtgt_key);
|
||||
}
|
||||
|
||||
|
@@ -1969,15 +1969,18 @@ server_lookup:
|
||||
goto out; /* kdc_check_flags() calls _kdc_audit_addreason() */
|
||||
|
||||
/* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */
|
||||
if (mspac) {
|
||||
krb5_pac_free(context, mspac);
|
||||
mspac = NULL;
|
||||
ret = _kdc_pac_generate(context, s4u2self_impersonated_client, &mspac);
|
||||
if (ret) {
|
||||
kdc_log(context, config, 4, "PAC generation failed for -- %s",
|
||||
tpn);
|
||||
goto out;
|
||||
}
|
||||
krb5_pac_free(context, mspac);
|
||||
mspac = NULL;
|
||||
|
||||
ret = _kdc_pac_generate(context,
|
||||
s4u2self_impersonated_client,
|
||||
server,
|
||||
NULL,
|
||||
NULL,
|
||||
&mspac);
|
||||
if (ret) {
|
||||
kdc_log(context, config, 4, "PAC generation failed for -- %s", tpn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
|
18
kdc/windc.c
18
kdc/windc.c
@@ -71,7 +71,10 @@ krb5_kdc_windc_init(krb5_context context)
|
||||
|
||||
struct generate_uc {
|
||||
hdb_entry_ex *client;
|
||||
hdb_entry_ex *server;
|
||||
const krb5_keyblock *reply_key;
|
||||
krb5_pac *pac;
|
||||
const krb5_boolean *pac_request;
|
||||
};
|
||||
|
||||
static krb5_error_code KRB5_LIB_CALL
|
||||
@@ -82,13 +85,22 @@ generate(krb5_context context, const void *plug, void *plugctx, void *userctx)
|
||||
|
||||
if (ft->pac_generate == NULL)
|
||||
return KRB5_PLUGIN_NO_HANDLE;
|
||||
return ft->pac_generate((void *)plug, context, uc->client, uc->pac);
|
||||
|
||||
return ft->pac_generate((void *)plug, context,
|
||||
uc->client,
|
||||
uc->server,
|
||||
uc->reply_key,
|
||||
uc->pac_request,
|
||||
uc->pac);
|
||||
}
|
||||
|
||||
|
||||
krb5_error_code
|
||||
_kdc_pac_generate(krb5_context context,
|
||||
hdb_entry_ex *client,
|
||||
hdb_entry_ex *server,
|
||||
const krb5_keyblock *reply_key,
|
||||
const krb5_boolean *pac_request,
|
||||
krb5_pac *pac)
|
||||
{
|
||||
krb5_error_code ret = 0;
|
||||
@@ -102,9 +114,11 @@ _kdc_pac_generate(krb5_context context,
|
||||
return 0;
|
||||
|
||||
if (have_plugin) {
|
||||
|
||||
uc.client = client;
|
||||
uc.server = server;
|
||||
uc.reply_key = reply_key;
|
||||
uc.pac = pac;
|
||||
uc.pac_request = pac_request;
|
||||
|
||||
ret = _krb5_plugin_run_f(context, &windc_plugin_data,
|
||||
0, &uc, generate);
|
||||
|
@@ -54,7 +54,11 @@ struct hdb_entry_ex;
|
||||
|
||||
typedef krb5_error_code
|
||||
(KRB5_CALLCONV *krb5plugin_windc_pac_generate)(void *, krb5_context,
|
||||
struct hdb_entry_ex *, krb5_pac *);
|
||||
struct hdb_entry_ex *, /* client */
|
||||
struct hdb_entry_ex *, /* server */
|
||||
const krb5_keyblock *, /* pk_replykey */
|
||||
const krb5_boolean *, /* pac_request */
|
||||
krb5_pac *);
|
||||
|
||||
typedef krb5_error_code
|
||||
(KRB5_CALLCONV *krb5plugin_windc_pac_verify)(void *, krb5_context,
|
||||
@@ -74,7 +78,7 @@ typedef krb5_error_code
|
||||
KDC_REQ *, METHOD_DATA *);
|
||||
|
||||
|
||||
#define KRB5_WINDC_PLUGIN_MINOR 6
|
||||
#define KRB5_WINDC_PLUGIN_MINOR 7
|
||||
#define KRB5_WINDC_PLUGING_MINOR KRB5_WINDC_PLUGIN_MINOR
|
||||
|
||||
typedef struct krb5plugin_windc_ftable {
|
||||
|
@@ -20,11 +20,20 @@ windc_fini(void *ctx)
|
||||
|
||||
static krb5_error_code KRB5_CALLCONV
|
||||
pac_generate(void *ctx, krb5_context context,
|
||||
struct hdb_entry_ex *client, krb5_pac *pac)
|
||||
struct hdb_entry_ex *client,
|
||||
struct hdb_entry_ex *server,
|
||||
const krb5_keyblock *pk_replykey,
|
||||
const krb5_boolean *pac_request,
|
||||
krb5_pac *pac)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_data data;
|
||||
|
||||
if (pac_request != NULL && *pac_request == FALSE) {
|
||||
*pac = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_warnx(context, "pac generate");
|
||||
|
||||
data.data = "\x00\x01";
|
||||
|
Reference in New Issue
Block a user