2 Unix SMB/CIFS implementation.
4 Copyright (C) Guenther Deschner <gd@samba.org> 2008
5 Copyright (C) Michael Adam 2008
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "libnet/libnet_dssync.h"
24 #include "libnet/libnet_keytab.h"
25 #include "librpc/gen_ndr/ndr_drsblobs.h"
29 static NTSTATUS
keytab_startup(struct dssync_context
*ctx
, TALLOC_CTX
*mem_ctx
,
30 struct replUpToDateVectorBlob
**pold_utdv
)
32 krb5_error_code ret
= 0;
33 struct libnet_keytab_context
*keytab_ctx
;
34 struct libnet_keytab_entry
*entry
;
35 struct replUpToDateVectorBlob
*old_utdv
= NULL
;
38 ret
= libnet_keytab_init(mem_ctx
, ctx
->output_filename
, &keytab_ctx
);
40 return krb5_to_nt_status(ret
);
43 keytab_ctx
->dns_domain_name
= ctx
->dns_domain_name
;
44 keytab_ctx
->clean_old_entries
= ctx
->clean_old_entries
;
45 ctx
->private_data
= keytab_ctx
;
47 principal
= talloc_asprintf(mem_ctx
, "UTDV/%s@%s",
48 ctx
->nc_dn
, ctx
->dns_domain_name
);
49 NT_STATUS_HAVE_NO_MEMORY(principal
);
51 entry
= libnet_keytab_search(keytab_ctx
, principal
, 0, ENCTYPE_NULL
,
54 enum ndr_err_code ndr_err
;
55 old_utdv
= talloc(mem_ctx
, struct replUpToDateVectorBlob
);
57 ndr_err
= ndr_pull_struct_blob(&entry
->password
, old_utdv
, old_utdv
,
58 (ndr_pull_flags_fn_t
)ndr_pull_replUpToDateVectorBlob
);
59 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
60 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
61 ctx
->error_message
= talloc_asprintf(ctx
,
62 "Failed to pull UpToDateVector: %s",
67 if (DEBUGLEVEL
>= 10) {
68 NDR_PRINT_DEBUG(replUpToDateVectorBlob
, old_utdv
);
73 *pold_utdv
= old_utdv
;
79 static NTSTATUS
keytab_finish(struct dssync_context
*ctx
, TALLOC_CTX
*mem_ctx
,
80 struct replUpToDateVectorBlob
*new_utdv
)
82 NTSTATUS status
= NT_STATUS_OK
;
83 krb5_error_code ret
= 0;
84 struct libnet_keytab_context
*keytab_ctx
=
85 (struct libnet_keytab_context
*)ctx
->private_data
;
88 enum ndr_err_code ndr_err
;
91 if (DEBUGLEVEL
>= 10) {
92 NDR_PRINT_DEBUG(replUpToDateVectorBlob
, new_utdv
);
95 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, new_utdv
,
96 (ndr_push_flags_fn_t
)ndr_push_replUpToDateVectorBlob
);
97 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
98 status
= ndr_map_error2ntstatus(ndr_err
);
99 ctx
->error_message
= talloc_asprintf(ctx
,
100 "Failed to push UpToDateVector: %s",
105 status
= libnet_keytab_add_to_keytab_entries(mem_ctx
, keytab_ctx
, 0,
109 if (!NT_STATUS_IS_OK(status
)) {
114 ret
= libnet_keytab_add(keytab_ctx
);
116 status
= krb5_to_nt_status(ret
);
117 ctx
->error_message
= talloc_asprintf(ctx
,
118 "Failed to add entries to keytab %s: %s",
119 keytab_ctx
->keytab_name
, error_message(ret
));
123 ctx
->result_message
= talloc_asprintf(ctx
,
124 "Vampired %d accounts to keytab %s",
126 keytab_ctx
->keytab_name
);
129 TALLOC_FREE(keytab_ctx
);
133 /****************************************************************
134 ****************************************************************/
136 static NTSTATUS
parse_supplemental_credentials(TALLOC_CTX
*mem_ctx
,
137 const DATA_BLOB
*blob
,
138 struct package_PrimaryKerberosCtr3
**pkb3
,
139 struct package_PrimaryKerberosCtr4
**pkb4
)
142 enum ndr_err_code ndr_err
;
143 struct supplementalCredentialsBlob scb
;
144 struct supplementalCredentialsPackage
*scpk
= NULL
;
146 struct package_PrimaryKerberosBlob
*pkb
;
147 bool newer_keys
= false;
150 ndr_err
= ndr_pull_struct_blob_all(blob
, mem_ctx
, &scb
,
151 (ndr_pull_flags_fn_t
)ndr_pull_supplementalCredentialsBlob
);
152 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
153 status
= ndr_map_error2ntstatus(ndr_err
);
156 if ((scb
.sub
.signature
!= SUPPLEMENTAL_CREDENTIALS_SIGNATURE
)
157 && (scb
.sub
.num_packages
!= 0))
159 if (DEBUGLEVEL
>= 10) {
160 NDR_PRINT_DEBUG(supplementalCredentialsBlob
, &scb
);
162 status
= NT_STATUS_INVALID_PARAMETER
;
165 for (j
=0; j
< scb
.sub
.num_packages
; j
++) {
166 if (strcmp("Primary:Kerberos-Newer-Keys",
167 scb
.sub
.packages
[j
].name
) == 0)
169 scpk
= &scb
.sub
.packages
[j
];
170 if (!scpk
->data
|| !scpk
->data
[0]) {
176 } else if (strcmp("Primary:Kerberos",
177 scb
.sub
.packages
[j
].name
) == 0)
180 * grab this but don't break here:
181 * there might still be newer-keys ...
183 scpk
= &scb
.sub
.packages
[j
];
184 if (!scpk
->data
|| !scpk
->data
[0]) {
192 status
= NT_STATUS_OK
;
196 scpk_blob
= strhex_to_data_blob(mem_ctx
, scpk
->data
);
197 if (!scpk_blob
.data
) {
198 status
= NT_STATUS_NO_MEMORY
;
202 pkb
= talloc_zero(mem_ctx
, struct package_PrimaryKerberosBlob
);
204 status
= NT_STATUS_NO_MEMORY
;
207 ndr_err
= ndr_pull_struct_blob(&scpk_blob
, mem_ctx
, pkb
,
208 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
209 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
210 status
= ndr_map_error2ntstatus(ndr_err
);
214 if (!newer_keys
&& pkb
->version
!= 3) {
215 status
= NT_STATUS_INVALID_PARAMETER
;
219 if (newer_keys
&& pkb
->version
!= 4) {
220 status
= NT_STATUS_INVALID_PARAMETER
;
224 if (pkb
->version
== 4 && pkb4
) {
225 *pkb4
= &pkb
->ctr
.ctr4
;
226 } else if (pkb
->version
== 3 && pkb3
) {
227 *pkb3
= &pkb
->ctr
.ctr3
;
230 status
= NT_STATUS_OK
;
236 static NTSTATUS
parse_object(TALLOC_CTX
*mem_ctx
,
237 struct libnet_keytab_context
*ctx
,
238 struct drsuapi_DsReplicaObjectListItemEx
*cur
)
240 NTSTATUS status
= NT_STATUS_OK
;
244 struct drsuapi_DsReplicaAttribute
*attr
;
245 bool got_pwd
= false;
247 struct package_PrimaryKerberosCtr3
*pkb3
= NULL
;
248 struct package_PrimaryKerberosCtr4
*pkb4
= NULL
;
250 char *object_dn
= NULL
;
253 uint32_t num_spns
= 0;
257 uint32_t sam_type
= 0;
259 uint32_t pwd_history_len
= 0;
260 uint8_t *pwd_history
= NULL
;
262 ZERO_STRUCT(nt_passwd
);
264 object_dn
= talloc_strdup(mem_ctx
, cur
->object
.identifier
->dn
);
266 return NT_STATUS_NO_MEMORY
;
269 DEBUG(3, ("parsing object '%s'\n", object_dn
));
271 for (i
=0; i
< cur
->object
.attribute_ctr
.num_attributes
; i
++) {
273 attr
= &cur
->object
.attribute_ctr
.attributes
[i
];
275 if (attr
->attid
== DRSUAPI_ATTID_servicePrincipalName
) {
277 num_spns
= attr
->value_ctr
.num_values
;
278 spn
= talloc_array(mem_ctx
, char *, num_spns
);
279 for (count
= 0; count
< num_spns
; count
++) {
280 blob
= attr
->value_ctr
.values
[count
].blob
;
281 pull_string_talloc(spn
, NULL
, 0,
283 blob
->data
, blob
->length
,
288 if (attr
->value_ctr
.num_values
!= 1) {
292 if (!attr
->value_ctr
.values
[0].blob
) {
296 blob
= attr
->value_ctr
.values
[0].blob
;
298 switch (attr
->attid
) {
299 case DRSUAPI_ATTID_unicodePwd
:
301 if (blob
->length
!= 16) {
305 memcpy(&nt_passwd
, blob
->data
, 16);
308 /* pick the kvno from the meta_data version,
309 * thanks, metze, for explaining this */
311 if (!cur
->meta_data_ctr
) {
314 if (cur
->meta_data_ctr
->count
!=
315 cur
->object
.attribute_ctr
.num_attributes
) {
318 kvno
= cur
->meta_data_ctr
->meta_data
[i
].version
;
320 case DRSUAPI_ATTID_ntPwdHistory
:
321 pwd_history_len
= blob
->length
/ 16;
322 pwd_history
= blob
->data
;
324 case DRSUAPI_ATTID_userPrincipalName
:
325 pull_string_talloc(mem_ctx
, NULL
, 0, &upn
,
326 blob
->data
, blob
->length
,
329 case DRSUAPI_ATTID_sAMAccountName
:
330 pull_string_talloc(mem_ctx
, NULL
, 0, &name
,
331 blob
->data
, blob
->length
,
334 case DRSUAPI_ATTID_sAMAccountType
:
335 sam_type
= IVAL(blob
->data
, 0);
337 case DRSUAPI_ATTID_userAccountControl
:
338 uacc
= IVAL(blob
->data
, 0);
340 case DRSUAPI_ATTID_supplementalCredentials
:
341 status
= parse_supplemental_credentials(mem_ctx
,
345 if (!NT_STATUS_IS_OK(status
)) {
346 DEBUG(2, ("parsing of supplemental "
347 "credentials failed: %s\n",
357 DEBUG(10, ("no password (unicodePwd) found - skipping.\n"));
362 status
= libnet_keytab_add_to_keytab_entries(mem_ctx
, ctx
, 0, object_dn
,
365 data_blob_talloc(mem_ctx
, name
,
367 if (!NT_STATUS_IS_OK(status
)) {
371 /* look into keytab ... */
372 struct libnet_keytab_entry
*entry
= NULL
;
373 char *principal
= NULL
;
375 DEBUG(10, ("looking for SAMACCOUNTNAME/%s@%s in keytayb...\n",
376 object_dn
, ctx
->dns_domain_name
));
378 principal
= talloc_asprintf(mem_ctx
, "%s/%s@%s",
381 ctx
->dns_domain_name
);
383 DEBUG(1, ("talloc failed\n"));
384 return NT_STATUS_NO_MEMORY
;
386 entry
= libnet_keytab_search(ctx
, principal
, 0, ENCTYPE_NULL
,
389 name
= (char *)talloc_memdup(mem_ctx
,
390 entry
->password
.data
,
391 entry
->password
.length
);
393 DEBUG(1, ("talloc failed!"));
394 return NT_STATUS_NO_MEMORY
;
396 DEBUG(10, ("found name %s\n", name
));
400 DEBUG(10, ("entry not found\n"));
402 TALLOC_FREE(principal
);
406 DEBUG(10, ("no name (sAMAccountName) found - skipping.\n"));
410 DEBUG(1,("#%02d: %s:%d, ", ctx
->count
, name
, kvno
));
411 DEBUGADD(1,("sAMAccountType: 0x%08x, userAccountControl: 0x%08x",
414 DEBUGADD(1,(", upn: %s", upn
));
417 DEBUGADD(1, (", spns: ["));
418 for (i
= 0; i
< num_spns
; i
++) {
419 DEBUGADD(1, ("%s%s", spn
[i
],
420 (i
+1 == num_spns
)?"]":", "));
425 status
= libnet_keytab_add_to_keytab_entries(mem_ctx
, ctx
, kvno
, name
, NULL
,
426 ENCTYPE_ARCFOUR_HMAC
,
427 data_blob_talloc(mem_ctx
, nt_passwd
, 16));
429 if (!NT_STATUS_IS_OK(status
)) {
433 /* add kerberos keys (if any) */
436 for (i
=0; i
< pkb4
->num_keys
; i
++) {
437 if (!pkb4
->keys
[i
].value
) {
440 status
= libnet_keytab_add_to_keytab_entries(mem_ctx
, ctx
, kvno
,
443 pkb4
->keys
[i
].keytype
,
444 *pkb4
->keys
[i
].value
);
445 if (!NT_STATUS_IS_OK(status
)) {
449 for (i
=0; i
< pkb4
->num_old_keys
; i
++) {
450 if (!pkb4
->old_keys
[i
].value
) {
453 status
= libnet_keytab_add_to_keytab_entries(mem_ctx
, ctx
, kvno
- 1,
456 pkb4
->old_keys
[i
].keytype
,
457 *pkb4
->old_keys
[i
].value
);
458 if (!NT_STATUS_IS_OK(status
)) {
462 for (i
=0; i
< pkb4
->num_older_keys
; i
++) {
463 if (!pkb4
->older_keys
[i
].value
) {
466 status
= libnet_keytab_add_to_keytab_entries(mem_ctx
, ctx
, kvno
- 2,
469 pkb4
->older_keys
[i
].keytype
,
470 *pkb4
->older_keys
[i
].value
);
471 if (!NT_STATUS_IS_OK(status
)) {
478 for (i
=0; i
< pkb3
->num_keys
; i
++) {
479 if (!pkb3
->keys
[i
].value
) {
482 status
= libnet_keytab_add_to_keytab_entries(mem_ctx
, ctx
, kvno
, name
,
484 pkb3
->keys
[i
].keytype
,
485 *pkb3
->keys
[i
].value
);
486 if (!NT_STATUS_IS_OK(status
)) {
490 for (i
=0; i
< pkb3
->num_old_keys
; i
++) {
491 if (!pkb3
->old_keys
[i
].value
) {
494 status
= libnet_keytab_add_to_keytab_entries(mem_ctx
, ctx
, kvno
- 1,
497 pkb3
->old_keys
[i
].keytype
,
498 *pkb3
->old_keys
[i
].value
);
499 if (!NT_STATUS_IS_OK(status
)) {
505 if ((kvno
< 0) && (kvno
< pwd_history_len
)) {
509 /* add password history */
511 /* skip first entry */
519 for (; i
<pwd_history_len
; i
++) {
520 status
= libnet_keytab_add_to_keytab_entries(mem_ctx
, ctx
, kvno
--, name
, NULL
,
521 ENCTYPE_ARCFOUR_HMAC
,
522 data_blob_talloc(mem_ctx
, &pwd_history
[i
*16], 16));
523 if (!NT_STATUS_IS_OK(status
)) {
531 static bool dn_is_in_object_list(struct dssync_context
*ctx
,
536 if (ctx
->object_count
== 0) {
540 for (count
= 0; count
< ctx
->object_count
; count
++) {
541 if (strequal(ctx
->object_dns
[count
], dn
)) {
549 /****************************************************************
550 ****************************************************************/
552 static NTSTATUS
keytab_process_objects(struct dssync_context
*ctx
,
554 struct drsuapi_DsReplicaObjectListItemEx
*cur
,
555 struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
)
557 NTSTATUS status
= NT_STATUS_OK
;
558 struct libnet_keytab_context
*keytab_ctx
=
559 (struct libnet_keytab_context
*)ctx
->private_data
;
561 for (; cur
; cur
= cur
->next_object
) {
563 * When not in single object replication mode,
564 * the object_dn list is used as a positive write filter.
566 if (!ctx
->single_object_replication
&&
567 !dn_is_in_object_list(ctx
, cur
->object
.identifier
->dn
))
572 status
= parse_object(mem_ctx
, keytab_ctx
, cur
);
573 if (!NT_STATUS_IS_OK(status
)) {
584 static NTSTATUS
keytab_startup(struct dssync_context
*ctx
, TALLOC_CTX
*mem_ctx
,
585 struct replUpToDateVectorBlob
**pold_utdv
)
587 return NT_STATUS_NOT_SUPPORTED
;
590 static NTSTATUS
keytab_finish(struct dssync_context
*ctx
, TALLOC_CTX
*mem_ctx
,
591 struct replUpToDateVectorBlob
*new_utdv
)
593 return NT_STATUS_NOT_SUPPORTED
;
596 static NTSTATUS
keytab_process_objects(struct dssync_context
*ctx
,
598 struct drsuapi_DsReplicaObjectListItemEx
*cur
,
599 struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
)
601 return NT_STATUS_NOT_SUPPORTED
;
603 #endif /* defined(HAVE_ADS) */
605 const struct dssync_ops libnet_dssync_keytab_ops
= {
606 .startup
= keytab_startup
,
607 .process_objects
= keytab_process_objects
,
608 .finish
= keytab_finish
,