2 Unix SMB/CIFS implementation.
4 Copyright (C) Guenther Deschner <gd@samba.org> 2008
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "libnet/libnet.h"
22 #include "librpc/gen_ndr/ndr_drsblobs.h"
24 #if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
26 static NTSTATUS
add_to_keytab_entries(TALLOC_CTX
*mem_ctx
,
27 struct libnet_keytab_context
*ctx
,
31 const krb5_enctype enctype
,
34 struct libnet_keytab_entry entry
;
37 entry
.name
= talloc_strdup(mem_ctx
, name
);
38 entry
.principal
= talloc_asprintf(mem_ctx
, "%s%s%s@%s",
41 name
, ctx
->dns_domain_name
);
42 entry
.enctype
= enctype
;
43 entry
.password
= blob
;
44 NT_STATUS_HAVE_NO_MEMORY(entry
.name
);
45 NT_STATUS_HAVE_NO_MEMORY(entry
.principal
);
46 NT_STATUS_HAVE_NO_MEMORY(entry
.password
.data
);
48 ADD_TO_ARRAY(mem_ctx
, struct libnet_keytab_entry
, entry
,
49 &ctx
->entries
, &ctx
->count
);
50 NT_STATUS_HAVE_NO_MEMORY(ctx
->entries
);
55 static NTSTATUS
keytab_startup(struct dssync_context
*ctx
, TALLOC_CTX
*mem_ctx
,
56 struct replUpToDateVectorBlob
**pold_utdv
)
58 krb5_error_code ret
= 0;
59 struct libnet_keytab_context
*keytab_ctx
;
60 struct libnet_keytab_entry
*entry
;
61 struct replUpToDateVectorBlob
*old_utdv
= NULL
;
64 ret
= libnet_keytab_init(mem_ctx
, ctx
->output_filename
, &keytab_ctx
);
66 return krb5_to_nt_status(ret
);
69 keytab_ctx
->dns_domain_name
= ctx
->dns_domain_name
;
70 ctx
->private_data
= keytab_ctx
;
72 principal
= talloc_asprintf(mem_ctx
, "UTDV/%s@%s",
73 ctx
->nc_dn
, ctx
->dns_domain_name
);
74 NT_STATUS_HAVE_NO_MEMORY(principal
);
76 entry
= libnet_keytab_search(keytab_ctx
, principal
, 0, ENCTYPE_NULL
,
79 enum ndr_err_code ndr_err
;
80 old_utdv
= talloc(mem_ctx
, struct replUpToDateVectorBlob
);
82 ndr_err
= ndr_pull_struct_blob(&entry
->password
, old_utdv
,
84 (ndr_pull_flags_fn_t
)ndr_pull_replUpToDateVectorBlob
);
85 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
86 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
87 ctx
->error_message
= talloc_asprintf(mem_ctx
,
88 "Failed to pull UpToDateVector: %s",
93 if (DEBUGLEVEL
>= 10) {
94 NDR_PRINT_DEBUG(replUpToDateVectorBlob
, old_utdv
);
99 *pold_utdv
= old_utdv
;
105 static NTSTATUS
keytab_finish(struct dssync_context
*ctx
, TALLOC_CTX
*mem_ctx
,
106 struct replUpToDateVectorBlob
*new_utdv
)
108 NTSTATUS status
= NT_STATUS_OK
;
109 krb5_error_code ret
= 0;
110 struct libnet_keytab_context
*keytab_ctx
=
111 (struct libnet_keytab_context
*)ctx
->private_data
;
114 enum ndr_err_code ndr_err
;
117 if (DEBUGLEVEL
>= 10) {
118 NDR_PRINT_DEBUG(replUpToDateVectorBlob
, new_utdv
);
121 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, new_utdv
,
122 (ndr_push_flags_fn_t
)ndr_push_replUpToDateVectorBlob
);
123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
124 status
= ndr_map_error2ntstatus(ndr_err
);
125 ctx
->error_message
= talloc_asprintf(mem_ctx
,
126 "Failed to push UpToDateVector: %s",
131 status
= add_to_keytab_entries(mem_ctx
, keytab_ctx
, 0,
135 if (!NT_STATUS_IS_OK(status
)) {
140 ret
= libnet_keytab_add(keytab_ctx
);
142 status
= krb5_to_nt_status(ret
);
143 ctx
->error_message
= talloc_asprintf(mem_ctx
,
144 "Failed to add entries to keytab %s: %s",
145 keytab_ctx
->keytab_name
, error_message(ret
));
149 ctx
->result_message
= talloc_asprintf(mem_ctx
,
150 "Vampired %d accounts to keytab %s",
152 keytab_ctx
->keytab_name
);
155 TALLOC_FREE(keytab_ctx
);
159 /****************************************************************
160 ****************************************************************/
162 static NTSTATUS
parse_supplemental_credentials(TALLOC_CTX
*mem_ctx
,
163 const DATA_BLOB
*blob
,
164 struct package_PrimaryKerberosCtr3
**pkb3
,
165 struct package_PrimaryKerberosCtr4
**pkb4
)
168 enum ndr_err_code ndr_err
;
169 struct supplementalCredentialsBlob scb
;
170 struct supplementalCredentialsPackage
*scpk
= NULL
;
172 struct package_PrimaryKerberosBlob
*pkb
;
173 bool newer_keys
= false;
176 ndr_err
= ndr_pull_struct_blob_all(blob
, mem_ctx
, &scb
,
177 (ndr_pull_flags_fn_t
)ndr_pull_supplementalCredentialsBlob
);
178 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
179 status
= ndr_map_error2ntstatus(ndr_err
);
182 if (scb
.sub
.signature
!=
183 SUPPLEMENTAL_CREDENTIALS_SIGNATURE
)
185 if (DEBUGLEVEL
>= 10) {
186 NDR_PRINT_DEBUG(supplementalCredentialsBlob
, &scb
);
188 status
= NT_STATUS_INVALID_PARAMETER
;
191 for (j
=0; j
< scb
.sub
.num_packages
; j
++) {
192 if (strcmp("Primary:Kerberos-Newer-Keys",
193 scb
.sub
.packages
[j
].name
) == 0)
195 scpk
= &scb
.sub
.packages
[j
];
196 if (!scpk
->data
|| !scpk
->data
[0]) {
202 } else if (strcmp("Primary:Kerberos",
203 scb
.sub
.packages
[j
].name
) == 0)
206 * grab this but don't break here:
207 * there might still be newer-keys ...
209 scpk
= &scb
.sub
.packages
[j
];
210 if (!scpk
->data
|| !scpk
->data
[0]) {
218 status
= NT_STATUS_OK
;
222 scpk_blob
= strhex_to_data_blob(mem_ctx
, scpk
->data
);
223 if (!scpk_blob
.data
) {
224 status
= NT_STATUS_NO_MEMORY
;
228 pkb
= TALLOC_ZERO_P(mem_ctx
, struct package_PrimaryKerberosBlob
);
230 status
= NT_STATUS_NO_MEMORY
;
233 ndr_err
= ndr_pull_struct_blob(&scpk_blob
, mem_ctx
, pkb
,
234 (ndr_pull_flags_fn_t
)ndr_pull_package_PrimaryKerberosBlob
);
235 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
236 status
= ndr_map_error2ntstatus(ndr_err
);
240 if (!newer_keys
&& pkb
->version
!= 3) {
241 status
= NT_STATUS_INVALID_PARAMETER
;
245 if (newer_keys
&& pkb
->version
!= 4) {
246 status
= NT_STATUS_INVALID_PARAMETER
;
250 if (pkb
->version
== 4 && pkb4
) {
251 *pkb4
= &pkb
->ctr
.ctr4
;
252 } else if (pkb
->version
== 3 && pkb3
) {
253 *pkb3
= &pkb
->ctr
.ctr3
;
256 status
= NT_STATUS_OK
;
262 static NTSTATUS
parse_object(TALLOC_CTX
*mem_ctx
,
263 struct libnet_keytab_context
*ctx
,
264 struct drsuapi_DsReplicaObjectListItemEx
*cur
)
266 NTSTATUS status
= NT_STATUS_OK
;
270 struct drsuapi_DsReplicaAttribute
*attr
;
271 bool got_pwd
= false;
273 struct package_PrimaryKerberosCtr3
*pkb3
= NULL
;
274 struct package_PrimaryKerberosCtr4
*pkb4
= NULL
;
276 char *object_dn
= NULL
;
279 uint32_t num_spns
= 0;
283 uint32_t sam_type
= 0;
285 uint32_t pwd_history_len
= 0;
286 uint8_t *pwd_history
= NULL
;
288 ZERO_STRUCT(nt_passwd
);
290 object_dn
= talloc_strdup(mem_ctx
, cur
->object
.identifier
->dn
);
292 return NT_STATUS_NO_MEMORY
;
295 DEBUG(3, ("parsing object '%s'\n", object_dn
));
297 for (i
=0; i
< cur
->object
.attribute_ctr
.num_attributes
; i
++) {
299 attr
= &cur
->object
.attribute_ctr
.attributes
[i
];
301 if (attr
->attid
== DRSUAPI_ATTRIBUTE_servicePrincipalName
) {
303 num_spns
= attr
->value_ctr
.num_values
;
304 spn
= TALLOC_ARRAY(mem_ctx
, char *, num_spns
);
305 for (count
= 0; count
< num_spns
; count
++) {
306 blob
= attr
->value_ctr
.values
[count
].blob
;
307 pull_string_talloc(spn
, NULL
, 0,
309 blob
->data
, blob
->length
,
314 if (attr
->value_ctr
.num_values
!= 1) {
318 if (!attr
->value_ctr
.values
[0].blob
) {
322 blob
= attr
->value_ctr
.values
[0].blob
;
324 switch (attr
->attid
) {
325 case DRSUAPI_ATTRIBUTE_unicodePwd
:
327 if (blob
->length
!= 16) {
331 memcpy(&nt_passwd
, blob
->data
, 16);
334 /* pick the kvno from the meta_data version,
335 * thanks, metze, for explaining this */
337 if (!cur
->meta_data_ctr
) {
340 if (cur
->meta_data_ctr
->count
!=
341 cur
->object
.attribute_ctr
.num_attributes
) {
344 kvno
= cur
->meta_data_ctr
->meta_data
[i
].version
;
346 case DRSUAPI_ATTRIBUTE_ntPwdHistory
:
347 pwd_history_len
= blob
->length
/ 16;
348 pwd_history
= blob
->data
;
350 case DRSUAPI_ATTRIBUTE_userPrincipalName
:
351 pull_string_talloc(mem_ctx
, NULL
, 0, &upn
,
352 blob
->data
, blob
->length
,
355 case DRSUAPI_ATTRIBUTE_sAMAccountName
:
356 pull_string_talloc(mem_ctx
, NULL
, 0, &name
,
357 blob
->data
, blob
->length
,
360 case DRSUAPI_ATTRIBUTE_sAMAccountType
:
361 sam_type
= IVAL(blob
->data
, 0);
363 case DRSUAPI_ATTRIBUTE_userAccountControl
:
364 uacc
= IVAL(blob
->data
, 0);
366 case DRSUAPI_ATTRIBUTE_supplementalCredentials
:
367 status
= parse_supplemental_credentials(mem_ctx
,
371 if (!NT_STATUS_IS_OK(status
)) {
372 DEBUG(2, ("parsing of supplemental "
373 "credentials failed: %s\n",
383 DEBUG(10, ("no password (unicodePwd) found - skipping.\n"));
388 status
= add_to_keytab_entries(mem_ctx
, ctx
, 0, object_dn
,
391 data_blob_talloc(mem_ctx
, name
,
393 if (!NT_STATUS_IS_OK(status
)) {
397 /* look into keytab ... */
398 struct libnet_keytab_entry
*entry
= NULL
;
399 char *principal
= NULL
;
401 DEBUG(10, ("looking for SAMACCOUNTNAME/%s@%s in keytayb...\n",
402 object_dn
, ctx
->dns_domain_name
));
404 principal
= talloc_asprintf(mem_ctx
, "%s/%s@%s",
407 ctx
->dns_domain_name
);
409 DEBUG(1, ("talloc failed\n"));
410 return NT_STATUS_NO_MEMORY
;
412 entry
= libnet_keytab_search(ctx
, principal
, 0, ENCTYPE_NULL
,
415 name
= (char *)TALLOC_MEMDUP(mem_ctx
,
416 entry
->password
.data
,
417 entry
->password
.length
);
419 DEBUG(1, ("talloc failed!"));
420 return NT_STATUS_NO_MEMORY
;
422 DEBUG(10, ("found name %s\n", name
));
426 DEBUG(10, ("entry not found\n"));
428 TALLOC_FREE(principal
);
432 DEBUG(10, ("no name (sAMAccountName) found - skipping.\n"));
436 DEBUG(1,("#%02d: %s:%d, ", ctx
->count
, name
, kvno
));
437 DEBUGADD(1,("sAMAccountType: 0x%08x, userAccountControl: 0x%08x",
440 DEBUGADD(1,(", upn: %s", upn
));
443 DEBUGADD(1, (", spns: ["));
444 for (i
= 0; i
< num_spns
; i
++) {
445 DEBUGADD(1, ("%s%s", spn
[i
],
446 (i
+1 == num_spns
)?"]":", "));
451 status
= add_to_keytab_entries(mem_ctx
, ctx
, kvno
, name
, NULL
,
452 ENCTYPE_ARCFOUR_HMAC
,
453 data_blob_talloc(mem_ctx
, nt_passwd
, 16));
455 if (!NT_STATUS_IS_OK(status
)) {
459 /* add kerberos keys (if any) */
462 for (i
=0; i
< pkb4
->num_keys
; i
++) {
463 if (!pkb4
->keys
[i
].value
) {
466 status
= add_to_keytab_entries(mem_ctx
, ctx
, kvno
,
469 pkb4
->keys
[i
].keytype
,
470 *pkb4
->keys
[i
].value
);
471 if (!NT_STATUS_IS_OK(status
)) {
475 for (i
=0; i
< pkb4
->num_old_keys
; i
++) {
476 if (!pkb4
->old_keys
[i
].value
) {
479 status
= add_to_keytab_entries(mem_ctx
, ctx
, kvno
- 1,
482 pkb4
->old_keys
[i
].keytype
,
483 *pkb4
->old_keys
[i
].value
);
484 if (!NT_STATUS_IS_OK(status
)) {
488 for (i
=0; i
< pkb4
->num_older_keys
; i
++) {
489 if (!pkb4
->older_keys
[i
].value
) {
492 status
= add_to_keytab_entries(mem_ctx
, ctx
, kvno
- 2,
495 pkb4
->older_keys
[i
].keytype
,
496 *pkb4
->older_keys
[i
].value
);
497 if (!NT_STATUS_IS_OK(status
)) {
504 for (i
=0; i
< pkb3
->num_keys
; i
++) {
505 if (!pkb3
->keys
[i
].value
) {
508 status
= add_to_keytab_entries(mem_ctx
, ctx
, kvno
, name
,
510 pkb3
->keys
[i
].keytype
,
511 *pkb3
->keys
[i
].value
);
512 if (!NT_STATUS_IS_OK(status
)) {
516 for (i
=0; i
< pkb3
->num_old_keys
; i
++) {
517 if (!pkb3
->old_keys
[i
].value
) {
520 status
= add_to_keytab_entries(mem_ctx
, ctx
, kvno
- 1,
523 pkb3
->old_keys
[i
].keytype
,
524 *pkb3
->old_keys
[i
].value
);
525 if (!NT_STATUS_IS_OK(status
)) {
531 if ((kvno
< 0) && (kvno
< pwd_history_len
)) {
535 /* add password history */
537 /* skip first entry */
545 for (; i
<pwd_history_len
; i
++) {
546 status
= add_to_keytab_entries(mem_ctx
, ctx
, kvno
--, name
, NULL
,
547 ENCTYPE_ARCFOUR_HMAC
,
548 data_blob_talloc(mem_ctx
, &pwd_history
[i
*16], 16));
549 if (!NT_STATUS_IS_OK(status
)) {
557 /****************************************************************
558 ****************************************************************/
560 static NTSTATUS
keytab_process_objects(struct dssync_context
*ctx
,
562 struct drsuapi_DsReplicaObjectListItemEx
*cur
,
563 struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
)
565 NTSTATUS status
= NT_STATUS_OK
;
566 struct libnet_keytab_context
*keytab_ctx
=
567 (struct libnet_keytab_context
*)ctx
->private_data
;
569 for (; cur
; cur
= cur
->next_object
) {
570 status
= parse_object(mem_ctx
, keytab_ctx
, cur
);
571 if (!NT_STATUS_IS_OK(status
)) {
582 static NTSTATUS
keytab_startup(struct dssync_context
*ctx
, TALLOC_CTX
*mem_ctx
,
583 struct replUpToDateVectorBlob
**pold_utdv
)
585 return NT_STATUS_NOT_SUPPORTED
;
588 static NTSTATUS
keytab_finish(struct dssync_context
*ctx
, TALLOC_CTX
*mem_ctx
,
589 struct replUpToDateVectorBlob
*new_utdv
)
591 return NT_STATUS_NOT_SUPPORTED
;
594 static NTSTATUS
keytab_process_objects(struct dssync_context
*ctx
,
596 struct drsuapi_DsReplicaObjectListItemEx
*cur
,
597 struct drsuapi_DsReplicaOIDMapping_Ctr
*mapping_ctr
)
599 return NT_STATUS_NOT_SUPPORTED
;
601 #endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */
603 const struct dssync_ops libnet_dssync_keytab_ops
= {
604 .startup
= keytab_startup
,
605 .process_objects
= keytab_process_objects
,
606 .finish
= keytab_finish
,