Server IP : 103.119.228.120 / Your IP : 52.14.6.41 Web Server : Apache System : Linux v8.techscape8.com 3.10.0-1160.119.1.el7.tuxcare.els2.x86_64 #1 SMP Mon Jul 15 12:09:18 UTC 2024 x86_64 User : nobody ( 99) PHP Version : 5.6.40 Disable Function : shell_exec,symlink,system,exec,proc_get_status,proc_nice,proc_terminate,define_syslog_variables,syslog,openlog,closelog,escapeshellcmd,passthru,ocinum cols,ini_alter,leak,listen,chgrp,apache_note,apache_setenv,debugger_on,debugger_off,ftp_exec,dl,dll,myshellexec,proc_open,socket_bind,proc_close,escapeshellarg,parse_ini_filepopen,fpassthru,exec,passthru,escapeshellarg,escapeshellcmd,proc_close,proc_open,ini_alter,popen,show_source,proc_nice,proc_terminate,proc_get_status,proc_close,pfsockopen,leak,apache_child_terminate,posix_kill,posix_mkfifo,posix_setpgid,posix_setsid,posix_setuid,dl,symlink,shell_exec,system,dl,passthru,escapeshellarg,escapeshellcmd,myshellexec,c99_buff_prepare,c99_sess_put,fpassthru,getdisfunc,fx29exec,fx29exec2,is_windows,disp_freespace,fx29sh_getupdate,fx29_buff_prepare,fx29_sess_put,fx29shexit,fx29fsearch,fx29ftpbrutecheck,fx29sh_tools,fx29sh_about,milw0rm,imagez,sh_name,myshellexec,checkproxyhost,dosyayicek,c99_buff_prepare,c99_sess_put,c99getsource,c99sh_getupdate,c99fsearch,c99shexit,view_perms,posix_getpwuid,posix_getgrgid,posix_kill,parse_perms,parsesort,view_perms_color,set_encoder_input,ls_setcheckboxall,ls_reverse_all,rsg_read,rsg_glob,selfURL,dispsecinfo,unix2DosTime,addFile,system,get_users,view_size,DirFiles,DirFilesWide,DirPrintHTMLHeaders,GetFilesTotal,GetTitles,GetTimeTotal,GetMatchesCount,GetFileMatchesCount,GetResultFiles,fs_copy_dir,fs_copy_obj,fs_move_dir,fs_move_obj,fs_rmdir,SearchText,getmicrotime MySQL : ON | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /usr/local/ssl/local/ssl/local/ssl/local/share/perl5/Net/ |
Upload File : |
# Copyright (c) 1997-2004 Graham Barr <gbarr@pobox.com>. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. package Net::LDAP; use strict; use Socket qw(AF_INET AF_INET6 AF_UNSPEC SOL_SOCKET SO_KEEPALIVE); use IO::Socket; use IO::Select; use Tie::Hash; use Convert::ASN1 qw(asn_read); use Net::LDAP::Message; use Net::LDAP::ASN qw(LDAPResponse); use Net::LDAP::Constant qw(LDAP_SUCCESS LDAP_OPERATIONS_ERROR LDAP_SASL_BIND_IN_PROGRESS LDAP_DECODING_ERROR LDAP_PROTOCOL_ERROR LDAP_ENCODING_ERROR LDAP_FILTER_ERROR LDAP_LOCAL_ERROR LDAP_PARAM_ERROR LDAP_INAPPROPRIATE_AUTH LDAP_SERVER_DOWN LDAP_USER_CANCELED LDAP_EXTENSION_START_TLS LDAP_UNAVAILABLE ); # check for IPv6 support: prefer IO::Socket::IP 0.20+ over IO::Socket::INET6 use constant CAN_IPV6 => do { local $SIG{__DIE__}; eval { require IO::Socket::IP; IO::Socket::IP->VERSION(0.20); } ? 'IO::Socket::IP' : eval { require IO::Socket::INET6; } ? 'IO::Socket::INET6' : ''; }; our $VERSION = '0.65'; our @ISA = qw(Tie::StdHash Net::LDAP::Extra); our $LDAP_VERSION = 3; # default LDAP protocol version # Net::LDAP::Extra will only exist is someone use's the module. But we need # to ensure the package stash exists or perl will complain that we inherit # from a non-existent package. I could just use the module, but I did not # want to. $Net::LDAP::Extra::create = $Net::LDAP::Extra::create = 0; sub import { shift; unshift @_, 'Net::LDAP::Constant'; require Net::LDAP::Constant; goto &{Net::LDAP::Constant->can('import')}; } sub _options { my %ret = @_; my $once = 0; for my $v (grep { /^-/ } keys %ret) { require Carp; $once++ or Carp::carp('deprecated use of leading - for options'); $ret{substr($v, 1)} = $ret{$v}; } $ret{control} = [ map { (ref($_) =~ /[^A-Z]/) ? $_->to_asn : $_ } ref($ret{control}) eq 'ARRAY' ? @{$ret{control}} : $ret{control} ] if exists $ret{control}; \%ret; } sub _dn_options { unshift @_, 'dn' if @_ & 1; &_options; } sub _err_msg { my $mesg = shift; my $errstr = $mesg->dn || ''; $errstr .= ': ' if $errstr; $errstr . $mesg->error; } my %onerror = ( die => sub { require Carp; Carp::croak(_err_msg(@_)) }, warn => sub { require Carp; Carp::carp(_err_msg(@_)); $_[0] }, undef => sub { require Carp; Carp::carp(_err_msg(@_)) if $^W; undef }, ); sub _error { my ($ldap, $mesg) = splice(@_, 0, 2); $mesg->set_error(@_); $ldap->{net_ldap_onerror} && !$ldap->{net_ldap_async} ? scalar &{$ldap->{net_ldap_onerror}}($mesg) : $mesg; } sub new { my $self = shift; my $type = ref($self) || $self; my $host = shift if @_ % 2; my $arg = &_options; my $obj = bless {}, $type; foreach my $uri (ref($host) ? @$host : ($host)) { my $scheme = $arg->{scheme} || 'ldap'; my $h = $uri; if (defined($h)) { $h =~ s,^(\w+)://,, and $scheme = lc($1); $h =~ s,/.*,,; # remove path part $h =~ s/%([A-Fa-f0-9]{2})/chr(hex($1))/eg; # unescape } my $meth = $obj->can("connect_$scheme") or next; if (&$meth($obj, $h, $arg)) { $obj->{net_ldap_uri} = $uri; $obj->{net_ldap_scheme} = $scheme; last; } } return undef unless $obj->{net_ldap_socket}; $obj->{net_ldap_socket}->setsockopt(SOL_SOCKET, SO_KEEPALIVE, $arg->{keepalive} ? 1 : 0) if (defined($arg->{keepalive})); $obj->{net_ldap_rawsocket} = $obj->{net_ldap_socket}; $obj->{net_ldap_resp} = {}; $obj->{net_ldap_version} = $arg->{version} || $LDAP_VERSION; $obj->{net_ldap_async} = $arg->{async} ? 1 : 0; $obj->{raw} = $arg->{raw} if ($arg->{raw}); if (defined(my $onerr = $arg->{onerror})) { $onerr = $onerror{$onerr} if exists $onerror{$onerr}; $obj->{net_ldap_onerror} = $onerr; } $obj->debug($arg->{debug} || 0 ); $obj->outer; } sub connect_ldap { my ($ldap, $host, $arg) = @_; my $port = $arg->{port} || 389; my $class = (CAN_IPV6) ? CAN_IPV6 : 'IO::Socket::INET'; my $domain = $arg->{inet4} ? AF_INET : ($arg->{inet6} ? AF_INET6 : AF_UNSPEC); # separate port from host overwriting given/default port $host =~ s/^([^:]+|\[.*\]):(\d+)$/$1/ and $port = $2; if ($arg->{inet6} && !CAN_IPV6) { $@ = 'unable to load IO::Socket::INET6; no IPv6 support'; return undef; } $ldap->{net_ldap_socket} = $class->new( PeerAddr => $host, PeerPort => $port, LocalAddr => $arg->{localaddr} || undef, Proto => 'tcp', Domain => $domain, MultiHomed => $arg->{multihomed}, Timeout => defined $arg->{timeout} ? $arg->{timeout} : 120 ) or return undef; $ldap->{net_ldap_host} = $host; $ldap->{net_ldap_port} = $port; } # Different OpenSSL verify modes. my %ssl_verify = qw(none 0 optional 1 require 3); sub connect_ldaps { my ($ldap, $host, $arg) = @_; my $port = $arg->{port} || 636; my $domain = $arg->{inet4} ? AF_INET : ($arg->{inet6} ? AF_INET6 : AF_UNSPEC); if ($arg->{inet6} && !CAN_IPV6) { $@ = 'unable to load IO::Socket::INET6; no IPv6 support'; return undef; } require IO::Socket::SSL; # separate port from host overwriting given/default port $host =~ s/^([^:]+|\[.*\]):(\d+)$/$1/ and $port = $2; $arg->{sslserver} = $host unless defined $arg->{sslserver}; $ldap->{net_ldap_socket} = IO::Socket::SSL->new( PeerAddr => $host, PeerPort => $port, LocalAddr => $arg->{localaddr} || undef, Proto => 'tcp', Domain => $domain, Timeout => defined $arg->{timeout} ? $arg->{timeout} : 120, _SSL_context_init_args($arg) ) or return undef; $ldap->{net_ldap_host} = $host; $ldap->{net_ldap_port} = $port; } sub _SSL_context_init_args { my $arg = shift; my $verify = 0; my %verifycn_ctx = (); my ($clientcert, $clientkey, $passwdcb); if (exists $arg->{verify}) { my $v = lc $arg->{verify}; $verify = 0 + (exists $ssl_verify{$v} ? $ssl_verify{$v} : $verify); if ($verify) { $verifycn_ctx{SSL_verifycn_scheme} = 'ldap'; $verifycn_ctx{SSL_verifycn_name} = $arg->{sslserver} if (defined $arg->{sslserver}); } } if (exists $arg->{clientcert}) { $clientcert = $arg->{clientcert}; if (exists $arg->{clientkey}) { $clientkey = $arg->{clientkey}; } else { require Carp; Carp::croak('Setting client public key but not client private key'); } } if ($arg->{checkcrl} && !$arg->{capath}) { require Carp; Carp::croak('Cannot check CRL without having CA certificates'); } if (exists $arg->{keydecrypt}) { $passwdcb = $arg->{keydecrypt}; } # allow deprecated "sslv2/3" in addition to IO::Socket::SSL's "sslv23" if (defined $arg->{sslversion}) { $arg->{sslversion} =~ s:sslv2/3:sslv23:io; } ( defined $arg->{ciphers} ? ( SSL_cipher_list => $arg->{ciphers} ) : (), defined $arg->{sslversion} ? ( SSL_version => $arg->{sslversion} ) : (), SSL_ca_file => exists $arg->{cafile} ? $arg->{cafile} : '', SSL_ca_path => exists $arg->{capath} ? $arg->{capath} : '', SSL_key_file => $clientcert ? $clientkey : undef, SSL_passwd_cb => $passwdcb, SSL_check_crl => $arg->{checkcrl} ? 1 : 0, SSL_use_cert => $clientcert ? 1 : 0, SSL_cert_file => $clientcert, SSL_verify_mode => $verify, %verifycn_ctx, ); } sub connect_ldapi { my ($ldap, $peer, $arg) = @_; $peer = $ENV{LDAPI_SOCK} || '/var/run/ldapi' unless length $peer; require IO::Socket::UNIX; $ldap->{net_ldap_socket} = IO::Socket::UNIX->new( Peer => $peer, Timeout => defined $arg->{timeout} ? $arg->{timeout} : 120 ) or return undef; # 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; } sub message { my $ldap = shift; shift->new($ldap, @_); } sub async { my $ldap = shift; @_ ? ($ldap->{net_ldap_async}, $ldap->{net_ldap_async} = shift)[0] : $ldap->{net_ldap_async}; } sub debug { my $ldap = shift; require Convert::ASN1::Debug if $_[0]; @_ ? ($ldap->{net_ldap_debug}, $ldap->{net_ldap_debug} = shift)[0] : $ldap->{net_ldap_debug}; } sub sasl { $_[0]->{sasl}; } sub socket { my $ldap = shift; my %opt = @_; (exists($opt{sasl_layer}) && !$opt{sasl_layer}) ? $ldap->{net_ldap_rawsocket} : $ldap->{net_ldap_socket}; } sub host { my $ldap = shift; ($ldap->{net_ldap_scheme} ne 'ldapi') ? $ldap->{net_ldap_host} : $ldap->{net_ldap_peer}; } sub port { $_[0]->{net_ldap_port} || undef; } sub scheme { $_[0]->{net_ldap_scheme}; } sub uri { $_[0]->{net_ldap_uri}; } sub unbind { my $ldap = shift; my $arg = &_options; my $mesg = $ldap->message('Net::LDAP::Unbind' => $arg); my $control = $arg->{control} and $ldap->{net_ldap_version} < 3 and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); $mesg->encode( unbindRequest => 1, controls => $control, ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); $ldap->_sendmesg($mesg); } # convenience alias *done = \&unbind; sub ldapbind { require Carp; Carp::carp('->ldapbind deprecated, use ->bind') if $^W; goto &bind; } my %ptype = qw( password simple krb41password krbv41 krb42password krbv42 kerberos41 krbv41 kerberos42 krbv42 sasl sasl noauth anon anonymous anon ); sub bind { my $ldap = shift; my $arg = &_dn_options; require Net::LDAP::Bind; my $mesg = $ldap->message('Net::LDAP::Bind' => $arg); $ldap->version(delete $arg->{version}) if exists $arg->{version}; my $dn = delete $arg->{dn} || ''; my $control = delete $arg->{control} and $ldap->{net_ldap_version} < 3 and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); my %stash = ( name => ref($dn) ? $dn->dn : $dn, version => $ldap->version, ); my($auth_type, $passwd) = scalar(keys %$arg) ? () : (simple => ''); keys %ptype; # Reset iterator while (my($param, $type) = each %ptype) { if (exists $arg->{$param}) { ($auth_type, $passwd) = $type eq 'anon' ? (simple => '') : ($type, $arg->{$param}); return _error($ldap, $mesg, LDAP_INAPPROPRIATE_AUTH, 'No password, did you mean noauth or anonymous ?') if $type eq 'simple' and $passwd eq ''; last; } } return _error($ldap, $mesg, LDAP_INAPPROPRIATE_AUTH, 'No AUTH supplied') unless $auth_type; if ($auth_type eq 'sasl') { return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'SASL requires LDAPv3') if $ldap->{net_ldap_version} < 3; my $sasl = $passwd; my $sasl_conn; if (ref($sasl) and $sasl->isa('Authen::SASL')) { # If we're talking to a round-robin, the canonical name of # the host we are talking to might not match the name we # requested. Look at the rawsocket because SASL layer filehandles # don't support socket methods. my $sasl_host; if (exists($arg->{sasl_host})) { if ($arg->{sasl_host}) { $sasl_host = $arg->{sasl_host}; } elsif ($ldap->{net_ldap_rawsocket}->can('peerhost')) { $sasl_host = $ldap->{net_ldap_rawsocket}->peerhost; } } $sasl_host ||= $ldap->{net_ldap_host}; $sasl_conn = eval { local ($SIG{__DIE__}); $sasl->client_new('ldap', $sasl_host); }; } else { $sasl_conn = $sasl; } return _error($ldap, $mesg, LDAP_LOCAL_ERROR, "$@") unless defined($sasl_conn); # Tell SASL the local and server IP addresses $sasl_conn->property( sockname => $ldap->{net_ldap_rawsocket}->sockname, peername => $ldap->{net_ldap_rawsocket}->peername, ); my $initial = $sasl_conn->client_start; return _error($ldap, $mesg, LDAP_LOCAL_ERROR, $sasl_conn->error) unless defined($initial); $passwd = { mechanism => $sasl_conn->mechanism, credentials => $initial, }; # Save data, we will need it later $mesg->_sasl_info($stash{name}, $control, $sasl_conn); } $stash{authentication} = { $auth_type => $passwd }; $mesg->encode( bindRequest => \%stash, controls => $control ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); $ldap->_sendmesg($mesg); } my %scope = qw(base 0 one 1 single 1 sub 2 subtree 2 children 3); my %deref = qw(never 0 search 1 find 2 always 3); sub search { my $ldap = shift; my $arg = &_options; require Net::LDAP::Search; $arg->{raw} = $ldap->{raw} if ($ldap->{raw} && !defined($arg->{raw})); my $mesg = $ldap->message('Net::LDAP::Search' => $arg); my $control = $arg->{control} and $ldap->{net_ldap_version} < 3 and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); my $base = $arg->{base} || ''; my $filter; unless (ref ($filter = $arg->{filter})) { require Net::LDAP::Filter; my $f = Net::LDAP::Filter->new; $f->parse($filter) or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Bad filter'); $filter = $f; } my %stash = ( baseObject => ref($base) ? $base->dn : $base, scope => 2, derefAliases => 2, sizeLimit => $arg->{sizelimit} || 0, timeLimit => $arg->{timelimit} || 0, typesOnly => $arg->{typesonly} || $arg->{attrsonly} || 0, filter => $filter, attributes => $arg->{attrs} || [] ); if (exists $arg->{scope}) { my $sc = lc $arg->{scope}; $stash{scope} = 0 + (exists $scope{$sc} ? $scope{$sc} : $sc); } if (exists $arg->{deref}) { my $dr = lc $arg->{deref}; $stash{derefAliases} = 0 + (exists $deref{$dr} ? $deref{$dr} : $dr); } $mesg->encode( searchRequest => \%stash, controls => $control ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); $ldap->_sendmesg($mesg); } sub add { my $ldap = shift; my $arg = &_dn_options; my $mesg = $ldap->message('Net::LDAP::Add' => $arg); my $control = $arg->{control} and $ldap->{net_ldap_version} < 3 and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); my $entry = $arg->{dn} or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No DN specified'); unless (ref $entry) { require Net::LDAP::Entry; $entry = Net::LDAP::Entry->new; $entry->dn($arg->{dn}); $entry->add(@{$arg->{attrs} || $arg->{attr} || []}); } $mesg->encode( addRequest => $entry->asn, controls => $control ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); $ldap->_sendmesg($mesg); } my %opcode = ( add => 0, delete => 1, replace => 2, increment => 3 ); sub modify { my $ldap = shift; my $arg = &_dn_options; my $mesg = $ldap->message('Net::LDAP::Modify' => $arg); my $control = $arg->{control} and $ldap->{net_ldap_version} < 3 and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); my $dn = $arg->{dn} or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No DN specified'); my @ops; my $opcode; if (exists $arg->{changes}) { my $opcode; my $j = 0; while ($j < @{$arg->{changes}}) { return _error($ldap, $mesg, LDAP_PARAM_ERROR, "Bad change type '" . $arg->{changes}[--$j] . "'") unless defined($opcode = $opcode{$arg->{changes}[$j++]}); my $chg = $arg->{changes}[$j++]; if (ref($chg)) { my $i = 0; while ($i < @$chg) { push @ops, { operation => $opcode, modification => { type => $chg->[$i], vals => ref($chg->[$i+1]) ? $chg->[$i+1] : [$chg->[$i+1]] } }; $i += 2; } } } } else { foreach my $op (qw(add delete replace increment)) { next unless exists $arg->{$op}; my $opt = $arg->{$op}; my $opcode = $opcode{$op}; if (ref($opt) eq 'HASH') { while (my ($k, $v) = each %$opt) { push @ops, { operation => $opcode, modification => { type => $k, vals => ref($v) ? $v : [$v] } }; } } elsif (ref($opt) eq 'ARRAY') { my $k = 0; while ($k < @{$opt}) { my $attr = ${$opt}[$k++]; my $val = $opcode == 1 ? [] : ${$opt}[$k++]; push @ops, { operation => $opcode, modification => { type => $attr, vals => ref($val) ? $val : [$val] } }; } } else { push @ops, { operation => $opcode, modification => { type => $opt, vals => [] } }; } } } $mesg->encode( modifyRequest => { object => ref($dn) ? $dn->dn : $dn, modification => \@ops }, controls => $control ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); $ldap->_sendmesg($mesg); } sub delete { my $ldap = shift; my $arg = &_dn_options; my $mesg = $ldap->message('Net::LDAP::Delete' => $arg); my $control = $arg->{control} and $ldap->{net_ldap_version} < 3 and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); my $dn = $arg->{dn} or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No DN specified'); $mesg->encode( delRequest => ref($dn) ? $dn->dn : $dn, controls => $control ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); $ldap->_sendmesg($mesg); } sub moddn { my $ldap = shift; my $arg = &_dn_options; my $del = $arg->{deleteoldrdn} || $arg->{delete} || 0; my $newsup = $arg->{newsuperior}; my $mesg = $ldap->message('Net::LDAP::ModDN' => $arg); my $control = $arg->{control} and $ldap->{net_ldap_version} < 3 and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); my $dn = $arg->{dn} or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No DN specified'); my $new = $arg->{newrdn} || $arg->{new} or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No NewRDN specified'); $mesg->encode( modDNRequest => { entry => ref($dn) ? $dn->dn : $dn, newrdn => ref($new) ? $new->dn : $new, deleteoldrdn => $del, newSuperior => ref($newsup) ? $newsup->dn : $newsup, }, controls => $control ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); $ldap->_sendmesg($mesg); } # now maps to the V3/X.500(93) modifydn map sub modrdn { goto &moddn } sub compare { my $ldap = shift; my $arg = &_dn_options; my $mesg = $ldap->message('Net::LDAP::Compare' => $arg); my $control = $arg->{control} and $ldap->{net_ldap_version} < 3 and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); my $dn = $arg->{dn} or return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'No DN specified'); my $attr = exists $arg->{attr} ? $arg->{attr} : exists $arg->{attrs} #compat ? $arg->{attrs}[0] : ''; my $value = exists $arg->{value} ? $arg->{value} : exists $arg->{attrs} #compat ? $arg->{attrs}[1] : ''; $mesg->encode( compareRequest => { entry => ref($dn) ? $dn->dn : $dn, ava => { attributeDesc => $attr, assertionValue => $value } }, controls => $control ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); $ldap->_sendmesg($mesg); } sub abandon { my $ldap = shift; unshift @_, 'id' if @_ & 1; my $arg = &_options; my $id = $arg->{id}; my $mesg = $ldap->message('Net::LDAP::Abandon' => $arg); my $control = $arg->{control} and $ldap->{net_ldap_version} < 3 and return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'Controls require LDAPv3'); $mesg->encode( abandonRequest => ref($id) ? $id->mesg_id : $id, controls => $control ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); $ldap->_sendmesg($mesg); } sub extension { my $ldap = shift; my $arg = &_options; require Net::LDAP::Extension; my $mesg = $ldap->message('Net::LDAP::Extension' => $arg); return _error($ldap, $mesg, LDAP_LOCAL_ERROR, 'ExtendedRequest requires LDAPv3') if $ldap->{net_ldap_version} < 3; $mesg->encode( extendedReq => { requestName => $arg->{name}, requestValue => $arg->{value} }, controls => $arg->{control} ) or return _error($ldap, $mesg, LDAP_ENCODING_ERROR, "$@"); $ldap->_sendmesg($mesg); } sub sync { my $ldap = shift; my $mid = shift; my $table = $ldap->{net_ldap_mesg}; my $err = LDAP_SUCCESS; return $err unless defined $table; $mid = $mid->mesg_id if ref($mid); while (defined($mid) ? exists $table->{$mid} : %$table) { last if $err = $ldap->process($mid); } $err; } sub disconnect { my $self = shift; _drop_conn($self, LDAP_USER_CANCELED, 'Explicit disconnect'); } sub _sendmesg { my $ldap = shift; my $mesg = shift; my $debug; if ($debug = $ldap->debug) { require Convert::ASN1::Debug; print STDERR "$ldap sending:\n"; Convert::ASN1::asn_hexdump(*STDERR, $mesg->pdu) if $debug & 1; Convert::ASN1::asn_dump(*STDERR, $mesg->pdu) if $debug & 4; } my $socket = $ldap->socket or return _error($ldap, $mesg, LDAP_SERVER_DOWN, "$!"); # send packets in sizes that IO::Socket::SSL can chew # originally it was: #syswrite($socket, $mesg->pdu, length($mesg->pdu)) # or return _error($ldap, $mesg, LDAP_LOCAL_ERROR, "$!") my $to_send = \( $mesg->pdu ); my $offset = 0; while ($offset < length($$to_send)) { my $s = substr($$to_send, $offset, 15000); my $n = syswrite($socket, $s, length($s)) or return _error($ldap, $mesg, LDAP_LOCAL_ERROR, "$!"); $offset += $n; } # for CLDAP, here we need to recode when we were sent # so that we can perform timeouts and resends my $mid = $mesg->mesg_id; my $sync = not $ldap->async; unless ($mesg->done) { # may not have a response $ldap->{net_ldap_mesg}->{$mid} = $mesg; if ($sync) { my $err = $ldap->sync($mid); return _error($ldap, $mesg, $err, $@) if $err; } } $sync && $ldap->{net_ldap_onerror} && $mesg->is_error ? scalar &{$ldap->{net_ldap_onerror}}($mesg) : $mesg; } sub data_ready { my $ldap = shift; my $sock = $ldap->socket or return; my $sel = IO::Select->new($sock); return defined $sel->can_read(0) || (ref($sock) eq 'IO::Socket::SSL' && $sock->pending()); } sub process { my $ldap = shift; my $what = shift; my $sock = $ldap->socket or return LDAP_SERVER_DOWN; for (my $ready = 1; $ready; $ready = $ldap->data_ready) { my $pdu; asn_read($sock, $pdu) or return _drop_conn($ldap, LDAP_OPERATIONS_ERROR, 'Communications Error'); my $debug; if ($debug = $ldap->debug) { require Convert::ASN1::Debug; print STDERR "$ldap received:\n"; Convert::ASN1::asn_hexdump(\*STDERR, $pdu) if $debug & 2; Convert::ASN1::asn_dump(\*STDERR, $pdu) if $debug & 8; } my $result = $LDAPResponse->decode($pdu) or return LDAP_DECODING_ERROR; my $mid = $result->{messageID}; my $mesg = $ldap->{net_ldap_mesg}->{$mid}; unless ($mesg) { if (my $ext = $result->{protocolOp}{extendedResp}) { if (($ext->{responseName} || '') eq '1.3.6.1.4.1.1466.20036') { # notice of disconnection return _drop_conn($ldap, LDAP_SERVER_DOWN, 'Notice of Disconnection'); } } print STDERR "Unexpected PDU, ignored\n" if $debug & 10; next; } $mesg->decode($result) or return $mesg->code; last if defined $what && $what == $mid; } # FIXME: in CLDAP here we need to check if any message has timed out # and if so do we resend it or what return LDAP_SUCCESS; } *_recvresp = \&process; # compat sub _drop_conn { my ($self, $err, $etxt) = @_; delete $self->{net_ldap_rawsocket}; my $sock = delete $self->{net_ldap_socket}; close($sock) if $sock; if (my $msgs = delete $self->{net_ldap_mesg}) { foreach my $mesg (values %$msgs) { next unless (defined $mesg); $mesg->set_error($err, $etxt); } } $err; } sub _forgetmesg { my $ldap = shift; my $mesg = shift; my $mid = $mesg->mesg_id; delete $ldap->{net_ldap_mesg}->{$mid}; } #Mark Wilcox 3-20-2000 #now accepts named parameters #dn => "dn of subschema entry" # # # Clif Harden 2-4-2001. # corrected filter for subschema search. # added attributes to retrieve on subschema search. # added attributes to retrieve on rootDSE search. # changed several double quote character to single quote # character, just to be consistent throughout the schema # and root_dse functions. # sub schema { require Net::LDAP::Schema; my $self = shift; my %arg = @_; my $base; my $mesg; if (exists $arg{dn}) { $base = $arg{dn}; } else { my $root = $self->root_dse( attrs => ['subschemaSubentry'] ) or return undef; $base = $root->get_value('subschemaSubentry') || 'cn=schema'; } $mesg = $self->search( base => $base, scope => 'base', filter => '(objectClass=subschema)', attrs => [qw( objectClasses attributeTypes matchingRules matchingRuleUse dITStructureRules dITContentRules nameForms ldapSyntaxes extendedAttributeInfo )], ); $mesg->code ? undef : Net::LDAP::Schema->new($mesg->entry); } sub root_dse { my $ldap = shift; my %arg = @_; my $attrs = $arg{attrs} || [qw( subschemaSubentry namingContexts altServer supportedExtension supportedControl supportedFeatures supportedSASLMechanisms supportedLDAPVersion vendorName vendorVersion )]; my $root = $arg{attrs} && $ldap->{net_ldap_root_dse}; return $root if $root; my $mesg = $ldap->search( base => '', scope => 'base', filter => '(objectClass=*)', attrs => $attrs, ); require Net::LDAP::RootDSE; $root = $mesg->entry; bless $root, 'Net::LDAP::RootDSE' if $root; # Naughty, but there you go :-) $ldap->{net_ldap_root_dse} = $root unless $arg{attrs}; return $root; } sub start_tls { my $ldap = shift; my $arg = &_options; my $sock = $ldap->socket; require IO::Socket::SSL; require Net::LDAP::Extension; my $mesg = $ldap->message('Net::LDAP::Extension' => $arg); return _error($ldap, $mesg, LDAP_OPERATIONS_ERROR, 'TLS already started') if $sock->isa('IO::Socket::SSL'); return _error($ldap, $mesg, LDAP_PARAM_ERROR, 'StartTLS requires LDAPv3') if $ldap->version < 3; $mesg->encode( extendedReq => { requestName => LDAP_EXTENSION_START_TLS, } ); $ldap->_sendmesg($mesg); $mesg->sync(); return $mesg if $mesg->code; delete $ldap->{net_ldap_root_dse}; $arg->{sslserver} = $ldap->{net_ldap_host} unless defined $arg->{sslserver}; my $sock_class = ref($sock); return $mesg if IO::Socket::SSL->start_SSL($sock, {_SSL_context_init_args($arg)}); my $err = $@ || $IO::Socket::SSL::SSL_ERROR || $IO::Socket::SSL::SSL_ERROR || ''; # avoid use on once warning if ($sock_class ne ref($sock)) { $err = $sock->errstr; bless $sock, $sock_class; } _error($ldap, $mesg, LDAP_OPERATIONS_ERROR, $err); } sub cipher { my $ldap = shift; $ldap->socket->isa('IO::Socket::SSL') ? $ldap->socket->get_cipher : undef; } sub certificate { my $ldap = shift; $ldap->socket->isa('IO::Socket::SSL') ? $ldap->socket->get_peer_certificate : undef; } # what version are we talking? sub version { my $ldap = shift; @_ ? ($ldap->{net_ldap_version}, $ldap->{net_ldap_version} = shift)[0] : $ldap->{net_ldap_version}; } sub outer { my $self = shift; return $self if tied(%$self); my %outer; tie %outer, ref($self), $self; ++$self->{net_ldap_refcnt}; bless \%outer, ref($self); } sub inner { tied(%{$_[0]}) || $_[0]; } sub TIEHASH { $_[1]; } sub DESTROY { my $ldap = shift; my $inner = tied(%$ldap) or return; _drop_conn($inner, LDAP_UNAVAILABLE, 'Implicit disconnect') unless --$inner->{net_ldap_refcnt}; } 1;