#!/usr/bin/perl -w
##!/usr/bin/env perl 
#
#
# LDAPv3 capabilities discovery
#
# Mike Jackson <mj@sci.fi>
#

use strict;
use Net::LDAP;

my $host	  = $ARGV[0];

my $ldap	  = new Net::LDAP($host) || die "$@";
my $result	= $ldap->search (
      base   => "",
      scope  => "base",
      filter => "(objectClass=*)",
      );
my $entry   = $result->entry(0);


my %capabilities = (
  '1.2.840.113556.1.4.800'    => 'LDAP Capabilities Active Directory',  # indicates an AD server
  '1.2.840.113556.1.4.1791'   => 'LDAP Capabilities Active Directory LDAP Integration',
);

my %controls     = (
  '1.2.826.0.1.3344810.2.3'   => 'Matched Values Control',
  '1.2.840.113556.1.4.319'    => 'Paged Results Control',
  '1.2.840.113556.1.4.417'    => 'LDAP Server Show Deleted',
  '1.2.840.113556.1.4.473'    => 'LDAP Server Sort Result',
  '1.2.840.113556.1.4.474'    => 'Sort Response',
  '1.2.840.113556.1.4.521'    => 'LDAP Server Cross Domain Move Target',
  '1.2.840.113556.1.4.528'    => 'LDAP Server Notification',
  '1.2.840.113556.1.4.529'    => 'LDAP Server Extended DN',
  '1.2.840.113556.1.4.619'    => 'LDAP Server Lazy Commit',
  '1.2.840.113556.1.4.801'    => 'LDAP Server Security Descriptor Flags',
  '1.2.840.113556.1.4.805'    => 'LDAP Server Tree Delete',
  '1.2.840.113556.1.4.841'    => 'LDAP Server DirSync',
  '1.2.840.113556.1.4.970'    => 'LDAP Server Query Time Statistics',
  '1.2.840.113556.1.4.1338'   => 'LDAP Server Verify name',
  '1.2.840.113556.1.4.1339'   => 'LDAP Server Domain Scope',
  '1.2.840.113556.1.4.1340'   => 'LDAP Server Search Options',
  '1.2.840.113556.1.4.1413'   => 'LDAP Server Permissive Modify',
  '1.3.6.1.1.7.1'             => 'LCUP Sync Request Control',
  '1.3.6.1.1.7.2'             => 'LCUP Sync Update Control',
  '1.3.6.1.1.7.3'             => 'LCUP Sync Done Control',
  '1.3.6.1.1.12'              => 'Assertion Control',
  '1.3.6.1.1.13.1'            => 'LDAP Pre-read Control',
  '1.3.6.1.1.13.2'            => 'LDAP Post-read Control',
  '1.3.6.1.4.1.42.2.27.8.5.1' => 'Password Policy Request/Response Control',
  '1.3.6.1.4.1.42.2.27.9.5.2' => 'Get Effective Rights Request Control',
  '1.3.6.1.4.1.1466.29539.12' => 'LDAP Server Chaining Loop Detection',
  '1.3.6.1.4.1.4203.1.9.1.1'  => 'LDAP Content Synchronization Control',
  '1.3.6.1.4.1.4203.1.10.1'   => 'Subentries',
  '2.16.840.1.113730.3.4.2'   => 'Manage DSA IT LDAPv3 Control',
  '2.16.840.1.113730.3.4.3'   => 'Persistent Search LDAPv3 Control',
  '2.16.840.1.113730.3.4.4'   => 'Netscape Password Expired LDAPv3 Control',
  '2.16.840.1.113730.3.4.5'   => 'Netscape Password Expiring LDAPv3 Control',
  '2.16.840.1.113730.3.4.9'   => 'VLV Request LDAPv3 Control',
  '2.16.840.1.113730.3.4.12'  => 'Proxied Authorization (version 1) Control',
  '2.16.840.1.113730.3.4.13'  => 'iPlanet Directory Server Replication Update Information Control',
  '2.16.840.1.113730.3.4.14'  => 'iPlanet Directory Server "search on specific backend" Control',
  '2.16.840.1.113730.3.4.15'  => 'Authorization Identity Response Control',
  '2.16.840.1.113730.3.4.16'  => 'Authorization Identity Request Control',
  '2.16.840.1.113730.3.4.17'  => 'Real Attributes Only Request Control',
  '2.16.840.1.113730.3.4.18'  => 'Proxied Authorization (version 2) Control',
  '2.16.840.1.113730.3.4.19'  => 'Virtual Attributes Only Request Control',
  '2.16.840.1.113730.3.4.20'  => 'Use One Backend',
);

my %extensions   = (
  '1.3.6.1.1.8'               => 'Cancel Operation',
  '1.3.6.1.4.1.1466.101.119.1'=> 'Dynamic Refresh',
  '1.3.6.1.4.1.1466.20037'    => 'Start TLS',
  '1.3.6.1.4.1.4203.1.11.1'   => 'Modify Password',
  '1.3.6.1.4.1.4203.1.11.3'   => 'Who am I?',
  '2.16.840.1.113730.3.5.3'   => 'iPlanet Start Replication Request Extended Operation',
  '2.16.840.1.113730.3.5.4'   => 'iPlanet Replication Response Extended Operation',
  '2.16.840.1.113730.3.5.5'   => 'iPlanet End Replication Request Extended Operation',
  '2.16.840.1.113730.3.5.6'   => 'iPlanet Replication Entry Request Extended Operation',
  '2.16.840.1.113730.3.5.7'   => 'iPlanet Bulk Import Start Extended Operation',
  '2.16.840.1.113730.3.5.8'   => 'iPlanet Bulk Import Finished Extended Operation',
  '2.16.840.1.113730.3.5.9'   => 'iPlanet Digest Authentication Calculation Extended Operation',
);

my %features     = (
  '1.3.6.1.1.14'              => 'Modify-Increment',
  '1.3.6.1.4.1.4203.1.5.1'    => 'All Op Attrs',
  '1.3.6.1.4.1.4203.1.5.2'    => 'OC AD Lists',
  '1.3.6.1.4.1.4203.1.5.3'    => 'True/False filters',
  '1.3.6.1.4.1.4203.1.5.4'    => 'Language Tag Options',
  '1.3.6.1.4.1.4203.1.5.5'    => 'Language Range Options',
);


my $aref        = $entry->get_value('supportedCapabilities',  asref => 1);
my $cref        = $entry->get_value('supportedControl',       asref => 1);
my $eref        = $entry->get_value('supportedExtension',     asref => 1);
my $fref        = $entry->get_value('supportedFeatures',      asref => 1);

my $ahref       = \%capabilities;
my $chref       = \%controls;
my $ehref       = \%extensions;
my $fhref       = \%features;
my $server_id   = &identify;

$ldap->unbind;


print "\n";
print "Server Identification:\t\t$server_id\n\n" if $server_id;

if ($aref) {
  print "Supported Capabilities\n";
  print "----------------------------------------------------------------------------------------------\n";
  &oids_to_desc($ahref, $aref);
  print "\n";
}
if ($cref) {
  print "Supported Controls\n";
  print "----------------------------------------------------------------------------------------------\n";
  &oids_to_desc($chref, $cref);
  print "\n";
}
if ($eref) {
  print "Supported Extensions\n";
  print "----------------------------------------------------------------------------------------------\n";
  &oids_to_desc($ehref, $eref);
  print "\n";
}
if ($fref) {
  print "Supported Features\n";
  print "----------------------------------------------------------------------------------------------\n";
  &oids_to_desc($fhref, $fref);
  print "\n";
}


#####################
# subs
#####################
sub oids_to_desc {
  my ($href, $ref)    = @_;

  for (1..@$ref) {
    my $i   = $_ - 1;
    my $oid = @$ref[$i];

    format STDOUT=
@<<<<<<<<<<<<<<<<<<<<<<<<       @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    $oid, ${$href}{$oid}
.
    write;
  }
}

sub identify {
  my $vn = $entry->get_value('vendorName');
  my $vv = $entry->get_value('vendorVersion');
  my $id;

  if ($aref) { $id = "Microsoft Active Directory" }

  if ( ($vn) && ($vv) ) {
    $id = $vn . " -  " . $vv;
  } elsif ($vn) {
    $id = $vn;
  } elsif ($vv) {
    $id = $vv;
  }
  return $id if $id;
}

