1198 lines
37 KiB
Text
1198 lines
37 KiB
Text
=head1 NAME
|
|
|
|
Net::LDAP::FAQ - Frequently Asked Questions about Net::LDAP
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
perldoc Net::LDAP::FAQ
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This document serves to answer the most frequently asked questions on both the
|
|
perl-ldap Mailing List and those sent to Graham Barr.
|
|
|
|
The latest version of this FAQ can be found at
|
|
http://perl-ldap.sourceforge.net/FAQ.html
|
|
|
|
=head1 GENERAL
|
|
|
|
=head2 What is perl-ldap ?
|
|
|
|
perl-ldap is the distribution name. The perl-ldap distribution contains
|
|
the Net::LDAP modules.
|
|
|
|
=head2 Why another perl LDAP implementation ?
|
|
|
|
perl-ldap's goal is to be as portable as possible. It does this by
|
|
being implemented completely in perl. So basically anywhere that perl
|
|
runs perl-ldap will run. This is not true for other implementations
|
|
which require a C compiler.
|
|
|
|
=head2 Where can I get it ?
|
|
|
|
Perl-ldap is available from CPAN. You will find it in the
|
|
authors/id/GBARR directory. Alternatively you can download
|
|
the latest version from
|
|
http://www.cpan.org/search?dist=perl-ldap
|
|
|
|
B<WARNING:> The perl-ldap module is stored on CPAN as a *.gz file.
|
|
Netscape on Windows systems sometimes has a problem storing the module
|
|
with the correct name, it will replace the *.tar.gz with *_tar.tar.
|
|
To correct the problem, with the pointer on the link, do a right click
|
|
and then select B<save link as> to save the file with the correct file
|
|
name.
|
|
|
|
=head2 Is there a web page for perl-ldap ?
|
|
|
|
Yes there is at http://perl-ldap.sourceforge.net/
|
|
|
|
=head2 Is there a mailing list ?
|
|
|
|
Yes there is at perl-ldap-dev@lists.sourceforge.net
|
|
|
|
You can subscribe to this list at
|
|
http://lists.sourceforge.net/mailman/listinfo/perl-ldap-dev
|
|
|
|
=head2 Is the mailing list archived ?
|
|
|
|
Archives of messages since we switched to using sourceforge can be
|
|
found at
|
|
|
|
http://www.geocrawler.com/lists/3/SourceForge/3482/0/
|
|
|
|
Please be aware that the geocrawler system munges
|
|
code that is in the email messages. In particular,
|
|
apostrophes (') are turned into back ticks (`) and
|
|
newlines escapes (\n) are removed (and probably other
|
|
escapes as well).
|
|
|
|
There is also an archive of the perl-ldap mailing list at
|
|
http://www.xray.mpe.mpg.de/mailing-lists/perl-ldap/
|
|
which also has messages from before the move to sourceforge.
|
|
The xray archive does not munge email messages.
|
|
|
|
=head2 Is there any online documentation ?
|
|
|
|
Yes. perl-ldap has online documentation at
|
|
http://perl-ldap.sourceforge.net/
|
|
which will have the latest documentation available.
|
|
|
|
=head2 Is there a public CVS repository ?
|
|
|
|
Yes, it is located at sourceforge.net
|
|
|
|
=head2 Can I get perl-ldap from the public CVS repository?
|
|
|
|
Yes, any one can pull perl-ldap from the public CVS repository
|
|
on sourceforge.net.
|
|
|
|
There are several ways this can be done.
|
|
|
|
Web;
|
|
|
|
You can download it from SourceForge by following the release link:
|
|
|
|
The 2 lines in the following example should be put together as
|
|
one continuous line. Example;
|
|
|
|
http://download.sourceforge.net/perl-ldap/perl-ldap-0.20.tar.gz
|
|
|
|
B<WARNING:> The perl-ldap module is stored on CPAN as a *.gz file.
|
|
Netscape on Windows systems sometimes has a problem storing the module
|
|
with the correct name, it will replace the *.tar.gz with *_tar.tar.
|
|
To correct the problem, with the pointer on the link, do a right click
|
|
and then select B<save link as> to save the file with the correct file
|
|
name.
|
|
|
|
CVS;
|
|
|
|
You can download latest version of perl-ldap from SourceForge by
|
|
executing a anonymous CVS "get" command. When the password is
|
|
requested press the enter key.
|
|
|
|
The 2 lines in the following example should be put together as
|
|
one continuous line. Example;
|
|
|
|
cvs -d:pserver:anonymous@cvs.perl-ldap.sourceforge.net:/cvsroot/perl-ldap
|
|
|
|
Web page;
|
|
|
|
Most of the time there is a URL link on the perl-ldap
|
|
home page on sourceforge that points to the latest released
|
|
version of perl-ldap. Due to the fact that humans must
|
|
update the web page to point to a new release it sometimes does
|
|
not get updated as quickly as it should.
|
|
|
|
B<WARNING:> The perl-ldap module is stored on CPAN as a *.gz file.
|
|
Netscape on Windows systems sometimes has a problem storing the module
|
|
with the correct name, it will replace the *.tar.gz with *_tar.tar.
|
|
To correct the problem, with the pointer on the link, do a right click
|
|
and then select B<save link as> to save the file with the correct file
|
|
name.
|
|
|
|
=head2 What is CVS.
|
|
|
|
"CVS" is an acronym for the "Concurrent Versions System".
|
|
CVS is a "Source Control" or "Revision Control" tool
|
|
designed to keep track of source changes made by groups of
|
|
developers working on the same files, allowing them to
|
|
stay in sync with each other as each individual chooses.
|
|
|
|
=head1 LDAP AND DIRECTORY TERMINOLOGY.
|
|
|
|
In order to help the user understand the perl-ldap module better
|
|
some key LDAP terminology is defined here.
|
|
|
|
=head2 What is a directory.
|
|
|
|
A directory is a special purpose database that usually contains
|
|
typed information such as text strings, binary data, or X.509
|
|
certificates.
|
|
|
|
=head2 What is LDAP.
|
|
|
|
LDAP stands for Lightweight Directory Access Protocol.
|
|
The word I<Protocol> is the key word in the definition given in
|
|
the preceding sentence, LDAP is I<NOT> hardware or software.
|
|
It is a protocol that defines how a client and server will
|
|
communicate with one another.
|
|
|
|
The Lightweight Directory Access Protocol is defined in a
|
|
series of Requests For Comments, better known as RFC(s).
|
|
The RFCs can be found on the Internet. A very good
|
|
source for all of the LDAP RFCs can be found in the
|
|
OpenLDAP, http://www.OpenLDAP.org/ , software bundle that can
|
|
be downloaded free of charge from the Internet. Some of the
|
|
more important RFC numbers are RFC 1777 for LDAPv2 and RFC 2251
|
|
for LDAPv3.
|
|
|
|
=head2 What is a LDAP Directory.
|
|
|
|
In the strictest terms of the definition there is no such
|
|
thing as a LDAP directory. To be practical about this
|
|
situation every day directory professionals refer to their
|
|
directory as " a LDAP directory" because it is easy to
|
|
say and it does convey the type of protocol used to
|
|
communicate with their directory. Using this definition
|
|
a LDAP directory is a directory whose server software
|
|
conforms to the Lightweight Directory Access Protocol when
|
|
communicating with a client.
|
|
|
|
=head2 What is an Entry.
|
|
|
|
The traditional directory definition of a directory object
|
|
is called an Entry. Entries are composed of attributes
|
|
that contain the information to be recorded about an object.
|
|
|
|
Another non-traditional definition of a directory object
|
|
is called a record. Some directory professionals prefer
|
|
to use this definition because of the confusion that sometimes
|
|
results when using the term Entry.
|
|
|
|
=head2 What is a Distinguished Name.
|
|
|
|
Every entry in a directory, whether it is X.500 or LDAP, has
|
|
a Distinguished Name, or DN. It is a unique Entry identifier
|
|
through out the complete directory. No two Entries can have the
|
|
same DN within the same directory.
|
|
|
|
Example of a DN:
|
|
|
|
cn=Road Runner, ou=bird, dc=carton, dc=com
|
|
ou=bird, dc=carton, dc=com
|
|
dc=carton, dc=com
|
|
dc=com
|
|
|
|
=head2 What is a Relative Distinguished Name.
|
|
|
|
Every Entry in a directory, whether it is X.500 or LDAP, has
|
|
a Distinguished Name which is made up of a sequence of Relative
|
|
Distinguished Names, or RDNs. The sequences of RDNs are separated
|
|
by commas (,) or semi-colons (;). There can be more than one
|
|
identical RDN in a directory, but they must be in different
|
|
bases, or branches, of the directory.
|
|
|
|
Example of a DN:
|
|
|
|
cn=Road Runner,ou=bird,dc=carton,dc=com
|
|
|
|
RDNs of the proceeding DN:
|
|
RDN => cn=Road Runner
|
|
RDN => ou=bird
|
|
RDN => dc=carton
|
|
RDN => dc=com
|
|
|
|
The RDNs are delimited by a comma.
|
|
|
|
=head2 What is a Naming RDN.
|
|
|
|
Example of a DN:
|
|
|
|
cn=Road Runner,ou=bird,dc=carton,dc=com
|
|
|
|
Naming RDN of the proceeding DN:
|
|
|
|
cn=Road Runner
|
|
|
|
Most of the time when directory professionals refer
|
|
to the RDN of an entry, this is the RDN that they
|
|
are referring to.
|
|
|
|
=head2 What is a search base.
|
|
|
|
A search base is a Distinguished Name that is the
|
|
starting point of search queries.
|
|
|
|
Example of a DN:
|
|
|
|
cn=Road Runner,ou=bird,dc=carton,dc=com
|
|
|
|
Possible search base(s) for the proceeding DN:
|
|
|
|
Base => cn=Road Runner,ou=bird,dc=carton,dc=com
|
|
Base => ou=bird,dc=carton,dc=com
|
|
Base => dc=carton,dc=com
|
|
Base => dc=com
|
|
|
|
Setting the search base to the lowest possible branch of
|
|
the directory will speed up searches considerably.
|
|
|
|
=head2 What is an attribute.
|
|
|
|
The entry(s) in a directory are composed of attributes that contain
|
|
information about the object. Each attribute has a type
|
|
and can contain one or more values. The attribute type is
|
|
associated with a syntax that defines what kind of information
|
|
can be stored in the attributes values and controls how
|
|
directory operations on the attribute behave. What attributes
|
|
are required and allowed in a entry is controlled by content
|
|
rules that are defined on a per-server basis or by a special
|
|
attribute in each entry called an objectClass.
|
|
|
|
=head2 What is the difference between a LDAP server and a relational database
|
|
|
|
The most basic difference is that a directory server is a
|
|
specialized database designed to provide fast searches. While a relational
|
|
database is optimized for transactions (where a series of operations is
|
|
counted as 1, thus if one of the steps fails, the RDBMS can roll-back to
|
|
the state it was in before you started).
|
|
|
|
Directories also typically are hierarchical in nature (RDBMS is typically
|
|
flat, but you can implement a hierarchy using tables and queries),
|
|
network-able, distributed and replicated.
|
|
|
|
LDAP provides an open-standard to a directory service.
|
|
|
|
Typically we use LDAP for email directories (all popular email clients
|
|
provide an LDAP client now) and authorization services (authentication and
|
|
access control).
|
|
|
|
You could use a RDBMS for these types of queries but there's not a
|
|
set standard, in particular over TCP/IP to connect to databases over the
|
|
network. There's language specific protocols (like Perl's DBI and Java's
|
|
JDBC) that hide this problem behind an API abstraction, but that's not a
|
|
replacement for a standard access protocol.
|
|
|
|
LDAP is starting to be used on roles traditionally played by RDBMS in
|
|
terms of general data management because it's easier to setup a LDAP
|
|
server (once you understand the basic nomenclature) and you don't need
|
|
a DBA to write your queries and more importantly all LDAP servers speak
|
|
the same essential protocol, thus you don't have to fuss with a
|
|
database driver trying to connect it to the Internet. Once you have an
|
|
LDAP server up and running, it's automatically available over the 'net.
|
|
It's possible to connect to a LDAP server from a variety of mechanisms,
|
|
including just about every possible programming language.
|
|
|
|
More information on this topic can be found on the following URLs;
|
|
|
|
http://www.openldap.org/faq/data/cache/378.html
|
|
|
|
http://www.messagingdirect.com/publications/IC-6055.html
|
|
|
|
=head2 What is the difference between a ldap reference and a ldap referral?
|
|
|
|
A referral is returned when the B<entire> operation must be resent to
|
|
another server.
|
|
|
|
A continuation reference is returned when B<part> of the operation must be
|
|
resent to another server.
|
|
|
|
See RFC 2251 section 4.5.3 for more details.
|
|
|
|
=head1 PERL-LDAP INSTALLATION
|
|
|
|
=head2 How do I install perl-ldap ?
|
|
|
|
To install the modules that are in the perl-ldap distribution follow the
|
|
same steps that you would for most other distributions found on CPAN, that
|
|
is
|
|
|
|
# replace 0.13 with the version you have
|
|
|
|
gunzip perl-ldap-0.13.tar.gz
|
|
tar xvf perl-ldap-0.13.tar
|
|
cd perl-ldap-0.13
|
|
|
|
perl Makefile.PL
|
|
make
|
|
make test
|
|
make install
|
|
|
|
=head2 But I do not have make, how can I install perl-ldap ?
|
|
|
|
Well as luck would have it the modules in perl-ldap do not do anything
|
|
complex, so a simple copy is enough to install. First run
|
|
|
|
perl -V
|
|
|
|
This will output information about the version of perl you have
|
|
installed. Near the bottom you will find something like
|
|
|
|
@INC:
|
|
/usr/local/perl/perl5.005/lib/5.00502/sun4-solaris
|
|
/usr/local/perl/perl5.005/lib/5.00502
|
|
/usr/local/perl/perl5.005/lib/site_perl/5.005/sun4-solaris
|
|
/usr/local/perl/perl5.005/lib/site_perl/5.005
|
|
.
|
|
|
|
This is a list of directories that perl searches when it is looking for
|
|
a module. The directory you need is the site_perl directory, but without
|
|
the system architecture name, in this case it is
|
|
C</usr/local/perl/perl5.005/lib/site_perl/5.005>. The files required
|
|
can then be installed with
|
|
|
|
# replace 0.13 with the version you have
|
|
|
|
gunzip perl-ldap-0.13.tar.gz
|
|
tar xvf perl-ldap-0.13.tar
|
|
cd perl-ldap-0.13/lib
|
|
|
|
cp * /usr/local/perl/perl5.005/lib/site_perl/5.005
|
|
|
|
|
|
=head2 What other modules will I need ?
|
|
|
|
perl-ldap does use other modules. Some are required, but some are
|
|
optional (ie required to use certain features)
|
|
|
|
=over 4
|
|
|
|
=item Convert::ASN1
|
|
|
|
This module is required for perl-ldap to work.
|
|
|
|
You can obtain the latest release from
|
|
http://search.cpan.org/search?module=Convert::ASN1
|
|
|
|
=item Digest::MD5
|
|
|
|
This module is optional. It also requires a C compiler when installing.
|
|
You only need to install Digest::MD5 if you want to use the SASL
|
|
authentication method.
|
|
|
|
You can obtain the latest release from
|
|
http://search.cpan.org/search?module=Digest::MD5
|
|
|
|
=item URI::ldap
|
|
|
|
This module is optional. You only need to install URI::ldap if you are
|
|
going to need to parse ldap referrals. L<Net::LDAP> does not do this
|
|
automatically yet, so this module is not used by perl-ldap.
|
|
|
|
You can obtain the latest release from
|
|
http://search.cpan.org/search?module=URI::ldap
|
|
|
|
=item OpenSSL and IO::Socket::SSL for Net::LDAPS
|
|
|
|
If you want to use Net::LDAP::LDAPS you will need this module
|
|
and the OpenSSL software package.
|
|
|
|
You can obtain the latest release of IO::Socket::SSL from
|
|
http://search.cpan.org/search?module=IO::Socket::SSL
|
|
|
|
You can obtain the latest release of OpenSSL from
|
|
http://www.openssl.org/
|
|
|
|
If you are using a Linux system, many of the distributions
|
|
have RPM packages that you can install. Use your favorite
|
|
web search engine to find the package that you need.
|
|
|
|
=item XML::Parser
|
|
|
|
If you want to use Net::LDAP::DSML you will need this module.
|
|
|
|
You can obtain the latest release from
|
|
http://search.cpan.org/search?module=XML::Parser
|
|
|
|
=back
|
|
|
|
=head1 USING NET::LDAP
|
|
|
|
=head2 How do I connect to my server ?
|
|
|
|
The connection to the server is created when you create a new Net::LDAP
|
|
object, e.g.
|
|
|
|
$ldap = Net::LDAP->new($server);
|
|
|
|
=head2 Net::LDAP->new sometimes returns undef, why ?
|
|
|
|
The constructor will return undef if there was a problem connecting
|
|
to the specified server. Any error message will be available in $@
|
|
|
|
=head2 How can I tell when the server returns an error, bind() always returns
|
|
true ?
|
|
|
|
Most methods in Net::LDAP return a L<Net::LDAP::Message>
|
|
object, or a sub-class of that. This object will hold the results
|
|
from the server, including the result code.
|
|
|
|
So, for example, to determine the result of the bind operation.
|
|
|
|
$mesg = $ldap->bind( $dn, password => $passwd );
|
|
|
|
if ( $mesg->code ) {
|
|
# Handle error codes here
|
|
}
|
|
|
|
=head2 How can I set the ldap version of a connection to my ldap server?
|
|
|
|
This is done by adding the version option when binding to the ldap
|
|
server.
|
|
|
|
For example;
|
|
|
|
$mesg = $ldap->bind( $dn, password => $passwd, version => 3 );
|
|
|
|
Valid version numbers are 2 and 3.
|
|
|
|
=head2 I did a search on my directory using the 'search' method. Where did
|
|
the results go ?
|
|
|
|
Your search results are stored in a 'search object' container.
|
|
Consider the following:
|
|
|
|
use Net::LDAP;
|
|
|
|
$ldap = Net::LDAP->new('ldap.acme.com') or die "$@";
|
|
$mesg = $ldap->search(
|
|
base => "o=acme.com",
|
|
filter => "uid=jsmith",
|
|
);
|
|
|
|
$mesg is a search object container. It is a reference blessed into the
|
|
L<Net::LDAP::Search> package. By calling methods on
|
|
this object you can obtain information about the result and also the
|
|
individual entries.
|
|
|
|
The first thing to check is if the search was successful. This is done with
|
|
with the method $mesg->code. This method will return the status code
|
|
that the server returned. A success will yield a zero value, but there are
|
|
other values, some of which could also be considered a success.
|
|
See L<Net::LDAP::Constant>
|
|
|
|
use Net::LDAP::Util qw(ldap_error_text);
|
|
|
|
die ldap_error_text($mesg->code)
|
|
if $mesg->code;
|
|
|
|
There are two ways in which you can access the entries. You can access
|
|
then with an index or you can treat the container like a stack and
|
|
shift each entry in turn. For example
|
|
|
|
# as an array
|
|
|
|
# How many entries were returned from the search
|
|
my $max = $mesg->count;
|
|
|
|
for( my $index = 0 ; $index < $max ; $index++) {
|
|
my $entry = $mesg->entry($index);
|
|
# ...
|
|
}
|
|
|
|
# or as a stack
|
|
|
|
while( my $entry = $mesg->shift_entry) {
|
|
# ...
|
|
}
|
|
|
|
In each case $entry is an entry object container. It is a reference blessed
|
|
into the L<Net::LDAP::Entry> package. By calling methods on this object
|
|
you can obtain information about the entry.
|
|
|
|
For example, to obtain the DN for the entry
|
|
|
|
$dn = $entry->dn;
|
|
|
|
To obtain the attributes that a given entry has
|
|
|
|
@attrs = $entry->attributes;
|
|
|
|
And to get the list of values for a given attribute
|
|
|
|
@values = $entry->get( 'sn' );
|
|
|
|
And to get the first of the values for a given attribute
|
|
|
|
$values = $entry->get( 'cn' );
|
|
|
|
One thing to remember is that attribute names are case
|
|
insensitive, so 'sn', 'Sn', 'sN' and 'SN' are all the same.
|
|
|
|
So, if you want to print all the values for the attribute C<'ou'> then this
|
|
is as simple as
|
|
|
|
foreach ($entry->get_value( 'ou' )) {
|
|
print $_,"\n";
|
|
}
|
|
|
|
Now if you just want to print all the values for all the attributes you
|
|
can do
|
|
|
|
foreach my $attr ($entry->attributes) {
|
|
foreach my $value ($entry->get_value($attr)) {
|
|
print $attr, ": ", $value, "\n";
|
|
}
|
|
}
|
|
|
|
=head2 How do I limit the scope of a directory search.
|
|
|
|
You limit the scope of a directory search by setting the
|
|
scope parameter of search request.
|
|
Consider the following:
|
|
|
|
use Net::LDAP;
|
|
|
|
$ldap = Net::LDAP->new('ldap.acme.com') or die "$@";
|
|
$mesg = $ldap->search(
|
|
base => "o=acme.com",
|
|
scope => 'sub',
|
|
filter => "uid=jsmith",
|
|
);
|
|
|
|
Values for the scope parameter are as follows.
|
|
|
|
base Search only the base object.
|
|
|
|
one Search the entries immediately below the base
|
|
object.
|
|
|
|
sub Search the whole tree below the base object.
|
|
This is the default.
|
|
|
|
=head1 GETTING SEARCH RESULTS
|
|
|
|
There are two ways of retrieving the results of a requested
|
|
LDAP search; inline and by using a callback subroutine.
|
|
|
|
=head2 USING THE INLINE APPROACH
|
|
|
|
Using the inline approach involves requesting the data and
|
|
then waiting for all of the data to be returned before the
|
|
user starts processing the data.
|
|
|
|
Example:
|
|
|
|
use Net::LDAP;
|
|
|
|
$ldap = Net::LDAP->new('ldap.acme.com') or die "$@";
|
|
$mesg = $ldap->search(
|
|
base => "o=acme.com",
|
|
scope => 'sub',
|
|
filter => "sn=smith",
|
|
);
|
|
#
|
|
# At this point the user can get the returned data as an array
|
|
# or as a stack.
|
|
# In this example we will use an array
|
|
|
|
# How many entries were returned from the search
|
|
my $max = $mesg->count;
|
|
|
|
for( my $index = 0 ; $index < $max ; $index++)
|
|
{
|
|
my $entry = $mesg->entry($index);
|
|
my $dn = $entry->dn; # Obtain DN of this entry
|
|
|
|
@attrs = $entry->attributes; # Obtain attributes for this entry.
|
|
foreach my $var (@attrs)
|
|
{
|
|
#get a list of values for a given attribute
|
|
$attr = $entry->get_value( $var, asref => 1 );
|
|
if ( defined($attr) )
|
|
{
|
|
foreach my $value ( @$attr )
|
|
{
|
|
print "$var: $value\n"; # Print each value for the attribute.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
As you can see the example is straight forward, but there is one
|
|
drawback to this approach. You must wait until all entries for the
|
|
request search to be returned before you can process the data. If
|
|
there several thousand entries that match the search filter this
|
|
could take quite a long time period.
|
|
|
|
=head2 USING THE CALLBACK SUBROUTINE APPROACH
|
|
|
|
Using the callback approach involves requesting the data be sent
|
|
to a callback subroutine as each entry arrives at the client.
|
|
|
|
A callback is just a subroutine that is passed two parameters when
|
|
it is called, the mesg and entry objects.
|
|
|
|
Example:
|
|
|
|
use Net::LDAP;
|
|
|
|
$ldap = Net::LDAP->new('ldap.acme.com') or die "$@";
|
|
$mesg = $ldap->search(
|
|
base => "o=acme.com",
|
|
scope => 'sub',
|
|
filter => "sn=smith",
|
|
callback => \&callback,
|
|
);
|
|
#
|
|
# At this point the user needs to check the status of the
|
|
# ldap search.
|
|
#
|
|
|
|
if ( $mesg->code )
|
|
{
|
|
$errstr = $mesg->code;
|
|
print "Error code: $errstr\n";
|
|
$errstr = ldap_error_text($errstr);
|
|
print "$errstr\n";
|
|
}
|
|
|
|
|
|
sub callback
|
|
{
|
|
my ( $mesg, $entry) = @_;
|
|
|
|
#
|
|
# First you must check to see if something was returned.
|
|
# Last execution of callback subroutine will have no
|
|
# defined entry and mesg object
|
|
#
|
|
if ( !defined($entry) )
|
|
{
|
|
print "No records found matching filter $match.\n"
|
|
if ($mesg->count == 0) ; # if mesg is not defined nothing will print.
|
|
return;
|
|
}
|
|
|
|
my $dn = $entry->dn; # Obtain DN of this entry
|
|
|
|
@attrs = $entry->attributes; # Obtain attributes for this entry.
|
|
foreach my $var (@attrs)
|
|
{
|
|
#get a list of values for a given attribute
|
|
$attr = $entry->get_value( $var, asref => 1 );
|
|
if ( defined($attr) )
|
|
{
|
|
foreach my $value ( @$attr )
|
|
{
|
|
print "$var: $value\n"; # Print each value for the attribute.
|
|
}
|
|
}
|
|
}
|
|
#
|
|
# For large search requests the following 2 lines of code
|
|
# may be very important, they will reduce the amount of memory
|
|
# used by the search results.
|
|
#
|
|
# If the user is not worried about memory useage then the 2 lines
|
|
# of code can be omitted.
|
|
#
|
|
$mesg->pop_entry;
|
|
|
|
} # End of callback subroutine
|
|
|
|
As you can see the example is straight forward and it does not waste
|
|
time waiting for all of the entries to be returned. However if the
|
|
pop_entry method is not used the callback approach can allocate a
|
|
lot of memory to the search request.
|
|
|
|
=head1 USING NET::LDAPS
|
|
|
|
=head2 Using a potentially encrypted (SSL) network connection, how do I connect to my server?
|
|
|
|
This class is a subclass of Net::LDAP so all the normal
|
|
Net::LDAP methods can be used with a Net::LDAPS object;
|
|
see the documentation for Net::LDAP to find out how to
|
|
query a directory server using the LDAP protocol.
|
|
|
|
The connection to the server is created when you create a new Net::LDAPS
|
|
object, e.g.
|
|
|
|
$ldaps = Net::LDAPS->new($server,
|
|
port => '10000',
|
|
verify => 'require',
|
|
capath => '/usr/local/cacerts/',
|
|
);
|
|
|
|
There are additional options to the LDAPS new method and
|
|
several additional methods are included in the LDAPS object class.
|
|
|
|
For further information and code examples read the LDAPS
|
|
module documentation; perldoc Net::LDAPS
|
|
|
|
=head1 USING LDAP GROUPS.
|
|
|
|
=head2 What are LDAP groups.
|
|
|
|
LDAP groups are a collection of distinguished names (DN) that are
|
|
listed in an attribute called member. One I<important note> to
|
|
remember is that a group can be a collection of groups. This
|
|
does I<NOT> imply that the subgroups will be flattened into one
|
|
big group.
|
|
|
|
Two scripts for working with groups are available in the contrib
|
|
directory. They are isMember.pl and printMembers.pl.
|
|
|
|
=head2 How do you format a filter to search for entries whose 'member'
|
|
attribute has a particular value?
|
|
|
|
Asking for (member=*) is OK - the directory uses the equality matching
|
|
rule which is defined for the member attribute.
|
|
|
|
Asking for (member=c*) is not OK - there is no defined substring
|
|
matching rule for the member attribute. That's because the member
|
|
values are *not* strings, but distinguished names. There is no
|
|
substring matching rule for DNs, see RFC 2256 section 5.50.
|
|
|
|
What you have to do is get the results of (member=*) and then select
|
|
the required results from the returned values. You need to do this
|
|
using knowledge of the string representation of DNs defined in RFC
|
|
2253, which is important because the same DN can have different string
|
|
representations. So you need to perform some canonicalization if you
|
|
want to be correct.
|
|
|
|
|
|
=head1 USING DSML.
|
|
|
|
=head2 How can I access DSML features from PERL-LDAP.
|
|
|
|
Directory Service Markup Language (DSML) is the XML
|
|
standard for representing directory service information in
|
|
XML.
|
|
|
|
Support for DSML is include in PERL-LDAP starting with version
|
|
.20.
|
|
|
|
At the moment this module only reads and writes DSML entry
|
|
entities. It cannot process any schema entities because
|
|
schema entities are processed differently than elements.
|
|
|
|
Eventually this module will be a full level 2 consumer and
|
|
producer enabling you to give you full DSML conformance.
|
|
|
|
The specification for DSML is at http://www.dsml.org
|
|
|
|
For further information and code examples read the DSML
|
|
module documentation; perldoc Net::LDAP::DSML
|
|
|
|
=head1 USING CONTROLS AND VIRTUAL LISTS.
|
|
|
|
=head2 How do I access the Control features.
|
|
|
|
Support for LDAP version 3 Control objects is included in
|
|
perl-ldap starting with version .20.
|
|
|
|
For further information and code examples read the Control
|
|
module documentation; perldoc Net::LDAP::Control
|
|
|
|
=head2 How do I access the Virtual List features.
|
|
|
|
Support for Virtual Lists is included in perl-ldap starting
|
|
with version .20.
|
|
|
|
For further information and code examples read the Control
|
|
module documentation; perldoc Net::LDAP::Control
|
|
|
|
=head1 GENERAL QUESTIONS.
|
|
|
|
=head2 Are there any other code examples.
|
|
|
|
Yes, there is an Examples pod file. To view the pod
|
|
do the following command; perldoc Net::LDAP::Examples
|
|
|
|
There is user contributed software in the contrib directory
|
|
that is supplied with the PERL-LDAP distribution. This is an
|
|
excellent source of information on how to use the PERL-LDAP module.
|
|
|
|
=head2 Can I contribute perl scripts that use perl-ldap
|
|
to the contrib section?
|
|
|
|
Any one can submit a perl script that uses perl-ldap for inclusion
|
|
in the contrib section. Graham Barr will determine if the script
|
|
will be included and will do the initial check in of the script
|
|
to the CVS system on sourceforge. Graham will make you the
|
|
owner/developer of the script.
|
|
|
|
There are a couple of requirements for consideration.
|
|
|
|
You must supply a one line description of your script to be included
|
|
in the contrib readme file.
|
|
|
|
Inside the script will be the pod documentation for the script.
|
|
No auxiliary documentation will be allowed. For examples of how
|
|
to do this see the tklkup or schema scripts currently in the contrib
|
|
section.
|
|
|
|
If Graham decides to include your script in the contrib section, you
|
|
must register with sourceforge before your scripts will be put into
|
|
the contrib CVS system.
|
|
|
|
=head2 Is possible to get a complete entry, dn and attributes
|
|
without specifying the attributes name?
|
|
|
|
Yes, just specify you want a list of no attributes back. The RFC says
|
|
that this tells the server to return all readable attributes back
|
|
(there may be access controls to prevent some from being returned.)
|
|
|
|
So in the search method, just set (for LDAPv2):
|
|
|
|
attrs => [ ]
|
|
|
|
If you are using LDAPv3, you can specify an attribute called "*"
|
|
instead, which lets you ask for additional (eg operational) attributes
|
|
in the same search.
|
|
|
|
attrs => [ "*" ]
|
|
|
|
=head2 How do I put a jpeg photo into a entry in the directory.
|
|
|
|
Follow the following code example, replacing the (...) with
|
|
whatever is relevant to your setup.
|
|
|
|
use Net::LDAP;
|
|
use Net::LDAP::Util qw(ldap_error_text);
|
|
use CGI;
|
|
|
|
local $/ = undef;
|
|
my $jpeg = <$filename>;
|
|
|
|
my $ldap = new Net::LDAP(...);
|
|
my $res = $ldap->bind(...);
|
|
$res = $ldap->modify(...,
|
|
add => [ 'jpegPhoto' => [ $jpeg ] ]);
|
|
$res = $ldap->unbind();
|
|
|
|
|
|
=head2 How do I add a jpeg photo into a entry in the directory via html-forms.
|
|
|
|
Follow the following code example, replacing the (...) with
|
|
whatever is relevant to your setup.
|
|
|
|
use Net::LDAP;
|
|
use Net::LDAP::Util qw(ldap_error_text);
|
|
use CGI;
|
|
|
|
my $q = new CGI;
|
|
|
|
print $q->header;
|
|
print $q->start_html(-title => 'Change JPEG photo');
|
|
|
|
if ($q->param('Update')) {
|
|
my $filename = $q->param('jpeg');
|
|
local $/ = undef;
|
|
my $jpeg = <$filename>;
|
|
|
|
my $ldap = new Net::LDAP(...);
|
|
my $res = $ldap->bind(...);
|
|
$res = $ldap->modify(...,
|
|
add => [ 'jpegPhoto' => [ $jpeg ] ]);
|
|
$res = $ldap->unbind();
|
|
} else {
|
|
print $q->start_multipart_form();
|
|
print $q->filefield(-name => 'jpeg', -size => 50);
|
|
print $q->submit('Update');
|
|
print $q->end_form();
|
|
}
|
|
|
|
print $q->end_html();
|
|
|
|
=head2 What happens when you delete an attribute that does not exist.
|
|
|
|
It is an error to delete an attribute that doesn't exist. When you
|
|
get the error back the server ignores the entire modify operation
|
|
you sent it, so you need to make sure the error doesn't happen.
|
|
|
|
Another approach, if you are using LDAPv3 (note Net::LDAP does not use
|
|
LDAPv3 by default) is to use a 'replace' with your attribute name and no
|
|
values. In LDAPv3, this is defined to always work even if that attribute
|
|
doesn't exist in the entry.
|
|
|
|
ie:
|
|
|
|
my $mesg = $ldap->modify( $entry, replace => { %qv_del_arry } );
|
|
|
|
But make sure you are using LDAPv3, because that is defined to *not* work
|
|
in LDAPv2. (A nice incompatibility between LDAPv2 and LDAPv3.)
|
|
|
|
=head2 How can I delete a referral from an LDAP tree.
|
|
|
|
Since this is a proprietary feature, you will have to check your
|
|
server's documentation. You might find that you need to use a control. If
|
|
there is a control called something like managedsait, that's the one you
|
|
should probably use. For proper operation you will need the oid number
|
|
for managedsait; 2.16.840.1.113730.3.4.2 and do not specify a value for
|
|
type.
|
|
|
|
The code required will look similar to the following code snippet.
|
|
|
|
$mesg = $ldap->delete("ref=\"ldap://acme/c=us,o=bricks\",o=clay",
|
|
control => {type => "2.16.840.1.113730.3.4.2"} );
|
|
|
|
=head2 How do I add an ACI/ACL entry to a directory server with
|
|
Perl-LDAP.
|
|
|
|
The following code snippet works with a Netscape directory server and
|
|
should work with any other LDAP directory server. You will need the
|
|
specify the correct DN (-DN-) and correct attribute(s) (-nom attr-).
|
|
|
|
my $aci = '(target="ldap:///-DN-")(targetattr="-nom attr-")(version 3.0;
|
|
acl "-nom acl-"; deny(all) userdn = "ldap:///self";)' ;
|
|
|
|
$ldap->modify($dn_modif, add => {'aci' => $aci });
|
|
|
|
=head2 How do I avoid file type and data type miss-matching when loading
|
|
data from a Win32 system.
|
|
|
|
When loading a binary attribute with data read from a file on a Win32
|
|
system, it has been noted that you should set "binmode" on the file
|
|
before reading the file contents into the data array.
|
|
|
|
Another possible solution to this problem is to convert the
|
|
binary data into a base64 encoded string and then store the encoded string
|
|
in the file. Then when reading the file, decode the base64 encoded
|
|
string back to binary and then use perl ldap to store the data
|
|
in the directory.
|
|
|
|
=head2 How do I create a Microsoft Exchange user.
|
|
|
|
This is a solution provide by a perl-ldap user.
|
|
|
|
This code works with ActiveState Perl running on WinNT 4. Please note that
|
|
this requires the Win32::Perms module, and needs valid NT account info to
|
|
replace the placeholders.
|
|
|
|
use Net::LDAP qw(:all);
|
|
use Net::LDAP::Util;
|
|
use Win32::Perms;
|
|
|
|
#Constants taken from ADSI Type Library
|
|
$ADS_RIGHT_EXCH_ADD_CHILD = 1;
|
|
$ADS_RIGHT_EXCH_DELETE = 0x10000;
|
|
$ADS_RIGHT_EXCH_DS_REPLICATION = 64;
|
|
$ADS_RIGHT_EXCH_DS_SEARCH = 256;
|
|
$ADS_RIGHT_EXCH_MAIL_ADMIN_AS = 32;
|
|
$ADS_RIGHT_EXCH_MAIL_RECEIVE_AS = 16;
|
|
$ADS_RIGHT_EXCH_MAIL_SEND_AS = 8;
|
|
$ADS_RIGHT_EXCH_MODIFY_ADMIN_ATT = 4;
|
|
$ADS_RIGHT_EXCH_MODIFY_SEC_ATT = 128;
|
|
$ADS_RIGHT_EXCH_MODIFY_USER_ATT = 2;
|
|
|
|
$EXCH_USER_RIGHTS = $ADS_RIGHT_EXCH_MAIL_RECEIVE_AS |
|
|
$ADS_RIGHT_EXCH_MAIL_SEND_AS |
|
|
$ADS_RIGHT_EXCH_MODIFY_USER_ATT;
|
|
|
|
$exch = new Net::LDAP('server', debug =>0) || die $@;
|
|
|
|
$exch->bind( 'cn=admin_user,cn=nt_domain,cn=admin', version =>3,
|
|
password=>'password');
|
|
|
|
$myObj = Win32::Perms->new();
|
|
$Result = $myObj->Owner('nt_domain\user_name');
|
|
$myObj->Group('nt_domain\Everyone');
|
|
$myObj->Allow('nt_domain\user_name',
|
|
$EXCH_USER_RIGHTS,OBJECT_INHERIT_ACE);
|
|
$BinarySD = $myObj->GetSD(SD_RELATIVE);
|
|
$TextSD = uc(unpack( "H*", $BinarySD ));
|
|
Win32::Perms::ResolveSid('nt_domain\user_name', $sid);
|
|
$mysid = uc(unpack("H*",$sid));
|
|
|
|
$result = $exch->add ( dn =>
|
|
'cn=user_name,cn=container,ou=site,o=organisation',
|
|
attr => [ 'objectClass' => ['organizationalPerson'],
|
|
'cn' => 'directory_name',
|
|
'uid' => 'mail_nickname',
|
|
'mail' => 'smtp_address',
|
|
'assoc-nt-account' => [ $mysid ],
|
|
'nt-security-descriptor' => [ $TextSD ],
|
|
'mailPreferenceOption' => 0
|
|
]
|
|
);
|
|
|
|
|
|
print ldap_error_name($result->code);
|
|
|
|
=head1 Using X.509 certificates.
|
|
|
|
=head2 How do I store X.509 certificates in the directory?
|
|
|
|
The first problem here is that there are many different formats to hold
|
|
certificates in, for example PEM, DER, PKCS#7 and PKCS#12. The directory
|
|
*only* uses the DER format (more correctly, it only uses the BER format)
|
|
which is a binary format.
|
|
|
|
Your first job is to ensure that your certificates are therefore in DER/BER
|
|
format. You could use OpenSSL to convert from PEM like this:
|
|
|
|
openssl x509 -inform PEM -in cert.pem -outform DER -out cert.der
|
|
|
|
Consult the OpenSSL documentation to find out how to perform other
|
|
conversions.
|
|
|
|
To add a certificate to the directory, just slurp in the DER/BER
|
|
certificate into a scalar variable, and add it to the entry's
|
|
userCertificate attribute. How you do that will depend on which version of
|
|
LDAP you are using.
|
|
|
|
To slurp in the certificate try something like this:
|
|
|
|
my $cert;
|
|
{
|
|
local $/ = undef; # Slurp mode
|
|
open CERT, "cert.der" or die;
|
|
$cert = <CERT>;
|
|
close CERT;
|
|
}
|
|
# The certificate is now in $cert
|
|
|
|
For LDAPv2, because most directory vendors ignore the string representation
|
|
of certificates defined in RFC 1778, you should add this value to the
|
|
directory like this:
|
|
|
|
$res = $ldap->modify("cn=My User, o=My Company,c=XY",
|
|
add => [
|
|
'userCertificate' => [ $cert ]
|
|
]);
|
|
die "Modify failed (" . ldap_error_name($res->code) . ")\n"
|
|
if $res->code;
|
|
|
|
For LDAPv3, you must do this instead:
|
|
|
|
$res = $ldap->modify("cn=My User, o=My Company, c=XY",
|
|
add => [
|
|
'userCertificate;binary' => [ $cert ]
|
|
]);
|
|
die "Modify failed (" . ldap_error_name($res->code) . ")\n"
|
|
if $res->code;
|
|
|
|
Of course, the entry you are trying to add the certificate to must use
|
|
object classes that permit the userCertificate attribute, otherwise the
|
|
modify will fail with an object class violation error. The inetOrgPerson
|
|
structural object class permits userCertificates, as does the
|
|
strongAuthenticationUser auxiliary object class. Others might also.
|
|
|
|
=head1 ADDITIONAL DIRECTORY AND LDAP RESOURCES.
|
|
|
|
=head2 URL(s).
|
|
|
|
Directory Services Mark Language (DSML)
|
|
http://www.dsml.org/
|
|
|
|
eMailman LDAP information
|
|
http://www.emailman.com/ldap/
|
|
|
|
LDAPS, also known as LDAPGURU.
|
|
I<This web site has a nasty habit of resizing the browser
|
|
to cover the WHOLE screen.>
|
|
http://www.ldaps.com
|
|
|
|
Jeff Hodges's Kings Mountain LDAP
|
|
http://www.kingsmountain.com/ldapRoadmap.shtml
|
|
|
|
Mark Wahl's LDAP World at Innosoft.
|
|
http://www.innosoft.com/ldapworld/
|
|
|
|
Open Source LDAP Directory Server.
|
|
http://www.openldap.org/
|
|
|
|
CriticalPath
|
|
http://www.cp.net/
|
|
|
|
Innosoft
|
|
http://www.innosoft.com
|
|
|
|
IPlanet - Netscape and SUN Alliance
|
|
http://www.iplanet.com/
|
|
|
|
MessagingDirect
|
|
http://www.messagingdirect.com/
|
|
|
|
Netscape Directory Developers Area
|
|
http://developer.netscape.com/directory/
|
|
|
|
Nexor's X.500 and Internet Directories
|
|
http://www.nexor.com/info/directory.htm/
|
|
|
|
Novell's LDAPzone
|
|
http://ldapzone.com/
|
|
|
|
SUN JAVA JNDI (Java Naming and Directory Interface)
|
|
http://www.java.sun.com/jndi/
|
|
|
|
Eine deutsche LDAP Website
|
|
A german LDAP Website
|
|
http://verzeichnisdienst.de/ldap/Perl/index.html
|
|
|
|
The 2 following URLs deal mainly with Microsoft's
|
|
Active Directory.
|
|
|
|
Directory Works
|
|
http://directoryworks.com/
|
|
|
|
ActiveX LDAP Client
|
|
http://www.polonia-online.com/ldap/
|
|
|
|
=head2 BOOK(s)
|
|
|
|
Developing LDAP and ADSI Clients for Microsoft(R) Exchange.
|
|
By Sven B. Schreiber. ISBN: 0201657775
|
|
|
|
Implementing LDAP.
|
|
By Mark Wilcox. ISBN: 1861002211
|
|
|
|
LDAP : Programming Directory-Enabled Applications With
|
|
Lightweight Directory Access Protocol.
|
|
By Tim Howes, Mark Smith. ISBN: 1578700000
|
|
|
|
LDAP Programming with Java.
|
|
By Rob Weltman, Tony Dahbura. ISBN: 0201657589
|
|
|
|
Solaris and LDAP Naming Services.
|
|
By Tom Bialaski, Michael Haines. ISBN: 0-13-030678-9
|
|
|
|
Understanding and Deploying Ldap Directory Services.
|
|
By Tim Howes, Mark Smith, Gordon Good, Timothy A. Howe
|
|
ISBN: 1578700701
|
|
|
|
=head1 AUTHOR(s)
|
|
|
|
Any good FAQ is made up of many authors, everyone that contributes
|
|
information to the perl-ldap mail list is a potential author.
|
|
|
|
An attempt to maintain this FAQ is being done by Clif Harden
|
|
<charden@pobox.com>.
|
|
|
|
The original author of this FAQ was Graham Barr <gbarr@pobox.com>
|
|
|
|
Please report any bugs, or post any suggestions, to the
|
|
perl-ldap mailing list
|
|
<perl-ldap-dev@lists.sourceforge.net>.
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright (c) 1999-2000 Graham Barr. All rights reserved. This document is
|
|
distributed, and may be redistributed, under the same terms as Perl itself.
|
|
|
|
=for html <hr>
|
|
|
|
=cut
|
|
|