Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Copyright (C) Guenther Deschner <gd@samba.org> 2008
5 : Copyright (C) Michael Adam 2008
6 :
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.
11 :
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.
16 :
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/>.
19 : */
20 :
21 : #include "includes.h"
22 : #include "smb_krb5.h"
23 : #include "libnet/libnet_dssync.h"
24 : #include "libnet/libnet_keytab.h"
25 : #include "librpc/gen_ndr/ndr_drsblobs.h"
26 :
27 : #if defined(HAVE_ADS)
28 :
29 0 : static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
30 : struct replUpToDateVectorBlob **pold_utdv)
31 : {
32 0 : krb5_error_code ret = 0;
33 0 : struct libnet_keytab_context *keytab_ctx;
34 0 : struct libnet_keytab_entry *entry;
35 0 : struct replUpToDateVectorBlob *old_utdv = NULL;
36 0 : char *principal;
37 :
38 0 : ret = libnet_keytab_init(mem_ctx, ctx->output_filename, &keytab_ctx);
39 0 : if (ret) {
40 0 : return krb5_to_nt_status(ret);
41 : }
42 :
43 0 : keytab_ctx->dns_domain_name = ctx->dns_domain_name;
44 0 : keytab_ctx->clean_old_entries = ctx->clean_old_entries;
45 0 : ctx->private_data = keytab_ctx;
46 :
47 0 : principal = talloc_asprintf(mem_ctx, "UTDV/%s@%s",
48 : ctx->nc_dn, ctx->dns_domain_name);
49 0 : NT_STATUS_HAVE_NO_MEMORY(principal);
50 :
51 0 : entry = libnet_keytab_search(keytab_ctx, principal, 0, ENCTYPE_NULL,
52 : mem_ctx);
53 0 : if (entry) {
54 0 : enum ndr_err_code ndr_err;
55 0 : old_utdv = talloc(mem_ctx, struct replUpToDateVectorBlob);
56 :
57 0 : ndr_err = ndr_pull_struct_blob(&entry->password, old_utdv, old_utdv,
58 : (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
59 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
60 0 : NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
61 0 : ctx->error_message = talloc_asprintf(ctx,
62 : "Failed to pull UpToDateVector: %s",
63 : nt_errstr(status));
64 0 : return status;
65 : }
66 :
67 0 : if (DEBUGLEVEL >= 10) {
68 0 : NDR_PRINT_DEBUG(replUpToDateVectorBlob, old_utdv);
69 : }
70 : }
71 :
72 0 : if (pold_utdv) {
73 0 : *pold_utdv = old_utdv;
74 : }
75 :
76 0 : return NT_STATUS_OK;
77 : }
78 :
79 0 : static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
80 : struct replUpToDateVectorBlob *new_utdv)
81 : {
82 0 : NTSTATUS status = NT_STATUS_OK;
83 0 : krb5_error_code ret = 0;
84 0 : struct libnet_keytab_context *keytab_ctx =
85 : (struct libnet_keytab_context *)ctx->private_data;
86 :
87 0 : if (new_utdv) {
88 0 : enum ndr_err_code ndr_err;
89 0 : DATA_BLOB blob;
90 :
91 0 : if (DEBUGLEVEL >= 10) {
92 0 : NDR_PRINT_DEBUG(replUpToDateVectorBlob, new_utdv);
93 : }
94 :
95 0 : ndr_err = ndr_push_struct_blob(&blob, mem_ctx, new_utdv,
96 : (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
97 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
98 0 : status = ndr_map_error2ntstatus(ndr_err);
99 0 : ctx->error_message = talloc_asprintf(ctx,
100 : "Failed to push UpToDateVector: %s",
101 : nt_errstr(status));
102 0 : goto done;
103 : }
104 :
105 0 : status = libnet_keytab_add_to_keytab_entries(mem_ctx, keytab_ctx, 0,
106 : ctx->nc_dn, "UTDV",
107 : ENCTYPE_NULL,
108 : blob);
109 0 : if (!NT_STATUS_IS_OK(status)) {
110 0 : goto done;
111 : }
112 : }
113 :
114 0 : ret = libnet_keytab_add(keytab_ctx);
115 0 : if (ret) {
116 0 : status = krb5_to_nt_status(ret);
117 0 : ctx->error_message = talloc_asprintf(ctx,
118 : "Failed to add entries to keytab %s: %s",
119 : keytab_ctx->keytab_name, error_message(ret));
120 0 : goto done;
121 : }
122 :
123 0 : ctx->result_message = talloc_asprintf(ctx,
124 : "Vampired %d accounts to keytab %s",
125 : keytab_ctx->count,
126 : keytab_ctx->keytab_name);
127 :
128 0 : done:
129 0 : TALLOC_FREE(keytab_ctx);
130 0 : return status;
131 : }
132 :
133 : /****************************************************************
134 : ****************************************************************/
135 :
136 0 : static NTSTATUS parse_supplemental_credentials(TALLOC_CTX *mem_ctx,
137 : const DATA_BLOB *blob,
138 : struct package_PrimaryKerberosCtr3 **pkb3,
139 : struct package_PrimaryKerberosCtr4 **pkb4)
140 : {
141 0 : NTSTATUS status;
142 0 : enum ndr_err_code ndr_err;
143 0 : struct supplementalCredentialsBlob scb;
144 0 : struct supplementalCredentialsPackage *scpk = NULL;
145 0 : DATA_BLOB scpk_blob;
146 0 : struct package_PrimaryKerberosBlob *pkb;
147 0 : bool newer_keys = false;
148 0 : uint32_t j;
149 :
150 0 : ndr_err = ndr_pull_struct_blob_all(blob, mem_ctx, &scb,
151 : (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
152 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
153 0 : status = ndr_map_error2ntstatus(ndr_err);
154 0 : goto done;
155 : }
156 0 : if ((scb.sub.signature != SUPPLEMENTAL_CREDENTIALS_SIGNATURE)
157 0 : && (scb.sub.num_packages != 0))
158 : {
159 0 : if (DEBUGLEVEL >= 10) {
160 0 : NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
161 : }
162 0 : status = NT_STATUS_INVALID_PARAMETER;
163 0 : goto done;
164 : }
165 0 : for (j=0; j < scb.sub.num_packages; j++) {
166 0 : if (strcmp("Primary:Kerberos-Newer-Keys",
167 0 : scb.sub.packages[j].name) == 0)
168 : {
169 0 : scpk = &scb.sub.packages[j];
170 0 : if (!scpk->data || !scpk->data[0]) {
171 0 : scpk = NULL;
172 0 : continue;
173 : }
174 0 : newer_keys = true;
175 0 : break;
176 0 : } else if (strcmp("Primary:Kerberos",
177 0 : scb.sub.packages[j].name) == 0)
178 : {
179 : /*
180 : * grab this but don't break here:
181 : * there might still be newer-keys ...
182 : */
183 0 : scpk = &scb.sub.packages[j];
184 0 : if (!scpk->data || !scpk->data[0]) {
185 0 : scpk = NULL;
186 : }
187 : }
188 : }
189 :
190 0 : if (!scpk) {
191 : /* no data */
192 0 : status = NT_STATUS_OK;
193 0 : goto done;
194 : }
195 :
196 0 : scpk_blob = strhex_to_data_blob(mem_ctx, scpk->data);
197 0 : if (!scpk_blob.data) {
198 0 : status = NT_STATUS_NO_MEMORY;
199 0 : goto done;
200 : }
201 :
202 0 : pkb = talloc_zero(mem_ctx, struct package_PrimaryKerberosBlob);
203 0 : if (!pkb) {
204 0 : status = NT_STATUS_NO_MEMORY;
205 0 : goto done;
206 : }
207 0 : ndr_err = ndr_pull_struct_blob(&scpk_blob, mem_ctx, pkb,
208 : (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
209 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
210 0 : status = ndr_map_error2ntstatus(ndr_err);
211 0 : goto done;
212 : }
213 :
214 0 : if (!newer_keys && pkb->version != 3) {
215 0 : status = NT_STATUS_INVALID_PARAMETER;
216 0 : goto done;
217 : }
218 :
219 0 : if (newer_keys && pkb->version != 4) {
220 0 : status = NT_STATUS_INVALID_PARAMETER;
221 0 : goto done;
222 : }
223 :
224 0 : if (pkb->version == 4 && pkb4) {
225 0 : *pkb4 = &pkb->ctr.ctr4;
226 0 : } else if (pkb->version == 3 && pkb3) {
227 0 : *pkb3 = &pkb->ctr.ctr3;
228 : }
229 :
230 0 : status = NT_STATUS_OK;
231 :
232 0 : done:
233 0 : return status;
234 : }
235 :
236 0 : static NTSTATUS parse_object(TALLOC_CTX *mem_ctx,
237 : struct libnet_keytab_context *ctx,
238 : struct drsuapi_DsReplicaObjectListItemEx *cur)
239 : {
240 0 : NTSTATUS status = NT_STATUS_OK;
241 0 : uchar nt_passwd[16];
242 0 : DATA_BLOB *blob;
243 0 : int i = 0;
244 0 : struct drsuapi_DsReplicaAttribute *attr;
245 0 : bool got_pwd = false;
246 :
247 0 : struct package_PrimaryKerberosCtr3 *pkb3 = NULL;
248 0 : struct package_PrimaryKerberosCtr4 *pkb4 = NULL;
249 :
250 0 : char *object_dn = NULL;
251 0 : char *upn = NULL;
252 0 : char **spn = NULL;
253 0 : uint32_t num_spns = 0;
254 0 : char *name = NULL;
255 0 : uint32_t kvno = 0;
256 0 : uint32_t uacc = 0;
257 0 : uint32_t sam_type = 0;
258 :
259 0 : uint32_t pwd_history_len = 0;
260 0 : uint8_t *pwd_history = NULL;
261 :
262 0 : ZERO_STRUCT(nt_passwd);
263 :
264 0 : object_dn = talloc_strdup(mem_ctx, cur->object.identifier->dn);
265 0 : if (!object_dn) {
266 0 : return NT_STATUS_NO_MEMORY;
267 : }
268 :
269 0 : DEBUG(3, ("parsing object '%s'\n", object_dn));
270 :
271 0 : for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
272 :
273 0 : attr = &cur->object.attribute_ctr.attributes[i];
274 :
275 0 : if (attr->attid == DRSUAPI_ATTID_servicePrincipalName) {
276 0 : uint32_t count;
277 0 : num_spns = attr->value_ctr.num_values;
278 0 : spn = talloc_array(mem_ctx, char *, num_spns);
279 0 : for (count = 0; count < num_spns; count++) {
280 0 : blob = attr->value_ctr.values[count].blob;
281 0 : pull_string_talloc(spn, NULL, 0,
282 0 : &spn[count],
283 0 : blob->data, blob->length,
284 : STR_UNICODE);
285 : }
286 : }
287 :
288 0 : if (attr->value_ctr.num_values != 1) {
289 0 : continue;
290 : }
291 :
292 0 : if (!attr->value_ctr.values[0].blob) {
293 0 : continue;
294 : }
295 :
296 0 : blob = attr->value_ctr.values[0].blob;
297 :
298 0 : switch (attr->attid) {
299 0 : case DRSUAPI_ATTID_unicodePwd:
300 :
301 0 : if (blob->length != 16) {
302 0 : break;
303 : }
304 :
305 0 : memcpy(&nt_passwd, blob->data, 16);
306 0 : got_pwd = true;
307 :
308 : /* pick the kvno from the meta_data version,
309 : * thanks, metze, for explaining this */
310 :
311 0 : if (!cur->meta_data_ctr) {
312 0 : break;
313 : }
314 0 : if (cur->meta_data_ctr->count !=
315 0 : cur->object.attribute_ctr.num_attributes) {
316 0 : break;
317 : }
318 0 : kvno = cur->meta_data_ctr->meta_data[i].version;
319 0 : break;
320 0 : case DRSUAPI_ATTID_ntPwdHistory:
321 0 : pwd_history_len = blob->length / 16;
322 0 : pwd_history = blob->data;
323 0 : break;
324 0 : case DRSUAPI_ATTID_userPrincipalName:
325 0 : pull_string_talloc(mem_ctx, NULL, 0, &upn,
326 0 : blob->data, blob->length,
327 : STR_UNICODE);
328 0 : break;
329 0 : case DRSUAPI_ATTID_sAMAccountName:
330 0 : pull_string_talloc(mem_ctx, NULL, 0, &name,
331 0 : blob->data, blob->length,
332 : STR_UNICODE);
333 0 : break;
334 0 : case DRSUAPI_ATTID_sAMAccountType:
335 0 : sam_type = IVAL(blob->data, 0);
336 0 : break;
337 0 : case DRSUAPI_ATTID_userAccountControl:
338 0 : uacc = IVAL(blob->data, 0);
339 0 : break;
340 0 : case DRSUAPI_ATTID_supplementalCredentials:
341 0 : status = parse_supplemental_credentials(mem_ctx,
342 : blob,
343 : &pkb3,
344 : &pkb4);
345 0 : if (!NT_STATUS_IS_OK(status)) {
346 0 : DEBUG(2, ("parsing of supplemental "
347 : "credentials failed: %s\n",
348 : nt_errstr(status)));
349 : }
350 0 : break;
351 0 : default:
352 0 : break;
353 : }
354 : }
355 :
356 0 : if (!got_pwd) {
357 0 : DEBUG(10, ("no password (unicodePwd) found - skipping.\n"));
358 0 : return NT_STATUS_OK;
359 : }
360 :
361 0 : if (name) {
362 0 : status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, 0, object_dn,
363 : "SAMACCOUNTNAME",
364 : ENCTYPE_NULL,
365 0 : data_blob_talloc(mem_ctx, name,
366 : strlen(name) + 1));
367 0 : if (!NT_STATUS_IS_OK(status)) {
368 0 : return status;
369 : }
370 : } else {
371 : /* look into keytab ... */
372 0 : struct libnet_keytab_entry *entry = NULL;
373 0 : char *principal = NULL;
374 :
375 0 : DEBUG(10, ("looking for SAMACCOUNTNAME/%s@%s in keytayb...\n",
376 : object_dn, ctx->dns_domain_name));
377 :
378 0 : principal = talloc_asprintf(mem_ctx, "%s/%s@%s",
379 : "SAMACCOUNTNAME",
380 : object_dn,
381 : ctx->dns_domain_name);
382 0 : if (!principal) {
383 0 : DEBUG(1, ("talloc failed\n"));
384 0 : return NT_STATUS_NO_MEMORY;
385 : }
386 0 : entry = libnet_keytab_search(ctx, principal, 0, ENCTYPE_NULL,
387 : mem_ctx);
388 0 : if (entry) {
389 0 : name = (char *)talloc_memdup(mem_ctx,
390 : entry->password.data,
391 : entry->password.length);
392 0 : if (!name) {
393 0 : DEBUG(1, ("talloc failed!\n"));
394 0 : return NT_STATUS_NO_MEMORY;
395 : } else {
396 0 : DEBUG(10, ("found name %s\n", name));
397 : }
398 0 : TALLOC_FREE(entry);
399 : } else {
400 0 : DEBUG(10, ("entry not found\n"));
401 : }
402 0 : TALLOC_FREE(principal);
403 : }
404 :
405 0 : if (!name) {
406 0 : DEBUG(10, ("no name (sAMAccountName) found - skipping.\n"));
407 0 : return NT_STATUS_OK;
408 : }
409 :
410 0 : DEBUG(1,("#%02d: %s:%d, ", ctx->count, name, kvno));
411 0 : DEBUGADD(1,("sAMAccountType: 0x%08x, userAccountControl: 0x%08x",
412 : sam_type, uacc));
413 0 : if (upn) {
414 0 : DEBUGADD(1,(", upn: %s", upn));
415 : }
416 0 : if (num_spns > 0) {
417 0 : DEBUGADD(1, (", spns: ["));
418 0 : for (i = 0; i < num_spns; i++) {
419 0 : DEBUGADD(1, ("%s%s", spn[i],
420 : (i+1 == num_spns)?"]":", "));
421 : }
422 : }
423 0 : DEBUGADD(1,("\n"));
424 :
425 0 : 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));
428 :
429 0 : if (!NT_STATUS_IS_OK(status)) {
430 0 : return status;
431 : }
432 :
433 : /* add kerberos keys (if any) */
434 :
435 0 : if (pkb4) {
436 0 : for (i=0; i < pkb4->num_keys; i++) {
437 0 : if (!pkb4->keys[i].value) {
438 0 : continue;
439 : }
440 0 : status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno,
441 : name,
442 : NULL,
443 0 : pkb4->keys[i].keytype,
444 0 : *pkb4->keys[i].value);
445 0 : if (!NT_STATUS_IS_OK(status)) {
446 0 : return status;
447 : }
448 : }
449 0 : for (i=0; i < pkb4->num_old_keys; i++) {
450 0 : if (!pkb4->old_keys[i].value) {
451 0 : continue;
452 : }
453 0 : status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno - 1,
454 : name,
455 : NULL,
456 0 : pkb4->old_keys[i].keytype,
457 0 : *pkb4->old_keys[i].value);
458 0 : if (!NT_STATUS_IS_OK(status)) {
459 0 : return status;
460 : }
461 : }
462 0 : for (i=0; i < pkb4->num_older_keys; i++) {
463 0 : if (!pkb4->older_keys[i].value) {
464 0 : continue;
465 : }
466 0 : status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno - 2,
467 : name,
468 : NULL,
469 0 : pkb4->older_keys[i].keytype,
470 0 : *pkb4->older_keys[i].value);
471 0 : if (!NT_STATUS_IS_OK(status)) {
472 0 : return status;
473 : }
474 : }
475 : }
476 :
477 0 : if (pkb3) {
478 0 : for (i=0; i < pkb3->num_keys; i++) {
479 0 : if (!pkb3->keys[i].value) {
480 0 : continue;
481 : }
482 0 : status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno, name,
483 : NULL,
484 0 : pkb3->keys[i].keytype,
485 0 : *pkb3->keys[i].value);
486 0 : if (!NT_STATUS_IS_OK(status)) {
487 0 : return status;
488 : }
489 : }
490 0 : for (i=0; i < pkb3->num_old_keys; i++) {
491 0 : if (!pkb3->old_keys[i].value) {
492 0 : continue;
493 : }
494 0 : status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno - 1,
495 : name,
496 : NULL,
497 0 : pkb3->old_keys[i].keytype,
498 0 : *pkb3->old_keys[i].value);
499 0 : if (!NT_STATUS_IS_OK(status)) {
500 0 : return status;
501 : }
502 : }
503 : }
504 :
505 0 : if (kvno < pwd_history_len) {
506 0 : return status;
507 : }
508 :
509 : /* add password history */
510 :
511 : /* skip first entry */
512 0 : if (got_pwd) {
513 0 : kvno--;
514 0 : i = 1;
515 : } else {
516 0 : i = 0;
517 : }
518 :
519 0 : for (; i<pwd_history_len; i++) {
520 0 : status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno--, name, NULL,
521 : ENCTYPE_ARCFOUR_HMAC,
522 0 : data_blob_talloc(mem_ctx, &pwd_history[i*16], 16));
523 0 : if (!NT_STATUS_IS_OK(status)) {
524 0 : break;
525 : }
526 : }
527 :
528 0 : return status;
529 : }
530 :
531 0 : static bool dn_is_in_object_list(struct dssync_context *ctx,
532 : const char *dn)
533 : {
534 0 : uint32_t count;
535 :
536 0 : if (ctx->object_count == 0) {
537 0 : return true;
538 : }
539 :
540 0 : for (count = 0; count < ctx->object_count; count++) {
541 0 : if (strequal(ctx->object_dns[count], dn)) {
542 0 : return true;
543 : }
544 : }
545 :
546 0 : return false;
547 : }
548 :
549 : /****************************************************************
550 : ****************************************************************/
551 :
552 0 : static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
553 : TALLOC_CTX *mem_ctx,
554 : struct drsuapi_DsReplicaObjectListItemEx *cur,
555 : struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
556 : {
557 0 : NTSTATUS status = NT_STATUS_OK;
558 0 : struct libnet_keytab_context *keytab_ctx =
559 : (struct libnet_keytab_context *)ctx->private_data;
560 :
561 0 : for (; cur; cur = cur->next_object) {
562 : /*
563 : * When not in single object replication mode,
564 : * the object_dn list is used as a positive write filter.
565 : */
566 0 : if (!ctx->single_object_replication &&
567 0 : !dn_is_in_object_list(ctx, cur->object.identifier->dn))
568 : {
569 0 : continue;
570 : }
571 :
572 0 : status = parse_object(mem_ctx, keytab_ctx, cur);
573 0 : if (!NT_STATUS_IS_OK(status)) {
574 0 : goto out;
575 : }
576 : }
577 :
578 0 : out:
579 0 : return status;
580 : }
581 :
582 : #else
583 :
584 0 : static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
585 : struct replUpToDateVectorBlob **pold_utdv)
586 : {
587 0 : return NT_STATUS_NOT_SUPPORTED;
588 : }
589 :
590 0 : static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
591 : struct replUpToDateVectorBlob *new_utdv)
592 : {
593 0 : return NT_STATUS_NOT_SUPPORTED;
594 : }
595 :
596 0 : static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
597 : TALLOC_CTX *mem_ctx,
598 : struct drsuapi_DsReplicaObjectListItemEx *cur,
599 : struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
600 : {
601 0 : return NT_STATUS_NOT_SUPPORTED;
602 : }
603 : #endif /* defined(HAVE_ADS) */
604 :
605 : const struct dssync_ops libnet_dssync_keytab_ops = {
606 : .startup = keytab_startup,
607 : .process_objects = keytab_process_objects,
608 : .finish = keytab_finish,
609 : };
|