I have a set of scripts to maintain a match table in a database between our local AD domain and an external (non-AD) directory. Net::LDAPS works great for this...when I use sAMAccountName as the search attribute. but since we routinely have people change that (and the match table is only populated whenpeople are onboarded) I need to use permanent unique identifiers. For AD this is objectGUID.

For most of the accounts in our Active Directory this works well, I save a base64 encoded copy of the binary objectGUID in the database and I can retrieve the details for the user regardless of any changes in the AD record. for most users this works both ways...I can retrieve the objectGUID using the sAMAccountName, then use that retrieved objectGUID to retrieve the sAMAccount name back.

for about 10% of the 700 accounts in this system, though, using objectGUID results in a 'Bad filter' error from the AD LDAP server. The code associated with it is param_error code 89

This has to be an error in the data side of the filter expression, since the filter statement is the same for both working and non-working accounts. There seems to be no pattern to the accounts that fail; I only really noticed this when I started trying to do some sweeps of the data to better ID changes in users (ie find people who are no longer in one or the other of the two directories.

I guess I'm asking has anyone ever run into this and how did you fix it? I cannot find any error in my code because it works, most of the time.

The following is a code template that I've used to test this:

#!/usr/bin/perl use strict; use Net::LDAPS; use Data::Dumper; my $ldu ='ldapquery'; my $ldp ='nottherealpassword'; my $unamein = 'johnson'; my $attr = ['objectGUID', 'sAMAccountName', 'employeeNumber', 'mail']; my $ldaps = Net::LDAPS->new("ldaps://host.domain") or return "FAIL LDA +P ERROR $0"; my $searchBase = "DC=host,DC=domain"; # Bind as AD user my $mesg1 = $ldaps->bind("$ldu\@host.domain", password=>$ldp); $mesg1->code && die "Authentication failed: " . $mesg1->error . "\n"; my $searchFilter = "(sAMAccountName=$unamein)"; $mesg1 = $ldaps->search ( # perform a search base => $searchBase, filter => $searchFilter, attrs => $attr ); $mesg1->code; print Dumper $mesg1; my $binguid= $mesg1->entry(0)->get_value('objectGUID'); $searchFilter = "(objectGUID=$binguid)"; $mesg1 = $ldaps->search ( # perform a search base => $searchBase, filter => $searchFilter, attrs => $attr ); $mesg1->code; print Dumper $mesg1; exit;

Running this for a user that works produces the following expected results:

LDAP query filter on sAMAccountName: ----------------------------------------------------- $VAR1 = bless( { 'reference' => [ 'ldaps://host.domain/CN=Configuratio +n,DC=Host,DC.Domain' ], 'errorMessage' => '', 'raw' => undef, 'parent' => bless( { 'net_ldap_scheme' => 'ldaps', 'net_ldap_refcnt' => 1, 'net_ldap_debug' => 0, 'net_ldap_host' => 'host.domain' +, 'net_ldap_rawsocket' => bless( \ +*Symbol::GEN2, 'IO::Socket::SSL' ), 'net_ldap_resp' => {}, 'net_ldap_uri' => 'ldaps://host. +domain', 'net_ldap_socket' => $VAR1->{'pa +rent'}{'net_ldap_rawsocket'}, 'net_ldap_async' => 0, 'net_ldap_mesg' => {}, 'net_ldap_port' => 636, 'net_ldap_version' => 3 }, 'Net::LDAPS' ), 'mesgid' => 6, 'ctrl_hash' => undef, 'entries' => [ bless( { 'asn' => { 'attributes' => [ +{ + 'vals' => [ + '06007454' + ], + 'type' => 'employeeNumber' +}, +{ + 'type' => 'objectGUID', + 'vals' => [ + 'T,.\\@?Ǔi2#FI' + ] +}, +{ + 'type' => 'sAMAccountName', + 'vals' => [ + 'johnson' + ] +}, +{ + 'vals' => [ + 'johnson@host.domain' + ], + 'type' => 'mail' +} ], 'objectName' => 'C +N=johnson,OU=ITStaff,OU=UA,OU=PharmAccounts,DC=Host,DC.Domain' }, 'changetype' => 'modify', 'changes' => [] }, 'Net::LDAP::Entry' ) ], 'controls' => undef, 'resultCode' => 0, 'matchedDN' => '', 'callback' => undef }, 'Net::LDAP::Search' ); LDAP query filter on objectGUID from previous query: ----------------------------------------------------- $VAR1 = bless( { 'entries' => [ bless( { 'changes' => [], 'changetype' => 'modify', 'asn' => { 'objectName' => 'C +N=johnson,OU=ITStaff,DC=Host,DC.Domain', 'attributes' => [ +{ + 'type' => 'employeeNumber', + 'vals' => [ + '06007454' + ] +}, +{ + 'vals' => [ + 'T,.\\@?Ǔi2#FI' + ], + 'type' => 'objectGUID' +}, +{ + 'vals' => [ + 'johnson' + ], + 'type' => 'sAMAccountName' +}, +{ + 'type' => 'mail', + 'vals' => [ + 'johnson@host.domain' + ] +} ] } }, 'Net::LDAP::Entry' ) ], 'controls' => undef, 'matchedDN' => '', 'callback' => undef, 'resultCode' => 0, 'reference' => [ 'ldaps://host.domain/CN=Configuratio +n,DC=Host,DC.Domain' ], 'parent' => bless( { 'net_ldap_scheme' => 'ldaps', 'net_ldap_refcnt' => 1, 'net_ldap_debug' => 0, 'net_ldap_host' => 'host.domain' +, 'net_ldap_rawsocket' => bless( \ +*Symbol::GEN2, 'IO::Socket::SSL' ), 'net_ldap_resp' => {}, 'net_ldap_uri' => 'ldaps://host. +domain', 'net_ldap_socket' => $VAR1->{'pa +rent'}{'net_ldap_rawsocket'}, 'net_ldap_async' => 0, 'net_ldap_mesg' => {}, 'net_ldap_port' => 636, 'net_ldap_version' => 3 }, 'Net::LDAPS' ), 'raw' => undef, 'errorMessage' => '', 'mesgid' => 7, 'ctrl_hash' => undef }, 'Net::LDAP::Search' );

Running this for a user that it does not work for results in the following for the second query; the first works as expected:

LDAP query filter on objectGUID from previous query: ----------------------------------------------------- $VAR1 = bless( { 'parent' => bless( { 'net_ldap_resp' => {}, 'net_ldap_host' => 'host.domain' +, 'net_ldap_version' => 3, 'net_ldap_rawsocket' => bless( \ +*Symbol::GEN2, 'IO::Socket::SSL' ), 'net_ldap_scheme' => 'ldaps', 'net_ldap_socket' => $VAR1->{'pa +rent'}{'net_ldap_rawsocket'}, 'net_ldap_refcnt' => 1, 'net_ldap_debug' => 0, 'net_ldap_uri' => 'host.domain', 'net_ldap_async' => 0, 'net_ldap_port' => 636, 'net_ldap_mesg' => {} }, 'Net::LDAPS' ), 'errorMessage' => 'Bad filter', 'resultCode' => 89, 'mesgid' => 7, 'raw' => undef, 'callback' => undef }, 'Net::LDAP::Search'

In reply to Peculiar problem with Net::LDAPS and AD LDAP by desertrat

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.