e1161b64e131c1bebc85dfaef37435638f0b6ad8
[pve-access-control.git] / PVE / Auth / LDAP.pm
1 package PVE::Auth::LDAP;
2
3 use strict;
4 use warnings;
5
6 use PVE::Auth::Plugin;
7 use Net::LDAP;
8 use Net::IP;
9 use base qw(PVE::Auth::Plugin);
10
11 sub type {
12     return 'ldap';
13 }
14
15 sub properties {
16     return {
17         base_dn => {
18             description => "LDAP base domain name",
19             type => 'string',
20             pattern => '\w+=[^,]+(,\s*\w+=[^,]+)*',
21             optional => 1,
22             maxLength => 256,
23         },
24         user_attr => {
25             description => "LDAP user attribute name",
26             type => 'string',
27             pattern => '\S{2,}',
28             optional => 1,
29             maxLength => 256,
30         },
31     };
32 }
33
34 sub options {
35     return {
36         server1 => {},
37         server2 => { optional => 1 },
38         base_dn => {},
39         user_attr => {},
40         port => { optional => 1 },
41         secure => { optional => 1 },
42         default => { optional => 1 },
43         comment => { optional => 1 },
44         tfa => { optional => 1 },
45     };
46 }
47
48 my $authenticate_user_ldap = sub {
49     my ($config, $server, $username, $password) = @_;
50
51     my $default_port = $config->{secure} ? 636: 389;
52     my $port = $config->{port} ? $config->{port} : $default_port;
53     my $scheme = $config->{secure} ? 'ldaps' : 'ldap';
54     $server = "[$server]" if Net::IP::ip_is_ipv6($server);
55     my $conn_string = "$scheme://${server}:$port";
56
57     my $ldap = Net::LDAP->new($conn_string, verify => 'none') || die "$@\n";
58     my $search = $config->{user_attr} . "=" . $username;
59     my $result = $ldap->search( base    => "$config->{base_dn}",
60                                 scope   => "sub",
61                                 filter  => "$search",
62                                 attrs   => ['dn']
63                                 );
64     die "no entries returned\n" if !$result->entries;
65     my @entries = $result->entries;
66     my $res = $ldap->bind($entries[0]->dn, password => $password);
67
68     my $code = $res->code();
69     my $err = $res->error;
70
71     $ldap->unbind();
72
73     die "$err\n" if ($code);
74 };
75
76 sub authenticate_user {
77     my ($class, $config, $realm, $username, $password) = @_;
78
79     eval { &$authenticate_user_ldap($config, $config->{server1}, $username, $password); };
80     my $err = $@;
81     return 1 if !$err;
82     die $err if !$config->{server2};
83     &$authenticate_user_ldap($config, $config->{server2}, $username, $password); 
84 }
85
86 1;