| 1 | #!/usr/bin/perl -w
 | 
|---|
| 2 | 
 | 
|---|
| 3 | # Copyright (C) Guenther Deschner <gd@samba.org> 2006
 | 
|---|
| 4 | 
 | 
|---|
| 5 | use strict;
 | 
|---|
| 6 | use IO::Socket;
 | 
|---|
| 7 | use Convert::ASN1 qw(:debug);
 | 
|---|
| 8 | use Getopt::Long;
 | 
|---|
| 9 | 
 | 
|---|
| 10 | # TODO: timeout handling, user CLDAP query
 | 
|---|
| 11 | 
 | 
|---|
| 12 | ##################################
 | 
|---|
| 13 | 
 | 
|---|
| 14 | my $server = "";
 | 
|---|
| 15 | my $domain = "";
 | 
|---|
| 16 | my $host   = "";
 | 
|---|
| 17 | 
 | 
|---|
| 18 | ##################################
 | 
|---|
| 19 | 
 | 
|---|
| 20 | my (
 | 
|---|
| 21 |         $opt_debug,
 | 
|---|
| 22 |         $opt_domain,
 | 
|---|
| 23 |         $opt_help,
 | 
|---|
| 24 |         $opt_host,
 | 
|---|
| 25 |         $opt_server,
 | 
|---|
| 26 | );
 | 
|---|
| 27 | 
 | 
|---|
| 28 | my %cldap_flags = (
 | 
|---|
| 29 |         ADS_PDC                 => 0x00000001, # DC is PDC
 | 
|---|
| 30 |         ADS_GC                  => 0x00000004, # DC is a GC of forest
 | 
|---|
| 31 |         ADS_LDAP                => 0x00000008, # DC is an LDAP server
 | 
|---|
| 32 |         ADS_DS                  => 0x00000010, # DC supports DS
 | 
|---|
| 33 |         ADS_KDC                 => 0x00000020, # DC is running KDC
 | 
|---|
| 34 |         ADS_TIMESERV            => 0x00000040, # DC is running time services
 | 
|---|
| 35 |         ADS_CLOSEST             => 0x00000080, # DC is closest to client
 | 
|---|
| 36 |         ADS_WRITABLE            => 0x00000100, # DC has writable DS
 | 
|---|
| 37 |         ADS_GOOD_TIMESERV       => 0x00000200, # DC has hardware clock (and running time) 
 | 
|---|
| 38 |         ADS_NDNC                => 0x00000400, # DomainName is non-domain NC serviced by LDAP server
 | 
|---|
| 39 | );
 | 
|---|
| 40 | 
 | 
|---|
| 41 | my %cldap_samlogon_types = (
 | 
|---|
| 42 |         SAMLOGON_AD_UNK_R       => 23,
 | 
|---|
| 43 |         SAMLOGON_AD_R           => 25,
 | 
|---|
| 44 | );
 | 
|---|
| 45 | 
 | 
|---|
| 46 | my $MAX_DNS_LABEL = 255 + 1;
 | 
|---|
| 47 | 
 | 
|---|
| 48 | my %cldap_netlogon_reply = (
 | 
|---|
| 49 |         type                    => 0,
 | 
|---|
| 50 |         flags                   => 0x0,
 | 
|---|
| 51 |         guid                    => 0,
 | 
|---|
| 52 |         forest                  => undef,
 | 
|---|
| 53 |         domain                  => undef,
 | 
|---|
| 54 |         hostname                => undef,
 | 
|---|
| 55 |         netbios_domain          => undef,
 | 
|---|
| 56 |         netbios_hostname        => undef,
 | 
|---|
| 57 |         unk                     => undef,
 | 
|---|
| 58 |         user_name               => undef,
 | 
|---|
| 59 |         server_site_name        => undef,
 | 
|---|
| 60 |         client_site_name        => undef,
 | 
|---|
| 61 |         version                 => 0,
 | 
|---|
| 62 |         lmnt_token              => 0x0,
 | 
|---|
| 63 |         lm20_token              => 0x0,
 | 
|---|
| 64 | );
 | 
|---|
| 65 | 
 | 
|---|
| 66 | sub usage { 
 | 
|---|
| 67 |         print "usage: $0 [--domain|-d domain] [--help] [--host|-h host] [--server|-s server]\n\n";
 | 
|---|
| 68 | }
 | 
|---|
| 69 | 
 | 
|---|
| 70 | sub connect_cldap ($) {
 | 
|---|
| 71 | 
 | 
|---|
| 72 |         my $server = shift || return undef;
 | 
|---|
| 73 |         
 | 
|---|
| 74 |         return IO::Socket::INET->new(
 | 
|---|
| 75 |                 PeerAddr        => $server,
 | 
|---|
| 76 |                 PeerPort        => 389,
 | 
|---|
| 77 |                 Proto           => 'udp',
 | 
|---|
| 78 |                 Type            => SOCK_DGRAM,
 | 
|---|
| 79 |                 Timeout         => 10,
 | 
|---|
| 80 |         );
 | 
|---|
| 81 | }
 | 
|---|
| 82 | 
 | 
|---|
| 83 | sub send_cldap_netlogon ($$$$) {
 | 
|---|
| 84 | 
 | 
|---|
| 85 |         my ($sock, $domain, $host, $ntver) = @_;
 | 
|---|
| 86 | 
 | 
|---|
| 87 |         my $asn_cldap_req = Convert::ASN1->new;
 | 
|---|
| 88 | 
 | 
|---|
| 89 |         $asn_cldap_req->prepare(q<
 | 
|---|
| 90 | 
 | 
|---|
| 91 |                 SEQUENCE {
 | 
|---|
| 92 |                         msgid INTEGER,
 | 
|---|
| 93 |                         [APPLICATION 3] SEQUENCE {
 | 
|---|
| 94 |                                 basedn OCTET STRING,
 | 
|---|
| 95 |                                 scope ENUMERATED,
 | 
|---|
| 96 |                                 dereference ENUMERATED,
 | 
|---|
| 97 |                                 sizelimit INTEGER,
 | 
|---|
| 98 |                                 timelimit INTEGER,
 | 
|---|
| 99 |                                 attronly BOOLEAN,
 | 
|---|
| 100 |                                 [CONTEXT 0] SEQUENCE {
 | 
|---|
| 101 |                                         [CONTEXT 3] SEQUENCE {
 | 
|---|
| 102 |                                                 dnsdom_attr OCTET STRING,
 | 
|---|
| 103 |                                                 dnsdom_val  OCTET STRING
 | 
|---|
| 104 |                                         }
 | 
|---|
| 105 |                                         [CONTEXT 3] SEQUENCE {
 | 
|---|
| 106 |                                                 host_attr OCTET STRING,
 | 
|---|
| 107 |                                                 host_val  OCTET STRING
 | 
|---|
| 108 |                                         }
 | 
|---|
| 109 |                                         [CONTEXT 3] SEQUENCE {
 | 
|---|
| 110 |                                                 ntver_attr OCTET STRING,
 | 
|---|
| 111 |                                                 ntver_val  OCTET STRING
 | 
|---|
| 112 |                                         }
 | 
|---|
| 113 |                                 }
 | 
|---|
| 114 |                                 SEQUENCE {
 | 
|---|
| 115 |                                         netlogon OCTET STRING
 | 
|---|
| 116 |                                 }
 | 
|---|
| 117 |                         }
 | 
|---|
| 118 |                 }
 | 
|---|
| 119 |         >);
 | 
|---|
| 120 | 
 | 
|---|
| 121 |         my $pdu_req = $asn_cldap_req->encode( 
 | 
|---|
| 122 |                                 msgid => 0,
 | 
|---|
| 123 |                                 basedn => "", 
 | 
|---|
| 124 |                                 scope => 0,
 | 
|---|
| 125 |                                 dereference => 0,
 | 
|---|
| 126 |                                 sizelimit => 0,
 | 
|---|
| 127 |                                 timelimit => 0,
 | 
|---|
| 128 |                                 attronly => 0,
 | 
|---|
| 129 |                                 dnsdom_attr => $domain ? 'DnsDomain' : "",
 | 
|---|
| 130 |                                 dnsdom_val => $domain ? $domain : "",
 | 
|---|
| 131 |                                 host_attr => 'Host',
 | 
|---|
| 132 |                                 host_val => $host,
 | 
|---|
| 133 |                                 ntver_attr => 'NtVer',
 | 
|---|
| 134 |                                 ntver_val => $ntver,
 | 
|---|
| 135 |                                 netlogon => 'NetLogon',
 | 
|---|
| 136 |                                 ) || die "failed to encode pdu: $@";
 | 
|---|
| 137 | 
 | 
|---|
| 138 |         if ($opt_debug) {
 | 
|---|
| 139 |                 print"------------\n";
 | 
|---|
| 140 |                 asn_dump($pdu_req);
 | 
|---|
| 141 |                 print"------------\n";
 | 
|---|
| 142 |         }
 | 
|---|
| 143 | 
 | 
|---|
| 144 |         return $sock->send($pdu_req) || die "no send: $@";
 | 
|---|
| 145 | }
 | 
|---|
| 146 | 
 | 
|---|
| 147 | # from source/libads/cldap.c :
 | 
|---|
| 148 | #
 | 
|---|
| 149 | #/*
 | 
|---|
| 150 | #  These seem to be strings as described in RFC1035 4.1.4 and can be:
 | 
|---|
| 151 | #
 | 
|---|
| 152 | #   - a sequence of labels ending in a zero octet
 | 
|---|
| 153 | #   - a pointer
 | 
|---|
| 154 | #   - a sequence of labels ending with a pointer
 | 
|---|
| 155 | #
 | 
|---|
| 156 | #  A label is a byte where the first two bits must be zero and the remaining
 | 
|---|
| 157 | #  bits represent the length of the label followed by the label itself.
 | 
|---|
| 158 | #  Therefore, the length of a label is at max 64 bytes.  Under RFC1035, a
 | 
|---|
| 159 | #  sequence of labels cannot exceed 255 bytes.
 | 
|---|
| 160 | #
 | 
|---|
| 161 | #  A pointer consists of a 14 bit offset from the beginning of the data.
 | 
|---|
| 162 | #
 | 
|---|
| 163 | #  struct ptr {
 | 
|---|
| 164 | #    unsigned ident:2; // must be 11
 | 
|---|
| 165 | #    unsigned offset:14; // from the beginning of data
 | 
|---|
| 166 | #  };
 | 
|---|
| 167 | #
 | 
|---|
| 168 | #  This is used as a method to compress the packet by eliminated duplicate
 | 
|---|
| 169 | #  domain components.  Since a UDP packet should probably be < 512 bytes and a
 | 
|---|
| 170 | #  DNS name can be up to 255 bytes, this actually makes a lot of sense.
 | 
|---|
| 171 | #*/
 | 
|---|
| 172 | 
 | 
|---|
| 173 | sub pull_netlogon_string (\$$$) {
 | 
|---|
| 174 |         
 | 
|---|
| 175 |         my ($ret, $ptr, $str) = @_;
 | 
|---|
| 176 | 
 | 
|---|
| 177 |         my $pos = $ptr;
 | 
|---|
| 178 | 
 | 
|---|
| 179 |         my $followed_ptr = 0;
 | 
|---|
| 180 |         my $ret_len = 0;
 | 
|---|
| 181 | 
 | 
|---|
| 182 |         my $retp = pack("x$MAX_DNS_LABEL");
 | 
|---|
| 183 | 
 | 
|---|
| 184 |         do {
 | 
|---|
| 185 |         
 | 
|---|
| 186 |                 $ptr = unpack("c", substr($str, $pos, 1));
 | 
|---|
| 187 |                 $pos++;
 | 
|---|
| 188 | 
 | 
|---|
| 189 |                 if (($ptr & 0xc0) == 0xc0) {
 | 
|---|
| 190 | 
 | 
|---|
| 191 |                         my $len;
 | 
|---|
| 192 | 
 | 
|---|
| 193 |                         if (!$followed_ptr) {
 | 
|---|
| 194 |                                 $ret_len += 2;
 | 
|---|
| 195 |                                 $followed_ptr = 1;
 | 
|---|
| 196 |                         }
 | 
|---|
| 197 | 
 | 
|---|
| 198 |                         my $tmp0 = $ptr; #unpack("c", substr($str, $pos-1, 1));
 | 
|---|
| 199 |                         my $tmp1 = unpack("c", substr($str, $pos, 1));
 | 
|---|
| 200 | 
 | 
|---|
| 201 |                         if ($opt_debug) {
 | 
|---|
| 202 |                                 printf("tmp0: 0x%x\n", $tmp0);
 | 
|---|
| 203 |                                 printf("tmp1: 0x%x\n", $tmp1);
 | 
|---|
| 204 |                         }
 | 
|---|
| 205 | 
 | 
|---|
| 206 |                         $len = (($tmp0 & 0x3f) << 8) | $tmp1;
 | 
|---|
| 207 |                         $ptr = unpack("c", substr($str, $len, 1));
 | 
|---|
| 208 |                         $pos = $len;
 | 
|---|
| 209 | 
 | 
|---|
| 210 |                 } elsif ($ptr) {
 | 
|---|
| 211 | 
 | 
|---|
| 212 |                         my $len = scalar $ptr;
 | 
|---|
| 213 | 
 | 
|---|
| 214 |                         if ($len + 1 > $MAX_DNS_LABEL) {
 | 
|---|
| 215 |                                 warn("invalid string size: %d", $len + 1);
 | 
|---|
| 216 |                                 return 0;
 | 
|---|
| 217 |                         }
 | 
|---|
| 218 | 
 | 
|---|
| 219 |                         $ptr = unpack("a*", substr($str, $pos, $len));
 | 
|---|
| 220 | 
 | 
|---|
| 221 |                         $retp = sprintf("%s%s\.", $retp, $ptr);
 | 
|---|
| 222 | 
 | 
|---|
| 223 |                         $pos += $len;
 | 
|---|
| 224 |                         if (!$followed_ptr) {
 | 
|---|
| 225 |                                 $ret_len += $len + 1;
 | 
|---|
| 226 |                         }
 | 
|---|
| 227 |                 }
 | 
|---|
| 228 | 
 | 
|---|
| 229 |         } while ($ptr);
 | 
|---|
| 230 | 
 | 
|---|
| 231 |         $retp =~ s/\.$//; #ugly hack...
 | 
|---|
| 232 | 
 | 
|---|
| 233 |         $$ret = $retp;
 | 
|---|
| 234 | 
 | 
|---|
| 235 |         return $followed_ptr ? $ret_len : $ret_len + 1;
 | 
|---|
| 236 | }
 | 
|---|
| 237 | 
 | 
|---|
| 238 | sub dump_cldap_flags ($) {
 | 
|---|
| 239 | 
 | 
|---|
| 240 |         my $flags = shift || return;
 | 
|---|
| 241 |         printf("Flags:\n".
 | 
|---|
| 242 |                  "\tIs a PDC:                                   %s\n".
 | 
|---|
| 243 |                  "\tIs a GC of the forest:                      %s\n".
 | 
|---|
| 244 |                  "\tIs an LDAP server:                          %s\n".
 | 
|---|
| 245 |                  "\tSupports DS:                                %s\n".
 | 
|---|
| 246 |                  "\tIs running a KDC:                           %s\n".
 | 
|---|
| 247 |                  "\tIs running time services:                   %s\n".
 | 
|---|
| 248 |                  "\tIs the closest DC:                          %s\n".
 | 
|---|
| 249 |                  "\tIs writable:                                %s\n".
 | 
|---|
| 250 |                  "\tHas a hardware clock:                       %s\n".
 | 
|---|
| 251 |                  "\tIs a non-domain NC serviced by LDAP server: %s\n",
 | 
|---|
| 252 |                  ($flags & $cldap_flags{ADS_PDC}) ? "yes" : "no",
 | 
|---|
| 253 |                  ($flags & $cldap_flags{ADS_GC}) ? "yes" : "no",
 | 
|---|
| 254 |                  ($flags & $cldap_flags{ADS_LDAP}) ? "yes" : "no",
 | 
|---|
| 255 |                  ($flags & $cldap_flags{ADS_DS}) ? "yes" : "no",
 | 
|---|
| 256 |                  ($flags & $cldap_flags{ADS_KDC}) ? "yes" : "no",
 | 
|---|
| 257 |                  ($flags & $cldap_flags{ADS_TIMESERV}) ? "yes" : "no",
 | 
|---|
| 258 |                  ($flags & $cldap_flags{ADS_CLOSEST}) ? "yes" : "no",
 | 
|---|
| 259 |                  ($flags & $cldap_flags{ADS_WRITABLE}) ? "yes" : "no",
 | 
|---|
| 260 |                  ($flags & $cldap_flags{ADS_GOOD_TIMESERV}) ? "yes" : "no",
 | 
|---|
| 261 |                  ($flags & $cldap_flags{ADS_NDNC}) ? "yes" : "no");
 | 
|---|
| 262 | }
 | 
|---|
| 263 | 
 | 
|---|
| 264 | sub guid_to_string ($) {
 | 
|---|
| 265 | 
 | 
|---|
| 266 |         my $guid = shift || return undef;
 | 
|---|
| 267 |         if ((my $len = length $guid) != 16) {
 | 
|---|
| 268 |                 printf("invalid length: %d\n", $len);
 | 
|---|
| 269 |                 return undef;
 | 
|---|
| 270 |         }
 | 
|---|
| 271 |         my $string = sprintf "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", 
 | 
|---|
| 272 |                 unpack("I", $guid), 
 | 
|---|
| 273 |                 unpack("S", substr($guid, 4, 2)), 
 | 
|---|
| 274 |                 unpack("S", substr($guid, 6, 2)),
 | 
|---|
| 275 |                 unpack("C", substr($guid, 8, 1)),
 | 
|---|
| 276 |                 unpack("C", substr($guid, 9, 1)),
 | 
|---|
| 277 |                 unpack("C", substr($guid, 10, 1)),
 | 
|---|
| 278 |                 unpack("C", substr($guid, 11, 1)),
 | 
|---|
| 279 |                 unpack("C", substr($guid, 12, 1)),
 | 
|---|
| 280 |                 unpack("C", substr($guid, 13, 1)),
 | 
|---|
| 281 |                 unpack("C", substr($guid, 14, 1)),
 | 
|---|
| 282 |                 unpack("C", substr($guid, 15, 1)); 
 | 
|---|
| 283 |         return lc($string);
 | 
|---|
| 284 | }
 | 
|---|
| 285 | 
 | 
|---|
| 286 | sub recv_cldap_netlogon ($\$) {
 | 
|---|
| 287 | 
 | 
|---|
| 288 |         my ($sock, $return_string) = @_;
 | 
|---|
| 289 |         my ($ret, $pdu_out);
 | 
|---|
| 290 | 
 | 
|---|
| 291 |         $ret = $sock->recv($pdu_out, 8192) || die "failed to read from socket: $@";
 | 
|---|
| 292 |         #$ret = sysread($sock, $pdu_out, 8192);
 | 
|---|
| 293 | 
 | 
|---|
| 294 |         if ($opt_debug) {
 | 
|---|
| 295 |                 print"------------\n";
 | 
|---|
| 296 |                 asn_dump($pdu_out);
 | 
|---|
| 297 |                 print"------------\n";
 | 
|---|
| 298 |         }
 | 
|---|
| 299 | 
 | 
|---|
| 300 |         my $asn_cldap_rep = Convert::ASN1->new;
 | 
|---|
| 301 |         my $asn_cldap_rep_fail = Convert::ASN1->new;
 | 
|---|
| 302 | 
 | 
|---|
| 303 |         $asn_cldap_rep->prepare(q<
 | 
|---|
| 304 |                 SEQUENCE {
 | 
|---|
| 305 |                         msgid INTEGER,
 | 
|---|
| 306 |                         [APPLICATION 4] SEQUENCE {
 | 
|---|
| 307 |                                 dn OCTET STRING,
 | 
|---|
| 308 |                                 SEQUENCE {
 | 
|---|
| 309 |                                         SEQUENCE {
 | 
|---|
| 310 |                                                 attr OCTET STRING,
 | 
|---|
| 311 |                                                 SET {
 | 
|---|
| 312 |                                                         val OCTET STRING
 | 
|---|
| 313 |                                                 }
 | 
|---|
| 314 |                                         }
 | 
|---|
| 315 |                                 }
 | 
|---|
| 316 |                         }
 | 
|---|
| 317 |                 }
 | 
|---|
| 318 |                 SEQUENCE {
 | 
|---|
| 319 |                         msgid2 INTEGER,
 | 
|---|
| 320 |                         [APPLICATION 5] SEQUENCE {
 | 
|---|
| 321 |                                 error_code ENUMERATED,
 | 
|---|
| 322 |                                 matched_dn OCTET STRING,
 | 
|---|
| 323 |                                 error_message OCTET STRING
 | 
|---|
| 324 |                         }
 | 
|---|
| 325 |                 }
 | 
|---|
| 326 |         >);
 | 
|---|
| 327 | 
 | 
|---|
| 328 |         $asn_cldap_rep_fail->prepare(q<
 | 
|---|
| 329 |                 SEQUENCE {
 | 
|---|
| 330 |                         msgid2 INTEGER,
 | 
|---|
| 331 |                         [APPLICATION 5] SEQUENCE {
 | 
|---|
| 332 |                                 error_code ENUMERATED,
 | 
|---|
| 333 |                                 matched_dn OCTET STRING,
 | 
|---|
| 334 |                                 error_message OCTET STRING
 | 
|---|
| 335 |                         }
 | 
|---|
| 336 |                 }
 | 
|---|
| 337 |         >);
 | 
|---|
| 338 | 
 | 
|---|
| 339 |         my $asn1_rep =  $asn_cldap_rep->decode($pdu_out) || 
 | 
|---|
| 340 |                         $asn_cldap_rep_fail->decode($pdu_out) || 
 | 
|---|
| 341 |                         die "failed to decode pdu: $@";
 | 
|---|
| 342 | 
 | 
|---|
| 343 |         if ($asn1_rep->{'error_code'} == 0) {
 | 
|---|
| 344 |                 $$return_string = $asn1_rep->{'val'};
 | 
|---|
| 345 |         } 
 | 
|---|
| 346 | 
 | 
|---|
| 347 |         return $ret;
 | 
|---|
| 348 | }
 | 
|---|
| 349 | 
 | 
|---|
| 350 | sub parse_cldap_reply ($) {
 | 
|---|
| 351 | 
 | 
|---|
| 352 |         my $str = shift || return undef;
 | 
|---|
| 353 |         my %hash;
 | 
|---|
| 354 |         my $p = 0;
 | 
|---|
| 355 | 
 | 
|---|
| 356 |         $hash{type}     = unpack("L", substr($str, $p, 4)); $p += 4;
 | 
|---|
| 357 |         $hash{flags}    = unpack("L", substr($str, $p, 4)); $p += 4;
 | 
|---|
| 358 |         $hash{guid}     = unpack("a16", substr($str, $p, 16)); $p += 16;
 | 
|---|
| 359 | 
 | 
|---|
| 360 |         $p += pull_netlogon_string($hash{forest}, $p, $str);
 | 
|---|
| 361 |         $p += pull_netlogon_string($hash{domain}, $p, $str);
 | 
|---|
| 362 |         $p += pull_netlogon_string($hash{hostname}, $p, $str);
 | 
|---|
| 363 |         $p += pull_netlogon_string($hash{netbios_domain}, $p, $str);
 | 
|---|
| 364 |         $p += pull_netlogon_string($hash{netbios_hostname}, $p, $str);
 | 
|---|
| 365 |         $p += pull_netlogon_string($hash{unk}, $p, $str);
 | 
|---|
| 366 | 
 | 
|---|
| 367 |         if ($hash{type} == $cldap_samlogon_types{SAMLOGON_AD_R}) {
 | 
|---|
| 368 |                 $p += pull_netlogon_string($hash{user_name}, $p, $str);
 | 
|---|
| 369 |         } else {
 | 
|---|
| 370 |                 $hash{user_name} = "";
 | 
|---|
| 371 |         }
 | 
|---|
| 372 | 
 | 
|---|
| 373 |         $p += pull_netlogon_string($hash{server_site_name}, $p, $str);
 | 
|---|
| 374 |         $p += pull_netlogon_string($hash{client_site_name}, $p, $str);
 | 
|---|
| 375 | 
 | 
|---|
| 376 |         $hash{version}          = unpack("L", substr($str, $p, 4)); $p += 4;
 | 
|---|
| 377 |         $hash{lmnt_token}       = unpack("S", substr($str, $p, 2)); $p += 2;
 | 
|---|
| 378 |         $hash{lm20_token}       = unpack("S", substr($str, $p, 2)); $p += 2;
 | 
|---|
| 379 | 
 | 
|---|
| 380 |         return %hash;
 | 
|---|
| 381 | }
 | 
|---|
| 382 | 
 | 
|---|
| 383 | sub display_cldap_reply {
 | 
|---|
| 384 | 
 | 
|---|
| 385 |         my $server = shift;
 | 
|---|
| 386 |         my (%hash) = @_;
 | 
|---|
| 387 | 
 | 
|---|
| 388 |         my ($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($server);
 | 
|---|
| 389 | 
 | 
|---|
| 390 |         printf("Information for Domain Controller: %s\n\n", $name);
 | 
|---|
| 391 | 
 | 
|---|
| 392 |         printf("Response Type: ");
 | 
|---|
| 393 |         if ($hash{type} == $cldap_samlogon_types{SAMLOGON_AD_R}) {
 | 
|---|
| 394 |                 printf("SAMLOGON_USER\n");
 | 
|---|
| 395 |         } elsif ($hash{type} == $cldap_samlogon_types{SAMLOGON_AD_UNK_R}) {
 | 
|---|
| 396 |                 printf("SAMLOGON\n");
 | 
|---|
| 397 |         } else {
 | 
|---|
| 398 |                 printf("unknown type 0x%x, please report\n", $hash{type});
 | 
|---|
| 399 |         }
 | 
|---|
| 400 | 
 | 
|---|
| 401 |         # guid
 | 
|---|
| 402 |         printf("GUID: %s\n", guid_to_string($hash{guid}));
 | 
|---|
| 403 | 
 | 
|---|
| 404 |         # flags
 | 
|---|
| 405 |         dump_cldap_flags($hash{flags});
 | 
|---|
| 406 | 
 | 
|---|
| 407 |         # strings
 | 
|---|
| 408 |         printf("Forest:\t\t\t%s\n", $hash{forest});
 | 
|---|
| 409 |         printf("Domain:\t\t\t%s\n", $hash{domain});
 | 
|---|
| 410 |         printf("Domain Controller:\t%s\n", $hash{hostname});
 | 
|---|
| 411 | 
 | 
|---|
| 412 |         printf("Pre-Win2k Domain:\t%s\n", $hash{netbios_domain});
 | 
|---|
| 413 |         printf("Pre-Win2k Hostname:\t%s\n", $hash{netbios_hostname});
 | 
|---|
| 414 | 
 | 
|---|
| 415 |         if ($hash{unk}) { 
 | 
|---|
| 416 |                 printf("Unk:\t\t\t%s\n", $hash{unk}); 
 | 
|---|
| 417 |         }
 | 
|---|
| 418 |         if ($hash{user_name}) { 
 | 
|---|
| 419 |                 printf("User name:\t%s\n", $hash{user_name}); 
 | 
|---|
| 420 |         }
 | 
|---|
| 421 | 
 | 
|---|
| 422 |         printf("Server Site Name:\t%s\n", $hash{server_site_name});
 | 
|---|
| 423 |         printf("Client Site Name:\t%s\n", $hash{client_site_name});
 | 
|---|
| 424 | 
 | 
|---|
| 425 |         # some more int
 | 
|---|
| 426 |         printf("NT Version:\t\t%d\n", $hash{version});
 | 
|---|
| 427 |         printf("LMNT Token:\t\t%.2x\n", $hash{lmnt_token});
 | 
|---|
| 428 |         printf("LM20 Token:\t\t%.2x\n", $hash{lm20_token});
 | 
|---|
| 429 | }
 | 
|---|
| 430 | 
 | 
|---|
| 431 | sub main() {
 | 
|---|
| 432 | 
 | 
|---|
| 433 |         my ($ret, $sock, $reply);
 | 
|---|
| 434 | 
 | 
|---|
| 435 |         GetOptions(
 | 
|---|
| 436 |                 'debug'         => \$opt_debug,
 | 
|---|
| 437 |                 'domain|d=s'    => \$opt_domain,
 | 
|---|
| 438 |                 'help'          => \$opt_help,
 | 
|---|
| 439 |                 'host|h=s'      => \$opt_host,
 | 
|---|
| 440 |                 'server|s=s'    => \$opt_server,
 | 
|---|
| 441 |         );
 | 
|---|
| 442 | 
 | 
|---|
| 443 |         $server = $server || $opt_server;
 | 
|---|
| 444 |         $domain = $domain || $opt_domain || undef;
 | 
|---|
| 445 |         $host = $host || $opt_host;
 | 
|---|
| 446 |         if (!$host) {
 | 
|---|
| 447 |                 $host = `/bin/hostname`;
 | 
|---|
| 448 |                 chomp($host);
 | 
|---|
| 449 |         }
 | 
|---|
| 450 | 
 | 
|---|
| 451 |         if (!$server || !$host || $opt_help) {
 | 
|---|
| 452 |                 usage();
 | 
|---|
| 453 |                 exit 1;
 | 
|---|
| 454 |         }
 | 
|---|
| 455 | 
 | 
|---|
| 456 |         my $ntver = sprintf("%c%c%c%c", 6,0,0,0);
 | 
|---|
| 457 | 
 | 
|---|
| 458 |         $sock = connect_cldap($server);
 | 
|---|
| 459 |         if (!$sock) {
 | 
|---|
| 460 |                 die("could not connect to $server");
 | 
|---|
| 461 |         }
 | 
|---|
| 462 | 
 | 
|---|
| 463 |         $ret = send_cldap_netlogon($sock, $domain, $host, $ntver);
 | 
|---|
| 464 |         if (!$ret) {
 | 
|---|
| 465 |                 close($sock);
 | 
|---|
| 466 |                 die("failed to send CLDAP request to $server");
 | 
|---|
| 467 |         }
 | 
|---|
| 468 | 
 | 
|---|
| 469 |         $ret = recv_cldap_netlogon($sock, $reply);
 | 
|---|
| 470 |         if (!$ret) {
 | 
|---|
| 471 |                 close($sock);
 | 
|---|
| 472 |                 die("failed to receive CLDAP reply from $server");
 | 
|---|
| 473 |         }
 | 
|---|
| 474 |         close($sock);
 | 
|---|
| 475 | 
 | 
|---|
| 476 |         if (!$reply) {
 | 
|---|
| 477 |                 printf("no 'NetLogon' attribute received\n");
 | 
|---|
| 478 |                 exit 0;
 | 
|---|
| 479 |         }
 | 
|---|
| 480 | 
 | 
|---|
| 481 |         %cldap_netlogon_reply = parse_cldap_reply($reply);
 | 
|---|
| 482 |         if (!%cldap_netlogon_reply) {
 | 
|---|
| 483 |                 die("failed to parse CLDAP reply from $server");
 | 
|---|
| 484 |         }
 | 
|---|
| 485 | 
 | 
|---|
| 486 |         display_cldap_reply($server, %cldap_netlogon_reply);
 | 
|---|
| 487 | 
 | 
|---|
| 488 |         exit 0;
 | 
|---|
| 489 | }
 | 
|---|
| 490 | 
 | 
|---|
| 491 | main();
 | 
|---|