Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 7 commits
  • 5 files changed
  • 0 commit comments
  • 1 contributor
Commits on Sep 28, 2012
@marschap marschap LDAP.pm: canonicalize host name with ldapi://
Having the host name canonicalized allows one to call start_tls()
with option verify => 'require', which does hostname checking.

Confirmed to work for Quanah.
6fdb90e
Commits on Oct 03, 2012
@marschap marschap Net/LDAP/Extra/AD.pm: new, convenience functions for AD 08ae450
@marschap marschap Extra.pm: refer to Net::LDAP::Extra::AD in man page 8da9113
@marschap marschap AD.pm: add methods is_AD & is_ADAM
Add methods to determine whether an LDAP server is an AD,
so that the AD specific methods can be used.
b23ae5f
Commits on Oct 06, 2012
@marschap marschap FAQ.pod: update, mention Net::LDAP::Extra::AD ed1907f
@marschap marschap LDAP.pod: consistency: use 'attrs' everywhere 2426f38
@marschap marschap FAQ.pm: add examples for Active Directory
Add examples for MS Active Directory
- how to create account and groups
- how to search for accounts & groups using the
  LDAP_MATCHING_RULE_BIT_AND matching rule
829ac83
View
14 lib/Net/LDAP.pm
@@ -271,7 +271,19 @@ sub connect_ldapi {
: 120
) or return undef;
- $ldap->{net_ldap_host} = 'localhost';
+ # try to get canonical host name [to allow start_tls on the connection]
+ require Socket;
+ if (Socket->can('getnameinfo') && Socket->can('getaddrinfo')) {
+ my @addrs;
+ my ($err,$host,$path) = Socket::getnameinfo($ldap->{net_ldap_socket}->peername, &Socket::AI_CANONNAME);
+
+ ($err, @addrs) = Socket::getaddrinfo($host, 0, { flags => &Socket::AI_CANONNAME } )
+ unless ($err);
+ map { $ldap->{net_ldap_host} = $_->{canonname} if ($_->{canonname}) } @addrs
+ unless ($err);
+ }
+
+ $ldap->{net_ldap_host} ||= 'localhost';
$ldap->{net_ldap_peer} = $peer;
}
View
2  lib/Net/LDAP.pod
@@ -30,7 +30,7 @@ Net::LDAP - Lightweight Directory Access Protocol
);
$result = $ldap->add( 'cn=Barbara Jensen, o=University of Michigan, c=US',
- attr => [
+ attrs => [
'cn' => ['Barbara Jensen', 'Barbs Jensen'],
'sn' => 'Jensen',
'mail' => 'b.jensen@umich.edu',
View
4 lib/Net/LDAP/Extra.pm
@@ -58,5 +58,9 @@ as a methods on all C<Net::LDAP> objects.
Care should be taken when choosing names for the functions to export
to ensure that they do not clash with others.
+=head1 SEE ALSO
+
+L<Net::LDAP::Extra::AD>,
+
=cut
View
136 lib/Net/LDAP/Extra/AD.pm
@@ -0,0 +1,136 @@
+package Net::LDAP::Extra::AD;
+
+use strict;
+use vars qw($VERSION @EXPORT);
+use Encode;
+use Exporter qw(import);
+use Net::LDAP::RootDSE;
+
+$VERSION = "0.02";
+@EXPORT = qw(is_AD is_ADAM reset_ADpassword change_ADpassword);
+
+
+sub is_AD {
+ my $self = shift;
+ my $rootdse = $self->root_dse(attrs => [ qw/supportedCapabilities/ ])
+ or return undef;
+
+ return (grep { $_ eq '1.2.840.113556.1.4.800' } $rootdse->get_value('supportedCapabilities'))
+ ? 1 : 0;
+}
+
+sub is_ADAM {
+ my $self = shift;
+ my $rootdse = $self->root_dse(attrs => [ qw/supportedCapabilities/ ])
+ or return undef;
+
+ return (grep { $_ eq '1.2.840.113556.1.4.1851' } $rootdse->get_value('supportedCapabilities'))
+ ? 1 : 0;
+}
+
+sub reset_ADpassword {
+ my ($self, $dn, $newpw, %opt) = @_;
+ my %attrs;
+
+ $attrs{unicodePwd} = encode('utf16le', '"'.decode('utf8', $newpw).'"');
+ $attrs{pwdLastSet} = 0 if ($opt{force_change});
+
+ $self->modify($dn, replace => \%attrs);
+}
+
+sub change_ADpassword {
+ my ($self, $dn, $oldpw, $newpw) = @_;
+
+ $oldpw = encode('utf16le', '"'.decode('utf8', $oldpw).'"');
+ $newpw = encode('utf16le', '"'.decode('utf8', $newpw).'"');
+
+ $self->modify($dn, changes => [ delete => { unicodePwd => $oldpw },
+ add => { unicodePwd => $newpw } ]);
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Net::LDAP::Extra:AD -- AD convenience methods
+
+=head1 SYNOPSIS
+
+ use Net::LDAP::Extra qw(AD);
+
+ $ldap = Net::LDAP->new( ... );
+
+ ...
+
+ if ($ldap->is_AD || $ldap->is_ADAM) {
+ $ldap->change_ADpassword($dn, $old_password, $new_password);
+ }
+
+=head1 DESCRIPTION
+
+Net::LDAP::Extra::AD tries to spare users the necessity to
+reinvent the wheel again and again in order to correctly encode
+password strings so that they can be used in AD password change
+operations.
+
+To do so, it provides the following methods:
+
+=head1 METHODS
+
+=over 4
+
+=item is_AD ( )
+
+Tell if the LDAP server queried is an Active Directory Domain Controller.
+
+As the check is done by querying the root DSE of the directory,
+it works without being bound to the directory.
+
+=item is_ADAM ( )
+
+Tell if the LDAP server queried is running AD LDS
+(Active Directory Lightweight Directory Services),
+previously known as ADAM (Active Directoy Application Mode).
+
+As the check is done by querying the root DSE of the directory,
+it works without being bound to the directory.
+
+=item change_ADpassword ( DN, OLD_PASSWORD, NEW_PASSWORD )
+
+Change the password of the account given by I<DN> from
+its old value I<OLD_PASSWORD> to the new value I<NEW_PASSWORD>.
+
+This method requires encrypted connections.
+
+=item reset_ADpassword ( DN, NEW_PASSWORD, OPTIONS )
+
+Reset the password of the account given by I<DN> to the value
+given in I<NEW_PASSWORD>.
+OPTIONS is a list of key/value pairs. The following keys are recognized:
+
+=over 4
+
+=item force_change
+
+If TRUE, the affected user is required to change the
+password at next login.
+
+=back
+
+For this method to work, the caller needs to be bound to AD with
+sufficient permissions, and the connection needs to be encrypted.
+
+=back
+
+=head1 AUTHOR
+
+Peter Marschall E<lt>peter@adpm.de<gt>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2012 Peter Marschall. All rights reserved. This program is
+free software; you can redistribute it and/or modify it under the same
+terms as Perl itself.
+
View
98 lib/Net/LDAP/FAQ.pod
@@ -1160,9 +1160,69 @@ 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.
+=item How do I create an account in Active Directory?
+
+Active Directory accounts need some AD-specific attributes
+(only the method we're interested in, no error checking):
+
+ $mesg = $ldap->add( 'cn=John Doe,cn=Users,dc=your,dc=ads,dc=domain',
+ attrs => [
+ objectClass => [ qw/top user/ ],
+ cn => 'John Doe',
+ sn => 'Doe',
+ givenName => 'John',
+ displayName => 'John "the one" Doe',
+ userAccountControl => 514, # disabled regular user
+ sAMAccountName => 'JohnDoe',
+ userPrincipalName => 'JohnDoe@your.ads.domain'
+ ]
+ );
+
+In order to find out what other attributes can be set, interactively
+edit the user in the Active Directory Users and Computers MCC plugin,
+perform an LDAP search operation to find out what changed, and update
+your "add" routine accordingly.
+
+=item How can I create a group in Active Directory?
+
+Similar to accounts, groups need some AD-specific attributes too:
+
+ $mesg = $ldap->add( 'cn=NewGroup,cn=Users,dc=your,dc=ads,dc=domain',
+ attrs => [
+ objectClass => [ qw/top group/ ],
+ cn => 'NewGroup',
+ sAMAccountName => 'NewGroup',
+ groupType => 0x80000002 # global, security enabled group
+ ]
+ );
+
+
+=item How do I search for disabled accounts in Active Directory
+
+The bit values in C<userAccountcontrol> require the LDAP_MATCHING_RULE_BIT_AND
+matching rule's OID to be used in an extensible filter term:
+
+ $mesg = $ldap->search( base => 'cn=Users,dc=your,dc=ads,dc=domain',
+ filter => '(&(objectclass=user)' .
+ (userAccountControl:1.2.840.113556.1.4.803:=2))',
+ attrs => [ '1.1' ]
+ );
+
+
+=item How can I search for security groups in Active Directory
+
+With groups, the same applies to the C<groupType> bit-field:
+
+ $mesg = $ldap->search( base => 'cn=Users,dc=your,dc=ads,dc=domain',
+ filter => '(&(objectclass=group)' .
+ (groupType:1.2.840.113556.1.4.803:=2147483648))',
+ # 2147483648 = 0x80000000
+ attrs => [ '1.1' ]
+ );
+
=head2 How do I create a Microsoft Exchange 5.x user?
-This is a solution provide by a perl-ldap user.
+This is a solution provided 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
@@ -1313,8 +1373,28 @@ adding the new one.
When doing it as a user with administrative priviledges replacing
the unicodePwd's value with a new one is allowed too.
+Perl-ldap contains convenience methods for Active Directory that
+allow one to perform this task very easily.
+
Here's an example that demonstrates setting your own password
-(again almost no error checking):
+from C<$oldPW> to C<$newPW> (again almost no error checking):
+
+ use Net::LDAP;
+ use Net::LDAP::Extra qw(AD);
+
+ my $ldap = Net::LDAP->new('ldaps://ads.domain.controller') or die "$@";
+
+ my $mesg = $ldap->bind('cn=Joe User,dc=your,dc=ads,dc=domain',
+ password => $oldPW);
+
+ $mesg = $ldap->change_ADpassword('cn=Joe User,dc=your,dc=ads,dc=domain',
+ $oldPW, $newPW);
+
+ $ldap->unbind();
+
+
+And the same for perl-ldap versions before 0.49, where everything needs
+to be done by hand:
use Net::LDAP;
use Unicode::Map8;
@@ -1331,7 +1411,7 @@ Here's an example that demonstrates setting your own password
my $ldap = Net::LDAP->new('ldaps://ads.domain.controller') or die "$@";
my $mesg = $ldap->bind('cn=Joe User,dc=your,dc=ads,dc=domain',
- password => 'oldPW');
+ password => $oldPW);
$mesg = $ldap->modify('cn=Joe User,dc=your,dc=ads,dc=domain',
changes => [
@@ -1346,7 +1426,12 @@ Here's an example that demonstrates setting your own password
Perl-ldap does not do server failover, however there are several
programming options for getting around this situation.
-Here is one possible solution.
+Here is one possible solution:
+
+ $ldaps = Net::LDAPS->new([ $ldapserverone, $ldapservertwo ],
+ port=>636, timeout=>5) or die "$@";
+
+For perl-ldap versions before 0.27, thes same goal can be achieved using:
unless ( $ldaps =
Net::LDAPS->new($ldapserverone,
@@ -1358,11 +1443,6 @@ Here is one possible solution.
"Can't connect to $ldapserverone or $ldapservertwo via LDAPS: $@";
}
-As of version 0.27 of perl-ldap the above can be expressed much simpler:
-
- $ldaps = Net::LDAPS->new([ $ldapserverone, $ldapservertwo ],
- port=>636, timeout=>5) or die "$@";
-
=head1 Using X.509 certificates.

No commit comments for this range

Something went wrong with that request. Please try again.