#!/usr/bin/perl

use strict;
use warnings;
use English;
use Net::LDAP;
use Net::LDAP::Control::Paged;
use Net::LDAP::Constant qw(LDAP_CONTROL_PAGED);

if (@ARGV != 2) {
  print "Usage: $0 ADSERVER ADDOMAIN\n";
  exit(0);
}

my ($dc, $domain) = @ARGV;
my $base = 'DC=' . $domain;
$base =~ s/\./,DC=/g;

print 'Username: ';
chomp(my $admin_name = STDIN->getline);
if (-t STDIN) {
  $SIG{'INT'} = $SIG{'TERM'} = sub {system '/bin/stty echo'; exit(1);};
  system '/bin/stty -echo';
}
print 'Password: ';
chomp(my $admin_pass = STDIN->getline);
print "\n";
system "/bin/stty echo" if (-t STDIN);
if (-t STDIN) {
  system '/bin/stty echo';
  $SIG{'INT'} = $SIG{'TERM'} = 'DEFAULT';
}

my $admin_dn = 'CN=' . $admin_name . ',CN=Users,' . $base;

my $ldap = Net::LDAP->new($dc) || die "$@";
$ldap->bind($admin_dn, 'password' => $admin_pass);
my $page = Net::LDAP::Control::Paged->new(size => 100);
my %args = (
  'base' =>	$base,
  'scope' =>	'sub',
  'control' =>	[$page],
  'filter' =>	'objectSid=*',
  'attrs' =>	['objectSid'],
);

my @entry = ();
my $cookie;
while (1) {
  my $mesg = $ldap->search(%args);
  $mesg->code && die $mesg->error;

  push(@entry, $mesg->entries);

  my ($resp) = $mesg->control(LDAP_CONTROL_PAGED) or last;
  $cookie = $resp->cookie or last;
  $page->cookie($cookie);
  print '.';
}

if ($cookie) {
  $page->cookie($cookie);
  $page->size(0);
  my $mesg = $ldap->search(%args);
  $mesg->code && die $mesg->error;

  push(@entry, $mesg->entries);
}

for my $entry (@entry) {
  my $sid_bin = $entry->get_value('objectSid');
  my ($sid_rev, $sid_num_auths, @sid_id_auth) = unpack('C8', $sid_bin);
  my @sid_sub_auths =
    (unpack('C8' . ('V' x $sid_num_auths), $sid_bin))[-$sid_num_auths..-1];
  my $sid =
    sprintf('S-%u-%u' . '-%u' x $sid_num_auths, $sid_rev, $sid_num_auths, @sid_sub_auths);

  print $entry->dn, ' ', $sid, "\n";
}

exit(0);

