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 */
|
/* only valid for tgs-req */
|
||||||
unsigned int rk_is_subkey : 1;
|
unsigned int rk_is_subkey : 1;
|
||||||
unsigned int fast_asserted : 1;
|
unsigned int fast_asserted : 1;
|
||||||
|
unsigned int replaced_reply_key : 1;
|
||||||
|
|
||||||
krb5_crypto armor_crypto;
|
krb5_crypto armor_crypto;
|
||||||
|
|
||||||
|
@@ -880,6 +880,7 @@ struct kdc_patypes {
|
|||||||
#define PA_ANNOUNCE 1
|
#define PA_ANNOUNCE 1
|
||||||
#define PA_REQ_FAST 2 /* only use inside fast */
|
#define PA_REQ_FAST 2 /* only use inside fast */
|
||||||
#define PA_SYNTHETIC_OK 4
|
#define PA_SYNTHETIC_OK 4
|
||||||
|
#define PA_REPLACE_REPLY_KEY 8
|
||||||
krb5_error_code (*validate)(astgs_request_t, const PA_DATA *pa);
|
krb5_error_code (*validate)(astgs_request_t, const PA_DATA *pa);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -887,11 +888,11 @@ static const struct kdc_patypes pat[] = {
|
|||||||
#ifdef PKINIT
|
#ifdef PKINIT
|
||||||
{
|
{
|
||||||
KRB5_PADATA_PK_AS_REQ, "PK-INIT(ietf)",
|
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
|
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
|
pa_pkinit_validate
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -920,7 +921,7 @@ static const struct kdc_patypes pat[] = {
|
|||||||
{ KRB5_PADATA_FX_COOKIE, "FX-COOKIE", 0, NULL },
|
{ KRB5_PADATA_FX_COOKIE, "FX-COOKIE", 0, NULL },
|
||||||
{
|
{
|
||||||
KRB5_PADATA_GSS , "GSS",
|
KRB5_PADATA_GSS , "GSS",
|
||||||
PA_ANNOUNCE | PA_SYNTHETIC_OK,
|
PA_ANNOUNCE | PA_SYNTHETIC_OK | PA_REPLACE_REPLY_KEY,
|
||||||
pa_gss_validate
|
pa_gss_validate
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1731,29 +1732,31 @@ _kdc_check_anon_policy(astgs_request_t r)
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static krb5_boolean
|
static krb5_error_code
|
||||||
send_pac_p(krb5_context context, KDC_REQ *req)
|
check_pa_pac_request(krb5_context context,
|
||||||
|
KDC_REQ *req,
|
||||||
|
krb5_boolean *include_pac)
|
||||||
{
|
{
|
||||||
krb5_error_code ret;
|
krb5_error_code ret;
|
||||||
PA_PAC_REQUEST pacreq;
|
PA_PAC_REQUEST pacreq;
|
||||||
const PA_DATA *pa;
|
const PA_DATA *pa;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
*include_pac = TRUE;
|
||||||
|
|
||||||
pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST);
|
pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST);
|
||||||
if (pa == NULL)
|
if (pa == NULL)
|
||||||
return TRUE;
|
return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
|
||||||
|
|
||||||
ret = decode_PA_PAC_REQUEST(pa->padata_value.data,
|
ret = decode_PA_PAC_REQUEST(pa->padata_value.data,
|
||||||
pa->padata_value.length,
|
pa->padata_value.length,
|
||||||
&pacreq,
|
&pacreq,
|
||||||
NULL);
|
NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
return TRUE;
|
return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
|
||||||
i = pacreq.include_pac;
|
*include_pac = pacreq.include_pac;
|
||||||
free_PA_PAC_REQUEST(&pacreq);
|
free_PA_PAC_REQUEST(&pacreq);
|
||||||
if (i == 0)
|
return 0;
|
||||||
return FALSE;
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1768,8 +1771,23 @@ generate_pac(astgs_request_t r, const Key *skey, const Key *tkey)
|
|||||||
krb5_data data;
|
krb5_data data;
|
||||||
uint16_t rodc_id;
|
uint16_t rodc_id;
|
||||||
krb5_principal client;
|
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) {
|
if (ret) {
|
||||||
_kdc_r_log(r, 4, "PAC generation failed for -- %s",
|
_kdc_r_log(r, 4, "PAC generation failed for -- %s",
|
||||||
r->cname);
|
r->cname);
|
||||||
@@ -2125,6 +2143,7 @@ _kdc_as_rep(astgs_request_t r)
|
|||||||
"%s pre-authentication succeeded -- %s",
|
"%s pre-authentication succeeded -- %s",
|
||||||
pat[n].name, r->cname);
|
pat[n].name, r->cname);
|
||||||
found_pa = 1;
|
found_pa = 1;
|
||||||
|
r->replaced_reply_key = (pat[n].flags & PA_REPLACE_REPLY_KEY) != 0;
|
||||||
r->et.flags.pre_authent = 1;
|
r->et.flags.pre_authent = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2502,7 +2521,7 @@ _kdc_as_rep(astgs_request_t r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Add the PAC */
|
/* 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);
|
generate_pac(r, skey, krbtgt_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1969,15 +1969,18 @@ server_lookup:
|
|||||||
goto out; /* kdc_check_flags() calls _kdc_audit_addreason() */
|
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 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);
|
||||||
krb5_pac_free(context, mspac);
|
mspac = NULL;
|
||||||
mspac = NULL;
|
|
||||||
ret = _kdc_pac_generate(context, s4u2self_impersonated_client, &mspac);
|
ret = _kdc_pac_generate(context,
|
||||||
if (ret) {
|
s4u2self_impersonated_client,
|
||||||
kdc_log(context, config, 4, "PAC generation failed for -- %s",
|
server,
|
||||||
tpn);
|
NULL,
|
||||||
goto out;
|
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 {
|
struct generate_uc {
|
||||||
hdb_entry_ex *client;
|
hdb_entry_ex *client;
|
||||||
|
hdb_entry_ex *server;
|
||||||
|
const krb5_keyblock *reply_key;
|
||||||
krb5_pac *pac;
|
krb5_pac *pac;
|
||||||
|
const krb5_boolean *pac_request;
|
||||||
};
|
};
|
||||||
|
|
||||||
static krb5_error_code KRB5_LIB_CALL
|
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)
|
if (ft->pac_generate == NULL)
|
||||||
return KRB5_PLUGIN_NO_HANDLE;
|
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
|
krb5_error_code
|
||||||
_kdc_pac_generate(krb5_context context,
|
_kdc_pac_generate(krb5_context context,
|
||||||
hdb_entry_ex *client,
|
hdb_entry_ex *client,
|
||||||
|
hdb_entry_ex *server,
|
||||||
|
const krb5_keyblock *reply_key,
|
||||||
|
const krb5_boolean *pac_request,
|
||||||
krb5_pac *pac)
|
krb5_pac *pac)
|
||||||
{
|
{
|
||||||
krb5_error_code ret = 0;
|
krb5_error_code ret = 0;
|
||||||
@@ -102,9 +114,11 @@ _kdc_pac_generate(krb5_context context,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (have_plugin) {
|
if (have_plugin) {
|
||||||
|
|
||||||
uc.client = client;
|
uc.client = client;
|
||||||
|
uc.server = server;
|
||||||
|
uc.reply_key = reply_key;
|
||||||
uc.pac = pac;
|
uc.pac = pac;
|
||||||
|
uc.pac_request = pac_request;
|
||||||
|
|
||||||
ret = _krb5_plugin_run_f(context, &windc_plugin_data,
|
ret = _krb5_plugin_run_f(context, &windc_plugin_data,
|
||||||
0, &uc, generate);
|
0, &uc, generate);
|
||||||
|
@@ -54,7 +54,11 @@ struct hdb_entry_ex;
|
|||||||
|
|
||||||
typedef krb5_error_code
|
typedef krb5_error_code
|
||||||
(KRB5_CALLCONV *krb5plugin_windc_pac_generate)(void *, krb5_context,
|
(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
|
typedef krb5_error_code
|
||||||
(KRB5_CALLCONV *krb5plugin_windc_pac_verify)(void *, krb5_context,
|
(KRB5_CALLCONV *krb5plugin_windc_pac_verify)(void *, krb5_context,
|
||||||
@@ -74,7 +78,7 @@ typedef krb5_error_code
|
|||||||
KDC_REQ *, METHOD_DATA *);
|
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
|
#define KRB5_WINDC_PLUGING_MINOR KRB5_WINDC_PLUGIN_MINOR
|
||||||
|
|
||||||
typedef struct krb5plugin_windc_ftable {
|
typedef struct krb5plugin_windc_ftable {
|
||||||
|
@@ -20,11 +20,20 @@ windc_fini(void *ctx)
|
|||||||
|
|
||||||
static krb5_error_code KRB5_CALLCONV
|
static krb5_error_code KRB5_CALLCONV
|
||||||
pac_generate(void *ctx, krb5_context context,
|
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_error_code ret;
|
||||||
krb5_data data;
|
krb5_data data;
|
||||||
|
|
||||||
|
if (pac_request != NULL && *pac_request == FALSE) {
|
||||||
|
*pac = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
krb5_warnx(context, "pac generate");
|
krb5_warnx(context, "pac generate");
|
||||||
|
|
||||||
data.data = "\x00\x01";
|
data.data = "\x00\x01";
|
||||||
|
Reference in New Issue
Block a user