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