1/*
2   Unix SMB/CIFS implementation.
3   RPC pipe client
4
5   Copyright (C) Guenther Deschner 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 "rpcclient.h"
23#include "../librpc/gen_ndr/cli_drsuapi.h"
24
25static WERROR cracknames(struct rpc_pipe_client *cli,
26			 TALLOC_CTX *mem_ctx,
27			 struct policy_handle *bind_handle,
28			 enum drsuapi_DsNameFormat format_offered,
29			 enum drsuapi_DsNameFormat format_desired,
30			 int argc,
31			 const char **argv,
32			 union drsuapi_DsNameCtr *ctr)
33{
34	NTSTATUS status;
35	WERROR werr;
36	int i;
37	int32_t level = 1;
38	union drsuapi_DsNameRequest req;
39	int32_t level_out;
40	struct drsuapi_DsNameString *names;
41
42	names = TALLOC_ZERO_ARRAY(mem_ctx, struct drsuapi_DsNameString, argc);
43	W_ERROR_HAVE_NO_MEMORY(names);
44
45	for (i=0; i<argc; i++) {
46		names[i].str = argv[i];
47	}
48
49	req.req1.codepage	= 1252; /* german */
50	req.req1.language	= 0x00000407; /* german */
51	req.req1.count		= argc;
52	req.req1.names		= names;
53	req.req1.format_flags	= DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
54	req.req1.format_offered	= format_offered;
55	req.req1.format_desired	= format_desired;
56
57	status = rpccli_drsuapi_DsCrackNames(cli, mem_ctx,
58					     bind_handle,
59					     level,
60					     &req,
61					     &level_out,
62					     ctr,
63					     &werr);
64	if (!NT_STATUS_IS_OK(status)) {
65		return ntstatus_to_werror(status);
66	}
67
68	if (!W_ERROR_IS_OK(werr)) {
69		return werr;
70	}
71
72	return WERR_OK;
73}
74
75static WERROR cmd_drsuapi_cracknames(struct rpc_pipe_client *cli,
76				     TALLOC_CTX *mem_ctx, int argc,
77				     const char **argv)
78{
79	NTSTATUS status;
80	WERROR werr;
81	int i;
82
83	struct GUID bind_guid;
84	struct policy_handle bind_handle;
85
86	union drsuapi_DsNameCtr ctr;
87
88	if (argc < 2) {
89		printf("usage: %s name\n", argv[0]);
90		return WERR_OK;
91	}
92
93	GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid);
94
95	status = rpccli_drsuapi_DsBind(cli, mem_ctx,
96				       &bind_guid,
97				       NULL,
98				       &bind_handle,
99				       &werr);
100
101	if (!NT_STATUS_IS_OK(status)) {
102		return ntstatus_to_werror(status);
103	}
104
105	werr = cracknames(cli, mem_ctx,
106			  &bind_handle,
107			  DRSUAPI_DS_NAME_FORMAT_UNKNOWN,
108			  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
109			  1,
110			  argv+1,
111			  &ctr);
112
113	if (!W_ERROR_IS_OK(werr)) {
114		goto out;
115	}
116
117	for (i=0; i < ctr.ctr1->count; i++) {
118		printf("status: %d\n",
119			ctr.ctr1->array[i].status);
120		printf("dns_domain_name: %s\n",
121			ctr.ctr1->array[i].dns_domain_name);
122		printf("result_name: %s\n",
123			ctr.ctr1->array[i].result_name);
124	}
125
126 out:
127	if (is_valid_policy_hnd(&bind_handle)) {
128		rpccli_drsuapi_DsUnbind(cli, mem_ctx, &bind_handle, &werr);
129	}
130
131	return werr;
132}
133
134static void display_domain_controller_info_01(struct drsuapi_DsGetDCConnection01 *r)
135{
136	printf("client_ip_address:\t%s\n", r->client_ip_address);
137	printf("unknown2:\t%d\n", r->unknown2);
138	printf("connection_time:\t%d\n", r->connection_time);
139	printf("unknown4:\t%d\n", r->unknown4);
140	printf("unknown5:\t%d\n", r->unknown5);
141	printf("unknown6:\t%d\n", r->unknown6);
142	printf("client_account:\t%s\n", r->client_account);
143}
144
145static void display_domain_controller_info_1(struct drsuapi_DsGetDCInfo1 *r)
146{
147	printf("netbios_name:\t%s\n", r->netbios_name);
148	printf("dns_name:\t%s\n", r->dns_name);
149	printf("site_name:\t%s\n", r->site_name);
150	printf("computer_dn:\t%s\n", r->computer_dn);
151	printf("server_dn:\t%s\n", r->server_dn);
152	printf("is_pdc:\t\t%s\n", r->is_pdc ? "true" : "false");
153	printf("is_enabled:\t%s\n", r->is_enabled ? "true" : "false");
154}
155
156static void display_domain_controller_info_2(struct drsuapi_DsGetDCInfo2 *r)
157{
158	printf("netbios_name:\t%s\n", r->netbios_name);
159	printf("dns_name:\t%s\n", r->dns_name);
160	printf("site_name:\t%s\n", r->site_name);
161	printf("site_dn:\t%s\n", r->site_dn);
162	printf("computer_dn:\t%s\n", r->computer_dn);
163	printf("server_dn:\t%s\n", r->server_dn);
164	printf("ntds_dn:\t%s\n", r->ntds_dn);
165	printf("is_pdc:\t\t%s\n", r->is_pdc ? "true" : "false");
166	printf("is_enabled:\t%s\n", r->is_enabled ? "true" : "false");
167	printf("is_gc:\t\t%s\n", r->is_gc ? "true" : "false");
168	printf("site_guid:\t%s\n", GUID_string(talloc_tos(), &r->site_guid));
169	printf("computer_guid:\t%s\n", GUID_string(talloc_tos(), &r->computer_guid));
170	printf("server_guid:\t%s\n", GUID_string(talloc_tos(), &r->server_guid));
171	printf("ntds_guid:\t%s\n", GUID_string(talloc_tos(), &r->ntds_guid));
172}
173
174static void display_domain_controller_info_3(struct drsuapi_DsGetDCInfo3 *r)
175{
176	printf("netbios_name:\t%s\n", r->netbios_name);
177	printf("dns_name:\t%s\n", r->dns_name);
178	printf("site_name:\t%s\n", r->site_name);
179	printf("site_dn:\t%s\n", r->site_dn);
180	printf("computer_dn:\t%s\n", r->computer_dn);
181	printf("server_dn:\t%s\n", r->server_dn);
182	printf("ntds_dn:\t%s\n", r->ntds_dn);
183	printf("is_pdc:\t\t%s\n", r->is_pdc ? "true" : "false");
184	printf("is_enabled:\t%s\n", r->is_enabled ? "true" : "false");
185	printf("is_gc:\t\t%s\n", r->is_gc ? "true" : "false");
186	printf("is_rodc:\t%s\n", r->is_rodc ? "true" : "false");
187	printf("site_guid:\t%s\n", GUID_string(talloc_tos(), &r->site_guid));
188	printf("computer_guid:\t%s\n", GUID_string(talloc_tos(), &r->computer_guid));
189	printf("server_guid:\t%s\n", GUID_string(talloc_tos(), &r->server_guid));
190	printf("ntds_guid:\t%s\n", GUID_string(talloc_tos(), &r->ntds_guid));
191}
192
193static void display_domain_controller_info(int32_t level,
194					   union drsuapi_DsGetDCInfoCtr *ctr)
195{
196	int i;
197
198	switch (level) {
199		case DRSUAPI_DC_CONNECTION_CTR_01:
200			for (i=0; i<ctr->ctr01.count; i++) {
201				printf("----------\n");
202				display_domain_controller_info_01(&ctr->ctr01.array[i]);
203			}
204			break;
205		case DRSUAPI_DC_INFO_CTR_1:
206			for (i=0; i<ctr->ctr1.count; i++) {
207				printf("----------\n");
208				display_domain_controller_info_1(&ctr->ctr1.array[i]);
209			}
210			break;
211		case DRSUAPI_DC_INFO_CTR_2:
212			for (i=0; i<ctr->ctr2.count; i++) {
213				printf("----------\n");
214				display_domain_controller_info_2(&ctr->ctr2.array[i]);
215			}
216			break;
217		case DRSUAPI_DC_INFO_CTR_3:
218			for (i=0; i<ctr->ctr3.count; i++) {
219				printf("----------\n");
220				display_domain_controller_info_3(&ctr->ctr3.array[i]);
221			}
222			break;
223		default:
224			break;
225	}
226}
227
228static WERROR cmd_drsuapi_getdcinfo(struct rpc_pipe_client *cli,
229				    TALLOC_CTX *mem_ctx, int argc,
230				    const char **argv)
231{
232	NTSTATUS status;
233	WERROR werr;
234
235	struct GUID bind_guid;
236	struct policy_handle bind_handle;
237
238	const char *domain = NULL;
239	int32_t level = 1;
240	int32_t level_out;
241	union drsuapi_DsGetDCInfoRequest req;
242	union drsuapi_DsGetDCInfoCtr ctr;
243
244	if (argc < 2) {
245		printf("usage: %s domain [level]\n", argv[0]);
246		return WERR_OK;
247	}
248
249	domain = argv[1];
250	if (argc >= 3) {
251		level = atoi(argv[2]);
252	}
253
254	GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid);
255
256	status = rpccli_drsuapi_DsBind(cli, mem_ctx,
257				       &bind_guid,
258				       NULL,
259				       &bind_handle,
260				       &werr);
261
262	if (!NT_STATUS_IS_OK(status)) {
263		return ntstatus_to_werror(status);
264	}
265
266	req.req1.domain_name = domain;
267	req.req1.level = level;
268
269	status = rpccli_drsuapi_DsGetDomainControllerInfo(cli, mem_ctx,
270							  &bind_handle,
271							  1,
272							  &req,
273							  &level_out,
274							  &ctr,
275							  &werr);
276	if (!NT_STATUS_IS_OK(status)) {
277		werr = ntstatus_to_werror(status);
278		goto out;
279	}
280
281	if (!W_ERROR_IS_OK(werr)) {
282		goto out;
283	}
284
285	display_domain_controller_info(level_out, &ctr);
286 out:
287	if (is_valid_policy_hnd(&bind_handle)) {
288		rpccli_drsuapi_DsUnbind(cli, mem_ctx, &bind_handle, &werr);
289	}
290
291	return werr;
292}
293
294static WERROR cmd_drsuapi_getncchanges(struct rpc_pipe_client *cli,
295				       TALLOC_CTX *mem_ctx, int argc,
296				       const char **argv)
297{
298	NTSTATUS status;
299	WERROR werr;
300
301	struct policy_handle bind_handle;
302
303	struct GUID bind_guid;
304	struct drsuapi_DsBindInfoCtr bind_info;
305	struct drsuapi_DsBindInfo28 info28;
306
307	const char *nc_dn = NULL;
308
309	DATA_BLOB session_key;
310
311	int32_t level = 8;
312	bool single = false;
313	int32_t level_out = 0;
314	union drsuapi_DsGetNCChangesRequest req;
315	union drsuapi_DsGetNCChangesCtr ctr;
316	struct drsuapi_DsReplicaObjectIdentifier nc;
317	struct dom_sid null_sid;
318
319	struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
320	struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
321	int32_t out_level = 0;
322	int y;
323
324	uint32_t supported_extensions = 0;
325	uint32_t replica_flags	= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE |
326				  DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP |
327				  DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS |
328				  DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS |
329				  DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED;
330
331	if (argc > 3) {
332		printf("usage: %s [naming_context_or_object_dn [single]]\n", argv[0]);
333		return WERR_OK;
334	}
335
336	if (argc >= 2) {
337		nc_dn = argv[1];
338	}
339
340	if (argc == 3) {
341		if (strequal(argv[2], "single")) {
342			single = true;
343		} else {
344			printf("warning: ignoring unknown argument '%s'\n",
345			       argv[2]);
346		}
347	}
348
349	ZERO_STRUCT(info28);
350
351	ZERO_STRUCT(null_sid);
352	ZERO_STRUCT(req);
353
354	GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid);
355
356	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_BASE;
357	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
358	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
359	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
360	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
361	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
362	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
363	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
364	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
365	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
366	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
367	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
368	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
369	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
370	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
371	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
372	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
373	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
374	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
375	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
376	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
377	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
378	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
379	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
380	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
381	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
382	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
383	info28.supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
384	info28.site_guid		= GUID_zero();
385	info28.pid			= 0;
386	info28.repl_epoch		= 0;
387
388	bind_info.length = 28;
389	bind_info.info.info28 = info28;
390
391	status = rpccli_drsuapi_DsBind(cli, mem_ctx,
392				       &bind_guid,
393				       &bind_info,
394				       &bind_handle,
395				       &werr);
396
397	if (!NT_STATUS_IS_OK(status)) {
398		return ntstatus_to_werror(status);
399	}
400
401	if (!W_ERROR_IS_OK(werr)) {
402		return werr;
403	}
404
405	if (bind_info.length == 24) {
406		supported_extensions = bind_info.info.info24.supported_extensions;
407	} else if (bind_info.length == 28) {
408		supported_extensions = bind_info.info.info28.supported_extensions;
409	} else if (bind_info.length == 48) {
410		supported_extensions = bind_info.info.info48.supported_extensions;
411	}
412
413	if (!nc_dn) {
414
415		union drsuapi_DsNameCtr crack_ctr;
416		const char *name;
417
418		name = talloc_asprintf(mem_ctx, "%s\\", lp_workgroup());
419		W_ERROR_HAVE_NO_MEMORY(name);
420
421		werr = cracknames(cli, mem_ctx,
422				  &bind_handle,
423				  DRSUAPI_DS_NAME_FORMAT_UNKNOWN,
424				  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
425				  1,
426				  &name,
427				  &crack_ctr);
428		if (!W_ERROR_IS_OK(werr)) {
429			return werr;
430		}
431
432		if (crack_ctr.ctr1->count != 1) {
433			return WERR_NO_SUCH_DOMAIN;
434		}
435
436		if (crack_ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
437			return WERR_NO_SUCH_DOMAIN;
438		}
439
440		nc_dn = talloc_strdup(mem_ctx, crack_ctr.ctr1->array[0].result_name);
441		W_ERROR_HAVE_NO_MEMORY(nc_dn);
442
443		printf("using: %s\n", nc_dn);
444	}
445
446	nc.dn = nc_dn;
447	nc.guid = GUID_zero();
448	nc.sid = null_sid;
449
450	if (supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
451		level = 8;
452		req.req8.naming_context		= &nc;
453		req.req8.replica_flags		= replica_flags;
454		req.req8.max_object_count	= 402;
455		req.req8.max_ndr_size		= 402116;
456		if (single) {
457			req.req8.extended_op	= DRSUAPI_EXOP_REPL_OBJ;
458		}
459	} else {
460		level = 5;
461		req.req5.naming_context		= &nc;
462		req.req5.replica_flags		= replica_flags;
463		req.req5.max_object_count	= 402;
464		req.req5.max_ndr_size		= 402116;
465		if (single) {
466			req.req5.extended_op	= DRSUAPI_EXOP_REPL_OBJ;
467		}
468	}
469
470	for (y=0; ;y++) {
471
472		if (level == 8) {
473			DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y,
474				(long long)req.req8.highwatermark.tmp_highest_usn,
475				(long long)req.req8.highwatermark.highest_usn));
476		}
477
478		status = rpccli_drsuapi_DsGetNCChanges(cli, mem_ctx,
479						       &bind_handle,
480						       level,
481						       &req,
482						       &level_out,
483						       &ctr,
484						       &werr);
485		if (!NT_STATUS_IS_OK(status)) {
486			printf("Failed to get NC Changes: %s",
487				get_friendly_werror_msg(werr));
488			goto out;
489		}
490
491		if (!W_ERROR_IS_OK(werr)) {
492			status = werror_to_ntstatus(werr);
493			goto out;
494		}
495
496		if (level_out == 1) {
497			out_level = 1;
498			ctr1 = &ctr.ctr1;
499		} else if (level_out == 2 && ctr.ctr2.mszip1.ts) {
500			out_level = 1;
501			ctr1 = &ctr.ctr2.mszip1.ts->ctr1;
502		}
503
504		status = cli_get_session_key(mem_ctx, cli, &session_key);
505		if (!NT_STATUS_IS_OK(status)) {
506			printf("Failed to get Session Key: %s",
507				nt_errstr(status));
508			return ntstatus_to_werror(status);
509		}
510
511		if (out_level == 1) {
512			DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y,
513				(long long)ctr1->new_highwatermark.tmp_highest_usn,
514				(long long)ctr1->new_highwatermark.highest_usn));
515#if 0
516			libnet_dssync_decrypt_attributes(mem_ctx,
517							 &session_key,
518							 ctr1->first_object);
519#endif
520			if (ctr1->more_data) {
521				req.req5.highwatermark = ctr1->new_highwatermark;
522				continue;
523			}
524		}
525
526		if (level_out == 6) {
527			out_level = 6;
528			ctr6 = &ctr.ctr6;
529		} else if (level_out == 7
530			   && ctr.ctr7.level == 6
531			   && ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP
532			   && ctr.ctr7.ctr.mszip6.ts) {
533			out_level = 6;
534			ctr6 = &ctr.ctr7.ctr.mszip6.ts->ctr6;
535		} else if (level_out == 7
536			   && ctr.ctr7.level == 6
537			   && ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS
538			   && ctr.ctr7.ctr.xpress6.ts) {
539			out_level = 6;
540			ctr6 = &ctr.ctr7.ctr.xpress6.ts->ctr6;
541		}
542
543		if (out_level == 6) {
544			DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y,
545				(long long)ctr6->new_highwatermark.tmp_highest_usn,
546				(long long)ctr6->new_highwatermark.highest_usn));
547#if 0
548			libnet_dssync_decrypt_attributes(mem_ctx,
549							 &session_key,
550							 ctr6->first_object);
551#endif
552			if (ctr6->more_data) {
553				req.req8.highwatermark = ctr6->new_highwatermark;
554				continue;
555			}
556		}
557
558		break;
559	}
560
561 out:
562	return werr;
563}
564
565/* List of commands exported by this module */
566
567struct cmd_set drsuapi_commands[] = {
568
569	{ "DRSUAPI" },
570	{ "dscracknames", RPC_RTYPE_WERROR, NULL, cmd_drsuapi_cracknames, &ndr_table_drsuapi.syntax_id, NULL, "Crack Name", "" },
571	{ "dsgetdcinfo", RPC_RTYPE_WERROR, NULL, cmd_drsuapi_getdcinfo, &ndr_table_drsuapi.syntax_id, NULL, "Get Domain Controller Info", "" },
572	{ "dsgetncchanges", RPC_RTYPE_WERROR, NULL, cmd_drsuapi_getncchanges, &ndr_table_drsuapi.syntax_id, NULL, "Get NC Changes", "" },
573	{ NULL }
574};
575