s3: Fix a bad memleak in the async echo responder
[Samba/gebeck_regimport.git] / source3 / libnet / libnet_dssync_keytab.c
blobf7f706d90cd9cba0e9b5aab006c3288c8d0812ab
1 /*
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/>.
21 #include "includes.h"
22 #include "libnet/libnet.h"
23 #include "librpc/gen_ndr/ndr_drsblobs.h"
25 #if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
27 static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
28 struct replUpToDateVectorBlob **pold_utdv)
30 krb5_error_code ret = 0;
31 struct libnet_keytab_context *keytab_ctx;
32 struct libnet_keytab_entry *entry;
33 struct replUpToDateVectorBlob *old_utdv = NULL;
34 char *principal;
36 ret = libnet_keytab_init(mem_ctx, ctx->output_filename, &keytab_ctx);
37 if (ret) {
38 return krb5_to_nt_status(ret);
41 keytab_ctx->dns_domain_name = ctx->dns_domain_name;
42 keytab_ctx->clean_old_entries = ctx->clean_old_entries;
43 ctx->private_data = keytab_ctx;
45 principal = talloc_asprintf(mem_ctx, "UTDV/%s@%s",
46 ctx->nc_dn, ctx->dns_domain_name);
47 NT_STATUS_HAVE_NO_MEMORY(principal);
49 entry = libnet_keytab_search(keytab_ctx, principal, 0, ENCTYPE_NULL,
50 mem_ctx);
51 if (entry) {
52 enum ndr_err_code ndr_err;
53 old_utdv = talloc(mem_ctx, struct replUpToDateVectorBlob);
55 ndr_err = ndr_pull_struct_blob(&entry->password, old_utdv, old_utdv,
56 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
57 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
58 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
59 ctx->error_message = talloc_asprintf(ctx,
60 "Failed to pull UpToDateVector: %s",
61 nt_errstr(status));
62 return status;
65 if (DEBUGLEVEL >= 10) {
66 NDR_PRINT_DEBUG(replUpToDateVectorBlob, old_utdv);
70 if (pold_utdv) {
71 *pold_utdv = old_utdv;
74 return NT_STATUS_OK;
77 static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
78 struct replUpToDateVectorBlob *new_utdv)
80 NTSTATUS status = NT_STATUS_OK;
81 krb5_error_code ret = 0;
82 struct libnet_keytab_context *keytab_ctx =
83 (struct libnet_keytab_context *)ctx->private_data;
85 if (new_utdv) {
86 enum ndr_err_code ndr_err;
87 DATA_BLOB blob;
89 if (DEBUGLEVEL >= 10) {
90 NDR_PRINT_DEBUG(replUpToDateVectorBlob, new_utdv);
93 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, new_utdv,
94 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
95 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
96 status = ndr_map_error2ntstatus(ndr_err);
97 ctx->error_message = talloc_asprintf(ctx,
98 "Failed to push UpToDateVector: %s",
99 nt_errstr(status));
100 goto done;
103 status = libnet_keytab_add_to_keytab_entries(mem_ctx, keytab_ctx, 0,
104 ctx->nc_dn, "UTDV",
105 ENCTYPE_NULL,
106 blob);
107 if (!NT_STATUS_IS_OK(status)) {
108 goto done;
112 ret = libnet_keytab_add(keytab_ctx);
113 if (ret) {
114 status = krb5_to_nt_status(ret);
115 ctx->error_message = talloc_asprintf(ctx,
116 "Failed to add entries to keytab %s: %s",
117 keytab_ctx->keytab_name, error_message(ret));
118 goto done;
121 ctx->result_message = talloc_asprintf(ctx,
122 "Vampired %d accounts to keytab %s",
123 keytab_ctx->count,
124 keytab_ctx->keytab_name);
126 done:
127 TALLOC_FREE(keytab_ctx);
128 return status;
131 /****************************************************************
132 ****************************************************************/
134 static NTSTATUS parse_supplemental_credentials(TALLOC_CTX *mem_ctx,
135 const DATA_BLOB *blob,
136 struct package_PrimaryKerberosCtr3 **pkb3,
137 struct package_PrimaryKerberosCtr4 **pkb4)
139 NTSTATUS status;
140 enum ndr_err_code ndr_err;
141 struct supplementalCredentialsBlob scb;
142 struct supplementalCredentialsPackage *scpk = NULL;
143 DATA_BLOB scpk_blob;
144 struct package_PrimaryKerberosBlob *pkb;
145 bool newer_keys = false;
146 uint32_t j;
148 ndr_err = ndr_pull_struct_blob_all(blob, mem_ctx, &scb,
149 (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
150 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
151 status = ndr_map_error2ntstatus(ndr_err);
152 goto done;
154 if (scb.sub.signature !=
155 SUPPLEMENTAL_CREDENTIALS_SIGNATURE)
157 if (DEBUGLEVEL >= 10) {
158 NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
160 status = NT_STATUS_INVALID_PARAMETER;
161 goto done;
163 for (j=0; j < scb.sub.num_packages; j++) {
164 if (strcmp("Primary:Kerberos-Newer-Keys",
165 scb.sub.packages[j].name) == 0)
167 scpk = &scb.sub.packages[j];
168 if (!scpk->data || !scpk->data[0]) {
169 scpk = NULL;
170 continue;
172 newer_keys = true;
173 break;
174 } else if (strcmp("Primary:Kerberos",
175 scb.sub.packages[j].name) == 0)
178 * grab this but don't break here:
179 * there might still be newer-keys ...
181 scpk = &scb.sub.packages[j];
182 if (!scpk->data || !scpk->data[0]) {
183 scpk = NULL;
188 if (!scpk) {
189 /* no data */
190 status = NT_STATUS_OK;
191 goto done;
194 scpk_blob = strhex_to_data_blob(mem_ctx, scpk->data);
195 if (!scpk_blob.data) {
196 status = NT_STATUS_NO_MEMORY;
197 goto done;
200 pkb = TALLOC_ZERO_P(mem_ctx, struct package_PrimaryKerberosBlob);
201 if (!pkb) {
202 status = NT_STATUS_NO_MEMORY;
203 goto done;
205 ndr_err = ndr_pull_struct_blob(&scpk_blob, mem_ctx, pkb,
206 (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
207 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
208 status = ndr_map_error2ntstatus(ndr_err);
209 goto done;
212 if (!newer_keys && pkb->version != 3) {
213 status = NT_STATUS_INVALID_PARAMETER;
214 goto done;
217 if (newer_keys && pkb->version != 4) {
218 status = NT_STATUS_INVALID_PARAMETER;
219 goto done;
222 if (pkb->version == 4 && pkb4) {
223 *pkb4 = &pkb->ctr.ctr4;
224 } else if (pkb->version == 3 && pkb3) {
225 *pkb3 = &pkb->ctr.ctr3;
228 status = NT_STATUS_OK;
230 done:
231 return status;
234 static NTSTATUS parse_object(TALLOC_CTX *mem_ctx,
235 struct libnet_keytab_context *ctx,
236 struct drsuapi_DsReplicaObjectListItemEx *cur)
238 NTSTATUS status = NT_STATUS_OK;
239 uchar nt_passwd[16];
240 DATA_BLOB *blob;
241 int i = 0;
242 struct drsuapi_DsReplicaAttribute *attr;
243 bool got_pwd = false;
245 struct package_PrimaryKerberosCtr3 *pkb3 = NULL;
246 struct package_PrimaryKerberosCtr4 *pkb4 = NULL;
248 char *object_dn = NULL;
249 char *upn = NULL;
250 char **spn = NULL;
251 uint32_t num_spns = 0;
252 char *name = NULL;
253 uint32_t kvno = 0;
254 uint32_t uacc = 0;
255 uint32_t sam_type = 0;
257 uint32_t pwd_history_len = 0;
258 uint8_t *pwd_history = NULL;
260 ZERO_STRUCT(nt_passwd);
262 object_dn = talloc_strdup(mem_ctx, cur->object.identifier->dn);
263 if (!object_dn) {
264 return NT_STATUS_NO_MEMORY;
267 DEBUG(3, ("parsing object '%s'\n", object_dn));
269 for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
271 attr = &cur->object.attribute_ctr.attributes[i];
273 if (attr->attid == DRSUAPI_ATTRIBUTE_servicePrincipalName) {
274 uint32_t count;
275 num_spns = attr->value_ctr.num_values;
276 spn = TALLOC_ARRAY(mem_ctx, char *, num_spns);
277 for (count = 0; count < num_spns; count++) {
278 blob = attr->value_ctr.values[count].blob;
279 pull_string_talloc(spn, NULL, 0,
280 &spn[count],
281 blob->data, blob->length,
282 STR_UNICODE);
286 if (attr->value_ctr.num_values != 1) {
287 continue;
290 if (!attr->value_ctr.values[0].blob) {
291 continue;
294 blob = attr->value_ctr.values[0].blob;
296 switch (attr->attid) {
297 case DRSUAPI_ATTRIBUTE_unicodePwd:
299 if (blob->length != 16) {
300 break;
303 memcpy(&nt_passwd, blob->data, 16);
304 got_pwd = true;
306 /* pick the kvno from the meta_data version,
307 * thanks, metze, for explaining this */
309 if (!cur->meta_data_ctr) {
310 break;
312 if (cur->meta_data_ctr->count !=
313 cur->object.attribute_ctr.num_attributes) {
314 break;
316 kvno = cur->meta_data_ctr->meta_data[i].version;
317 break;
318 case DRSUAPI_ATTRIBUTE_ntPwdHistory:
319 pwd_history_len = blob->length / 16;
320 pwd_history = blob->data;
321 break;
322 case DRSUAPI_ATTRIBUTE_userPrincipalName:
323 pull_string_talloc(mem_ctx, NULL, 0, &upn,
324 blob->data, blob->length,
325 STR_UNICODE);
326 break;
327 case DRSUAPI_ATTRIBUTE_sAMAccountName:
328 pull_string_talloc(mem_ctx, NULL, 0, &name,
329 blob->data, blob->length,
330 STR_UNICODE);
331 break;
332 case DRSUAPI_ATTRIBUTE_sAMAccountType:
333 sam_type = IVAL(blob->data, 0);
334 break;
335 case DRSUAPI_ATTRIBUTE_userAccountControl:
336 uacc = IVAL(blob->data, 0);
337 break;
338 case DRSUAPI_ATTRIBUTE_supplementalCredentials:
339 status = parse_supplemental_credentials(mem_ctx,
340 blob,
341 &pkb3,
342 &pkb4);
343 if (!NT_STATUS_IS_OK(status)) {
344 DEBUG(2, ("parsing of supplemental "
345 "credentials failed: %s\n",
346 nt_errstr(status)));
348 break;
349 default:
350 break;
354 if (!got_pwd) {
355 DEBUG(10, ("no password (unicodePwd) found - skipping.\n"));
356 return NT_STATUS_OK;
359 if (name) {
360 status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, 0, object_dn,
361 "SAMACCOUNTNAME",
362 ENCTYPE_NULL,
363 data_blob_talloc(mem_ctx, name,
364 strlen(name) + 1));
365 if (!NT_STATUS_IS_OK(status)) {
366 return status;
368 } else {
369 /* look into keytab ... */
370 struct libnet_keytab_entry *entry = NULL;
371 char *principal = NULL;
373 DEBUG(10, ("looking for SAMACCOUNTNAME/%s@%s in keytayb...\n",
374 object_dn, ctx->dns_domain_name));
376 principal = talloc_asprintf(mem_ctx, "%s/%s@%s",
377 "SAMACCOUNTNAME",
378 object_dn,
379 ctx->dns_domain_name);
380 if (!principal) {
381 DEBUG(1, ("talloc failed\n"));
382 return NT_STATUS_NO_MEMORY;
384 entry = libnet_keytab_search(ctx, principal, 0, ENCTYPE_NULL,
385 mem_ctx);
386 if (entry) {
387 name = (char *)TALLOC_MEMDUP(mem_ctx,
388 entry->password.data,
389 entry->password.length);
390 if (!name) {
391 DEBUG(1, ("talloc failed!"));
392 return NT_STATUS_NO_MEMORY;
393 } else {
394 DEBUG(10, ("found name %s\n", name));
396 TALLOC_FREE(entry);
397 } else {
398 DEBUG(10, ("entry not found\n"));
400 TALLOC_FREE(principal);
403 if (!name) {
404 DEBUG(10, ("no name (sAMAccountName) found - skipping.\n"));
405 return NT_STATUS_OK;
408 DEBUG(1,("#%02d: %s:%d, ", ctx->count, name, kvno));
409 DEBUGADD(1,("sAMAccountType: 0x%08x, userAccountControl: 0x%08x",
410 sam_type, uacc));
411 if (upn) {
412 DEBUGADD(1,(", upn: %s", upn));
414 if (num_spns > 0) {
415 DEBUGADD(1, (", spns: ["));
416 for (i = 0; i < num_spns; i++) {
417 DEBUGADD(1, ("%s%s", spn[i],
418 (i+1 == num_spns)?"]":", "));
421 DEBUGADD(1,("\n"));
423 status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno, name, NULL,
424 ENCTYPE_ARCFOUR_HMAC,
425 data_blob_talloc(mem_ctx, nt_passwd, 16));
427 if (!NT_STATUS_IS_OK(status)) {
428 return status;
431 /* add kerberos keys (if any) */
433 if (pkb4) {
434 for (i=0; i < pkb4->num_keys; i++) {
435 if (!pkb4->keys[i].value) {
436 continue;
438 status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno,
439 name,
440 NULL,
441 pkb4->keys[i].keytype,
442 *pkb4->keys[i].value);
443 if (!NT_STATUS_IS_OK(status)) {
444 return status;
447 for (i=0; i < pkb4->num_old_keys; i++) {
448 if (!pkb4->old_keys[i].value) {
449 continue;
451 status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno - 1,
452 name,
453 NULL,
454 pkb4->old_keys[i].keytype,
455 *pkb4->old_keys[i].value);
456 if (!NT_STATUS_IS_OK(status)) {
457 return status;
460 for (i=0; i < pkb4->num_older_keys; i++) {
461 if (!pkb4->older_keys[i].value) {
462 continue;
464 status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno - 2,
465 name,
466 NULL,
467 pkb4->older_keys[i].keytype,
468 *pkb4->older_keys[i].value);
469 if (!NT_STATUS_IS_OK(status)) {
470 return status;
475 if (pkb3) {
476 for (i=0; i < pkb3->num_keys; i++) {
477 if (!pkb3->keys[i].value) {
478 continue;
480 status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno, name,
481 NULL,
482 pkb3->keys[i].keytype,
483 *pkb3->keys[i].value);
484 if (!NT_STATUS_IS_OK(status)) {
485 return status;
488 for (i=0; i < pkb3->num_old_keys; i++) {
489 if (!pkb3->old_keys[i].value) {
490 continue;
492 status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno - 1,
493 name,
494 NULL,
495 pkb3->old_keys[i].keytype,
496 *pkb3->old_keys[i].value);
497 if (!NT_STATUS_IS_OK(status)) {
498 return status;
503 if ((kvno < 0) && (kvno < pwd_history_len)) {
504 return status;
507 /* add password history */
509 /* skip first entry */
510 if (got_pwd) {
511 kvno--;
512 i = 1;
513 } else {
514 i = 0;
517 for (; i<pwd_history_len; i++) {
518 status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno--, name, NULL,
519 ENCTYPE_ARCFOUR_HMAC,
520 data_blob_talloc(mem_ctx, &pwd_history[i*16], 16));
521 if (!NT_STATUS_IS_OK(status)) {
522 break;
526 return status;
529 static bool dn_is_in_object_list(struct dssync_context *ctx,
530 const char *dn)
532 uint32_t count;
534 if (ctx->object_count == 0) {
535 return true;
538 for (count = 0; count < ctx->object_count; count++) {
539 if (strequal(ctx->object_dns[count], dn)) {
540 return true;
544 return false;
547 /****************************************************************
548 ****************************************************************/
550 static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
551 TALLOC_CTX *mem_ctx,
552 struct drsuapi_DsReplicaObjectListItemEx *cur,
553 struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
555 NTSTATUS status = NT_STATUS_OK;
556 struct libnet_keytab_context *keytab_ctx =
557 (struct libnet_keytab_context *)ctx->private_data;
559 for (; cur; cur = cur->next_object) {
561 * When not in single object replication mode,
562 * the object_dn list is used as a positive write filter.
564 if (!ctx->single_object_replication &&
565 !dn_is_in_object_list(ctx, cur->object.identifier->dn))
567 continue;
570 status = parse_object(mem_ctx, keytab_ctx, cur);
571 if (!NT_STATUS_IS_OK(status)) {
572 goto out;
576 out:
577 return status;
580 #else
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,
595 TALLOC_CTX *mem_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,