• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.0.25b/examples/misc/
1#!/usr/bin/perl -w
2#
3# adssearch.pl 	- query an Active Directory server and
4#		  display objects in a human readable format
5#
6# Copyright (C) Guenther Deschner <gd@samba.org> 2003-2007
7#
8# TODO: add range retrieval
9#	write sddl-converter, decode userParameters
10#	apparently only win2k3 allows simple-binds with machine-accounts.
11#	make sasl support independent from Authen::SASL::Cyrus v >0.11
12use strict;
13
14use Net::LDAP;
15use Net::LDAP::Control;
16use Net::LDAP::Constant qw(LDAP_REFERRAL);
17use Convert::ASN1;
18use Time::Local;
19use POSIX qw(strftime);
20use Getopt::Long;
21
22my $have_sasl;
23my $works_sasl;
24my $pref_version;
25BEGIN {
26	my $class = 'Authen::SASL';
27	$pref_version = "0.32";
28        if ( eval "require $class;" ) {
29                $have_sasl = 1;
30        }
31        if ( eval "Net::LDAP->VERSION($pref_version);" ) {
32                $works_sasl = 1;
33        }
34}
35
36# users may set defaults here
37my $base 	= "";
38my $binddn 	= "";
39my $password 	= "";
40my $server 	= "";
41my $rebind_url;
42
43
44my $tdbdump	= "/usr/bin/tdbdump";
45my $testparm	= "/usr/bin/testparm";
46my $net		= "/usr/bin/net";
47my $dig		= "/usr/bin/dig";
48my $nmblookup	= "/usr/bin/nmblookup";
49my $secrets_tdb = "/etc/samba/secrets.tdb";
50my $klist	= "/usr/bin/klist";
51my $kinit	= "/usr/bin/kinit";
52my $workgroup	= "";
53my $machine	= "";
54my $realm	= "";
55
56# parse input
57my (
58	$opt_asq,
59	$opt_base,
60	$opt_binddn,
61	$opt_debug,
62	$opt_display_extendeddn,
63	$opt_display_metadata,
64	$opt_display_raw,
65	$opt_domain_scope,
66	$opt_dump_rootdse,
67	$opt_dump_schema,
68	$opt_dump_wknguid,
69	$opt_fastbind,
70	$opt_help,
71	$opt_host,
72	$opt_machine,
73	$opt_notify,
74	$opt_notify_nodiffs,
75	$opt_paging,
76	$opt_password,
77	$opt_port,
78	$opt_realm,
79	$opt_saslmech,
80	$opt_scope,
81	$opt_simpleauth,
82	$opt_starttls,
83	$opt_user,
84	$opt_verbose,
85	$opt_workgroup,
86);
87
88GetOptions(
89	'asq=s'		=> \$opt_asq,
90	'base|b=s'	=> \$opt_base,
91	'D|DN=s'	=> \$opt_binddn,
92	'debug=i'	=> \$opt_debug,
93	'domain_scope'	=> \$opt_domain_scope,
94	'extendeddn|e:i'	=> \$opt_display_extendeddn,
95	'fastbind'	=> \$opt_fastbind,
96	'help'		=> \$opt_help,
97	'host|h=s'	=> \$opt_host,
98	'machine|P'	=> \$opt_machine,
99	'metadata|m'	=> \$opt_display_metadata,
100	'nodiffs'	=> \$opt_notify_nodiffs,
101	'notify|n'	=> \$opt_notify,
102	'paging:i'	=> \$opt_paging,
103	'password|w=s'	=> \$opt_password,
104	'port=i'	=> \$opt_port,
105	'rawdisplay'	=> \$opt_display_raw,
106	'realm|R=s'	=> \$opt_realm,
107	'rootDSE'	=> \$opt_dump_rootdse,
108	'saslmech|Y=s'	=> \$opt_saslmech,
109	'schema|c'	=> \$opt_dump_schema,
110	'scope|s=s'	=> \$opt_scope,
111	'simpleauth|x'	=> \$opt_simpleauth,
112	'tls|Z'		=> \$opt_starttls,
113	'user|U=s'	=> \$opt_user,
114	'verbose|v'	=> \$opt_verbose,
115	'wknguid'	=> \$opt_dump_wknguid,
116	'workgroup|k=s'	=> \$opt_workgroup,
117	);
118
119
120if (!@ARGV && !$opt_dump_schema && !$opt_dump_rootdse && !$opt_notify || $opt_help) {
121	usage();
122	exit 1;
123}
124
125if ($opt_fastbind && !$opt_simpleauth) {
126	printf("LDAP fast bind can only be performed with simple binds\n");
127	exit 1;
128}
129
130if ($opt_notify) {
131	$opt_paging = undef;
132}
133
134# get the query
135my $query 	= shift;
136my @attrs	= @ARGV;
137
138# some global vars
139my $filter = "";
140my ($dse, $uri);
141my ($attr, $value);
142my (@ctrls, @ctrls_s);
143my ($ctl_paged, $cookie);
144my ($page_count, $total_entry_count);
145my ($sasl_hd, $async_ldap_hd, $sync_ldap_hd);
146my ($mesg, $usn);
147my (%entry_store);
148my $async_search;
149
150# fixed values and vars
151my $set   	= "X";
152my $unset 	= "-";
153my $tabsize 	= "\t\t\t";
154
155# get defaults
156my $scope 	= $opt_scope 	|| "sub";
157my $port 	= $opt_port;
158
159my %ads_controls = (
160"LDAP_SERVER_NOTIFICATION_OID"	 	=> "1.2.840.113556.1.4.528",
161"LDAP_SERVER_EXTENDED_DN_OID" 		=> "1.2.840.113556.1.4.529",
162"LDAP_PAGED_RESULT_OID_STRING"		=> "1.2.840.113556.1.4.319",
163"LDAP_SERVER_SD_FLAGS_OID"		=> "1.2.840.113556.1.4.801",
164"LDAP_SERVER_SORT_OID"			=> "1.2.840.113556.1.4.473",
165"LDAP_SERVER_RESP_SORT_OID"		=> "1.2.840.113556.1.4.474",
166"LDAP_CONTROL_VLVREQUEST"		=> "2.16.840.1.113730.3.4.9",
167"LDAP_CONTROL_VLVRESPONSE"		=> "2.16.840.1.113730.3.4.10",
168"LDAP_SERVER_RANGE_RETRIEVAL"		=> "1.2.840.113556.1.4.802", #unsure
169"LDAP_SERVER_SHOW_DELETED_OID"		=> "1.2.840.113556.1.4.417",
170"LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID" 	=> "1.2.840.113556.1.4.521",
171"LDAP_SERVER_LAZY_COMMIT_OID"		=> "1.2.840.113556.1.4.619",
172"LDAP_SERVER_TREE_DELETE_OID"		=> "1.2.840.113556.1.4.805",
173"LDAP_SERVER_DIRSYNC_OID"		=> "1.2.840.113556.1.4.841",
174"LDAP_SERVER_VERIFY_NAME_OID"		=> "1.2.840.113556.1.4.1338",
175"LDAP_SERVER_DOMAIN_SCOPE_OID"		=> "1.2.840.113556.1.4.1339",
176"LDAP_SERVER_SEARCH_OPTIONS_OID"	=> "1.2.840.113556.1.4.1340",
177"LDAP_SERVER_PERMISSIVE_MODIFY_OID" 	=> "1.2.840.113556.1.4.1413",
178"LDAP_SERVER_ASQ_OID"			=> "1.2.840.113556.1.4.1504",
179"NONE (Get stats control)"		=> "1.2.840.113556.1.4.970",
180"LDAP_SERVER_QUOTA_CONTROL_OID"		=> "1.2.840.113556.1.4.1852",
181"LDAP_SERVER_SHUTDOWN_NOTIFY_OID"	=> "1.2.840.113556.1.4.1907",
182);
183
184my %ads_capabilities = (
185"LDAP_CAP_ACTIVE_DIRECTORY_OID"		=> "1.2.840.113556.1.4.800",
186"LDAP_CAP_ACTIVE_DIRECTORY_V51_OID" 	=> "1.2.840.113556.1.4.1670",
187"LDAP_CAP_ACTIVE_DIRECTORY_LDAP_INTEG_OID" => "1.2.840.113556.1.4.1791",
188);
189
190my %ads_extensions = (
191"LDAP_START_TLS_OID"			=> "1.3.6.1.4.1.1466.20037",
192"LDAP_TTL_EXTENDED_OP_OID"		=> "1.3.6.1.4.1.1466.101.119.1",
193"LDAP_SERVER_FAST_BIND_OID"		=> "1.2.840.113556.1.4.1781",
194"NONE (TTL refresh extended op)" 	=> "1.3.6.1.4.1.1466.101.119.1",
195);
196
197my %ads_matching_rules = (
198"LDAP_MATCHING_RULE_BIT_AND"		=> "1.2.840.113556.1.4.803",
199"LDAP_MATCHING_RULE_BIT_OR"		=> "1.2.840.113556.1.4.804",
200);
201
202my %wknguids = (
203"WELL_KNOWN_GUID_COMPUTERS"		=> "AA312825768811D1ADED00C04FD8D5CD",
204"WELL_KNOWN_GUID_DOMAIN_CONTROLLERS"	=> "A361B2FFFFD211D1AA4B00C04FD7D83A",
205"WELL_KNOWN_GUID_SYSTEM"		=> "AB1D30F3768811D1ADED00C04FD8D5CD",
206"WELL_KNOWN_GUID_USERS"			=> "A9D1CA15768811D1ADED00C04FD8D5CD",
207);
208
209my %ads_systemflags = (
210"FLAG_DONT_REPLICATE"			=> 0x00000001,	# 1
211"FLAG_REPLICATE_TO_GC"			=> 0x00000002,	# 2
212"FLAG_ATTRIBUTE_CONSTRUCT"		=> 0x00000004,	# 4
213"FLAG_CATEGORY_1_OBJECT"		=> 0x00000010,	# 16
214"FLAG_DELETE_WITHOUT_TOMBSTONE"		=> 0x02000000,	# 33554432
215"FLAG_DOMAIN_DISALLOW_MOVE"		=> 0x04000000,	# 67108864
216"FLAG_DOMAIN_DISALLOW_RENAME"		=> 0x08000000,	# 134217728
217#"FLAG_CONFIG_CAN_MOVE_RESTRICTED"	=> 0x10000000,	# 268435456	# only setable on creation
218#"FLAG_CONFIG_CAN_MOVE"			=> 0x20000000,	# 536870912	# only setable on creation
219#"FLAG_CONFIG_CAN_RENAME"		=> 0x40000000,	# 1073741824	# only setable on creation
220"FLAG_DISALLOW_DELETE"			=> 0x80000000,	# 2147483648
221);
222
223my %ads_mixed_domain = (
224"NATIVE_LEVEL_DOMAIN"			=> 0,
225"MIXED_LEVEL_DOMAIN"			=> 1,
226);
227
228my %ads_ds_func = (
229"DS_BEHAVIOR_WIN2000"			=> 0,	# untested
230"DS_BEHAVIOR_WIN2003"			=> 2,
231);
232
233my %ads_instance_type = (
234"DS_INSTANCETYPE_IS_NC_HEAD"		=> 0x1,
235"IT_WRITE"				=> 0x4,
236"IT_NC_ABOVE"				=> 0x8,
237);
238
239my %ads_uacc = (
240	"ACCOUNT_NEVER_EXPIRES"		=> 0x000000, # 0
241	"ACCOUNT_OK"			=> 0x800000, # 8388608
242	"ACCOUNT_LOCKED_OUT"		=> 0x800010, # 8388624
243);
244
245my %ads_gpoptions = (
246	"GPOPTIONS_INHERIT"		=> 0,
247	"GPOPTIONS_BLOCK_INHERITANCE"	=> 1,
248);
249
250my %ads_gplink_opts = (
251	"GPLINK_OPT_NONE"		=> 0,
252	"GPLINK_OPT_DISABLED"		=> 1,
253	"GPLINK_OPT_ENFORCED"		=> 2,
254);
255
256my %ads_gpflags = (
257	"GPFLAGS_ALL_ENABLED"			=> 0,
258	"GPFLAGS_USER_SETTINGS_DISABLED"	=> 1,
259	"GPFLAGS_MACHINE_SETTINGS_DISABLED"	=> 2,
260	"GPFLAGS_ALL_DISABLED"			=> 3,
261);
262
263my %ads_serverstate = (
264	"SERVER_ENABLED"		=> 1,
265	"SERVER_DISABLED"		=> 2,
266);
267
268my %ads_sdeffective = (
269	"OWNER_SECURITY_INFORMATION"	=> 0x01,
270	"DACL_SECURITY_INFORMATION"	=> 0x04,
271	"SACL_SECURITY_INFORMATION"	=> 0x10,
272);
273
274my %ads_trustattrs = (
275	"TRUST_ATTRIBUTE_NON_TRANSITIVE"	=> 1,
276	"TRUST_ATTRIBUTE_TREE_PARENT"		=> 2,
277	"TRUST_ATTRIBUTE_TREE_ROOT"		=> 3,
278	"TRUST_ATTRIBUTE_UPLEVEL_ONLY"		=> 4,
279);
280
281my %ads_trustdirection = (
282	"TRUST_DIRECTION_INBOUND"		=> 1,
283	"TRUST_DIRECTION_OUTBOUND"		=> 2,
284	"TRUST_DIRECTION_BIDIRECTIONAL"		=> 3,
285);
286
287my %ads_trusttype = (
288	"TRUST_TYPE_DOWNLEVEL"			=> 1,
289	"TRUST_TYPE_UPLEVEL"			=> 2,
290	"TRUST_TYPE_KERBEROS"			=> 3,
291	"TRUST_TYPE_DCE"			=> 4,
292);
293
294my %ads_pwdproperties = (
295	"DOMAIN_PASSWORD_COMPLEX"		=> 1,
296	"DOMAIN_PASSWORD_NO_ANON_CHANGE" 	=> 2,
297	"DOMAIN_PASSWORD_NO_CLEAR_CHANGE"	=> 4,
298	"DOMAIN_LOCKOUT_ADMINS"			=> 8,
299	"DOMAIN_PASSWORD_STORE_CLEARTEXT"	=> 16,
300	"DOMAIN_REFUSE_PASSWORD_CHANGE"		=> 32,
301);
302
303my %ads_uascompat = (
304	"LANMAN_USER_ACCOUNT_SYSTEM_NOLIMITS"	=> 0,
305	"LANMAN_USER_ACCOUNT_SYSTEM_COMPAT"	=> 1,
306);
307
308my %ads_frstypes = (
309	"REPLICA_SET_SYSVOL"		=> 2,	# unsure
310	"REPLICA_SET_DFS"		=> 1,	# unsure
311	"REPLICA_SET_OTHER"		=> 0,	# unsure
312);
313
314my %ads_gp_cse_extensions = (
315"Administrative Templates Extension"	=> "35378EAC-683F-11D2-A89A-00C04FBBCFA2",
316"Disk Quotas"				=> "3610EDA5-77EF-11D2-8DC5-00C04FA31A66",
317"EFS Recovery"				=> "B1BE8D72-6EAC-11D2-A4EA-00C04F79F83A",
318"Folder Redirection"			=> "25537BA6-77A8-11D2-9B6C-0000F8080861",
319"IP Security"				=> "E437BC1C-AA7D-11D2-A382-00C04F991E27",
320"Internet Explorer Maintenance"		=> "A2E30F80-D7DE-11d2-BBDE-00C04F86AE3B",
321"QoS Packet Scheduler"			=> "426031c0-0b47-4852-b0ca-ac3d37bfcb39",
322"Scripts"				=> "42B5FAAE-6536-11D2-AE5A-0000F87571E3",
323"Security"				=> "827D319E-6EAC-11D2-A4EA-00C04F79F83A",
324"Software Installation"			=> "C6DC5466-785A-11D2-84D0-00C04FB169F7",
325);
326
327# guess work
328my %ads_gpcextensions = (
329"Administrative Templates"		=> "0F6B957D-509E-11D1-A7CC-0000F87571E3",
330"Certificates"				=> "53D6AB1D-2488-11D1-A28C-00C04FB94F17",
331"EFS recovery policy processing"	=> "B1BE8D72-6EAC-11D2-A4EA-00C04F79F83A",
332"Folder Redirection policy processing"	=> "25537BA6-77A8-11D2-9B6C-0000F8080861",
333"Folder Redirection"			=> "88E729D6-BDC1-11D1-BD2A-00C04FB9603F",
334"Registry policy processing"		=> "35378EAC-683F-11D2-A89A-00C04FBBCFA2",
335"Remote Installation Services"		=> "3060E8CE-7020-11D2-842D-00C04FA372D4",
336"Security Settings"			=> "803E14A0-B4FB-11D0-A0D0-00A0C90F574B",
337"Security policy processing"		=> "827D319E-6EAC-11D2-A4EA-00C04F79F83A",
338"unknown"				=> "3060E8D0-7020-11D2-842D-00C04FA372D4",
339"unknown2"				=> "53D6AB1B-2488-11D1-A28C-00C04FB94F17",
340);
341
342my %ads_gpo_default_guids = (
343"Default Domain Policy"			=> "31B2F340-016D-11D2-945F-00C04FB984F9",
344"Default Domain Controllers Policy"	=> "6AC1786C-016F-11D2-945F-00C04fB984F9",
345"mist"					=> "61718096-3D3F-4398-8318-203A48976F9E",
346);
347
348my %ads_uf = (
349	"UF_SCRIPT"				=> 0x00000001,
350	"UF_ACCOUNTDISABLE"			=> 0x00000002,
351#	"UF_UNUSED_1"				=> 0x00000004,
352	"UF_HOMEDIR_REQUIRED"			=> 0x00000008,
353	"UF_LOCKOUT"				=> 0x00000010,
354	"UF_PASSWD_NOTREQD"			=> 0x00000020,
355	"UF_PASSWD_CANT_CHANGE"			=> 0x00000040,
356	"UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED"	=> 0x00000080,
357	"UF_TEMP_DUPLICATE_ACCOUNT"		=> 0x00000100,
358	"UF_NORMAL_ACCOUNT"			=> 0x00000200,
359#	"UF_UNUSED_2"				=> 0x00000400,
360	"UF_INTERDOMAIN_TRUST_ACCOUNT"		=> 0x00000800,
361	"UF_WORKSTATION_TRUST_ACCOUNT"		=> 0x00001000,
362	"UF_SERVER_TRUST_ACCOUNT"		=> 0x00002000,
363#	"UF_UNUSED_3"				=> 0x00004000,
364#	"UF_UNUSED_4"				=> 0x00008000,
365	"UF_DONT_EXPIRE_PASSWD"			=> 0x00010000,
366	"UF_MNS_LOGON_ACCOUNT"			=> 0x00020000,
367	"UF_SMARTCARD_REQUIRED"			=> 0x00040000,
368	"UF_TRUSTED_FOR_DELEGATION"		=> 0x00080000,
369	"UF_NOT_DELEGATED"			=> 0x00100000,
370	"UF_USE_DES_KEY_ONLY"			=> 0x00200000,
371	"UF_DONT_REQUIRE_PREAUTH"		=> 0x00400000,
372	"UF_PASSWORD_EXPIRED"			=> 0x00800000,
373	"UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION" => 0x01000000,
374	"UF_NO_AUTH_DATA_REQUIRED"		=> 0x02000000,
375#	"UF_UNUSED_8"				=> 0x04000000,
376#	"UF_UNUSED_9"				=> 0x08000000,
377#	"UF_UNUSED_10"				=> 0x10000000,
378#	"UF_UNUSED_11"				=> 0x20000000,
379#	"UF_UNUSED_12"				=> 0x40000000,
380#	"UF_UNUSED_13"				=> 0x80000000,
381);
382
383my %ads_grouptype = (
384	"GROUP_TYPE_BUILTIN_LOCAL_GROUP"	=> 0x00000001,
385	"GROUP_TYPE_ACCOUNT_GROUP"		=> 0x00000002,
386	"GROUP_TYPE_RESOURCE_GROUP"		=> 0x00000004,
387	"GROUP_TYPE_UNIVERSAL_GROUP"		=> 0x00000008,
388	"GROUP_TYPE_APP_BASIC_GROUP"		=> 0x00000010,
389	"GROUP_TYPE_APP_QUERY_GROUP"		=> 0x00000020,
390	"GROUP_TYPE_SECURITY_ENABLED"		=> 0x80000000,
391);
392
393my %ads_atype = (
394	"ATYPE_NORMAL_ACCOUNT"			=> 0x30000000,
395	"ATYPE_WORKSTATION_TRUST"		=> 0x30000001,
396	"ATYPE_INTERDOMAIN_TRUST"		=> 0x30000002,
397	"ATYPE_SECURITY_GLOBAL_GROUP"		=> 0x10000000,
398	"ATYPE_DISTRIBUTION_GLOBAL_GROUP"	=> 0x10000001,
399	"ATYPE_DISTRIBUTION_UNIVERSAL_GROUP"	=> 0x10000001, # ATYPE_DISTRIBUTION_GLOBAL_GROUP
400	"ATYPE_SECURITY_LOCAL_GROUP"		=> 0x20000000,
401	"ATYPE_DISTRIBUTION_LOCAL_GROUP"	=> 0x20000001,
402	"ATYPE_ACCOUNT"				=> 0x30000000, # ATYPE_NORMAL_ACCOUNT
403	"ATYPE_GLOBAL_GROUP"			=> 0x10000000, # ATYPE_SECURITY_GLOBAL_GROUP
404	"ATYPE_LOCAL_GROUP"			=> 0x20000000, # ATYPE_SECURITY_LOCAL_GROUP
405);
406
407my %ads_gtype = (
408	"GTYPE_SECURITY_BUILTIN_LOCAL_GROUP"	=> 0x80000005,
409	"GTYPE_SECURITY_DOMAIN_LOCAL_GROUP"	=> 0x80000004,
410	"GTYPE_SECURITY_GLOBAL_GROUP"		=> 0x80000002,
411	"GTYPE_DISTRIBUTION_GLOBAL_GROUP"	=> 0x00000002,
412	"GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP"	=> 0x00000004,
413	"GTYPE_DISTRIBUTION_UNIVERSAL_GROUP"	=> 0x00000008,
414);
415
416my %munged_dial = (
417	"CtxCfgPresent"		=> \&dump_int,
418	"CtxCfgFlags1"		=> \&dump_int,
419	"CtxCallback"		=> \&dump_string,
420	"CtxShadow"		=> \&dump_string,
421	"CtxMaxConnectionTime"	=> \&dump_int,
422	"CtxMaxDisconnectionTime"=> \&dump_int,
423	"CtxMaxIdleTime"	=> \&dump_int,
424	"CtxKeyboardLayout"	=> \&dump_int,
425	"CtxMinEncryptionLevel"	=> \&dump_int,
426	"CtxWorkDirectory"	=> \&dump_string,
427	"CtxNWLogonServer"	=> \&dump_string,
428	"CtxWFHomeDir"		=> \&dump_string,
429	"CtxWFHomeDirDrive"	=> \&dump_string,
430	"CtxWFProfilePath"	=> \&dump_string,
431	"CtxInitialProgram"	=> \&dump_string,
432	"CtxCallbackNumber"	=> \&dump_string,
433);
434
435$SIG{__WARN__} = sub {
436	use Carp;
437	Carp::cluck (shift);
438};
439
440# if there is data missing, we try to autodetect with samba-tools (if installed)
441# this might fill up workgroup, machine, realm
442get_samba_info();
443
444# get a workgroup
445$workgroup	= $workgroup || $opt_workgroup || "";
446
447# get the server
448$server 	= process_servername($opt_host) ||
449		  detect_server($workgroup,$opt_realm) ||
450		  die "please define server to query with -h host\n";
451
452
453# get the base
454$base 		= defined($opt_base)? $opt_base : "" ||
455	 	  get_base_from_rootdse($server,$dse);
456
457# get the realm
458$realm		= $opt_realm ||
459		  get_realm_from_rootdse($server,$dse);
460
461# get sasl mechs
462my @sasl_mechs	= get_sasl_mechs_from_rootdse($server,$dse);
463my $sasl_mech	= "GSSAPI";
464if ($opt_saslmech) {
465	$sasl_mech = sprintf("%s", (check_sasl_mech($opt_saslmech) == 0)?uc($opt_saslmech):"");
466}
467
468# set bind type
469my $sasl_bind = 1 if (!$opt_simpleauth);
470
471# get username
472my $user 	= check_user($opt_user) || $ENV{'USER'} || "";
473
474# gen upn
475my $upn		= sprintf("%s", gen_upn($opt_machine ? "$machine\$" : $user, $realm));
476
477# get binddn
478$binddn		= $opt_binddn || $upn;
479
480# get the password
481$password 	= $password || $opt_password;
482if (!$password) {
483	$password = $opt_machine ? get_machine_password($workgroup) : get_password();
484}
485
486my %attr_handler = (
487	"Token-Groups-No-GC-Acceptable" => \&dump_sid,	#wrong name
488	"accountExpires"		=> \&dump_nttime,
489	"badPasswordTime"		=> \&dump_nttime,
490	"creationTime"			=> \&dump_nttime,
491	"currentTime"			=> \&dump_timestr,
492	"domainControllerFunctionality" => \&dump_ds_func,
493	"domainFunctionality" 		=> \&dump_ds_func,
494	"fRSReplicaSetGUID"		=> \&dump_guid,
495	"fRSReplicaSetType"		=> \&dump_frstype,
496	"fRSVersionGUID"		=> \&dump_guid,
497	"flags"				=> \&dump_gpflags,	# fixme: possibly only on gpos!
498	"forceLogoff"			=> \&dump_nttime_abs,
499	"forestFunctionality" 		=> \&dump_ds_func,
500#	"gPCMachineExtensionNames"	=> \&dump_gpcextensions,
501#	"gPCUserExtensionNames"		=> \&dump_gpcextensions,
502	"gPLink"			=> \&dump_gplink,
503	"gPOptions"			=> \&dump_gpoptions,
504	"groupType"			=> \&dump_gtype,
505	"instanceType"			=> \&dump_instance_type,
506	"lastLogon"			=> \&dump_nttime,
507	"lastLogonTimestamp"		=> \&dump_nttime,
508	"lockOutObservationWindow"	=> \&dump_nttime_abs,
509	"lockoutDuration"		=> \&dump_nttime_abs,
510	"lockoutTime"			=> \&dump_nttime,
511#	"logonHours"			=> \&dump_logonhours,
512	"maxPwdAge"			=> \&dump_nttime_abs,
513	"minPwdAge"			=> \&dump_nttime_abs,
514	"modifyTimeStamp"		=> \&dump_timestr,
515	"msDS-Behavior-Version"		=> \&dump_ds_func,	#unsure
516	"msDS-User-Account-Control-Computed" => \&dump_uacc,
517	"mS-DS-CreatorSID"		=> \&dump_sid,
518#	"msRADIUSFramedIPAddress"	=> \&dump_ipaddr,
519#	"msRASSavedFramedIPAddress" 	=> \&dump_ipaddr,
520	"netbootGUID"			=> \&dump_guid,
521	"nTMixedDomain"			=> \&dump_mixed_domain,
522	"nTSecurityDescriptor"		=> \&dump_secdesc,
523	"objectGUID"			=> \&dump_guid,
524	"objectSid"			=> \&dump_sid,
525	"pKT"				=> \&dump_pkt,
526	"pKTGuid"			=> \&dump_guid,
527	"pwdLastSet"			=> \&dump_nttime,
528	"pwdProperties"			=> \&dump_pwdproperties,
529	"sAMAccountType"		=> \&dump_atype,
530	"sDRightsEffective"		=> \&dump_sdeffective,
531	"securityIdentifier"		=> \&dump_sid,
532	"serverState"			=> \&dump_serverstate,
533	"supportedCapabilities",	=> \&dump_capabilities,
534	"supportedControl",		=> \&dump_controls,
535	"supportedExtension",		=> \&dump_extension,
536	"systemFlags"			=> \&dump_systemflags,
537	"tokenGroups",			=> \&dump_sid,
538	"tokenGroupsGlobalAndUniversal" => \&dump_sid,
539	"tokenGroupsNoGCAcceptable"	=> \&dump_sid,
540	"trustAttributes"		=> \&dump_trustattr,
541	"trustDirection"		=> \&dump_trustdirection,
542	"trustType"			=> \&dump_trusttype,
543	"uASCompat"			=> \&dump_uascompat,
544	"userAccountControl"		=> \&dump_uac,
545	"userCertificate"		=> \&dump_cert,
546	"userFlags"			=> \&dump_uf,
547	"userParameters"		=> \&dump_munged_dial,
548	"whenChanged"			=> \&dump_timestr,
549	"whenCreated"			=> \&dump_timestr,
550#	"dSCorePropagationData"		=> \&dump_timestr,
551);
552
553
554
555################
556# subfunctions #
557################
558
559sub usage {
560	print "usage: $0 [--asq] [--base|-b base] [--debug level] [--debug level] [--DN|-D binddn] [--extendeddn|-e] [--help] [--host|-h host] [--machine|-P] [--metadata|-m] [--nodiffs] [--notify|-n] [--password|-w password] [--port port] [--rawdisplay] [--realm|-R realm] [--rootdse] [--saslmech|-Y saslmech] [--schema|-c] [--scope|-s scope] [--simpleauth|-x] [--starttls|-Z] [--user|-U user] [--wknguid] [--workgroup|-k workgroup] filter [attrs]\n";
561	print "\t--asq [attribute]\n\t\tAttribute to use for a attribute scoped query (LDAP_SERVER_ASQ_OID)\n";
562	print "\t--base|-b [base]\n\t\tUse base [base]\n";
563	print "\t--debug [level]\n\t\tUse debuglevel (for Net::LDAP)\n";
564	print "\t--domain_scope\n\t\tLimit LDAP search to local domain (LDAP_SERVER_DOMAIN_SCOPE_OID)\n";
565	print "\t--DN|-D [binddn]\n\t\tUse binddn or principal\n";
566	print "\t--extendeddn|-e [value]\n\t\tDisplay extended dn (LDAP_SERVER_EXTENDED_DN_OID)\n";
567	print "\t--fastbind\n\t\tDo LDAP fast bind using LDAP_SERVER_FAST_BIND_OID extension\n";
568	print "\t--help\n\t\tDisplay help page\n";
569	print "\t--host|-h [host]\n\t\tQuery Host [host] (either a hostname or an LDAP uri)\n";
570	print "\t--machine|-P\n\t\tUse samba3 machine account stored in $secrets_tdb (needs root access)\n";
571	print "\t--metdata|-m\n\t\tDisplay replication metadata\n";
572	print "\t--nodiffs\n\t\tDisplay no diffs but full entry dump when running in notify mode\n";
573	print "\t--notify|-n\n\t\tActivate asynchronous change notification (LDAP_SERVER_NOTIFICATION_OID)\n";
574	print "\t--paging [pagesize]\n\t\tUse paged results when searching\n";
575	print "\t--password|-w [password]\n\t\tUse [password] for binddn\n";
576	print "\t--port [port]\n\t\tUse [port] when connecting ADS\n";
577	print "\t--rawdisplay\n\t\tDo not interpret values\n";
578	print "\t--realm|-R [realm]\n\t\tUse [realm] when trying to construct bind-principal\n";
579	print "\t--rootdse\n\t\tDisplay RootDSE (anonymously)\n";
580	print "\t--saslmech|-Y [saslmech]\n\t\tUse SASL Mechanism [saslmech] when binding\n";
581	print "\t--schema|-c\n\t\tDisplay DSE-Schema\n";
582	print "\t--scope|-s [scope]\n\t\tUse scope [scope] (sub, base, one)\n";
583	print "\t--simpleauth|-x\n\t\tUse simple bind (otherwise SASL binds are performed)\n";
584	print "\t--starttls|-Z\n\t\tUse Start TLS extended operation to secure LDAP traffic\n";
585	print "\t--user|-U [user]\n\t\tUse [user]\n";
586	print "\t--wknguid\n\t\tDisplay well known guids\n";
587	print "\t--workgroup|-k [workgroup]\n\t\tWhen LDAP-Server is not known try to find a Domain-Controller for [workgroup]\n";
588}
589
590sub write_ads_list {
591	my ($mod,$attr,$value) = @_;
592	my $ofh = select(STDOUT);
593	$~ = "ADS_LIST";
594	select($ofh);
595	write();
596
597format ADS_LIST =
598@<<<< @>>>>>>>>>>>>>>>>>>>>>>>: @*
599$mod, $attr, $value
600.
601}
602
603sub write_ads {
604	my ($mod,$attr,$value) = @_;
605	my $ofh = select(STDOUT);
606	$~ = "ADS";
607	select($ofh);
608	write();
609
610format ADS =
611@<<<< @>>>>>>>>>>>>>>>>>>>>>>>: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
612$mod, $attr, $value
613~~				^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
614				$value
615.
616}
617
618sub detect_server {
619
620	my $workgroup = shift;
621	my $realm = shift;
622	my $result;
623	my $found;
624
625	# try net cache (nbt records)
626	if ( -x $net && $workgroup ) {
627		my $key = sprintf("NBT/%s#1C", uc($workgroup));
628		chomp($result = `$net cache search $key 2>&1 /dev/null`);
629		$result =~ s/^.*Value: //;
630		$result =~ s/:.*//;
631		return $result if $result;
632		printf("%10s query failed for [%s]\n", "net cache", $key);
633	}
634
635	# try dns SRV entries
636	if ( -x $dig && $realm ) {
637		my $key = sprintf("_ldap._tcp.%s", lc($realm));
638		chomp($result = `$dig $key SRV +short +search | egrep "^[0-9]" | head -1`);
639		$result =~ s/.* //g;
640		$result =~ s/.$//g;
641		return $result if $result;
642		printf("%10s query failed for [%s]\n", "dns", $key);
643	}
644
645	# try netbios broadcast query
646	if ( -x $nmblookup && $workgroup ) {
647		my $key = sprintf("%s#1C", uc($workgroup));
648		my $pattern = sprintf("%s<1c>", uc($workgroup));
649		chomp($result = `$nmblookup $key -d 0 | grep '$pattern'`);
650		$result =~ s/\s.*//;
651		return $result if $result;
652		printf("%10s query failed for [%s]\n", "nmblookup", $key);
653	}
654
655	return "";
656}
657
658sub get_samba_info {
659
660	if (! -x $testparm) { return -1; }
661
662	my $tmp;
663	open(TESTPARM, "$testparm -s -v 2> /dev/null |");
664	while (my $line = <TESTPARM>) {
665		chomp($line);
666		if ($line =~ /netbios name/) {
667			($tmp, $machine) = split(/=/, $line);
668			$machine =~ s/\s+|\t+//g;
669		}
670		if ($line =~ /realm/) {
671			($tmp, $realm) = split(/=/, $line);
672			$realm =~ s/\s+|\t+//g;
673		}
674		if ($line =~ /workgroup/) {
675			($tmp, $workgroup) = split(/=/, $line);
676			$workgroup =~ s/\s+|\t+//g;
677		}
678	}
679	close(TESTPARM);
680	return 0;
681}
682
683sub gen_upn {
684	my $machine = shift;
685	my $realm = shift;
686	if ($machine && $realm) {
687		return sprintf("%s\@%s", lc($machine), uc($realm));
688	};
689	return undef;
690}
691
692sub get_password {
693	if (!$password && $opt_simpleauth && check_ticket($user)) {
694		return prompt_password($user);
695	}
696	return "";
697}
698
699sub get_user {
700	my $user = shift || prompt_user();
701	return $user;
702}
703
704sub get_machine_password {
705
706	my $workgroup = shift || "";
707	$workgroup = uc($workgroup);
708
709	my ($found, $tmp);
710	-x $tdbdump || die "tdbdump is not installed. cannot proceed autodetection\n";
711	-r $secrets_tdb || die "cannot read $secrets_tdb. cannot proceed autodetection\n";
712
713	# get machine-password
714	my $key = sprintf("SECRETS/MACHINE_PASSWORD/%s", $workgroup);
715	open(SECRETS,"$tdbdump $secrets_tdb |");
716	while(my $line = <SECRETS>) {
717		chomp($line);
718		if ($found) {
719			$line =~ s/\\00//;
720			($line,$password) = split(/"/, $line);
721			last;
722		}
723		if ($line =~ /$key/) {
724			$found = 1;
725		}
726	}
727	close(SECRETS);
728
729	if ($found) {
730		print "Successfully autodetected machine password for workgroup: $workgroup\n";
731		return $password;
732	} else {
733		warn "No machine password available for $workgroup\n";
734	}
735	return "";
736}
737
738sub prompt_password {
739
740	my $acct = shift || "";
741	if ($acct =~ /\%/) {
742		($acct, $password) = split(/\%/, $acct);
743		return $password;
744	}
745	system "stty -echo";
746	print "Enter password for $acct:";
747	my $password = <STDIN>;
748	chomp($password);
749	print "\n";
750	system "stty echo";
751	return $password;
752}
753
754sub prompt_user {
755
756	print "Enter Username:";
757	my $user = <STDIN>;
758	chomp($user);
759	print "\n";
760	return $user;
761}
762
763sub check_ticket {
764	return 0;
765	# works only for heimdal
766	return system("$klist -t");
767}
768
769sub get_ticket {
770
771	my $KRB5_CONFIG = "/tmp/.krb5.conf.telads-$<";
772
773	open(KRB5CONF, "> $KRB5_CONFIG") || die "cannot write $KRB5_CONFIG";
774	printf  KRB5CONF "# autogenerated by $0\n";
775	printf  KRB5CONF "[libdefaults]\n\tdefault_realm = %s\n\tclockskew = %d\n", uc($realm), 60*60;
776	printf  KRB5CONF "[realms]\n\t%s = {\n\t\tkdc = %s\n\t}\n", uc($realm), $server;
777	close(KRB5CONF);
778
779	if ( system("KRB5_CONFIG=$KRB5_CONFIG $kinit $user") != 0) {
780		return -1;
781	}
782
783	return 0;
784}
785
786sub check_user {
787	my $acct = shift || "";
788	if ($acct =~ /\%/) {
789		($acct, $password) = split(/\%/, $acct);
790	}
791	return $acct;
792}
793
794sub check_root_dse($$$@) {
795
796	# bogus function??
797	my $server = shift || "";
798	$dse = shift || get_dse($server) || return -1;
799	my $attr = shift || die "unknown query";
800	my @array = @_;
801
802	my $dse_list = $dse->get_value($attr, asref => '1');
803	my @dse_array = @$dse_list;
804
805	foreach my $i (@array) {
806		# we could use -> supported_control but this
807		# is only available in newer versions of perl-ldap
808#		if ( ! $dse->supported_control( $i ) ) {
809		if ( grep(/$i->type()/, @dse_array) ) {
810			printf("required \"$attr\": %s is not supported by ADS-server.\n", $i->type());
811			return -1;
812		}
813	}
814	return 0;
815}
816
817sub check_ctrls ($$@) {
818	my $server = shift;
819	my $dse = shift;
820	my @array = @_;
821	return check_root_dse($server, $dse, "supportedControl", @array);
822}
823
824sub check_exts ($$@) {
825	my $server = shift;
826	my $dse = shift;
827	my @array = @_;
828	return check_root_dse($server, $dse, "supportedExtension", @array);
829}
830
831sub get_base_from_rootdse {
832
833	my $server = shift || "";
834	$dse = shift || get_dse($server,$async_ldap_hd) || return -1;
835	return $dse->get_value('defaultNamingContext');
836}
837
838sub get_realm_from_rootdse {
839
840	my $server = shift || "";
841	$dse = shift || get_dse($server,$async_ldap_hd) || return -1;
842	my $service = $dse->get_value('ldapServiceName') || "";
843	if ($service) {
844		my ($t,$realm) = split(/\@/, $service);
845		return $realm;
846	} else {
847		die "very odd: could not get realm";
848	}
849}
850
851sub get_sasl_mechs_from_rootdse {
852
853	my $server = shift || "";
854	$dse = shift || get_dse($server,$async_ldap_hd) || return -1;
855	my $mechs = $dse->get_value('supportedSASLMechanisms', asref => 1);
856	return @$mechs;
857}
858
859sub get_dse {
860
861	my $server = shift || return undef;
862	$async_ldap_hd = shift || get_ldap_hd($server,1);
863	if (!$async_ldap_hd) {
864		print "oh, no connection\n";
865		return undef;
866	}
867	my $mesg = $async_ldap_hd->bind() || die "cannot bind\n";
868	if ($mesg->code) { die "failed to bind\n"; };
869	my $dse = $async_ldap_hd->root_dse( attrs => ['*', "supportedExtension", "supportedFeatures" ] );
870
871	return $dse;
872}
873
874sub process_servername {
875
876	my $name = shift || return "";
877	if ($name =~ /^ldaps:\/\//i ) {
878		$name =~ s#^ldaps://##i;
879		$uri = sprintf("%s://%s", "ldaps", $name);
880	} else {
881		$name =~ s#^ldap://##i;
882		$uri = sprintf("%s://%s", "ldap", $name);
883	}
884	return $name;
885}
886
887sub get_ldap_hd {
888
889	my $server = shift || return undef;
890	my $async = shift || "0";
891	my $hd;
892	die "uri unavailable" if (!$uri);
893	if ($uri =~ /^ldaps:\/\//i ) {
894		$port = $port || 636;
895		use Net::LDAPS;
896		$hd = Net::LDAPS->new( $server, async => $async, port => $port ) ||
897			die "host $server not available: $!";
898	} else {
899		$port = $port || 389;
900		$hd = Net::LDAP->new( $server, async => $async, port => $port ) ||
901			die "host $server not available: $!";
902	}
903	$hd->debug($opt_debug);
904	if ($opt_starttls) {
905		$hd->start_tls( verify => 'none' );
906	}
907
908	return $hd;
909}
910
911sub get_sasl_hd {
912
913	if (!$have_sasl) {
914		print "no sasl support\n";
915		return undef;
916	}
917
918	my $hd;
919	if ($sasl_mech && $sasl_mech eq "GSSAPI") {
920		my $user = sprintf("%s\@%s", $user, uc($realm));
921		if (check_ticket($user) != 0 && get_ticket($user) != 0) {
922			print "Could not get Kerberos ticket for user [$user]\n";
923			return undef;
924		}
925
926		$hd = Authen::SASL->new( mechanism => 'GSSAPI' ) || die "nope";
927		my $conn = $hd->client_new("ldap", $server);
928
929		if ($conn->code == -1) {
930		        printf "%s\n", $conn->error();
931			return undef;
932		};
933
934	} elsif ($sasl_mech) {
935
936		# here comes generic sasl code
937		$hd = Authen::SASL->new( mechanism => $sasl_mech,
938			callback  => {
939				user => \&get_user,
940				pass => \&get_password
941			}
942		) || die "nope";
943	} else {
944		$sasl_bind = 0;
945		print "no supported SASL mechanism found (@sasl_mechs).\n";
946		print "falling back to simple bind.\n";
947		return undef;
948	}
949
950	return $hd;
951}
952
953sub check_sasl_mech {
954	my $mech_check = shift;
955	my $have_mech = 0;
956	foreach my $mech (@sasl_mechs) {
957		$have_mech = 1 if ($mech eq uc($mech_check));
958	}
959	if (!$have_mech) {
960		return -1;
961	}
962	return 0;
963}
964
965sub store_result ($) {
966
967	my $entry = shift;
968	return if (!$entry);
969	$entry_store{$entry->dn} = $entry;
970}
971
972sub display_result_diff ($) {
973
974	my $entry_new = shift;
975	return if ( !$entry_new);
976
977	if ( !exists $entry_store{$entry_new->dn}) {
978		print "can't display any differences yet. full dump...\n";
979		display_entry_generic($entry_new);
980		return;
981	}
982
983	my $entry_old = $entry_store{$entry_new->dn};
984
985	foreach my $attr (sort $entry_new->attributes) {
986		if ( $entry_new->exists($attr) && ! $entry_old->exists($attr)) {
987			display_attr_generic("add:\t", $entry_new, $attr);
988			next;
989		}
990	}
991
992	foreach my $attr (sort $entry_old->attributes) {
993		if (! $entry_new->exists($attr) && $entry_old->exists($attr)) {
994			display_attr_generic("del:\t", $entry_old, $attr);
995			next;
996		}
997
998		# now check for all values if they have changed, display changes
999		my ($old_vals, $new_vals, @old_vals, @new_vals, %old, %new);
1000
1001		$old_vals = $entry_old->get_value($attr, asref => 1);
1002		@old_vals = @$old_vals;
1003		$new_vals = $entry_new->get_value($attr, asref => 1);
1004		@new_vals = @$new_vals;
1005
1006		if (scalar(@old_vals) == 1 && scalar(@new_vals) == 1) {
1007			if ($old_vals[0] ne $new_vals[0]) {
1008				display_attr_generic("old:\t", $entry_old, $attr);
1009				display_attr_generic("new:\t", $entry_new, $attr);
1010			}
1011			next;
1012		}
1013
1014		# handle multivalued diffs
1015		foreach my $val (@old_vals) { $old{$val} = "dummy"; };
1016		foreach my $val (@new_vals) { $new{$val} = "dummy"; };
1017		foreach my $val (sort keys %new) {
1018			if (!exists $old{$val}) {
1019				display_value_generic("add:\t", $attr, $val);
1020			}
1021		}
1022		foreach my $val (sort keys %old) {
1023			if (!exists $new{$val}) {
1024				display_value_generic("del:\t", $attr, $val);
1025			}
1026		}
1027	}
1028	print "\n";
1029
1030}
1031
1032sub sid2string ($) {
1033
1034	# Fix from Michael James <michael@james.st>
1035	my $binary_sid = shift;
1036	my($sid_rev, $num_auths, $id1, $id2, @ids) = unpack("H2 H2 n N V*", $binary_sid);
1037	my $sid_string = join("-", "S", hex($sid_rev), ($id1<<32)+$id2, @ids);
1038	return $sid_string;
1039
1040}
1041
1042sub string_to_guid {
1043	my $string = shift;
1044	return undef unless $string =~ /([0-9,a-z]{8})-([0-9,a-z]{4})-([0-9,a-z]{4})-([0-9,a-z]{2})([0-9,a-z]{2})-([0-9,a-z]{2})([0-9,a-z]{2})([0-9,a-z]{2})([0-9,a-z]{2})([0-9,a-z]{2})([0-9,a-z]{2})/i;
1045
1046	return 	pack("I", hex $1) .
1047		pack("S", hex $2) .
1048		pack("S", hex $3) .
1049		pack("C", hex $4) .
1050		pack("C", hex $5) .
1051		pack("C", hex $6) .
1052		pack("C", hex $7) .
1053		pack("C", hex $8) .
1054		pack("C", hex $9) .
1055		pack("C", hex $10) .
1056		pack("C", hex $11);
1057
1058#	print "$1\n$2\n$3\n$4\n$5\n$6\n$7\n$8\n$9\n$10\n$11\n";
1059}
1060
1061sub bindstring_to_guid {
1062	my $str = shift;
1063	return pack("H2 H2 H2 H2 H2 H2 H2 H2 H2 H2 H2 H2 H2 H2 H2 H2",
1064		substr($str,0,2),
1065		substr($str,2,2),
1066		substr($str,4,2),
1067		substr($str,6,2),
1068		substr($str,8,2),
1069		substr($str,10,2),
1070		substr($str,12,2),
1071		substr($str,14,2),
1072		substr($str,16,2),
1073		substr($str,18,2),
1074		substr($str,20,2),
1075		substr($str,22,2),
1076		substr($str,24,2),
1077		substr($str,26,2),
1078		substr($str,28,2),
1079		substr($str,30,2));
1080}
1081
1082sub guid_to_string {
1083	my $guid = shift;
1084	my $string = sprintf "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
1085		unpack("I", $guid),
1086		unpack("S", substr($guid, 4, 2)),
1087		unpack("S", substr($guid, 6, 2)),
1088		unpack("C", substr($guid, 8, 1)),
1089		unpack("C", substr($guid, 9, 1)),
1090		unpack("C", substr($guid, 10, 1)),
1091		unpack("C", substr($guid, 11, 1)),
1092		unpack("C", substr($guid, 12, 1)),
1093		unpack("C", substr($guid, 13, 1)),
1094		unpack("C", substr($guid, 14, 1)),
1095		unpack("C", substr($guid, 15, 1));
1096	return lc($string);
1097}
1098
1099sub guid_to_bindstring {
1100	my $guid = shift;
1101	return unpack("H" . 2 * length($guid), $guid),
1102}
1103
1104sub dump_wknguid {
1105	print "Dumping Well known GUIDs:\n";
1106	foreach my $wknguid (keys %wknguids) {
1107
1108		my $guid = bindstring_to_guid($wknguids{$wknguid});
1109		my $str  = guid_to_string($guid);
1110		my $back = guid_to_bindstring($guid);
1111
1112		printf "wkguid:             %s\n", $wknguid;
1113		printf "bind_string format: %s\n", $wknguids{$wknguid};
1114		printf "string format:      %s\n", guid_to_string($guid);
1115		printf "back to bind_string:%s\n", guid_to_bindstring($guid);
1116
1117		printf "use base: \"<WKGUID=%s,%s>\"\n", guid_to_bindstring($guid), $base;
1118	}
1119}
1120
1121sub gen_bitmask_string_format($%) {
1122	my $mod = shift;
1123        my (%tmp) = @_;
1124	my @list;
1125	foreach my $key (sort keys %tmp) {
1126		push(@list, sprintf("%s %s", $tmp{$key}, $key));
1127	}
1128	return join("\n$mod$tabsize",@list);
1129}
1130
1131
1132sub nt_to_unixtime ($) {
1133	# the number of 100 nanosecond intervals since jan. 1. 1601 (utc)
1134	my $t64 = shift;
1135	$t64 =~ s/(.+).{7,7}/$1/;
1136	$t64 -= 11644473600;
1137	return $t64;
1138}
1139
1140sub dump_equal {
1141	my $val = shift;
1142	my $mod = shift || die "no mod";
1143        my (%header) = @_;
1144	my %tmp;
1145	my $found = 0;
1146	foreach my $key (keys %header) {
1147		if ($header{$key} eq $val) {
1148			$tmp{"($val)"} = $key;
1149			$found = 1;
1150			last;
1151		}
1152	}
1153	if (!$found) { $tmp{$val} = ""; };
1154	return gen_bitmask_string_format($mod,%tmp);
1155}
1156
1157sub dump_bitmask {
1158	my $op = shift || die "no op";
1159	my $val = shift;
1160	my $mod = shift || die "no mod";
1161        my (%header) = @_;
1162	my %tmp;
1163	$tmp{""} = sprintf("%s (0x%08x)", $val, $val);
1164	foreach my $key (sort keys %header) {	# sort by val !
1165		my $val_hex = sprintf("0x%08x", $header{$key});
1166		if ($op eq "&") {
1167			$tmp{"$key ($val_hex)"} = ( $val & $header{$key} ) ? $set:$unset;
1168		} elsif ($op eq "==") {
1169			$tmp{"$key ($val_hex)"} = ( $val == $header{$key} ) ? $set:$unset;
1170		} else {
1171			print "unknown operator: $op\n";
1172			return;
1173		}
1174	}
1175	return gen_bitmask_string_format($mod,%tmp);
1176}
1177
1178sub dump_bitmask_and {
1179	return dump_bitmask("&",@_);
1180}
1181
1182sub dump_bitmask_equal {
1183	return dump_bitmask("==",@_);
1184}
1185
1186sub dump_uac {
1187	return dump_bitmask_and(@_,%ads_uf); # ads_uf ?
1188}
1189
1190sub dump_uascompat {
1191	return dump_bitmask_equal(@_,%ads_uascompat);
1192}
1193
1194sub dump_gpoptions {
1195	return dump_bitmask_equal(@_,%ads_gpoptions);
1196}
1197
1198sub dump_gpflags {
1199	return dump_bitmask_equal(@_,%ads_gpflags);
1200}
1201
1202sub dump_uacc {
1203	return dump_bitmask_equal(@_,%ads_uacc);
1204}
1205
1206sub dump_uf {
1207	return dump_bitmask_and(@_,%ads_uf);
1208}
1209
1210sub dump_gtype {
1211	my $ret = dump_bitmask_and(@_,%ads_grouptype);
1212	$ret .= "\n$tabsize\t";
1213	$ret .= dump_bitmask_equal(@_,%ads_gtype);
1214	return $ret;
1215}
1216
1217sub dump_atype {
1218	return dump_bitmask_equal(@_,%ads_atype);
1219}
1220
1221sub dump_controls {
1222	return dump_equal(@_,%ads_controls);
1223}
1224
1225sub dump_capabilities {
1226	return dump_equal(@_,%ads_capabilities);
1227}
1228
1229sub dump_extension {
1230	return dump_equal(@_,%ads_extensions);
1231}
1232
1233sub dump_systemflags {
1234	return dump_bitmask_and(@_,%ads_systemflags);
1235}
1236
1237sub dump_instance_type {
1238	return dump_bitmask_and(@_,%ads_instance_type);
1239}
1240
1241sub dump_ds_func {
1242	return dump_bitmask_equal(@_,%ads_ds_func);
1243}
1244
1245sub dump_serverstate {
1246	return dump_bitmask_equal(@_,%ads_serverstate);
1247}
1248
1249sub dump_sdeffective {
1250	return dump_bitmask_and(@_,%ads_sdeffective);
1251}
1252
1253sub dump_trustattr {
1254	return dump_bitmask_equal(@_,%ads_trustattrs);
1255}
1256
1257sub dump_trusttype {
1258	return dump_bitmask_equal(@_,%ads_trusttype);
1259}
1260
1261sub dump_trustdirection {
1262	return dump_bitmask_equal(@_,%ads_trustdirection);
1263}
1264
1265sub dump_pwdproperties {
1266	return dump_bitmask_and(@_,%ads_pwdproperties);
1267}
1268
1269sub dump_frstype {
1270	return dump_bitmask_equal(@_,%ads_frstypes)
1271}
1272
1273sub dump_mixed_domain {
1274	return dump_bitmask_equal(@_,%ads_mixed_domain);
1275}
1276
1277sub dump_sid {
1278	my $bin_sid = shift;
1279	return sid2string($bin_sid);
1280}
1281
1282sub dump_guid {
1283	my $guid = shift;
1284	return guid_to_string($guid);
1285}
1286
1287sub dump_secdesc {
1288	my $val = shift;
1289	return "FIXME: write sddl-converter!";
1290}
1291
1292sub dump_nttime {
1293	my $nttime = shift;
1294	if ($nttime == 0) {
1295		return sprintf("%s (%s)", "never", $nttime);
1296	}
1297	my $localtime = localtime(nt_to_unixtime($nttime));
1298	return sprintf("%s (%s)", $localtime, $nttime);
1299}
1300
1301sub dump_nttime_abs {
1302	if ($_[0] == 9223372036854775807) {
1303		return sprintf("%s (%s)", "now", $_[0]);
1304	}
1305	if ($_[0] == -9223372036854775808 || $_[0] == 0) { # 0x7FFFFFFFFFFFFFFF
1306		return sprintf("%s (%s)", "never", $_[0]);
1307	}
1308	# FIXME: actually *do* abs time !
1309	return dump_nttime($_[0]);
1310}
1311
1312sub dump_timestr {
1313	my $time = shift;
1314	if ($time eq "16010101000010.0Z") {
1315		return sprintf("%s (%s)", "never", $time);
1316	}
1317	my ($year,$mon,$mday,$hour,$min,$sec,$zone) =
1318		unpack('a4 a2 a2 a2 a2 a2 a4', $time);
1319	$mon -= 1;
1320	my $localtime = localtime(timegm($sec,$min,$hour,$mday,$mon,$year));
1321	return sprintf("%s (%s)", $localtime, $time);
1322}
1323
1324sub dump_string {
1325	return $_[0];
1326}
1327
1328sub dump_int {
1329	return sprintf("%d", $_[0]);
1330}
1331
1332sub dump_munged_dial {
1333	my $dial = shift;
1334	return "FIXME! decode this";
1335}
1336
1337sub dump_cert {
1338
1339	my $cert = shift;
1340	open(OPENSSL, "| /usr/bin/openssl x509 -text -inform der");
1341	print OPENSSL $cert;
1342	close(OPENSSL);
1343	return "";
1344}
1345
1346sub dump_pkt {
1347	my $pkt = shift;
1348	return "not yet";
1349	printf("%s: ", $pkt);
1350	printf("%02X", $pkt);
1351
1352}
1353
1354sub dump_gplink {
1355
1356	my $gplink = shift;
1357	my $gplink_mod = $gplink;
1358	my @links = split("\\]\\[", $gplink_mod);
1359	foreach my $link (@links) {
1360		$link =~ s/^\[|\]$//g;
1361		my ($ldap_link, $opt) = split(";", $link);
1362		my @array = ( "$opt", "\t" );
1363		printf("%slink: %s, opt: %s\n", $tabsize, $ldap_link, dump_bitmask_and(@array, %ads_gplink_opts));
1364	}
1365	return $gplink;
1366}
1367
1368sub construct_filter {
1369
1370	my $tmp = shift;
1371
1372	if (!$tmp || $opt_notify) {
1373		return "(objectclass=*)";
1374	}
1375
1376	if ($tmp && $tmp !~ /^.*\=/) {
1377		return "(ANR=$tmp)";
1378	}
1379
1380	return $tmp;
1381	# (&(objectCategory=person)
1382	# (userAccountControl:$ads_matching_rules{LDAP_MATCHING_RULE_BIT_AND}:=2))
1383}
1384
1385sub construct_attrs {
1386
1387	my @attrs = @_;
1388
1389	if (!@attrs) {
1390		push(@attrs,"*");
1391	}
1392
1393	if ($opt_notify) {
1394		push(@attrs,"uSNChanged");
1395	}
1396
1397	if ($opt_display_metadata) {
1398		push(@attrs,"msDS-ReplAttributeMetaData");
1399		push(@attrs,"replPropertyMetaData");
1400	}
1401
1402	if ($opt_verbose) {
1403		push(@attrs,"nTSecurityDescriptor");
1404		push(@attrs,"msDS-KeyVersionNumber");
1405		push(@attrs,"msDS-User-Account-Control-Computed");
1406		push(@attrs,"modifyTimeStamp");
1407	}
1408
1409	return sort @attrs;
1410}
1411
1412sub print_header {
1413
1414	print "\n";
1415	printf "%10s: %s\n", "uri", $uri;
1416	printf "%10s: %s\n", "port", $port;
1417	printf "%10s: %s\n", "base", $base;
1418	printf "%10s: %s\n", $sasl_bind ? "principal" : "binddn", $sasl_bind ? $upn : $binddn;
1419	printf "%10s: %s\n", "password", $password;
1420	printf "%10s: %s\n", "bind-type", $sasl_bind ? "SASL" : "simple";
1421	printf "%10s: %s\n", "sasl-mech", $sasl_mech if ($sasl_mech);
1422	printf "%10s: %s\n", "filter", $filter;
1423	printf "%10s: %s\n", "scope", $scope;
1424	printf "%10s: %s\n", "attrs", join(", ", @attrs);
1425	printf "%10s: %s\n", "controls", join(", ", @ctrls_s);
1426	printf "%10s: %s\n", "page_size", $opt_paging if ($opt_paging);
1427	printf "%10s: %s\n", "start_tls", $opt_starttls ? "yes" : "no";
1428	printf "\n";
1429}
1430
1431sub gen_controls {
1432
1433	# setup attribute-scoped query control
1434	my $asq_asn = Convert::ASN1->new;
1435	$asq_asn->prepare(
1436		q<	asq ::=	SEQUENCE {
1437				sourceAttribute   OCTET_STRING
1438		  	}
1439		>
1440	);
1441	my $ctl_asq_val = $asq_asn->encode( sourceAttribute => $opt_asq);
1442	my $ctl_asq = Net::LDAP::Control->new(
1443		type => $ads_controls{'LDAP_SERVER_ASQ_OID'},
1444		critical => 'true',
1445		value => $ctl_asq_val);
1446
1447
1448	# setup extended dn control
1449	my $asn_extended_dn = Convert::ASN1->new;
1450	$asn_extended_dn->prepare(
1451		q<	ExtendedDn ::= SEQUENCE {
1452				mode     INTEGER
1453			}
1454		>
1455	);
1456
1457	# only w2k3 accepts '1' and needs the ctl_val, w2k does not accept a ctl_val
1458	my $ctl_extended_dn_val = $asn_extended_dn->encode( mode => $opt_display_extendeddn);
1459	my $ctl_extended_dn = Net::LDAP::Control->new(
1460			type => $ads_controls{'LDAP_SERVER_EXTENDED_DN_OID'},
1461			critical => 'true',
1462			value => $opt_display_extendeddn ? $ctl_extended_dn_val : "");
1463
1464	# setup notify control
1465	my $ctl_notification = Net::LDAP::Control->new(
1466		type => $ads_controls{'LDAP_SERVER_NOTIFICATION_OID'},
1467		critical => 'true');
1468
1469
1470	# setup paging control
1471	$ctl_paged = Net::LDAP::Control->new(
1472		type => $ads_controls{'LDAP_PAGED_RESULT_OID_STRING'},
1473		critical => 'true',
1474		size => $opt_paging ? $opt_paging : 1000);
1475
1476	# setup domscope control
1477	my $ctl_domscope = Net::LDAP::Control->new(
1478		type => $ads_controls{'LDAP_SERVER_DOMAIN_SCOPE_OID'},
1479		critical => 'true',
1480		value => "");
1481
1482	if (defined($opt_paging)) {
1483		push(@ctrls, $ctl_paged);
1484		push(@ctrls_s, "LDAP_PAGED_RESULT_OID_STRING" );
1485	}
1486
1487	if (defined($opt_display_extendeddn)) {
1488		push(@ctrls, $ctl_extended_dn);
1489		push(@ctrls_s, "LDAP_SERVER_EXTENDED_DN_OID");
1490	}
1491	if ($opt_notify) {
1492		push(@ctrls, $ctl_notification);
1493		push(@ctrls_s, "LDAP_SERVER_NOTIFICATION_OID");
1494	}
1495
1496	if ($opt_asq) {
1497		push(@ctrls, $ctl_asq);
1498		push(@ctrls_s, "LDAP_SERVER_ASQ_OID");
1499	}
1500
1501	if ($opt_domain_scope) {
1502		push(@ctrls, $ctl_domscope);
1503		push(@ctrls_s, "LDAP_SERVER_DOMAIN_SCOPE_OID");
1504	}
1505
1506	return @ctrls;
1507}
1508
1509sub display_value_generic ($$$) {
1510
1511	my ($mod,$attr,$value) = @_;
1512	return unless (defined($value) and defined($attr));
1513
1514	if ( ! $opt_display_raw && exists $attr_handler{$attr} ) {
1515		$value = $attr_handler{$attr}($value,$mod);
1516		write_ads_list($mod,$attr,$value);
1517		return;
1518	}
1519	write_ads($mod,$attr,$value);
1520}
1521
1522sub display_attr_generic ($$$) {
1523
1524	my ($mod,$entry,$attr) = @_;
1525	return if (!$attr || !$entry);
1526
1527	my $ref = $entry->get_value($attr, asref => 1);
1528	my @values = @$ref;
1529
1530	foreach my $value ( sort @values) {
1531		display_value_generic($mod,$attr,$value);
1532	}
1533}
1534
1535sub display_entry_generic ($) {
1536
1537	my $entry = shift;
1538	return if (!$entry);
1539
1540	foreach my $attr ( sort $entry->attributes) {
1541		display_attr_generic("\t",$entry,$attr);
1542	}
1543}
1544
1545sub display_ldap_err ($) {
1546
1547	my $msg = shift;
1548	print_header();
1549	my ($package, $filename, $line, $subroutine) = caller(0);
1550
1551	print "got error from ADS:\n";
1552	printf("%s(%d): ERROR (%s): %s\n",
1553		$subroutine, $line, $msg->code, $msg->error);
1554
1555}
1556
1557sub process_result {
1558
1559	my ($msg,$entry) = @_;
1560
1561	if (!defined($msg)) {
1562		return;
1563	}
1564
1565	if ($msg->code) {
1566		display_ldap_err($msg);
1567		exit 1;
1568	}
1569
1570	if (!defined($entry)) {
1571		return;
1572	}
1573
1574	if ($entry->isa('Net::LDAP::Reference')) {
1575		foreach my $ref ($entry->references) {
1576			print "\ngot Reference: [$ref]\n";
1577		}
1578		return;
1579	}
1580
1581	print "\nfound entry: ".$entry->dn."\n".("-" x 80)."\n";
1582
1583	display_entry_generic($entry);
1584}
1585
1586sub default_callback {
1587
1588	my ($res,$obj) = @_;
1589
1590	if (!$opt_notify && $res->code == LDAP_REFERRAL) {
1591		return;
1592	}
1593
1594	if (!$opt_notify) {
1595		return process_result($res,$obj);
1596	}
1597}
1598
1599sub error_callback {
1600
1601	my ($msg,$entry) = @_;
1602
1603	if (!$msg) {
1604		return;
1605	}
1606
1607	if ($msg->code) {
1608		display_ldap_err($msg);
1609		exit(1);
1610	}
1611	return;
1612}
1613
1614sub notify_callback {
1615
1616	my ($msg, $obj) = @_;
1617
1618	if (! defined($obj)) {
1619		return;
1620	}
1621
1622	if ($obj->isa('Net::LDAP::Reference')) {
1623		foreach my $ref ($obj->references) {
1624			print "\ngot Reference: [$ref]\n";
1625		}
1626		return;
1627	}
1628
1629	while (1) {
1630
1631		my $async_entry = $async_search->pop_entry;
1632
1633		if (!$async_entry->dn) {
1634			print "very weird. entry has no dn\n";
1635			next;
1636		}
1637
1638		printf("\ngot changenotify for dn: [%s]\n%s\n", $async_entry->dn, ("-" x 80));
1639
1640		my $new_usn = $async_entry->get_value('uSNChanged');
1641		if (!$new_usn) {
1642			die "odd, no usnChanged attribute???";
1643		}
1644		if (!$usn || $new_usn > $usn) {
1645			$usn = $new_usn;
1646			if ($opt_notify_nodiffs) {
1647				display_entry_generic($async_entry);
1648			} else {
1649				display_result_diff($async_entry);
1650			}
1651		}
1652		store_result($async_entry);
1653	}
1654}
1655
1656sub do_bind($$) {
1657
1658	my $async_ldap_hd = shift || return undef;
1659	my $sasl_bind = shift;
1660
1661	if ($sasl_bind) {
1662		if (!$works_sasl) {
1663			print "this version of Net::LDAP does not have proper SASL support.\n";
1664			print "Need at least perl-ldap V. $pref_version\n";
1665		}
1666		$sasl_hd = get_sasl_hd();
1667		if (!$sasl_hd) {
1668			print "failed to create SASL handle\n";
1669			return -1;
1670		}
1671		$mesg = $async_ldap_hd->bind(
1672			sasl => $sasl_hd,
1673			callback => \&error_callback
1674		) || die "doesnt work";
1675	} else {
1676		$sasl_mech = "";
1677		$mesg = $async_ldap_hd->bind(
1678			$binddn,
1679			password => $password,
1680			callback => $opt_fastbind ? undef : \&error_callback
1681		) || die "doesnt work";
1682	};
1683	if ($mesg->code) {
1684		display_ldap_err($mesg) if (!$opt_fastbind);
1685		return -1;
1686	}
1687	return 0;
1688}
1689
1690sub do_fast_bind() {
1691
1692	do_unbind();
1693
1694	my $hd = get_ldap_hd($server,1);
1695
1696	my $mesg = $hd->extension(
1697		name => $ads_extensions{'LDAP_SERVER_FAST_BIND_OID'});
1698
1699	if ($mesg->code) {
1700		display_ldap_err($mesg);
1701		return -1;
1702	}
1703
1704	my $ret = do_bind($hd, undef);
1705	$hd->unbind;
1706
1707	printf("bind using 'LDAP_SERVER_FAST_BIND_OID' %s\n",
1708		$ret == 0 ? "succeeded" : "failed");
1709
1710	return $ret;
1711}
1712
1713sub do_unbind() {
1714	if ($async_ldap_hd) {
1715		$async_ldap_hd->unbind;
1716	}
1717	if ($sync_ldap_hd) {
1718		$sync_ldap_hd->unbind;
1719	}
1720}
1721
1722###############################################################################
1723
1724sub main () {
1725
1726	if ($opt_fastbind) {
1727
1728		if (check_exts($server,$dse,"LDAP_SERVER_FAST_BIND_OID") == -1) {
1729			print "LDAP_SERVER_FAST_BIND_OID not supported by this server\n";
1730			exit 1;
1731		}
1732
1733		# fast bind can only bind, not search
1734		exit do_fast_bind();
1735	}
1736
1737	$filter = construct_filter($query);
1738	@attrs  = construct_attrs(@attrs);
1739	@ctrls  = gen_controls();
1740
1741	if (check_ctrls($server,$dse,@ctrls) == -1) {
1742		print "not all required LDAP Controls are supported by this server\n";
1743		exit 1;
1744	}
1745
1746	if ($opt_dump_rootdse) {
1747		print "Dumping Root-DSE:\n";
1748		display_entry_generic($dse);
1749		exit 0;
1750	}
1751
1752	if ($opt_dump_wknguid) {
1753		dump_wknguid();
1754		exit 0;
1755	}
1756
1757	if (do_bind($async_ldap_hd, $sasl_bind) == -1) {
1758		exit 1;
1759	}
1760
1761	print_header();
1762
1763	if ($opt_dump_schema) {
1764		print "Dumping Schema:\n";
1765		my $ads_schema = $async_ldap_hd->schema;
1766		$ads_schema->dump;
1767		exit 0;
1768	}
1769
1770	while (1) {
1771
1772		$async_search = $async_ldap_hd->search(
1773			base => $base,
1774			filter => $filter,
1775			attrs => [ @attrs ],
1776			control => [ @ctrls ],
1777			callback => \&default_callback,
1778			scope => $scope,
1779				) || die "cannot search";
1780
1781		if (!$opt_notify && ($async_search->code == LDAP_REFERRAL)) {
1782			foreach my $ref ($async_search->referrals) {
1783				print "\ngot Referral: [$ref]\n";
1784				$async_ldap_hd->unbind();
1785				$async_ldap_hd = get_ldap_hd($ref, 1);
1786				if (do_bind($async_ldap_hd, $sasl_bind) == -1) {
1787					$async_ldap_hd->unbind();
1788					next;
1789				}
1790				print "\nsuccessful rebind to: [$ref]\n";
1791				last;
1792			}
1793			next; # repeat the query
1794		}
1795
1796		if ($opt_notify) {
1797
1798			print "Base [$base] is registered now for change-notify\n";
1799			print "\nWaiting for change-notify...\n";
1800
1801			my $sync_ldap_hd = get_ldap_hd($server,0);
1802			if (do_bind($sync_ldap_hd, $sasl_bind) == -1) {
1803				exit 2;
1804			}
1805
1806			my $sync_search = $sync_ldap_hd->search(
1807				base => $base,
1808				filter => $filter,
1809				attrs => [ @attrs ],
1810				callback => \&notify_callback,
1811				scope => $scope,
1812				) || die "cannot search";
1813
1814		}
1815
1816		$total_entry_count += $async_search->count;
1817		++$page_count;
1818		print "-" x 80 . "\n";
1819		printf("Got %d Entries in Page %d \n\n",
1820			$async_search->count, $page_count);
1821		my ($resp) = $async_search->control(
1822			$ads_controls{'LDAP_PAGED_RESULT_OID_STRING'} ) or last;
1823		last unless ref $resp && $ctl_paged->cookie($resp->cookie);
1824	}
1825
1826	if ($async_search->entries == 0) {
1827		print "No entries found with filter $filter\n";
1828	} else {
1829		print "Got $total_entry_count Entries in $page_count Pages\n" if ($opt_paging);
1830	}
1831
1832	do_unbind()
1833}
1834
1835main();
1836
1837exit 0;
1838