1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00

s4-kdc: fixed handling of previous vs current trust password

This sorts out the correct handling for the 'kvno=255'
problem. Windows will use the previous trust password for 1 hour after
a password set, and indicates that the previous password is being used
by sending current_kvno-1. That maps to 255 if the trust password has
not actually been changed, so the initial trust password is being
used.

Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
Andrew Tridgell 2011-09-30 06:47:08 +10:00
parent 71f3a25ff7
commit 0ef8dca9fb

View File

@ -823,12 +823,15 @@ out:
/*
* Construct an hdb_entry from a directory entry.
* The kvno is what the remote client asked for
*/
static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
struct samba_kdc_db_context *kdc_db_ctx,
TALLOC_CTX *mem_ctx, krb5_const_principal principal,
enum trust_direction direction,
struct ldb_dn *realm_dn,
unsigned flags,
uint32_t kvno,
struct ldb_message *msg,
hdb_entry_ex *entry_ex)
{
@ -840,10 +843,12 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
const struct ldb_val *password_val;
struct trustAuthInOutBlob password_blob;
struct samba_kdc_entry *p;
bool use_previous;
uint32_t current_kvno;
enum ndr_err_code ndr_err;
int ret, trust_direction_flags;
unsigned int i;
struct AuthenticationInformationArray *auth_array;
p = talloc(mem_ctx, struct samba_kdc_entry);
if (!p) {
@ -896,25 +901,58 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
goto out;
}
entry_ex->entry.kvno = 0;
/*
we usually don't have a TRUST_AUTH_TYPE_VERSION field, as
windows doesn't create one, so we rely on the fact that both
windows and Samba don't actually check the kvno and instead
just check against the latest password blob. If we do have a
TRUST_AUTH_TYPE_VERSION field then we do use it, otherwise
we just use 0.
/* we need to work out if we are going to use the current or
* the previous password hash.
* We base this on the kvno the client passes in. If the kvno
* passed in is equal to the current kvno in our database then
* we use the current structure. If it is the current kvno-1,
* then we use the previous substrucure.
*/
/* first work out the current kvno */
current_kvno = 0;
for (i=0; i < password_blob.count; i++) {
if (password_blob.current.array[i].AuthType == TRUST_AUTH_TYPE_VERSION) {
entry_ex->entry.kvno = password_blob.current.array[i].AuthInfo.version.version;
current_kvno = password_blob.current.array[i].AuthInfo.version.version;
}
}
for (i=0; i < password_blob.count; i++) {
if (password_blob.current.array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
password_utf16 = data_blob_const(password_blob.current.array[i].AuthInfo.clear.password,
password_blob.current.array[i].AuthInfo.clear.size);
/* work out whether we will use the previous or current
password */
if (password_blob.previous.count == 0) {
/* there is no previous password */
use_previous = false;
} else if (!(flags & HDB_F_KVNO_SPECIFIED) ||
kvno == current_kvno) {
use_previous = false;
} else if ((kvno+1 == current_kvno) ||
(kvno == 255 && current_kvno == 0)) {
use_previous = true;
} else {
DEBUG(1,(__location__ ": Request for unknown kvno %u - current kvno is %u\n",
kvno, current_kvno));
ret = ENOENT;
goto out;
}
if (use_previous) {
auth_array = &password_blob.previous;
} else {
auth_array = &password_blob.current;
}
/* use the kvno the client specified, if available */
if (flags & HDB_F_KVNO_SPECIFIED) {
entry_ex->entry.kvno = kvno;
} else {
entry_ex->entry.kvno = current_kvno;
}
for (i=0; i < auth_array->count; i++) {
if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
password_utf16 = data_blob_const(auth_array->array[i].AuthInfo.clear.password,
auth_array->array[i].AuthInfo.clear.size);
/* In the future, generate all sorts of
* hashes, but for now we can't safely convert
* the random strings windows uses into
@ -923,13 +961,13 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
/* but as it is utf16 already, we can get the NT password/arcfour-hmac-md5 key */
mdfour(password_hash.hash, password_utf16.data, password_utf16.length);
break;
} else if (password_blob.current.array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
password_hash = password_blob.current.array[i].AuthInfo.nt4owf.password;
} else if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
password_hash = auth_array->array[i].AuthInfo.nt4owf.password;
break;
}
}
if (i < password_blob.count) {
if (i < auth_array->count) {
Key key;
/* Must have found a cleartext or MD4 password */
entry_ex->entry.keys.val = calloc(1, sizeof(Key));
@ -946,6 +984,9 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
ENCTYPE_ARCFOUR_HMAC,
password_hash.hash, sizeof(password_hash.hash),
&key.key);
if (ret != 0) {
goto out;
}
entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
entry_ex->entry.keys.len++;
@ -1122,7 +1163,7 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
TALLOC_CTX *mem_ctx,
krb5_const_principal principal,
unsigned flags,
uint32_t krbtgt_number,
uint32_t kvno,
hdb_entry_ex *entry_ex)
{
struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
@ -1147,6 +1188,20 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
* krbtgt */
int lret;
unsigned int krbtgt_number;
/* w2k8r2 sometimes gives us a kvno of 255 for inter-domain
trust tickets. We don't yet know what this means, but we do
seem to need to treat it as unspecified */
if (flags & HDB_F_KVNO_SPECIFIED) {
krbtgt_number = SAMBA_KVNO_GET_KRBTGT(kvno);
if (kdc_db_ctx->rodc) {
if (krbtgt_number != kdc_db_ctx->my_krbtgt_number) {
return HDB_ERR_NOT_FOUND_HERE;
}
}
} else {
krbtgt_number = kdc_db_ctx->my_krbtgt_number;
}
if (krbtgt_number == kdc_db_ctx->my_krbtgt_number) {
lret = dsdb_search_one(kdc_db_ctx->samdb, mem_ctx,
@ -1251,8 +1306,8 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
}
ret = samba_kdc_trust_message2entry(context, kdc_db_ctx, mem_ctx,
principal, direction,
realm_dn, msg, entry_ex);
principal, direction,
realm_dn, flags, kvno, msg, entry_ex);
if (ret != 0) {
krb5_warnx(context, "samba_kdc_fetch: trust_message2entry failed");
}
@ -1383,20 +1438,6 @@ krb5_error_code samba_kdc_fetch(krb5_context context,
{
krb5_error_code ret = HDB_ERR_NOENTRY;
TALLOC_CTX *mem_ctx;
unsigned int krbtgt_number;
/* w2k8r2 sometimes gives us a kvno of 255 for inter-domain
trust tickets. We don't yet know what this means, but we do
seem to need to treat it as unspecified */
if ((flags & HDB_F_KVNO_SPECIFIED) && kvno != 255) {
krbtgt_number = SAMBA_KVNO_GET_KRBTGT(kvno);
if (kdc_db_ctx->rodc) {
if (krbtgt_number != kdc_db_ctx->my_krbtgt_number) {
return HDB_ERR_NOT_FOUND_HERE;
}
}
} else {
krbtgt_number = kdc_db_ctx->my_krbtgt_number;
}
mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_fetch context");
if (!mem_ctx) {
@ -1411,7 +1452,7 @@ krb5_error_code samba_kdc_fetch(krb5_context context,
}
if (flags & HDB_F_GET_SERVER) {
/* krbtgt fits into this situation for trusted realms, and for resolving different versions of our own realm name */
ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, krbtgt_number, entry_ex);
ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, kvno, entry_ex);
if (ret != HDB_ERR_NOENTRY) goto done;
/* We return 'no entry' if it does not start with krbtgt/, so move to the common case quickly */
@ -1419,7 +1460,7 @@ krb5_error_code samba_kdc_fetch(krb5_context context,
if (ret != HDB_ERR_NOENTRY) goto done;
}
if (flags & HDB_F_GET_KRBTGT) {
ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, krbtgt_number, entry_ex);
ret = samba_kdc_fetch_krbtgt(context, kdc_db_ctx, mem_ctx, principal, flags, kvno, entry_ex);
if (ret != HDB_ERR_NOENTRY) goto done;
}