| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # Copyright (c) 1997-2004 Graham Barr . All rights reserved. | 
| 2 |  |  |  |  |  |  | # This program is free software; you can redistribute it and/or | 
| 3 |  |  |  |  |  |  | # modify it under the same terms as Perl itself. | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | package Net::LDAP; | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 21 |  |  | 21 |  | 512080 | use strict; | 
|  | 21 |  |  |  |  | 113 |  | 
|  | 21 |  |  |  |  | 809 |  | 
| 8 | 21 |  |  | 21 |  | 12547 | use Socket qw(AF_INET AF_INET6 AF_UNSPEC SOL_SOCKET SO_KEEPALIVE); | 
|  | 21 |  |  |  |  | 76494 |  | 
|  | 21 |  |  |  |  | 3783 |  | 
| 9 | 21 |  |  | 21 |  | 11730 | use IO::Socket; | 
|  | 21 |  |  |  |  | 380664 |  | 
|  | 21 |  |  |  |  | 98 |  | 
| 10 | 21 |  |  | 21 |  | 20331 | use IO::Select; | 
|  | 21 |  |  |  |  | 35863 |  | 
|  | 21 |  |  |  |  | 1007 |  | 
| 11 | 21 |  |  | 21 |  | 12508 | use Tie::Hash; | 
|  | 21 |  |  |  |  | 20713 |  | 
|  | 21 |  |  |  |  | 810 |  | 
| 12 | 21 |  |  | 21 |  | 10446 | use Convert::ASN1 qw(asn_read); | 
|  | 21 |  |  |  |  | 682556 |  | 
|  | 21 |  |  |  |  | 1312 |  | 
| 13 | 21 |  |  | 21 |  | 10381 | use Net::LDAP::Message; | 
|  | 21 |  |  |  |  | 100 |  | 
|  | 21 |  |  |  |  | 968 |  | 
| 14 | 21 |  |  | 21 |  | 160 | use Net::LDAP::ASN qw(LDAPResponse); | 
|  | 21 |  |  |  |  | 44 |  | 
|  | 21 |  |  |  |  | 107 |  | 
| 15 | 21 |  |  |  |  | 3866 | use Net::LDAP::Constant qw(LDAP_SUCCESS | 
| 16 |  |  |  |  |  |  | LDAP_OPERATIONS_ERROR | 
| 17 |  |  |  |  |  |  | LDAP_SASL_BIND_IN_PROGRESS | 
| 18 |  |  |  |  |  |  | LDAP_DECODING_ERROR | 
| 19 |  |  |  |  |  |  | LDAP_PROTOCOL_ERROR | 
| 20 |  |  |  |  |  |  | LDAP_ENCODING_ERROR | 
| 21 |  |  |  |  |  |  | LDAP_FILTER_ERROR | 
| 22 |  |  |  |  |  |  | LDAP_LOCAL_ERROR | 
| 23 |  |  |  |  |  |  | LDAP_PARAM_ERROR | 
| 24 |  |  |  |  |  |  | LDAP_INAPPROPRIATE_AUTH | 
| 25 |  |  |  |  |  |  | LDAP_SERVER_DOWN | 
| 26 |  |  |  |  |  |  | LDAP_USER_CANCELED | 
| 27 |  |  |  |  |  |  | LDAP_EXTENSION_START_TLS | 
| 28 |  |  |  |  |  |  | LDAP_UNAVAILABLE | 
| 29 | 21 |  |  | 21 |  | 135 | ); | 
|  | 21 |  |  |  |  | 46 |  | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | # check for IPv6 support: prefer IO::Socket::IP 0.20+ over IO::Socket::INET6 | 
| 32 | 21 |  |  |  |  | 45 | use constant CAN_IPV6 => do { | 
| 33 | 21 |  |  |  |  | 101 | local $SIG{__DIE__}; | 
| 34 |  |  |  |  |  |  |  | 
| 35 | 21 |  |  |  |  | 19050 | eval { require IO::Socket::IP; IO::Socket::IP->VERSION(0.20); } | 
|  | 21 |  |  |  |  | 452884 |  | 
| 36 |  |  |  |  |  |  | ? 'IO::Socket::IP' | 
| 37 | 21 | 0 |  |  |  | 79 | : eval { require IO::Socket::INET6; } | 
|  | 0 | 50 |  |  |  | 0 |  | 
| 38 |  |  |  |  |  |  | ? 'IO::Socket::INET6' | 
| 39 |  |  |  |  |  |  | : ''; | 
| 40 | 21 |  |  | 21 |  | 183 | }; | 
|  | 21 |  |  |  |  | 49 |  | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | our $VERSION 	= '0.67'; | 
| 43 |  |  |  |  |  |  | our @ISA     	= qw(Tie::StdHash Net::LDAP::Extra); | 
| 44 |  |  |  |  |  |  | our $LDAP_VERSION 	= 3;      # default LDAP protocol version | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | # Net::LDAP::Extra will only exist is someone use's the module. But we need | 
| 47 |  |  |  |  |  |  | # to ensure the package stash exists or perl will complain that we inherit | 
| 48 |  |  |  |  |  |  | # from a non-existent package. I could just use the module, but I did not | 
| 49 |  |  |  |  |  |  | # want to. | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | $Net::LDAP::Extra::create = $Net::LDAP::Extra::create = 0; | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | sub import { | 
| 54 | 27 |  |  | 27 |  | 2267 | shift; | 
| 55 | 27 |  |  |  |  | 83 | unshift @_, 'Net::LDAP::Constant'; | 
| 56 | 27 |  |  |  |  | 204 | require Net::LDAP::Constant; | 
| 57 | 27 |  |  |  |  | 109 | goto &{Net::LDAP::Constant->can('import')}; | 
|  | 27 |  |  |  |  | 1911 |  | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | sub _options { | 
| 61 | 4 |  |  | 4 |  | 9 | my %ret = @_; | 
| 62 | 4 |  |  |  |  | 8 | my $once = 0; | 
| 63 | 4 |  |  |  |  | 13 | for my $v (grep { /^-/ } keys %ret) { | 
|  | 2 |  |  |  |  | 10 |  | 
| 64 | 0 |  |  |  |  | 0 | require Carp; | 
| 65 | 0 | 0 |  |  |  | 0 | $once++  or Carp::carp('deprecated use of leading - for options'); | 
| 66 | 0 |  |  |  |  | 0 | $ret{substr($v, 1)} = $ret{$v}; | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 0 | 0 |  |  |  | 0 | $ret{control} = [ map { (ref($_) =~ /[^A-Z]/) ? $_->to_asn : $_ } | 
| 70 |  |  |  |  |  |  | ref($ret{control}) eq 'ARRAY' | 
| 71 | 0 |  |  |  |  | 0 | ? @{$ret{control}} | 
| 72 |  |  |  |  |  |  | : $ret{control} | 
| 73 |  |  |  |  |  |  | ] | 
| 74 | 4 | 0 |  |  |  | 13 | if exists $ret{control}; | 
|  |  | 50 |  |  |  |  |  | 
| 75 |  |  |  |  |  |  |  | 
| 76 | 4 |  |  |  |  | 9 | \%ret; | 
| 77 |  |  |  |  |  |  | } | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | sub _dn_options { | 
| 80 | 2 | 50 |  | 2 |  | 9 | unshift @_, 'dn'  if @_ & 1; | 
| 81 | 2 |  |  |  |  | 5 | &_options; | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | sub _err_msg { | 
| 85 | 0 |  |  | 0 |  | 0 | my $mesg = shift; | 
| 86 | 0 |  | 0 |  |  | 0 | my $errstr = $mesg->dn || ''; | 
| 87 | 0 | 0 |  |  |  | 0 | $errstr .= ': '  if $errstr; | 
| 88 | 0 |  |  |  |  | 0 | $errstr . $mesg->error; | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | my %onerror = ( | 
| 92 |  |  |  |  |  |  | die   => sub { require Carp; Carp::croak(_err_msg(@_)) }, | 
| 93 |  |  |  |  |  |  | warn  => sub { require Carp; Carp::carp(_err_msg(@_)); $_[0] }, | 
| 94 |  |  |  |  |  |  | undef => sub { require Carp; Carp::carp(_err_msg(@_))  if $^W; undef }, | 
| 95 |  |  |  |  |  |  | ); | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | sub _error { | 
| 98 | 0 |  |  | 0 |  | 0 | my ($ldap, $mesg) = splice(@_, 0, 2); | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 0 |  |  |  |  | 0 | $mesg->set_error(@_); | 
| 101 |  |  |  |  |  |  | $ldap->{net_ldap_onerror} && !$ldap->{net_ldap_async} | 
| 102 | 0 | 0 | 0 |  |  | 0 | ? scalar &{$ldap->{net_ldap_onerror}}($mesg) | 
|  | 0 |  |  |  |  | 0 |  | 
| 103 |  |  |  |  |  |  | : $mesg; | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | sub new { | 
| 107 | 2 |  |  | 2 | 1 | 1564 | my $self = shift; | 
| 108 | 2 |  | 33 |  |  | 15 | my $type = ref($self) || $self; | 
| 109 | 2 | 50 |  |  |  | 8 | my $host = shift  if @_ % 2; | 
| 110 | 2 |  |  |  |  | 6 | my $arg  = &_options; | 
| 111 | 2 |  |  |  |  | 6 | my $obj  = bless {}, $type; | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 2 | 50 |  |  |  | 7 | foreach my $uri (ref($host) ? @$host : ($host)) { | 
| 114 | 2 |  | 50 |  |  | 9 | my $scheme = $arg->{scheme} || 'ldap'; | 
| 115 | 2 |  |  |  |  | 4 | my $h = $uri; | 
| 116 | 2 | 50 |  |  |  | 6 | if (defined($h)) { | 
| 117 | 2 | 50 |  |  |  | 6 | $h =~ s,^(\w+)://,, and $scheme = lc($1); | 
| 118 | 2 |  |  |  |  | 5 | $h =~ s,/.*,,; # remove path part | 
| 119 | 2 |  |  |  |  | 4 | $h =~ s/%([A-Fa-f0-9]{2})/chr(hex($1))/eg; # unescape | 
|  | 0 |  |  |  |  | 0 |  | 
| 120 |  |  |  |  |  |  | } | 
| 121 | 2 | 50 |  |  |  | 19 | my $meth = $obj->can("connect_$scheme")  or next; | 
| 122 | 2 | 50 |  |  |  | 9 | if (&$meth($obj, $h, $arg)) { | 
| 123 | 2 |  |  |  |  | 209 | $obj->{net_ldap_uri} = $uri; | 
| 124 | 2 |  |  |  |  | 5 | $obj->{net_ldap_scheme} = $scheme; | 
| 125 | 2 |  |  |  |  | 4 | last; | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 2 | 50 |  |  |  | 15 | return undef  unless $obj->{net_ldap_socket}; | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | $obj->{net_ldap_socket}->setsockopt(SOL_SOCKET, SO_KEEPALIVE, $arg->{keepalive} ? 1 : 0) | 
| 132 | 2 | 0 |  |  |  | 6 | if (defined($arg->{keepalive})); | 
|  |  | 50 |  |  |  |  |  | 
| 133 |  |  |  |  |  |  |  | 
| 134 | 2 |  |  |  |  | 6 | $obj->{net_ldap_rawsocket} = $obj->{net_ldap_socket}; | 
| 135 | 2 |  |  |  |  | 3 | $obj->{net_ldap_resp}    = {}; | 
| 136 | 2 |  | 33 |  |  | 10 | $obj->{net_ldap_version} = $arg->{version} || $LDAP_VERSION; | 
| 137 | 2 | 50 |  |  |  | 6 | $obj->{net_ldap_async}   = $arg->{async} ? 1 : 0; | 
| 138 | 2 | 50 |  |  |  | 5 | $obj->{raw} = $arg->{raw}  if ($arg->{raw}); | 
| 139 |  |  |  |  |  |  |  | 
| 140 | 2 | 50 |  |  |  | 6 | if (defined(my $onerr = $arg->{onerror})) { | 
| 141 | 0 | 0 |  |  |  | 0 | $onerr = $onerror{$onerr}  if exists $onerror{$onerr}; | 
| 142 | 0 |  |  |  |  | 0 | $obj->{net_ldap_onerror} = $onerr; | 
| 143 |  |  |  |  |  |  | } | 
| 144 |  |  |  |  |  |  |  | 
| 145 | 2 |  | 50 |  |  | 15 | $obj->debug($arg->{debug} || 0 ); | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 2 |  |  |  |  | 18 | $obj->outer; | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | sub connect_ldap { | 
| 151 | 0 |  |  | 0 | 0 | 0 | my ($ldap, $host, $arg) = @_; | 
| 152 | 0 |  | 0 |  |  | 0 | my $port = $arg->{port} || 389; | 
| 153 | 0 |  |  |  |  | 0 | my $class = (CAN_IPV6) ? CAN_IPV6 : 'IO::Socket::INET'; | 
| 154 | 0 | 0 |  |  |  | 0 | my $domain = $arg->{inet4} ? AF_INET : ($arg->{inet6} ? AF_INET6 : AF_UNSPEC); | 
|  |  | 0 |  |  |  |  |  | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | # separate port from host overwriting given/default port | 
| 157 | 0 | 0 |  |  |  | 0 | $host =~ s/^([^:]+|\[.*\]):(\d+)$/$1/ and $port = $2; | 
| 158 |  |  |  |  |  |  |  | 
| 159 | 0 | 0 | 0 |  |  | 0 | if ($arg->{inet6} && !CAN_IPV6) { | 
| 160 | 0 |  |  |  |  | 0 | $@ = 'unable to load IO::Socket::INET6; no IPv6 support'; | 
| 161 | 0 |  |  |  |  | 0 | return undef; | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | $ldap->{net_ldap_socket} = $class->new( | 
| 165 |  |  |  |  |  |  | PeerAddr   => $host, | 
| 166 |  |  |  |  |  |  | PeerPort   => $port, | 
| 167 |  |  |  |  |  |  | LocalAddr  => $arg->{localaddr} || undef, | 
| 168 |  |  |  |  |  |  | Proto      => 'tcp', | 
| 169 |  |  |  |  |  |  | ($class eq 'IO::Socket::IP' ? 'Family' : 'Domain')     => $domain, | 
| 170 |  |  |  |  |  |  | MultiHomed => $arg->{multihomed}, | 
| 171 |  |  |  |  |  |  | Timeout    => defined $arg->{timeout} | 
| 172 |  |  |  |  |  |  | ? $arg->{timeout} | 
| 173 | 0 | 0 | 0 |  |  | 0 | : 120 | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | ) or return undef; | 
| 175 |  |  |  |  |  |  |  | 
| 176 | 0 |  |  |  |  | 0 | $ldap->{net_ldap_host} = $host; | 
| 177 | 0 |  |  |  |  | 0 | $ldap->{net_ldap_port} = $port; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | # Different OpenSSL verify modes. | 
| 182 |  |  |  |  |  |  | my %ssl_verify = qw(none 0 optional 1 require 3); | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | sub connect_ldaps { | 
| 185 | 0 |  |  | 0 | 0 | 0 | my ($ldap, $host, $arg) = @_; | 
| 186 | 0 |  | 0 |  |  | 0 | my $port = $arg->{port} || 636; | 
| 187 | 0 | 0 |  |  |  | 0 | my $domain = $arg->{inet4} ? AF_INET : ($arg->{inet6} ? AF_INET6 : AF_UNSPEC); | 
|  |  | 0 |  |  |  |  |  | 
| 188 |  |  |  |  |  |  |  | 
| 189 | 0 | 0 | 0 |  |  | 0 | if ($arg->{inet6} && !CAN_IPV6) { | 
| 190 | 0 |  |  |  |  | 0 | $@ = 'unable to load IO::Socket::INET6; no IPv6 support'; | 
| 191 | 0 |  |  |  |  | 0 | return undef; | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 | 0 |  |  |  |  | 0 | require IO::Socket::SSL; | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | # separate port from host overwriting given/default port | 
| 197 | 0 | 0 |  |  |  | 0 | $host =~ s/^([^:]+|\[.*\]):(\d+)$/$1/ and $port = $2; | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | $ldap->{net_ldap_socket} = IO::Socket::SSL->new( | 
| 200 |  |  |  |  |  |  | PeerAddr 	    => $host, | 
| 201 |  |  |  |  |  |  | PeerPort 	    => $port, | 
| 202 |  |  |  |  |  |  | LocalAddr       => $arg->{localaddr} || undef, | 
| 203 |  |  |  |  |  |  | Proto    	    => 'tcp', | 
| 204 |  |  |  |  |  |  | Domain          => $domain, | 
| 205 | 0 | 0 | 0 |  |  | 0 | Timeout  	    => defined $arg->{timeout} ? $arg->{timeout} : 120, | 
|  |  | 0 |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | _SSL_context_init_args({sslserver => $host, %$arg}) | 
| 207 |  |  |  |  |  |  | ) or return undef; | 
| 208 |  |  |  |  |  |  |  | 
| 209 | 0 |  |  |  |  | 0 | $ldap->{net_ldap_host} = $host; | 
| 210 | 0 |  |  |  |  | 0 | $ldap->{net_ldap_port} = $port; | 
| 211 |  |  |  |  |  |  | } | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | sub _SSL_context_init_args { | 
| 214 | 0 |  |  | 0 |  | 0 | my $arg = shift; | 
| 215 |  |  |  |  |  |  |  | 
| 216 | 0 |  |  |  |  | 0 | my $verify = 0; | 
| 217 | 0 |  |  |  |  | 0 | my %verifycn_ctx = (); | 
| 218 | 0 |  |  |  |  | 0 | my ($clientcert, $clientkey, $passwdcb); | 
| 219 |  |  |  |  |  |  |  | 
| 220 | 0 | 0 |  |  |  | 0 | if (exists $arg->{verify}) { | 
| 221 | 0 |  |  |  |  | 0 | my $v = lc $arg->{verify}; | 
| 222 | 0 | 0 |  |  |  | 0 | $verify = 0 + (exists $ssl_verify{$v} ? $ssl_verify{$v} : $verify); | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 0 | 0 |  |  |  | 0 | if ($verify) { | 
| 225 | 0 |  |  |  |  | 0 | $verifycn_ctx{SSL_verifycn_scheme} = 'ldap'; | 
| 226 |  |  |  |  |  |  | $verifycn_ctx{SSL_verifycn_name} = $arg->{sslserver} | 
| 227 | 0 | 0 |  |  |  | 0 | if (defined $arg->{sslserver}); | 
| 228 |  |  |  |  |  |  | } | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  |  | 
| 231 | 0 | 0 |  |  |  | 0 | if (exists $arg->{clientcert}) { | 
| 232 | 0 |  |  |  |  | 0 | $clientcert = $arg->{clientcert}; | 
| 233 | 0 | 0 |  |  |  | 0 | if (exists $arg->{clientkey}) { | 
| 234 | 0 |  |  |  |  | 0 | $clientkey = $arg->{clientkey}; | 
| 235 |  |  |  |  |  |  | } else { | 
| 236 | 0 |  |  |  |  | 0 | require Carp; | 
| 237 | 0 |  |  |  |  | 0 | Carp::croak('Setting client public key but not client private key'); | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  |  | 
| 241 | 0 | 0 | 0 |  |  | 0 | if ($arg->{checkcrl} && !$arg->{capath}) { | 
| 242 | 0 |  |  |  |  | 0 | require Carp; | 
| 243 | 0 |  |  |  |  | 0 | Carp::croak('Cannot check CRL without having CA certificates'); | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  |  | 
| 246 | 0 | 0 |  |  |  | 0 | if (exists $arg->{keydecrypt}) { | 
| 247 | 0 |  |  |  |  | 0 | $passwdcb = $arg->{keydecrypt}; | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | # allow deprecated "sslv2/3" in addition to IO::Socket::SSL's "sslv23" | 
| 251 | 0 | 0 |  |  |  | 0 | if (defined $arg->{sslversion}) { | 
| 252 | 0 |  |  |  |  | 0 | $arg->{sslversion} =~ s:sslv2/3:sslv23:io; | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | ( | 
| 256 |  |  |  |  |  |  | defined $arg->{ciphers} ? | 
| 257 |  |  |  |  |  |  | ( SSL_cipher_list => $arg->{ciphers} ) : (), | 
| 258 |  |  |  |  |  |  | defined $arg->{sslversion} ? | 
| 259 |  |  |  |  |  |  | ( SSL_version     => $arg->{sslversion} ) : (), | 
| 260 |  |  |  |  |  |  | SSL_ca_file         => exists  $arg->{cafile}  ? $arg->{cafile}  : '', | 
| 261 |  |  |  |  |  |  | SSL_ca_path         => exists  $arg->{capath}  ? $arg->{capath}  : '', | 
| 262 |  |  |  |  |  |  | SSL_key_file        => $clientcert ? $clientkey : undef, | 
| 263 |  |  |  |  |  |  | SSL_passwd_cb       => $passwdcb, | 
| 264 | 0 | 0 |  |  |  | 0 | SSL_check_crl       => $arg->{checkcrl} ? 1 : 0, | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 265 |  |  |  |  |  |  | SSL_use_cert        => $clientcert ? 1 : 0, | 
| 266 |  |  |  |  |  |  | SSL_cert_file       => $clientcert, | 
| 267 |  |  |  |  |  |  | SSL_verify_mode     => $verify, | 
| 268 |  |  |  |  |  |  | %verifycn_ctx, | 
| 269 |  |  |  |  |  |  | ); | 
| 270 |  |  |  |  |  |  | } | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | sub connect_ldapi { | 
| 273 | 0 |  |  | 0 | 0 | 0 | my ($ldap, $peer, $arg) = @_; | 
| 274 |  |  |  |  |  |  |  | 
| 275 | 0 | 0 | 0 |  |  | 0 | $peer = $ENV{LDAPI_SOCK} || '/var/run/ldapi' | 
| 276 |  |  |  |  |  |  | unless length $peer; | 
| 277 |  |  |  |  |  |  |  | 
| 278 | 0 |  |  |  |  | 0 | require IO::Socket::UNIX; | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | $ldap->{net_ldap_socket} = IO::Socket::UNIX->new( | 
| 281 |  |  |  |  |  |  | Peer => $peer, | 
| 282 |  |  |  |  |  |  | Timeout  => defined $arg->{timeout} | 
| 283 |  |  |  |  |  |  | ? $arg->{timeout} | 
| 284 | 0 | 0 |  |  |  | 0 | : 120 | 
|  |  | 0 |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | ) or return undef; | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | # try to get canonical host name [to allow start_tls on the connection] | 
| 288 | 0 |  |  |  |  | 0 | require Socket; | 
| 289 | 0 | 0 | 0 |  |  | 0 | if (Socket->can('getnameinfo') && Socket->can('getaddrinfo')) { | 
| 290 | 0 |  |  |  |  | 0 | my @addrs; | 
| 291 | 0 |  |  |  |  | 0 | my ($err, $host, $path) = Socket::getnameinfo($ldap->{net_ldap_socket}->peername, &Socket::AI_CANONNAME); | 
| 292 |  |  |  |  |  |  |  | 
| 293 | 0 | 0 |  |  |  | 0 | ($err, @addrs) = Socket::getaddrinfo($host, 0, { flags => &Socket::AI_CANONNAME } ) | 
| 294 |  |  |  |  |  |  | unless ($err); | 
| 295 | 0 | 0 |  |  |  | 0 | map { $ldap->{net_ldap_host} = $_->{canonname}  if ($_->{canonname}) }  @addrs | 
|  | 0 | 0 |  |  |  | 0 |  | 
| 296 |  |  |  |  |  |  | unless ($err); | 
| 297 |  |  |  |  |  |  | } | 
| 298 |  |  |  |  |  |  |  | 
| 299 | 0 |  | 0 |  |  | 0 | $ldap->{net_ldap_host} ||= 'localhost'; | 
| 300 | 0 |  |  |  |  | 0 | $ldap->{net_ldap_peer} = $peer; | 
| 301 |  |  |  |  |  |  | } | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | sub message { | 
| 304 | 2 |  |  | 2 | 0 | 4 | my $ldap = shift; | 
| 305 | 2 |  |  |  |  | 17 | shift->new($ldap, @_); | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | sub async { | 
| 309 | 2 |  |  | 2 | 1 | 3 | my $ldap = shift; | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | @_ | 
| 312 |  |  |  |  |  |  | ? ($ldap->{net_ldap_async}, $ldap->{net_ldap_async} = shift)[0] | 
| 313 | 2 | 50 |  |  |  | 11 | : $ldap->{net_ldap_async}; | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | sub debug { | 
| 317 | 4 |  |  | 4 | 1 | 6 | my $ldap = shift; | 
| 318 |  |  |  |  |  |  |  | 
| 319 | 4 | 50 |  |  |  | 9 | require Convert::ASN1::Debug  if $_[0]; | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | @_ | 
| 322 |  |  |  |  |  |  | ? ($ldap->{net_ldap_debug}, $ldap->{net_ldap_debug} = shift)[0] | 
| 323 | 4 | 100 |  |  |  | 18 | : $ldap->{net_ldap_debug}; | 
| 324 |  |  |  |  |  |  | } | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | sub sasl { | 
| 327 | 0 |  |  | 0 | 1 | 0 | $_[0]->{sasl}; | 
| 328 |  |  |  |  |  |  | } | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | sub socket { | 
| 331 | 2 |  |  | 2 | 1 | 4 | my $ldap = shift; | 
| 332 | 2 |  |  |  |  | 13 | my %opt = @_; | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | (exists($opt{sasl_layer}) && !$opt{sasl_layer}) | 
| 335 |  |  |  |  |  |  | ? $ldap->{net_ldap_rawsocket} | 
| 336 | 2 | 50 | 33 |  |  | 12 | : $ldap->{net_ldap_socket}; | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | sub host { | 
| 340 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 341 |  |  |  |  |  |  | ($ldap->{net_ldap_scheme} ne 'ldapi') | 
| 342 |  |  |  |  |  |  | ? $ldap->{net_ldap_host} | 
| 343 | 0 | 0 |  |  |  | 0 | : $ldap->{net_ldap_peer}; | 
| 344 |  |  |  |  |  |  | } | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | sub port { | 
| 347 | 0 | 0 |  | 0 | 1 | 0 | $_[0]->{net_ldap_port} || undef; | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | sub scheme { | 
| 351 | 0 |  |  | 0 | 1 | 0 | $_[0]->{net_ldap_scheme}; | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | sub uri { | 
| 355 | 0 |  |  | 0 | 1 | 0 | $_[0]->{net_ldap_uri}; | 
| 356 |  |  |  |  |  |  | } | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | sub unbind { | 
| 360 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 361 | 0 |  |  |  |  | 0 | my $arg  = &_options; | 
| 362 |  |  |  |  |  |  |  | 
| 363 | 0 |  |  |  |  | 0 | my $mesg = $ldap->message('Net::LDAP::Unbind' => $arg); | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | my $control = $arg->{control} | 
| 366 | 0 | 0 | 0 |  |  | 0 | and $ldap->{net_ldap_version} < 3 | 
| 367 |  |  |  |  |  |  | and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); | 
| 368 |  |  |  |  |  |  |  | 
| 369 | 0 | 0 |  |  |  | 0 | $mesg->encode( | 
| 370 |  |  |  |  |  |  | unbindRequest => 1, | 
| 371 |  |  |  |  |  |  | controls      => $control, | 
| 372 |  |  |  |  |  |  | ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); | 
| 373 |  |  |  |  |  |  |  | 
| 374 | 0 |  |  |  |  | 0 | $ldap->_sendmesg($mesg); | 
| 375 |  |  |  |  |  |  | } | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | # convenience alias | 
| 378 |  |  |  |  |  |  | *done = \&unbind; | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | sub ldapbind { | 
| 382 | 0 |  |  | 0 | 0 | 0 | require Carp; | 
| 383 | 0 | 0 |  |  |  | 0 | Carp::carp('->ldapbind deprecated, use ->bind')  if $^W; | 
| 384 | 0 |  |  |  |  | 0 | goto &bind; | 
| 385 |  |  |  |  |  |  | } | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | my %ptype = qw( | 
| 389 |  |  |  |  |  |  | password        simple | 
| 390 |  |  |  |  |  |  | krb41password   krbv41 | 
| 391 |  |  |  |  |  |  | krb42password   krbv42 | 
| 392 |  |  |  |  |  |  | kerberos41      krbv41 | 
| 393 |  |  |  |  |  |  | kerberos42      krbv42 | 
| 394 |  |  |  |  |  |  | sasl            sasl | 
| 395 |  |  |  |  |  |  | noauth          anon | 
| 396 |  |  |  |  |  |  | anonymous       anon | 
| 397 |  |  |  |  |  |  | ); | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | sub bind { | 
| 400 | 2 |  |  | 2 | 1 | 11 | my $ldap = shift; | 
| 401 | 2 |  |  |  |  | 5 | my $arg  = &_dn_options; | 
| 402 |  |  |  |  |  |  |  | 
| 403 | 2 |  |  |  |  | 498 | require Net::LDAP::Bind; | 
| 404 | 2 |  |  |  |  | 25 | my $mesg = $ldap->message('Net::LDAP::Bind' => $arg); | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | $ldap->version(delete $arg->{version}) | 
| 407 | 2 | 50 |  |  |  | 6 | if exists $arg->{version}; | 
| 408 |  |  |  |  |  |  |  | 
| 409 | 2 |  | 50 |  |  | 8 | my $dn      = delete $arg->{dn} || ''; | 
| 410 |  |  |  |  |  |  | my $control = delete $arg->{control} | 
| 411 | 2 | 50 | 33 |  |  | 7 | and $ldap->{net_ldap_version} < 3 | 
| 412 |  |  |  |  |  |  | and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); | 
| 413 |  |  |  |  |  |  |  | 
| 414 | 2 | 50 |  |  |  | 10 | my %stash = ( | 
| 415 |  |  |  |  |  |  | name    => ref($dn) ? $dn->dn : $dn, | 
| 416 |  |  |  |  |  |  | version => $ldap->version, | 
| 417 |  |  |  |  |  |  | ); | 
| 418 |  |  |  |  |  |  |  | 
| 419 | 2 | 50 |  |  |  | 30 | my($auth_type, $passwd) = scalar(keys %$arg) ? () : (simple => ''); | 
| 420 |  |  |  |  |  |  |  | 
| 421 | 2 |  |  |  |  | 5 | keys %ptype; # Reset iterator | 
| 422 | 2 |  |  |  |  | 10 | while (my($param, $type) = each %ptype) { | 
| 423 | 16 | 50 |  |  |  | 53 | if (exists $arg->{$param}) { | 
| 424 | 0 | 0 |  |  |  | 0 | ($auth_type, $passwd) = $type eq 'anon' ? (simple => '') : ($type, $arg->{$param}); | 
| 425 | 0 | 0 | 0 |  |  | 0 | return _error($ldap, $mesg, LDAP_INAPPROPRIATE_AUTH, 'No password, did you mean noauth or anonymous ?') | 
| 426 |  |  |  |  |  |  | if $type eq 'simple' and $passwd eq ''; | 
| 427 | 0 |  |  |  |  | 0 | last; | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  |  | 
| 431 | 2 | 50 |  |  |  | 6 | return _error($ldap, $mesg, LDAP_INAPPROPRIATE_AUTH, 'No AUTH supplied') | 
| 432 |  |  |  |  |  |  | unless $auth_type; | 
| 433 |  |  |  |  |  |  |  | 
| 434 | 2 | 50 |  |  |  | 6 | if ($auth_type eq 'sasl') { | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'SASL requires LDAPv3') | 
| 437 | 0 | 0 |  |  |  | 0 | if $ldap->{net_ldap_version} < 3; | 
| 438 |  |  |  |  |  |  |  | 
| 439 | 0 |  |  |  |  | 0 | my $sasl = $passwd; | 
| 440 | 0 |  |  |  |  | 0 | my $sasl_conn; | 
| 441 |  |  |  |  |  |  |  | 
| 442 | 0 | 0 | 0 |  |  | 0 | if (ref($sasl) and $sasl->isa('Authen::SASL')) { | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | # If we're talking to a round-robin, the canonical name of | 
| 445 |  |  |  |  |  |  | # the host we are talking to might not match the name we | 
| 446 |  |  |  |  |  |  | # requested. Look at the rawsocket because SASL layer filehandles | 
| 447 |  |  |  |  |  |  | # don't support socket methods. | 
| 448 | 0 |  |  |  |  | 0 | my $sasl_host; | 
| 449 |  |  |  |  |  |  |  | 
| 450 | 0 | 0 |  |  |  | 0 | if (exists($arg->{sasl_host})) { | 
| 451 | 0 | 0 |  |  |  | 0 | if ($arg->{sasl_host}) { | 
|  |  | 0 |  |  |  |  |  | 
| 452 | 0 |  |  |  |  | 0 | $sasl_host = $arg->{sasl_host}; | 
| 453 |  |  |  |  |  |  | } | 
| 454 |  |  |  |  |  |  | elsif ($ldap->{net_ldap_rawsocket}->can('peerhost')) { | 
| 455 | 0 |  |  |  |  | 0 | $sasl_host = $ldap->{net_ldap_rawsocket}->peerhost; | 
| 456 |  |  |  |  |  |  | } | 
| 457 |  |  |  |  |  |  | } | 
| 458 | 0 |  | 0 |  |  | 0 | $sasl_host ||= $ldap->{net_ldap_host}; | 
| 459 |  |  |  |  |  |  |  | 
| 460 | 0 |  |  |  |  | 0 | $sasl_conn = eval { | 
| 461 | 0 |  |  |  |  | 0 | local ($SIG{__DIE__}); | 
| 462 | 0 |  |  |  |  | 0 | $sasl->client_new('ldap', $sasl_host); | 
| 463 |  |  |  |  |  |  | }; | 
| 464 |  |  |  |  |  |  | } | 
| 465 |  |  |  |  |  |  | else { | 
| 466 | 0 |  |  |  |  | 0 | $sasl_conn = $sasl; | 
| 467 |  |  |  |  |  |  | } | 
| 468 |  |  |  |  |  |  |  | 
| 469 | 0 | 0 |  |  |  | 0 | return _error($ldap, $mesg, LDAP_LOCAL_ERROR, "$@") | 
| 470 |  |  |  |  |  |  | unless defined($sasl_conn); | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | # Tell SASL the local and server IP addresses | 
| 473 |  |  |  |  |  |  | $sasl_conn->property( | 
| 474 |  |  |  |  |  |  | sockname => $ldap->{net_ldap_rawsocket}->sockname, | 
| 475 |  |  |  |  |  |  | peername => $ldap->{net_ldap_rawsocket}->peername, | 
| 476 | 0 |  |  |  |  | 0 | ); | 
| 477 |  |  |  |  |  |  |  | 
| 478 | 0 |  |  |  |  | 0 | my $initial = $sasl_conn->client_start; | 
| 479 |  |  |  |  |  |  |  | 
| 480 | 0 | 0 |  |  |  | 0 | return _error($ldap, $mesg, LDAP_LOCAL_ERROR, $sasl_conn->error) | 
| 481 |  |  |  |  |  |  | unless defined($initial); | 
| 482 |  |  |  |  |  |  |  | 
| 483 | 0 |  |  |  |  | 0 | $passwd = { | 
| 484 |  |  |  |  |  |  | mechanism   => $sasl_conn->mechanism, | 
| 485 |  |  |  |  |  |  | credentials => $initial, | 
| 486 |  |  |  |  |  |  | }; | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | # Save data, we will need it later | 
| 489 | 0 |  |  |  |  | 0 | $mesg->_sasl_info($stash{name}, $control, $sasl_conn); | 
| 490 |  |  |  |  |  |  | } | 
| 491 |  |  |  |  |  |  |  | 
| 492 | 2 |  |  |  |  | 6 | $stash{authentication} = { $auth_type => $passwd }; | 
| 493 |  |  |  |  |  |  |  | 
| 494 | 2 | 50 |  |  |  | 10 | $mesg->encode( | 
| 495 |  |  |  |  |  |  | bindRequest => \%stash, | 
| 496 |  |  |  |  |  |  | controls    => $control | 
| 497 |  |  |  |  |  |  | ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); | 
| 498 |  |  |  |  |  |  |  | 
| 499 | 2 |  |  |  |  | 11 | $ldap->_sendmesg($mesg); | 
| 500 |  |  |  |  |  |  | } | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  |  | 
| 503 |  |  |  |  |  |  | my %scope = qw(base  0 one    1 single 1 sub    2 subtree 2 children 3); | 
| 504 |  |  |  |  |  |  | my %deref = qw(never 0 search 1 find   2 always 3); | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | sub search { | 
| 507 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 508 | 0 |  |  |  |  | 0 | my $arg  = &_options; | 
| 509 |  |  |  |  |  |  |  | 
| 510 | 0 |  |  |  |  | 0 | require Net::LDAP::Search; | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | $arg->{raw} = $ldap->{raw} | 
| 513 | 0 | 0 | 0 |  |  | 0 | if ($ldap->{raw} && !defined($arg->{raw})); | 
| 514 |  |  |  |  |  |  |  | 
| 515 | 0 |  |  |  |  | 0 | my $mesg = $ldap->message('Net::LDAP::Search' => $arg); | 
| 516 |  |  |  |  |  |  |  | 
| 517 |  |  |  |  |  |  | my $control = $arg->{control} | 
| 518 | 0 | 0 | 0 |  |  | 0 | and $ldap->{net_ldap_version} < 3 | 
| 519 |  |  |  |  |  |  | and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); | 
| 520 |  |  |  |  |  |  |  | 
| 521 | 0 |  | 0 |  |  | 0 | my $base = $arg->{base} || ''; | 
| 522 | 0 |  |  |  |  | 0 | my $filter; | 
| 523 |  |  |  |  |  |  |  | 
| 524 | 0 | 0 |  |  |  | 0 | unless (ref ($filter = $arg->{filter})) { | 
| 525 | 0 |  |  |  |  | 0 | require Net::LDAP::Filter; | 
| 526 | 0 |  |  |  |  | 0 | my $f = Net::LDAP::Filter->new; | 
| 527 | 0 | 0 |  |  |  | 0 | $f->parse($filter) | 
| 528 |  |  |  |  |  |  | or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Bad filter'); | 
| 529 | 0 |  |  |  |  | 0 | $filter = $f; | 
| 530 |  |  |  |  |  |  | } | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | my %stash = ( | 
| 533 |  |  |  |  |  |  | baseObject   => ref($base) ? $base->dn : $base, | 
| 534 |  |  |  |  |  |  | scope        => 2, | 
| 535 |  |  |  |  |  |  | derefAliases => 2, | 
| 536 |  |  |  |  |  |  | sizeLimit    => $arg->{sizelimit} || 0, | 
| 537 |  |  |  |  |  |  | timeLimit    => $arg->{timelimit} || 0, | 
| 538 |  |  |  |  |  |  | typesOnly    => $arg->{typesonly} || $arg->{attrsonly} || 0, | 
| 539 |  |  |  |  |  |  | filter       => $filter, | 
| 540 | 0 | 0 | 0 |  |  | 0 | attributes   => $arg->{attrs} || [] | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 541 |  |  |  |  |  |  | ); | 
| 542 |  |  |  |  |  |  |  | 
| 543 | 0 | 0 |  |  |  | 0 | if (exists $arg->{scope}) { | 
| 544 | 0 |  |  |  |  | 0 | my $sc = lc $arg->{scope}; | 
| 545 | 0 | 0 |  |  |  | 0 | $stash{scope} = 0 + (exists $scope{$sc} ? $scope{$sc} : $sc); | 
| 546 |  |  |  |  |  |  | } | 
| 547 |  |  |  |  |  |  |  | 
| 548 | 0 | 0 |  |  |  | 0 | if (exists $arg->{deref}) { | 
| 549 | 0 |  |  |  |  | 0 | my $dr = lc $arg->{deref}; | 
| 550 | 0 | 0 |  |  |  | 0 | $stash{derefAliases} = 0 + (exists $deref{$dr} ? $deref{$dr} : $dr); | 
| 551 |  |  |  |  |  |  | } | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | $mesg->encode( | 
| 554 | 0 | 0 |  |  |  | 0 | searchRequest => \%stash, | 
| 555 |  |  |  |  |  |  | controls      => $control | 
| 556 |  |  |  |  |  |  | ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); | 
| 557 |  |  |  |  |  |  |  | 
| 558 | 0 |  |  |  |  | 0 | $ldap->_sendmesg($mesg); | 
| 559 |  |  |  |  |  |  | } | 
| 560 |  |  |  |  |  |  |  | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | sub add { | 
| 563 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 564 | 0 |  |  |  |  | 0 | my $arg  = &_dn_options; | 
| 565 |  |  |  |  |  |  |  | 
| 566 | 0 |  |  |  |  | 0 | my $mesg = $ldap->message('Net::LDAP::Add' => $arg); | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | my $control = $arg->{control} | 
| 569 | 0 | 0 | 0 |  |  | 0 | and $ldap->{net_ldap_version} < 3 | 
| 570 |  |  |  |  |  |  | and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | my $entry = $arg->{dn} | 
| 573 | 0 | 0 |  |  |  | 0 | or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No DN specified'); | 
| 574 |  |  |  |  |  |  |  | 
| 575 | 0 | 0 |  |  |  | 0 | unless (ref $entry) { | 
| 576 | 0 |  |  |  |  | 0 | require Net::LDAP::Entry; | 
| 577 | 0 |  |  |  |  | 0 | $entry = Net::LDAP::Entry->new; | 
| 578 | 0 |  |  |  |  | 0 | $entry->dn($arg->{dn}); | 
| 579 | 0 | 0 | 0 |  |  | 0 | $entry->add(@{$arg->{attrs} || $arg->{attr} || []}); | 
|  | 0 |  |  |  |  | 0 |  | 
| 580 |  |  |  |  |  |  | } | 
| 581 |  |  |  |  |  |  |  | 
| 582 |  |  |  |  |  |  | $mesg->encode( | 
| 583 | 0 | 0 |  |  |  | 0 | addRequest => $entry->asn, | 
| 584 |  |  |  |  |  |  | controls   => $control | 
| 585 |  |  |  |  |  |  | ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); | 
| 586 |  |  |  |  |  |  |  | 
| 587 | 0 |  |  |  |  | 0 | $ldap->_sendmesg($mesg); | 
| 588 |  |  |  |  |  |  | } | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | my %opcode = ( add => 0, delete => 1, replace => 2, increment => 3 ); | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | sub modify { | 
| 594 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 595 | 0 |  |  |  |  | 0 | my $arg  = &_dn_options; | 
| 596 |  |  |  |  |  |  |  | 
| 597 | 0 |  |  |  |  | 0 | my $mesg = $ldap->message('Net::LDAP::Modify' => $arg); | 
| 598 |  |  |  |  |  |  |  | 
| 599 |  |  |  |  |  |  | my $control = $arg->{control} | 
| 600 | 0 | 0 | 0 |  |  | 0 | and $ldap->{net_ldap_version} < 3 | 
| 601 |  |  |  |  |  |  | and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); | 
| 602 |  |  |  |  |  |  |  | 
| 603 |  |  |  |  |  |  | my $dn = $arg->{dn} | 
| 604 | 0 | 0 |  |  |  | 0 | or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No DN specified'); | 
| 605 |  |  |  |  |  |  |  | 
| 606 | 0 |  |  |  |  | 0 | my @ops; | 
| 607 |  |  |  |  |  |  | my $opcode; | 
| 608 |  |  |  |  |  |  |  | 
| 609 | 0 | 0 |  |  |  | 0 | if (exists $arg->{changes}) { | 
| 610 | 0 |  |  |  |  | 0 | my $opcode; | 
| 611 | 0 |  |  |  |  | 0 | my $j = 0; | 
| 612 | 0 |  |  |  |  | 0 | while ($j < @{$arg->{changes}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 613 |  |  |  |  |  |  | return _error($ldap, $mesg, LDAP_PARAM_ERROR, "Bad change type '" . $arg->{changes}[--$j] . "'") | 
| 614 | 0 | 0 |  |  |  | 0 | unless defined($opcode = $opcode{$arg->{changes}[$j++]}); | 
| 615 |  |  |  |  |  |  |  | 
| 616 | 0 |  |  |  |  | 0 | my $chg = $arg->{changes}[$j++]; | 
| 617 | 0 | 0 |  |  |  | 0 | if (ref($chg)) { | 
| 618 | 0 |  |  |  |  | 0 | my $i = 0; | 
| 619 | 0 |  |  |  |  | 0 | while ($i < @$chg) { | 
| 620 | 0 | 0 |  |  |  | 0 | push @ops, { | 
| 621 |  |  |  |  |  |  | operation => $opcode, | 
| 622 |  |  |  |  |  |  | modification => { | 
| 623 |  |  |  |  |  |  | type => $chg->[$i], | 
| 624 |  |  |  |  |  |  | vals => ref($chg->[$i+1]) ? $chg->[$i+1] : [$chg->[$i+1]] | 
| 625 |  |  |  |  |  |  | } | 
| 626 |  |  |  |  |  |  | }; | 
| 627 | 0 |  |  |  |  | 0 | $i += 2; | 
| 628 |  |  |  |  |  |  | } | 
| 629 |  |  |  |  |  |  | } | 
| 630 |  |  |  |  |  |  | } | 
| 631 |  |  |  |  |  |  | } | 
| 632 |  |  |  |  |  |  | else { | 
| 633 | 0 |  |  |  |  | 0 | foreach my $op (qw(add delete replace increment)) { | 
| 634 | 0 | 0 |  |  |  | 0 | next  unless exists $arg->{$op}; | 
| 635 | 0 |  |  |  |  | 0 | my $opt = $arg->{$op}; | 
| 636 | 0 |  |  |  |  | 0 | my $opcode = $opcode{$op}; | 
| 637 |  |  |  |  |  |  |  | 
| 638 | 0 | 0 |  |  |  | 0 | if (ref($opt) eq 'HASH') { | 
|  |  | 0 |  |  |  |  |  | 
| 639 | 0 |  |  |  |  | 0 | while (my ($k, $v) = each %$opt) { | 
| 640 | 0 | 0 |  |  |  | 0 | push @ops, { | 
| 641 |  |  |  |  |  |  | operation => $opcode, | 
| 642 |  |  |  |  |  |  | modification => { | 
| 643 |  |  |  |  |  |  | type => $k, | 
| 644 |  |  |  |  |  |  | vals => ref($v) ? $v : [$v] | 
| 645 |  |  |  |  |  |  | } | 
| 646 |  |  |  |  |  |  | }; | 
| 647 |  |  |  |  |  |  | } | 
| 648 |  |  |  |  |  |  | } | 
| 649 |  |  |  |  |  |  | elsif (ref($opt) eq 'ARRAY') { | 
| 650 | 0 |  |  |  |  | 0 | my $k = 0; | 
| 651 |  |  |  |  |  |  |  | 
| 652 | 0 |  |  |  |  | 0 | while ($k < @{$opt}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 653 | 0 |  |  |  |  | 0 | my $attr = ${$opt}[$k++]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 654 | 0 | 0 |  |  |  | 0 | my $val = $opcode == 1 ? [] : ${$opt}[$k++]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 655 | 0 | 0 |  |  |  | 0 | push @ops, { | 
| 656 |  |  |  |  |  |  | operation => $opcode, | 
| 657 |  |  |  |  |  |  | modification => { | 
| 658 |  |  |  |  |  |  | type => $attr, | 
| 659 |  |  |  |  |  |  | vals => ref($val) ? $val : [$val] | 
| 660 |  |  |  |  |  |  | } | 
| 661 |  |  |  |  |  |  | }; | 
| 662 |  |  |  |  |  |  | } | 
| 663 |  |  |  |  |  |  | } | 
| 664 |  |  |  |  |  |  | else { | 
| 665 | 0 |  |  |  |  | 0 | push @ops, { | 
| 666 |  |  |  |  |  |  | operation => $opcode, | 
| 667 |  |  |  |  |  |  | modification => { | 
| 668 |  |  |  |  |  |  | type => $opt, | 
| 669 |  |  |  |  |  |  | vals => [] | 
| 670 |  |  |  |  |  |  | } | 
| 671 |  |  |  |  |  |  | }; | 
| 672 |  |  |  |  |  |  | } | 
| 673 |  |  |  |  |  |  | } | 
| 674 |  |  |  |  |  |  | } | 
| 675 |  |  |  |  |  |  |  | 
| 676 | 0 | 0 |  |  |  | 0 | $mesg->encode( | 
|  |  | 0 |  |  |  |  |  | 
| 677 |  |  |  |  |  |  | modifyRequest => { | 
| 678 |  |  |  |  |  |  | object       => ref($dn) ? $dn->dn : $dn, | 
| 679 |  |  |  |  |  |  | modification => \@ops | 
| 680 |  |  |  |  |  |  | }, | 
| 681 |  |  |  |  |  |  | controls => $control | 
| 682 |  |  |  |  |  |  | ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); | 
| 683 |  |  |  |  |  |  |  | 
| 684 | 0 |  |  |  |  | 0 | $ldap->_sendmesg($mesg); | 
| 685 |  |  |  |  |  |  | } | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | sub delete { | 
| 688 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 689 | 0 |  |  |  |  | 0 | my $arg  = &_dn_options; | 
| 690 |  |  |  |  |  |  |  | 
| 691 | 0 |  |  |  |  | 0 | my $mesg = $ldap->message('Net::LDAP::Delete' => $arg); | 
| 692 |  |  |  |  |  |  |  | 
| 693 |  |  |  |  |  |  | my $control = $arg->{control} | 
| 694 | 0 | 0 | 0 |  |  | 0 | and $ldap->{net_ldap_version} < 3 | 
| 695 |  |  |  |  |  |  | and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); | 
| 696 |  |  |  |  |  |  |  | 
| 697 |  |  |  |  |  |  | my $dn = $arg->{dn} | 
| 698 | 0 | 0 |  |  |  | 0 | or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No DN specified'); | 
| 699 |  |  |  |  |  |  |  | 
| 700 | 0 | 0 |  |  |  | 0 | $mesg->encode( | 
|  |  | 0 |  |  |  |  |  | 
| 701 |  |  |  |  |  |  | delRequest => ref($dn) ? $dn->dn : $dn, | 
| 702 |  |  |  |  |  |  | controls   => $control | 
| 703 |  |  |  |  |  |  | ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); | 
| 704 |  |  |  |  |  |  |  | 
| 705 | 0 |  |  |  |  | 0 | $ldap->_sendmesg($mesg); | 
| 706 |  |  |  |  |  |  | } | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | sub moddn { | 
| 709 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 710 | 0 |  |  |  |  | 0 | my $arg  = &_dn_options; | 
| 711 | 0 |  | 0 |  |  | 0 | my $del  = $arg->{deleteoldrdn} || $arg->{delete} || 0; | 
| 712 | 0 |  |  |  |  | 0 | my $newsup = $arg->{newsuperior}; | 
| 713 |  |  |  |  |  |  |  | 
| 714 | 0 |  |  |  |  | 0 | my $mesg = $ldap->message('Net::LDAP::ModDN' => $arg); | 
| 715 |  |  |  |  |  |  |  | 
| 716 |  |  |  |  |  |  | my $control = $arg->{control} | 
| 717 | 0 | 0 | 0 |  |  | 0 | and $ldap->{net_ldap_version} < 3 | 
| 718 |  |  |  |  |  |  | and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); | 
| 719 |  |  |  |  |  |  |  | 
| 720 |  |  |  |  |  |  | my $dn = $arg->{dn} | 
| 721 | 0 | 0 |  |  |  | 0 | or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No DN specified'); | 
| 722 |  |  |  |  |  |  |  | 
| 723 |  |  |  |  |  |  | my $new  = $arg->{newrdn} || $arg->{new} | 
| 724 | 0 | 0 | 0 |  |  | 0 | or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No NewRDN specified'); | 
| 725 |  |  |  |  |  |  |  | 
| 726 | 0 | 0 |  |  |  | 0 | $mesg->encode( | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 727 |  |  |  |  |  |  | modDNRequest => { | 
| 728 |  |  |  |  |  |  | entry        => ref($dn) ? $dn->dn : $dn, | 
| 729 |  |  |  |  |  |  | newrdn       => ref($new) ? $new->dn : $new, | 
| 730 |  |  |  |  |  |  | deleteoldrdn => $del, | 
| 731 |  |  |  |  |  |  | newSuperior  => ref($newsup) ? $newsup->dn : $newsup, | 
| 732 |  |  |  |  |  |  | }, | 
| 733 |  |  |  |  |  |  | controls => $control | 
| 734 |  |  |  |  |  |  | ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); | 
| 735 |  |  |  |  |  |  |  | 
| 736 | 0 |  |  |  |  | 0 | $ldap->_sendmesg($mesg); | 
| 737 |  |  |  |  |  |  | } | 
| 738 |  |  |  |  |  |  |  | 
| 739 |  |  |  |  |  |  | # now maps to the V3/X.500(93) modifydn map | 
| 740 | 0 |  |  | 0 | 0 | 0 | sub modrdn { goto &moddn } | 
| 741 |  |  |  |  |  |  |  | 
| 742 |  |  |  |  |  |  | sub compare { | 
| 743 | 0 |  |  | 0 | 1 | 0 | my $ldap  = shift; | 
| 744 | 0 |  |  |  |  | 0 | my $arg   = &_dn_options; | 
| 745 |  |  |  |  |  |  |  | 
| 746 | 0 |  |  |  |  | 0 | my $mesg = $ldap->message('Net::LDAP::Compare' => $arg); | 
| 747 |  |  |  |  |  |  |  | 
| 748 |  |  |  |  |  |  | my $control = $arg->{control} | 
| 749 | 0 | 0 | 0 |  |  | 0 | and $ldap->{net_ldap_version} < 3 | 
| 750 |  |  |  |  |  |  | and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); | 
| 751 |  |  |  |  |  |  |  | 
| 752 |  |  |  |  |  |  | my $dn = $arg->{dn} | 
| 753 | 0 | 0 |  |  |  | 0 | or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No DN specified'); | 
| 754 |  |  |  |  |  |  |  | 
| 755 |  |  |  |  |  |  | my $attr = exists $arg->{attr} | 
| 756 |  |  |  |  |  |  | ? $arg->{attr} | 
| 757 |  |  |  |  |  |  | : exists $arg->{attrs} #compat | 
| 758 | 0 | 0 |  |  |  | 0 | ? $arg->{attrs}[0] | 
|  |  | 0 |  |  |  |  |  | 
| 759 |  |  |  |  |  |  | : ''; | 
| 760 |  |  |  |  |  |  |  | 
| 761 |  |  |  |  |  |  | my $value = exists $arg->{value} | 
| 762 |  |  |  |  |  |  | ? $arg->{value} | 
| 763 |  |  |  |  |  |  | : exists $arg->{attrs} #compat | 
| 764 | 0 | 0 |  |  |  | 0 | ? $arg->{attrs}[1] | 
|  |  | 0 |  |  |  |  |  | 
| 765 |  |  |  |  |  |  | : ''; | 
| 766 |  |  |  |  |  |  |  | 
| 767 |  |  |  |  |  |  |  | 
| 768 | 0 | 0 |  |  |  | 0 | $mesg->encode( | 
|  |  | 0 |  |  |  |  |  | 
| 769 |  |  |  |  |  |  | compareRequest => { | 
| 770 |  |  |  |  |  |  | entry => ref($dn) ? $dn->dn : $dn, | 
| 771 |  |  |  |  |  |  | ava   => { | 
| 772 |  |  |  |  |  |  | attributeDesc  => $attr, | 
| 773 |  |  |  |  |  |  | assertionValue => $value | 
| 774 |  |  |  |  |  |  | } | 
| 775 |  |  |  |  |  |  | }, | 
| 776 |  |  |  |  |  |  | controls => $control | 
| 777 |  |  |  |  |  |  | ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); | 
| 778 |  |  |  |  |  |  |  | 
| 779 | 0 |  |  |  |  | 0 | $ldap->_sendmesg($mesg); | 
| 780 |  |  |  |  |  |  | } | 
| 781 |  |  |  |  |  |  |  | 
| 782 |  |  |  |  |  |  | sub abandon { | 
| 783 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 784 | 0 | 0 |  |  |  | 0 | unshift @_, 'id'  if @_ & 1; | 
| 785 | 0 |  |  |  |  | 0 | my $arg = &_options; | 
| 786 |  |  |  |  |  |  |  | 
| 787 | 0 |  |  |  |  | 0 | my $id = $arg->{id}; | 
| 788 |  |  |  |  |  |  |  | 
| 789 | 0 |  |  |  |  | 0 | my $mesg = $ldap->message('Net::LDAP::Abandon' => $arg); | 
| 790 |  |  |  |  |  |  |  | 
| 791 |  |  |  |  |  |  | my $control = $arg->{control} | 
| 792 | 0 | 0 | 0 |  |  | 0 | and $ldap->{net_ldap_version} < 3 | 
| 793 |  |  |  |  |  |  | and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); | 
| 794 |  |  |  |  |  |  |  | 
| 795 | 0 | 0 |  |  |  | 0 | $mesg->encode( | 
|  |  | 0 |  |  |  |  |  | 
| 796 |  |  |  |  |  |  | abandonRequest => ref($id) ? $id->mesg_id : $id, | 
| 797 |  |  |  |  |  |  | controls       => $control | 
| 798 |  |  |  |  |  |  | ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); | 
| 799 |  |  |  |  |  |  |  | 
| 800 | 0 |  |  |  |  | 0 | $ldap->_sendmesg($mesg); | 
| 801 |  |  |  |  |  |  | } | 
| 802 |  |  |  |  |  |  |  | 
| 803 |  |  |  |  |  |  | sub extension { | 
| 804 | 0 |  |  | 0 | 0 | 0 | my $ldap = shift; | 
| 805 | 0 |  |  |  |  | 0 | my $arg  = &_options; | 
| 806 |  |  |  |  |  |  |  | 
| 807 | 0 |  |  |  |  | 0 | require Net::LDAP::Extension; | 
| 808 | 0 |  |  |  |  | 0 | my $mesg = $ldap->message('Net::LDAP::Extension' => $arg); | 
| 809 |  |  |  |  |  |  |  | 
| 810 |  |  |  |  |  |  | return _error($ldap, $mesg, LDAP_LOCAL_ERROR, 'ExtendedRequest requires LDAPv3') | 
| 811 | 0 | 0 |  |  |  | 0 | if $ldap->{net_ldap_version} < 3; | 
| 812 |  |  |  |  |  |  |  | 
| 813 |  |  |  |  |  |  | $mesg->encode( | 
| 814 |  |  |  |  |  |  | extendedReq => { | 
| 815 |  |  |  |  |  |  | requestName  => $arg->{name}, | 
| 816 |  |  |  |  |  |  | requestValue => $arg->{value} | 
| 817 |  |  |  |  |  |  | }, | 
| 818 |  |  |  |  |  |  | controls => $arg->{control} | 
| 819 | 0 | 0 |  |  |  | 0 | ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); | 
| 820 |  |  |  |  |  |  |  | 
| 821 | 0 |  |  |  |  | 0 | $ldap->_sendmesg($mesg); | 
| 822 |  |  |  |  |  |  | } | 
| 823 |  |  |  |  |  |  |  | 
| 824 |  |  |  |  |  |  | sub sync { | 
| 825 | 0 |  |  | 0 | 1 | 0 | my $ldap  = shift; | 
| 826 | 0 |  |  |  |  | 0 | my $mid   = shift; | 
| 827 | 0 |  |  |  |  | 0 | my $table = $ldap->{net_ldap_mesg}; | 
| 828 | 0 |  |  |  |  | 0 | my $err   = LDAP_SUCCESS; | 
| 829 |  |  |  |  |  |  |  | 
| 830 | 0 | 0 |  |  |  | 0 | return $err  unless defined $table; | 
| 831 |  |  |  |  |  |  |  | 
| 832 | 0 | 0 |  |  |  | 0 | $mid = $mid->mesg_id  if ref($mid); | 
| 833 | 0 | 0 |  |  |  | 0 | while (defined($mid) ? exists $table->{$mid} : %$table) { | 
| 834 | 0 | 0 |  |  |  | 0 | last  if $err = $ldap->process($mid); | 
| 835 |  |  |  |  |  |  | } | 
| 836 |  |  |  |  |  |  |  | 
| 837 | 0 |  |  |  |  | 0 | $err; | 
| 838 |  |  |  |  |  |  | } | 
| 839 |  |  |  |  |  |  |  | 
| 840 |  |  |  |  |  |  | sub disconnect { | 
| 841 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 842 | 0 |  |  |  |  | 0 | _drop_conn($self, LDAP_USER_CANCELED, 'Explicit disconnect'); | 
| 843 |  |  |  |  |  |  | } | 
| 844 |  |  |  |  |  |  |  | 
| 845 |  |  |  |  |  |  | sub _sendmesg { | 
| 846 | 2 |  |  | 2 |  | 3 | my $ldap = shift; | 
| 847 | 2 |  |  |  |  | 4 | my $mesg = shift; | 
| 848 |  |  |  |  |  |  |  | 
| 849 | 2 |  |  |  |  | 3 | my $debug; | 
| 850 | 2 | 50 |  |  |  | 9 | if ($debug = $ldap->debug) { | 
| 851 | 0 |  |  |  |  | 0 | require Convert::ASN1::Debug; | 
| 852 | 0 |  |  |  |  | 0 | print STDERR "$ldap sending:\n"; | 
| 853 |  |  |  |  |  |  |  | 
| 854 | 0 | 0 |  |  |  | 0 | Convert::ASN1::asn_hexdump(*STDERR, $mesg->pdu) | 
| 855 |  |  |  |  |  |  | if $debug & 1; | 
| 856 |  |  |  |  |  |  |  | 
| 857 | 0 | 0 |  |  |  | 0 | Convert::ASN1::asn_dump(*STDERR, $mesg->pdu) | 
| 858 |  |  |  |  |  |  | if $debug & 4; | 
| 859 |  |  |  |  |  |  | } | 
| 860 |  |  |  |  |  |  |  | 
| 861 | 2 | 50 |  |  |  | 18 | my $socket = $ldap->socket | 
| 862 |  |  |  |  |  |  | or return _error($ldap, $mesg, LDAP_SERVER_DOWN, "$!"); | 
| 863 |  |  |  |  |  |  |  | 
| 864 |  |  |  |  |  |  | # send packets in sizes that IO::Socket::SSL can chew | 
| 865 |  |  |  |  |  |  | # originally it was: | 
| 866 |  |  |  |  |  |  | #syswrite($socket, $mesg->pdu, length($mesg->pdu)) | 
| 867 |  |  |  |  |  |  | #  or return _error($ldap, $mesg, LDAP_LOCAL_ERROR, "$!") | 
| 868 | 2 |  |  |  |  | 29 | my $to_send = \( $mesg->pdu ); | 
| 869 | 2 |  |  |  |  | 3 | my $offset = 0; | 
| 870 | 2 |  |  |  |  | 7 | while ($offset < length($$to_send)) { | 
| 871 | 2 |  |  |  |  | 5 | my $s = substr($$to_send, $offset, 15000); | 
| 872 | 2 | 50 |  |  |  | 22 | my $n = syswrite($socket, $s, length($s)) | 
| 873 |  |  |  |  |  |  | or return _error($ldap, $mesg, LDAP_LOCAL_ERROR, "$!"); | 
| 874 | 2 |  |  |  |  | 9 | $offset += $n; | 
| 875 |  |  |  |  |  |  | } | 
| 876 |  |  |  |  |  |  |  | 
| 877 |  |  |  |  |  |  | # for CLDAP, here we need to recode when we were sent | 
| 878 |  |  |  |  |  |  | # so that we can perform timeouts and resends | 
| 879 |  |  |  |  |  |  |  | 
| 880 | 2 |  |  |  |  | 11 | my $mid  = $mesg->mesg_id; | 
| 881 | 2 |  |  |  |  | 8 | my $sync = not $ldap->async; | 
| 882 |  |  |  |  |  |  |  | 
| 883 | 2 | 50 |  |  |  | 23 | unless ($mesg->done) { # may not have a response | 
| 884 |  |  |  |  |  |  |  | 
| 885 | 2 |  |  |  |  | 7 | $ldap->{net_ldap_mesg}->{$mid} = $mesg; | 
| 886 |  |  |  |  |  |  |  | 
| 887 | 2 | 50 |  |  |  | 74 | if ($sync) { | 
| 888 | 0 |  |  |  |  | 0 | my $err = $ldap->sync($mid); | 
| 889 | 0 | 0 |  |  |  | 0 | return _error($ldap, $mesg, $err, $@)  if $err; | 
| 890 |  |  |  |  |  |  | } | 
| 891 |  |  |  |  |  |  | } | 
| 892 |  |  |  |  |  |  |  | 
| 893 |  |  |  |  |  |  | $sync && $ldap->{net_ldap_onerror} && $mesg->is_error | 
| 894 | 2 | 50 | 0 |  |  | 18 | ? scalar &{$ldap->{net_ldap_onerror}}($mesg) | 
|  | 0 |  |  |  |  | 0 |  | 
| 895 |  |  |  |  |  |  | : $mesg; | 
| 896 |  |  |  |  |  |  | } | 
| 897 |  |  |  |  |  |  |  | 
| 898 |  |  |  |  |  |  | sub data_ready { | 
| 899 | 0 |  |  | 0 | 0 | 0 | my $ldap = shift; | 
| 900 | 0 | 0 |  |  |  | 0 | my $sock = $ldap->socket  or return; | 
| 901 | 0 |  |  |  |  | 0 | my $sel = IO::Select->new($sock); | 
| 902 |  |  |  |  |  |  |  | 
| 903 | 0 |  | 0 |  |  | 0 | return defined $sel->can_read(0) || (ref($sock) eq 'IO::Socket::SSL' && $sock->pending()); | 
| 904 |  |  |  |  |  |  | } | 
| 905 |  |  |  |  |  |  |  | 
| 906 |  |  |  |  |  |  | sub process { | 
| 907 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 908 | 0 |  |  |  |  | 0 | my $what = shift; | 
| 909 | 0 | 0 |  |  |  | 0 | my $sock = $ldap->socket  or return LDAP_SERVER_DOWN; | 
| 910 |  |  |  |  |  |  |  | 
| 911 | 0 |  |  |  |  | 0 | for (my $ready = 1; $ready; $ready = $ldap->data_ready) { | 
| 912 | 0 |  |  |  |  | 0 | my $pdu; | 
| 913 | 0 | 0 |  |  |  | 0 | asn_read($sock, $pdu) | 
| 914 |  |  |  |  |  |  | or return _drop_conn($ldap, LDAP_OPERATIONS_ERROR, 'Communications Error'); | 
| 915 |  |  |  |  |  |  |  | 
| 916 | 0 |  |  |  |  | 0 | my $debug; | 
| 917 | 0 | 0 |  |  |  | 0 | if ($debug = $ldap->debug) { | 
| 918 | 0 |  |  |  |  | 0 | require Convert::ASN1::Debug; | 
| 919 | 0 |  |  |  |  | 0 | print STDERR "$ldap received:\n"; | 
| 920 |  |  |  |  |  |  |  | 
| 921 | 0 | 0 |  |  |  | 0 | Convert::ASN1::asn_hexdump(\*STDERR, $pdu) | 
| 922 |  |  |  |  |  |  | if $debug & 2; | 
| 923 |  |  |  |  |  |  |  | 
| 924 | 0 | 0 |  |  |  | 0 | Convert::ASN1::asn_dump(\*STDERR, $pdu) | 
| 925 |  |  |  |  |  |  | if $debug & 8; | 
| 926 |  |  |  |  |  |  | } | 
| 927 |  |  |  |  |  |  |  | 
| 928 | 0 | 0 |  |  |  | 0 | my $result = $LDAPResponse->decode($pdu) | 
| 929 |  |  |  |  |  |  | or return LDAP_DECODING_ERROR; | 
| 930 |  |  |  |  |  |  |  | 
| 931 | 0 |  |  |  |  | 0 | my $mid  = $result->{messageID}; | 
| 932 | 0 |  |  |  |  | 0 | my $mesg = $ldap->{net_ldap_mesg}->{$mid}; | 
| 933 |  |  |  |  |  |  |  | 
| 934 | 0 | 0 |  |  |  | 0 | unless ($mesg) { | 
| 935 | 0 | 0 |  |  |  | 0 | if (my $ext = $result->{protocolOp}{extendedResp}) { | 
| 936 | 0 | 0 | 0 |  |  | 0 | if (($ext->{responseName} || '') eq '1.3.6.1.4.1.1466.20036') { | 
| 937 |  |  |  |  |  |  | # notice of disconnection | 
| 938 | 0 |  |  |  |  | 0 | return _drop_conn($ldap, LDAP_SERVER_DOWN, 'Notice of Disconnection'); | 
| 939 |  |  |  |  |  |  | } | 
| 940 |  |  |  |  |  |  | } | 
| 941 |  |  |  |  |  |  |  | 
| 942 | 0 | 0 |  |  |  | 0 | print STDERR "Unexpected PDU, ignored\n"  if $debug & 10; | 
| 943 | 0 |  |  |  |  | 0 | next; | 
| 944 |  |  |  |  |  |  | } | 
| 945 |  |  |  |  |  |  |  | 
| 946 | 0 | 0 |  |  |  | 0 | $mesg->decode($result) | 
| 947 |  |  |  |  |  |  | or return $mesg->code; | 
| 948 |  |  |  |  |  |  |  | 
| 949 | 0 | 0 | 0 |  |  | 0 | last  if defined $what && $what == $mid; | 
| 950 |  |  |  |  |  |  | } | 
| 951 |  |  |  |  |  |  |  | 
| 952 |  |  |  |  |  |  | # FIXME: in CLDAP here we need to check if any message has timed out | 
| 953 |  |  |  |  |  |  | # and if so do we resend it or what | 
| 954 |  |  |  |  |  |  |  | 
| 955 | 0 |  |  |  |  | 0 | return LDAP_SUCCESS; | 
| 956 |  |  |  |  |  |  | } | 
| 957 |  |  |  |  |  |  |  | 
| 958 |  |  |  |  |  |  | *_recvresp = \&process; # compat | 
| 959 |  |  |  |  |  |  |  | 
| 960 |  |  |  |  |  |  | sub _drop_conn { | 
| 961 | 2 |  |  | 2 |  | 6 | my ($self, $err, $etxt) = @_; | 
| 962 |  |  |  |  |  |  |  | 
| 963 | 2 |  |  |  |  | 3 | delete $self->{net_ldap_rawsocket}; | 
| 964 | 2 |  |  |  |  | 4 | my $sock = delete $self->{net_ldap_socket}; | 
| 965 | 2 | 50 |  |  |  | 34 | close($sock)  if $sock; | 
| 966 |  |  |  |  |  |  |  | 
| 967 | 2 | 50 |  |  |  | 10 | if (my $msgs = delete $self->{net_ldap_mesg}) { | 
| 968 | 2 |  |  |  |  | 7 | foreach my $mesg (values %$msgs) { | 
| 969 | 2 | 50 |  |  |  | 13 | next  unless (defined $mesg); | 
| 970 | 2 |  |  |  |  | 30 | $mesg->set_error($err, $etxt); | 
| 971 |  |  |  |  |  |  | } | 
| 972 |  |  |  |  |  |  | } | 
| 973 |  |  |  |  |  |  |  | 
| 974 | 2 |  |  |  |  | 40 | $err; | 
| 975 |  |  |  |  |  |  | } | 
| 976 |  |  |  |  |  |  |  | 
| 977 |  |  |  |  |  |  |  | 
| 978 |  |  |  |  |  |  | sub _forgetmesg { | 
| 979 | 0 |  |  | 0 |  | 0 | my $ldap = shift; | 
| 980 | 0 |  |  |  |  | 0 | my $mesg = shift; | 
| 981 |  |  |  |  |  |  |  | 
| 982 | 0 |  |  |  |  | 0 | my $mid = $mesg->mesg_id; | 
| 983 |  |  |  |  |  |  |  | 
| 984 | 0 |  |  |  |  | 0 | delete $ldap->{net_ldap_mesg}->{$mid}; | 
| 985 |  |  |  |  |  |  | } | 
| 986 |  |  |  |  |  |  |  | 
| 987 |  |  |  |  |  |  | #Mark Wilcox 3-20-2000 | 
| 988 |  |  |  |  |  |  | #now accepts named parameters | 
| 989 |  |  |  |  |  |  | #dn => "dn of subschema entry" | 
| 990 |  |  |  |  |  |  | # | 
| 991 |  |  |  |  |  |  | # | 
| 992 |  |  |  |  |  |  | # Clif Harden 2-4-2001. | 
| 993 |  |  |  |  |  |  | # corrected filter for subschema search. | 
| 994 |  |  |  |  |  |  | # added attributes to retrieve on subschema search. | 
| 995 |  |  |  |  |  |  | # added attributes to retrieve on rootDSE search. | 
| 996 |  |  |  |  |  |  | # changed several double quote character to single quote | 
| 997 |  |  |  |  |  |  | # character, just to be consistent throughout the schema | 
| 998 |  |  |  |  |  |  | # and root_dse functions. | 
| 999 |  |  |  |  |  |  | # | 
| 1000 |  |  |  |  |  |  |  | 
| 1001 |  |  |  |  |  |  | sub schema { | 
| 1002 | 0 |  |  | 0 | 1 | 0 | require Net::LDAP::Schema; | 
| 1003 | 0 |  |  |  |  | 0 | my $self = shift; | 
| 1004 | 0 |  |  |  |  | 0 | my %arg = @_; | 
| 1005 | 0 |  |  |  |  | 0 | my $base; | 
| 1006 |  |  |  |  |  |  | my $mesg; | 
| 1007 |  |  |  |  |  |  |  | 
| 1008 | 0 | 0 |  |  |  | 0 | if (exists $arg{dn}) { | 
| 1009 | 0 |  |  |  |  | 0 | $base = $arg{dn}; | 
| 1010 |  |  |  |  |  |  | } | 
| 1011 |  |  |  |  |  |  | else { | 
| 1012 | 0 | 0 |  |  |  | 0 | my $root = $self->root_dse( attrs => ['subschemaSubentry'] ) | 
| 1013 |  |  |  |  |  |  | or return undef; | 
| 1014 |  |  |  |  |  |  |  | 
| 1015 | 0 |  | 0 |  |  | 0 | $base = $root->get_value('subschemaSubentry') || 'cn=schema'; | 
| 1016 |  |  |  |  |  |  | } | 
| 1017 |  |  |  |  |  |  |  | 
| 1018 | 0 |  |  |  |  | 0 | $mesg = $self->search( | 
| 1019 |  |  |  |  |  |  | base   => $base, | 
| 1020 |  |  |  |  |  |  | scope  => 'base', | 
| 1021 |  |  |  |  |  |  | filter => '(objectClass=subschema)', | 
| 1022 |  |  |  |  |  |  | attrs  => [qw( | 
| 1023 |  |  |  |  |  |  | objectClasses | 
| 1024 |  |  |  |  |  |  | attributeTypes | 
| 1025 |  |  |  |  |  |  | matchingRules | 
| 1026 |  |  |  |  |  |  | matchingRuleUse | 
| 1027 |  |  |  |  |  |  | dITStructureRules | 
| 1028 |  |  |  |  |  |  | dITContentRules | 
| 1029 |  |  |  |  |  |  | nameForms | 
| 1030 |  |  |  |  |  |  | ldapSyntaxes | 
| 1031 |  |  |  |  |  |  | extendedAttributeInfo | 
| 1032 |  |  |  |  |  |  | )], | 
| 1033 |  |  |  |  |  |  | ); | 
| 1034 |  |  |  |  |  |  |  | 
| 1035 | 0 | 0 |  |  |  | 0 | $mesg->code | 
| 1036 |  |  |  |  |  |  | ? undef | 
| 1037 |  |  |  |  |  |  | : Net::LDAP::Schema->new($mesg->entry); | 
| 1038 |  |  |  |  |  |  | } | 
| 1039 |  |  |  |  |  |  |  | 
| 1040 |  |  |  |  |  |  |  | 
| 1041 |  |  |  |  |  |  | sub root_dse { | 
| 1042 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 1043 | 0 |  |  |  |  | 0 | my %arg  = @_; | 
| 1044 | 0 |  | 0 |  |  | 0 | my $attrs = $arg{attrs} || [qw( | 
| 1045 |  |  |  |  |  |  | subschemaSubentry | 
| 1046 |  |  |  |  |  |  | namingContexts | 
| 1047 |  |  |  |  |  |  | altServer | 
| 1048 |  |  |  |  |  |  | supportedExtension | 
| 1049 |  |  |  |  |  |  | supportedControl | 
| 1050 |  |  |  |  |  |  | supportedFeatures | 
| 1051 |  |  |  |  |  |  | supportedSASLMechanisms | 
| 1052 |  |  |  |  |  |  | supportedLDAPVersion | 
| 1053 |  |  |  |  |  |  | vendorName | 
| 1054 |  |  |  |  |  |  | vendorVersion | 
| 1055 |  |  |  |  |  |  | )]; | 
| 1056 | 0 |  | 0 |  |  | 0 | my $root = $arg{attrs} && $ldap->{net_ldap_root_dse}; | 
| 1057 |  |  |  |  |  |  |  | 
| 1058 | 0 | 0 |  |  |  | 0 | return $root  if $root; | 
| 1059 |  |  |  |  |  |  |  | 
| 1060 | 0 |  |  |  |  | 0 | my $mesg = $ldap->search( | 
| 1061 |  |  |  |  |  |  | base   => '', | 
| 1062 |  |  |  |  |  |  | scope  => 'base', | 
| 1063 |  |  |  |  |  |  | filter => '(objectClass=*)', | 
| 1064 |  |  |  |  |  |  | attrs  => $attrs, | 
| 1065 |  |  |  |  |  |  | ); | 
| 1066 |  |  |  |  |  |  |  | 
| 1067 | 0 |  |  |  |  | 0 | require Net::LDAP::RootDSE; | 
| 1068 | 0 |  |  |  |  | 0 | $root = $mesg->entry; | 
| 1069 | 0 | 0 |  |  |  | 0 | bless $root, 'Net::LDAP::RootDSE'  if $root; # Naughty, but there you go :-) | 
| 1070 |  |  |  |  |  |  |  | 
| 1071 | 0 | 0 |  |  |  | 0 | $ldap->{net_ldap_root_dse} = $root  unless $arg{attrs}; | 
| 1072 |  |  |  |  |  |  |  | 
| 1073 | 0 |  |  |  |  | 0 | return $root; | 
| 1074 |  |  |  |  |  |  | } | 
| 1075 |  |  |  |  |  |  |  | 
| 1076 |  |  |  |  |  |  | sub start_tls { | 
| 1077 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 1078 | 0 |  |  |  |  | 0 | my $arg  = &_options; | 
| 1079 | 0 |  |  |  |  | 0 | my $sock = $ldap->socket; | 
| 1080 |  |  |  |  |  |  |  | 
| 1081 | 0 |  |  |  |  | 0 | require IO::Socket::SSL; | 
| 1082 | 0 |  |  |  |  | 0 | require Net::LDAP::Extension; | 
| 1083 | 0 |  |  |  |  | 0 | my $mesg = $ldap->message('Net::LDAP::Extension' => $arg); | 
| 1084 |  |  |  |  |  |  |  | 
| 1085 | 0 | 0 |  |  |  | 0 | return _error($ldap, $mesg, LDAP_OPERATIONS_ERROR, 'TLS already started') | 
| 1086 |  |  |  |  |  |  | if $sock->isa('IO::Socket::SSL'); | 
| 1087 |  |  |  |  |  |  |  | 
| 1088 | 0 | 0 |  |  |  | 0 | return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'StartTLS requires LDAPv3') | 
| 1089 |  |  |  |  |  |  | if $ldap->version < 3; | 
| 1090 |  |  |  |  |  |  |  | 
| 1091 | 0 |  |  |  |  | 0 | $mesg->encode( | 
| 1092 |  |  |  |  |  |  | extendedReq => { | 
| 1093 |  |  |  |  |  |  | requestName => LDAP_EXTENSION_START_TLS, | 
| 1094 |  |  |  |  |  |  | } | 
| 1095 |  |  |  |  |  |  | ); | 
| 1096 |  |  |  |  |  |  |  | 
| 1097 | 0 |  |  |  |  | 0 | $ldap->_sendmesg($mesg); | 
| 1098 | 0 |  |  |  |  | 0 | $mesg->sync(); | 
| 1099 |  |  |  |  |  |  |  | 
| 1100 | 0 | 0 |  |  |  | 0 | return $mesg | 
| 1101 |  |  |  |  |  |  | if $mesg->code; | 
| 1102 |  |  |  |  |  |  |  | 
| 1103 | 0 |  |  |  |  | 0 | delete $ldap->{net_ldap_root_dse}; | 
| 1104 |  |  |  |  |  |  |  | 
| 1105 | 0 | 0 |  |  |  | 0 | $arg->{sslserver} = $ldap->{net_ldap_host}  unless defined $arg->{sslserver}; | 
| 1106 |  |  |  |  |  |  |  | 
| 1107 | 0 |  |  |  |  | 0 | my $sock_class = ref($sock); | 
| 1108 |  |  |  |  |  |  |  | 
| 1109 | 0 | 0 |  |  |  | 0 | return $mesg | 
| 1110 |  |  |  |  |  |  | if IO::Socket::SSL->start_SSL($sock, {_SSL_context_init_args($arg)}); | 
| 1111 |  |  |  |  |  |  |  | 
| 1112 | 0 |  | 0 |  |  | 0 | my $err = $@ || $IO::Socket::SSL::SSL_ERROR || $IO::Socket::SSL::SSL_ERROR || ''; # avoid use on once warning | 
| 1113 |  |  |  |  |  |  |  | 
| 1114 | 0 | 0 |  |  |  | 0 | if ($sock_class ne ref($sock)) { | 
| 1115 | 0 |  |  |  |  | 0 | $err = $sock->errstr; | 
| 1116 | 0 |  |  |  |  | 0 | bless $sock, $sock_class; | 
| 1117 |  |  |  |  |  |  | } | 
| 1118 |  |  |  |  |  |  |  | 
| 1119 | 0 |  |  |  |  | 0 | _error($ldap, $mesg, LDAP_OPERATIONS_ERROR, $err); | 
| 1120 |  |  |  |  |  |  | } | 
| 1121 |  |  |  |  |  |  |  | 
| 1122 |  |  |  |  |  |  | sub cipher { | 
| 1123 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 1124 | 0 | 0 |  |  |  | 0 | $ldap->socket->isa('IO::Socket::SSL') | 
| 1125 |  |  |  |  |  |  | ? $ldap->socket->get_cipher | 
| 1126 |  |  |  |  |  |  | : undef; | 
| 1127 |  |  |  |  |  |  | } | 
| 1128 |  |  |  |  |  |  |  | 
| 1129 |  |  |  |  |  |  | sub certificate { | 
| 1130 | 0 |  |  | 0 | 1 | 0 | my $ldap = shift; | 
| 1131 | 0 | 0 |  |  |  | 0 | $ldap->socket->isa('IO::Socket::SSL') | 
| 1132 |  |  |  |  |  |  | ? $ldap->socket->get_peer_certificate | 
| 1133 |  |  |  |  |  |  | : undef; | 
| 1134 |  |  |  |  |  |  | } | 
| 1135 |  |  |  |  |  |  |  | 
| 1136 |  |  |  |  |  |  | # what version are we talking? | 
| 1137 |  |  |  |  |  |  | sub version { | 
| 1138 | 2 |  |  | 2 | 1 | 5 | my $ldap = shift; | 
| 1139 |  |  |  |  |  |  |  | 
| 1140 |  |  |  |  |  |  | @_ | 
| 1141 |  |  |  |  |  |  | ? ($ldap->{net_ldap_version}, $ldap->{net_ldap_version} = shift)[0] | 
| 1142 | 2 | 50 |  |  |  | 16 | : $ldap->{net_ldap_version}; | 
| 1143 |  |  |  |  |  |  | } | 
| 1144 |  |  |  |  |  |  |  | 
| 1145 |  |  |  |  |  |  | sub outer { | 
| 1146 | 3 |  |  | 3 | 0 | 5 | my $self = shift; | 
| 1147 | 3 | 50 |  |  |  | 7 | return $self  if tied(%$self); | 
| 1148 | 3 |  |  |  |  | 12 | my %outer; | 
| 1149 | 3 |  |  |  |  | 18 | tie %outer, ref($self), $self; | 
| 1150 | 3 |  |  |  |  | 7 | ++$self->{net_ldap_refcnt}; | 
| 1151 | 3 |  |  |  |  | 14 | bless \%outer, ref($self); | 
| 1152 |  |  |  |  |  |  | } | 
| 1153 |  |  |  |  |  |  |  | 
| 1154 |  |  |  |  |  |  | sub inner { | 
| 1155 | 3 | 50 |  | 3 | 0 | 11 | tied(%{$_[0]}) || $_[0]; | 
|  | 3 |  |  |  |  | 15 |  | 
| 1156 |  |  |  |  |  |  | } | 
| 1157 |  |  |  |  |  |  |  | 
| 1158 |  |  |  |  |  |  | sub TIEHASH { | 
| 1159 | 3 |  |  | 3 |  | 7 | $_[1]; | 
| 1160 |  |  |  |  |  |  | } | 
| 1161 |  |  |  |  |  |  |  | 
| 1162 |  |  |  |  |  |  | sub DESTROY { | 
| 1163 | 5 |  |  | 5 |  | 889 | my $ldap = shift; | 
| 1164 | 5 | 100 |  |  |  | 16 | my $inner = tied(%$ldap)  or return; | 
| 1165 |  |  |  |  |  |  | _drop_conn($inner, LDAP_UNAVAILABLE, 'Implicit disconnect') | 
| 1166 | 3 | 100 |  |  |  | 14 | unless --$inner->{net_ldap_refcnt}; | 
| 1167 |  |  |  |  |  |  | } | 
| 1168 |  |  |  |  |  |  |  | 
| 1169 |  |  |  |  |  |  | 1; | 
| 1170 |  |  |  |  |  |  |  |