summaryrefslogtreecommitdiff
path: root/scripts/ldap-pam.pl
blob: 675a2c9bdc64fc3a10532a50897f47d2e8c6a392 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#
# checkpassword compatible LDAP pam for ADDRESSS (version 0.9.2)
#
# Usage: ldpap-pam -h:host:port -d:DN -w:password -b:base -s:scope -c:certificate -l(ogging) -ll
#
# Warning: This PERL code might not work on all versions of PERL !
#
# Some code taken from: 
# 		ldap_verify.pl by Ted Fines, Jan. 2005.  version 0.1
#
# Customization:
#       Default search attribute is 'Mail'.
#
# Requires:
# 	'Net::LDAP' on http://www.cpan.org 
#
# History: 
#	0.9.2	Fixed bug evalulation reference mailname (tx. Sven)
#
#---------------------------------------------------------------------
use Net::LDAP;
use IO::Handle;
use warnings;
use strict;
#
## Verbose output
#
my $ME='ldap-pam';
my $LOGGING=0;
#
my @INPUT;
my $LDAPHOST='localhost';
my $LDAPPORT='default';
my $LDAPUSR='';
my $LDAPPWD='';
my $LDAPBASE='';
my $LDAPCERT='';
my $TIMEOUT='30';
#
my $LDAPSCOPE='sub';
my $ATTRIBUTE='mail';
my $PROTOCOL_LEN=512;
#
## Check Arguments
#
if ( $#ARGV == -1) {
	print STDERR "[Usage] ldpap-pam -h:host:port -t:timeout -d:DN -w:password -b:base -s:scope -c:certificate -l[l](ogging)\n";
        print STDERR "[Defaults] Host:$LDAPHOST - Port:$LDAPPORT - DN:anonymous - Scope:$LDAPSCOPE - Attribute:$ATTRIBUTE - Timeout:$TIMEOUT \n";
	exit 111;
}

while ( $#ARGV >=  0 ) {
        $_=$ARGV[0];
        s/^-h//        && do { @INPUT = split(/:/,$_); $LDAPHOST=$INPUT[1]; 
				if ( $INPUT[2] ) { $LDAPPORT=$INPUT[2]; } };
        s/^-t//        && do { @INPUT = split(/:/,$_); $TIMEOUT=$INPUT[1]; };
        s/^-d//        && do { @INPUT = split(/:/,$_); $LDAPUSR=$INPUT[1]; };
        s/^-w//        && do { @INPUT = split(/:/,$_); $LDAPPWD=$INPUT[1]; };
        s/^-b//        && do { @INPUT = split(/:/,$_); $LDAPBASE=$INPUT[1]; };
        s/^-s//        && do { @INPUT = split(/:/,$_); $LDAPSCOPE=$INPUT[1]; };
        s/^-c//        && do { @INPUT = split(/:/,$_); $LDAPCERT=$INPUT[1]; };
        s/^-ll//       && do { $LOGGING = 2; };
        s/^-l//        && do { $LOGGING = 1; };
        shift;
}
#
##
my $ldap;
my $mesg;
my $rawinput;
my $verifyaddr;
my $verifyresult;
#
my $num_params = 1;
my $input_descriptor = 3;
#
# These codes from DJB's checkpassword page.
#
my ($verify_ok,$verify_none,$resp_misused,$resp_tempfailure) = (0,1,2,111);
#
my $fhin = new IO::Handle;
my $fherr = new IO::Handle;
$fhin->fdopen($input_descriptor,"r");
$fherr->fdopen(fileno(STDERR),"w");

if (($fhin->opened) && ($fherr->opened)) {
    $fhin->read($rawinput,$PROTOCOL_LEN);
    my @checkfields = split(/\0/,$rawinput);
    if (scalar(@checkfields) != $num_params) {
        if ($LOGGING) { print STDERR "$ME [Error] Wrong format of input address specified.\n"; }
        exit $resp_misused;
    }
    $verifyaddr = $checkfields[0];
    #
    # This section is the 'bottom line' so to speak, where the yea or nay is given.
    #
    $verifyresult = &ldap_mail($verifyaddr);
    if ($verifyresult == $verify_ok) {
        if ($LOGGING) { print STDERR "$ME [Info] Address '$verifyaddr' verified at LDAP Server '$LDAPHOST'.\n"; }
    } elsif ($verifyresult == $verify_none) {
        if ($LOGGING) { print STDERR "$ME [Info] Could not verify address '$verifyaddr' at LDAP Server '$LDAPHOST'.\n"; }
    }
    exit $verifyresult;
}
print STDERR "$ME [Error] Could not connect to LDAP Server '$LDAPHOST:$LDAPPORT'.\n"; 
exit $resp_tempfailure;

sub ldap_mail {
    (my $mailaddr) = @_;

    if ( $LDAPCERT ne "" && $LDAPUSR ne "" && $LDAPPWD ne "" ) {
        if ( $LDAPPORT eq "default" ) { $LDAPPORT='636'; }
        $ldap = Net::LDAPS->new($LDAPHOST, port => $LDAPPORT, timeout => $TIMEOUT) or &mydie();    
        $mesg = $ldap->bind($LDAPUSR, password => $LDAPPWD, version => 3, verify => require, cafile => $LDAPCERT);
    } elsif ( $LDAPUSR ne "" && $LDAPPWD ne "" ) {
        if ( $LDAPPORT eq "default" ) { $LDAPPORT='389'; }
        $ldap = Net::LDAP->new($LDAPHOST, port => $LDAPPORT, timeout => $TIMEOUT) or &mydie();    
        $mesg = $ldap->bind($LDAPUSR, password => $LDAPPWD, version => 3);
    } else {
        if ( $LDAPPORT eq "default" ) { $LDAPPORT='389'; }
        $ldap = Net::LDAP->new($LDAPHOST, port => $LDAPPORT, timeout => $TIMEOUT) or &mydie();    
        $mesg = $ldap->bind(version => 3);
    }
    if ( $mesg->code ) { &mydie($mesg->code) };

    $mesg = $ldap->search (base => $LDAPBASE, scope => $LDAPSCOPE, filter => "$ATTRIBUTE=$mailaddr");
    $mesg->code && &mydie($mesg);
    $ldap->unbind; 

    my $href= $mesg->as_struct;
    my @mailnames = keys %$href;
    foreach (@mailnames) {
      my $DN = $_;
      my $valref = $$href{$_};
      my $mailname = @$valref{$ATTRIBUTE};
      if ( $LOGGING == 2 ) { print STDERR "$ME [Debug] Returned DN '$DN' with '@$mailname' for address '$mailaddr' at LDAP Server '$LDAPHOST'.\n"; }
      if ( "lc($mailaddr)" eq "lc((@$mailname)[0])" ) { return $verify_ok; }
    }
    return $verify_none;
}

sub mydie {
    if (scalar(@_) > 0) {
	print STDERR "$ME [Error] Strange message received from LDAP Server '$LDAPHOST:$LDAPPORT': '$mesg->code', '$mesg->error_name', '$mesg->error->text'.\n"; 
    } else {
	print STDERR "$ME [Error] Could not connect to LDAP Server '$LDAPHOST:$LDAPPORT'.\n"; 
    }
    exit $resp_tempfailure;
}