mirror of
https://github.com/samba-team/samba.git
synced 2025-03-01 04:58:35 +03:00
When retreiving a diff replication, the sAMAccountName attribute is usually not replicated. So in order to build the principle, we need to store the sAMAccounName in the keytab, referenced by the DN of the object, so that it can be retrieved if necessary. It is stored in the form of SAMACCOUNTNAME/object_dn@dns_domain_name with kvno=0 and ENCTYPE_NONE. Michael (This used to be commit 54e2dc1f4e0e2c7a6dcb171e51a608d831c8946e)
422 lines
11 KiB
C
422 lines
11 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
|
|
Copyright (C) Guenther Deschner <gd@samba.org> 2008
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "libnet/libnet.h"
|
|
#include "librpc/gen_ndr/ndr_drsblobs.h"
|
|
|
|
#if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
|
|
|
|
static NTSTATUS add_to_keytab_entries(TALLOC_CTX *mem_ctx,
|
|
struct libnet_keytab_context *ctx,
|
|
uint32_t kvno,
|
|
const char *name,
|
|
const char *prefix,
|
|
const krb5_enctype enctype,
|
|
DATA_BLOB blob)
|
|
{
|
|
struct libnet_keytab_entry entry;
|
|
|
|
entry.kvno = kvno;
|
|
entry.name = talloc_strdup(mem_ctx, name);
|
|
entry.principal = talloc_asprintf(mem_ctx, "%s%s%s@%s",
|
|
prefix ? prefix : "",
|
|
prefix ? "/" : "",
|
|
name, ctx->dns_domain_name);
|
|
entry.enctype = enctype;
|
|
entry.password = blob;
|
|
NT_STATUS_HAVE_NO_MEMORY(entry.name);
|
|
NT_STATUS_HAVE_NO_MEMORY(entry.principal);
|
|
NT_STATUS_HAVE_NO_MEMORY(entry.password.data);
|
|
|
|
ADD_TO_ARRAY(mem_ctx, struct libnet_keytab_entry, entry,
|
|
&ctx->entries, &ctx->count);
|
|
NT_STATUS_HAVE_NO_MEMORY(ctx->entries);
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
|
|
struct replUpToDateVectorBlob **pold_utdv)
|
|
{
|
|
krb5_error_code ret = 0;
|
|
struct libnet_keytab_context *keytab_ctx;
|
|
struct libnet_keytab_entry *entry;
|
|
struct replUpToDateVectorBlob *old_utdv = NULL;
|
|
char *principal;
|
|
|
|
ret = libnet_keytab_init(mem_ctx, ctx->output_filename, &keytab_ctx);
|
|
if (ret) {
|
|
return krb5_to_nt_status(ret);
|
|
}
|
|
|
|
keytab_ctx->dns_domain_name = ctx->dns_domain_name;
|
|
ctx->private_data = keytab_ctx;
|
|
|
|
principal = talloc_asprintf(mem_ctx, "UTDV/%s@%s",
|
|
ctx->nc_dn, ctx->dns_domain_name);
|
|
NT_STATUS_HAVE_NO_MEMORY(principal);
|
|
|
|
entry = libnet_keytab_search(keytab_ctx, principal, 0, ENCTYPE_NULL,
|
|
mem_ctx);
|
|
if (entry) {
|
|
enum ndr_err_code ndr_err;
|
|
old_utdv = talloc(mem_ctx, struct replUpToDateVectorBlob);
|
|
|
|
ndr_err = ndr_pull_struct_blob(&entry->password, old_utdv,
|
|
old_utdv,
|
|
(ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
|
|
ctx->error_message = talloc_asprintf(mem_ctx,
|
|
"Failed to pull UpToDateVector: %s",
|
|
nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
if (DEBUGLEVEL >= 10) {
|
|
NDR_PRINT_DEBUG(replUpToDateVectorBlob, old_utdv);
|
|
}
|
|
}
|
|
|
|
if (pold_utdv) {
|
|
*pold_utdv = old_utdv;
|
|
}
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
|
|
struct replUpToDateVectorBlob *new_utdv)
|
|
{
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
krb5_error_code ret = 0;
|
|
struct libnet_keytab_context *keytab_ctx =
|
|
(struct libnet_keytab_context *)ctx->private_data;
|
|
|
|
if (new_utdv) {
|
|
enum ndr_err_code ndr_err;
|
|
DATA_BLOB blob;
|
|
|
|
if (DEBUGLEVEL >= 10) {
|
|
NDR_PRINT_DEBUG(replUpToDateVectorBlob, new_utdv);
|
|
}
|
|
|
|
ndr_err = ndr_push_struct_blob(&blob, mem_ctx, new_utdv,
|
|
(ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
ctx->error_message = talloc_asprintf(mem_ctx,
|
|
"Failed to push UpToDateVector: %s",
|
|
nt_errstr(status));
|
|
goto done;
|
|
}
|
|
|
|
status = add_to_keytab_entries(mem_ctx, keytab_ctx, 0,
|
|
ctx->nc_dn, "UTDV",
|
|
ENCTYPE_NULL,
|
|
blob);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
ret = libnet_keytab_add(keytab_ctx);
|
|
if (ret) {
|
|
status = krb5_to_nt_status(ret);
|
|
ctx->error_message = talloc_asprintf(mem_ctx,
|
|
"Failed to add entries to keytab %s: %s",
|
|
keytab_ctx->keytab_name, error_message(ret));
|
|
goto done;
|
|
}
|
|
|
|
ctx->result_message = talloc_asprintf(mem_ctx,
|
|
"Vampired %d accounts to keytab %s",
|
|
keytab_ctx->count,
|
|
keytab_ctx->keytab_name);
|
|
|
|
done:
|
|
TALLOC_FREE(keytab_ctx);
|
|
return status;
|
|
}
|
|
|
|
/****************************************************************
|
|
****************************************************************/
|
|
|
|
static NTSTATUS parse_object(TALLOC_CTX *mem_ctx,
|
|
struct libnet_keytab_context *ctx,
|
|
struct drsuapi_DsReplicaObjectListItemEx *cur)
|
|
{
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
uchar nt_passwd[16];
|
|
DATA_BLOB *blob;
|
|
int i = 0;
|
|
struct drsuapi_DsReplicaAttribute *attr;
|
|
bool got_pwd = false;
|
|
|
|
char *object_dn = NULL;
|
|
char *upn = NULL;
|
|
char **spn = NULL;
|
|
uint32_t num_spns = 0;
|
|
char *name = NULL;
|
|
uint32_t kvno = 0;
|
|
uint32_t uacc = 0;
|
|
uint32_t sam_type = 0;
|
|
|
|
uint32_t pwd_history_len = 0;
|
|
uint8_t *pwd_history = NULL;
|
|
|
|
ZERO_STRUCT(nt_passwd);
|
|
|
|
object_dn = talloc_strdup(mem_ctx, cur->object.identifier->dn);
|
|
if (!object_dn) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
DEBUG(3, ("parsing object '%s'\n", object_dn));
|
|
|
|
for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
|
|
|
|
attr = &cur->object.attribute_ctr.attributes[i];
|
|
|
|
if (attr->attid == DRSUAPI_ATTRIBUTE_servicePrincipalName) {
|
|
uint32_t count;
|
|
num_spns = attr->value_ctr.num_values;
|
|
spn = TALLOC_ARRAY(mem_ctx, char *, num_spns);
|
|
for (count = 0; count < num_spns; count++) {
|
|
blob = attr->value_ctr.values[count].blob;
|
|
pull_string_talloc(spn, NULL, 0,
|
|
&spn[count],
|
|
blob->data, blob->length,
|
|
STR_UNICODE);
|
|
}
|
|
}
|
|
|
|
if (attr->value_ctr.num_values != 1) {
|
|
continue;
|
|
}
|
|
|
|
if (!attr->value_ctr.values[0].blob) {
|
|
continue;
|
|
}
|
|
|
|
blob = attr->value_ctr.values[0].blob;
|
|
|
|
switch (attr->attid) {
|
|
case DRSUAPI_ATTRIBUTE_unicodePwd:
|
|
|
|
if (blob->length != 16) {
|
|
break;
|
|
}
|
|
|
|
memcpy(&nt_passwd, blob->data, 16);
|
|
got_pwd = true;
|
|
|
|
/* pick the kvno from the meta_data version,
|
|
* thanks, metze, for explaining this */
|
|
|
|
if (!cur->meta_data_ctr) {
|
|
break;
|
|
}
|
|
if (cur->meta_data_ctr->count !=
|
|
cur->object.attribute_ctr.num_attributes) {
|
|
break;
|
|
}
|
|
kvno = cur->meta_data_ctr->meta_data[i].version;
|
|
break;
|
|
case DRSUAPI_ATTRIBUTE_ntPwdHistory:
|
|
pwd_history_len = blob->length / 16;
|
|
pwd_history = blob->data;
|
|
break;
|
|
case DRSUAPI_ATTRIBUTE_userPrincipalName:
|
|
pull_string_talloc(mem_ctx, NULL, 0, &upn,
|
|
blob->data, blob->length,
|
|
STR_UNICODE);
|
|
break;
|
|
case DRSUAPI_ATTRIBUTE_sAMAccountName:
|
|
pull_string_talloc(mem_ctx, NULL, 0, &name,
|
|
blob->data, blob->length,
|
|
STR_UNICODE);
|
|
break;
|
|
case DRSUAPI_ATTRIBUTE_sAMAccountType:
|
|
sam_type = IVAL(blob->data, 0);
|
|
break;
|
|
case DRSUAPI_ATTRIBUTE_userAccountControl:
|
|
uacc = IVAL(blob->data, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!got_pwd) {
|
|
DEBUG(10, ("no password (unicodePwd) found - skipping.\n"));
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
if (name) {
|
|
status = add_to_keytab_entries(mem_ctx, ctx, 0, object_dn,
|
|
"SAMACCOUNTNAME",
|
|
ENCTYPE_NULL,
|
|
data_blob_talloc(mem_ctx, name,
|
|
strlen(name) + 1));
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
} else {
|
|
/* look into keytab ... */
|
|
struct libnet_keytab_entry *entry = NULL;
|
|
char *principal = NULL;
|
|
|
|
DEBUG(10, ("looking for SAMACCOUNTNAME/%s@%s in keytayb...\n",
|
|
object_dn, ctx->dns_domain_name));
|
|
|
|
principal = talloc_asprintf(mem_ctx, "%s/%s@%s",
|
|
"SAMACCOUNTNAME",
|
|
object_dn,
|
|
ctx->dns_domain_name);
|
|
if (!principal) {
|
|
DEBUG(1, ("talloc failed\n"));
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
entry = libnet_keytab_search(ctx, principal, 0, ENCTYPE_NULL,
|
|
mem_ctx);
|
|
if (entry) {
|
|
name = (char *)TALLOC_MEMDUP(mem_ctx,
|
|
entry->password.data,
|
|
entry->password.length);
|
|
if (!name) {
|
|
DEBUG(1, ("talloc failed!"));
|
|
return NT_STATUS_NO_MEMORY;
|
|
} else {
|
|
DEBUG(10, ("found name %s\n", name));
|
|
}
|
|
TALLOC_FREE(entry);
|
|
} else {
|
|
DEBUG(10, ("entry not found\n"));
|
|
}
|
|
TALLOC_FREE(principal);
|
|
}
|
|
|
|
if (!name) {
|
|
DEBUG(10, ("no name (sAMAccountName) found - skipping.\n"));
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
DEBUG(1,("#%02d: %s:%d, ", ctx->count, name, kvno));
|
|
DEBUGADD(1,("sAMAccountType: 0x%08x, userAccountControl: 0x%08x",
|
|
sam_type, uacc));
|
|
if (upn) {
|
|
DEBUGADD(1,(", upn: %s", upn));
|
|
}
|
|
if (num_spns > 0) {
|
|
DEBUGADD(1, (", spns: ["));
|
|
for (i = 0; i < num_spns; i++) {
|
|
DEBUGADD(1, ("%s%s", spn[i],
|
|
(i+1 == num_spns)?"]":", "));
|
|
}
|
|
}
|
|
DEBUGADD(1,("\n"));
|
|
|
|
status = add_to_keytab_entries(mem_ctx, ctx, kvno, name, NULL,
|
|
ENCTYPE_ARCFOUR_HMAC,
|
|
data_blob_talloc(mem_ctx, nt_passwd, 16));
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if ((kvno < 0) && (kvno < pwd_history_len)) {
|
|
return status;
|
|
}
|
|
|
|
/* add password history */
|
|
|
|
/* skip first entry */
|
|
if (got_pwd) {
|
|
kvno--;
|
|
i = 1;
|
|
} else {
|
|
i = 0;
|
|
}
|
|
|
|
for (; i<pwd_history_len; i++) {
|
|
status = add_to_keytab_entries(mem_ctx, ctx, kvno--, name, NULL,
|
|
ENCTYPE_ARCFOUR_HMAC,
|
|
data_blob_talloc(mem_ctx, &pwd_history[i*16], 16));
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/****************************************************************
|
|
****************************************************************/
|
|
|
|
static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct drsuapi_DsReplicaObjectListItemEx *cur,
|
|
struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
|
|
{
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
struct libnet_keytab_context *keytab_ctx =
|
|
(struct libnet_keytab_context *)ctx->private_data;
|
|
|
|
for (; cur; cur = cur->next_object) {
|
|
status = parse_object(mem_ctx, keytab_ctx, cur);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
#else
|
|
|
|
static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
|
|
struct replUpToDateVectorBlob **pold_utdv)
|
|
{
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
|
|
struct replUpToDateVectorBlob *new_utdv)
|
|
{
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct drsuapi_DsReplicaObjectListItemEx *cur,
|
|
struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
|
|
{
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
}
|
|
#endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */
|
|
|
|
const struct dssync_ops libnet_dssync_keytab_ops = {
|
|
.startup = keytab_startup,
|
|
.process_objects = keytab_process_objects,
|
|
.finish = keytab_finish,
|
|
};
|