]> git.proxmox.com Git - pve-access-control.git/blame - PVE/Auth/AD.pm
VM.Snapshot.Rollback privilege added
[pve-access-control.git] / PVE / Auth / AD.pm
CommitLineData
5bb4e06a
DM
1package PVE::Auth::AD;
2
3use strict;
4use warnings;
5use PVE::Auth::Plugin;
6use Net::LDAP;
8b600c4d 7use Net::IP;
5bb4e06a
DM
8
9use base qw(PVE::Auth::Plugin);
10
11sub type {
12 return 'ad';
13}
14
15sub properties {
16 return {
8bdbfd4d
DC
17 server1 => {
18 description => "Server IP address (or DNS name)",
5bb4e06a 19 type => 'string',
8b600c4d 20 format => 'address',
5bb4e06a
DM
21 maxLength => 256,
22 },
8bdbfd4d 23 server2 => {
5bb4e06a
DM
24 description => "Fallback Server IP address (or DNS name)",
25 type => 'string',
26 optional => 1,
8b600c4d 27 format => 'address',
5bb4e06a
DM
28 maxLength => 256,
29 },
8bdbfd4d 30 secure => {
5bb4e06a 31 description => "Use secure LDAPS protocol.",
8bdbfd4d 32 type => 'boolean',
5bb4e06a
DM
33 optional => 1,
34
35 },
8bdbfd4d 36 default => {
5bb4e06a 37 description => "Use this as default realm",
8bdbfd4d 38 type => 'boolean',
5bb4e06a
DM
39 optional => 1,
40 },
8bdbfd4d 41 comment => {
5bb4e06a 42 description => "Description.",
8bdbfd4d 43 type => 'string',
5bb4e06a
DM
44 optional => 1,
45 maxLength => 4096,
46 },
47 port => {
48 description => "Server port.",
49 type => 'integer',
50 minimum => 1,
51 maximum => 65535,
52 optional => 1,
53 },
54 domain => {
55 description => "AD domain name",
56 type => 'string',
57 pattern => '\S+',
58 optional => 1,
59 maxLength => 256,
60 },
8bdbfd4d 61 tfa => PVE::JSONSchema::get_standard_option('tfa'),
5bb4e06a
DM
62 };
63}
64
65sub options {
66 return {
67 server1 => {},
68 server2 => { optional => 1 },
69 domain => {},
70 port => { optional => 1 },
71 secure => { optional => 1 },
72 default => { optional => 1 },,
73 comment => { optional => 1 },
96f8ebd6 74 tfa => { optional => 1 },
23e0cf85
DC
75 verify => { optional => 1 },
76 capath => { optional => 1 },
77 cert => { optional => 1 },
78 certkey => { optional => 1 },
5bb4e06a
DM
79 };
80}
81
82my $authenticate_user_ad = sub {
83 my ($config, $server, $username, $password) = @_;
84
85 my $default_port = $config->{secure} ? 636: 389;
86 my $port = $config->{port} ? $config->{port} : $default_port;
87 my $scheme = $config->{secure} ? 'ldaps' : 'ldap';
8b600c4d 88 $server = "[$server]" if Net::IP::ip_is_ipv6($server);
5bb4e06a 89 my $conn_string = "$scheme://${server}:$port";
5bb4e06a 90
23e0cf85
DC
91 my %ad_args;
92 if ($config->{verify}) {
93 $ad_args{verify} = 'require';
94 if (defined(my $cert = $config->{cert})) {
95 $ad_args{clientcert} = $cert;
96 }
97 if (defined(my $key = $config->{certkey})) {
98 $ad_args{clientkey} = $key;
99 }
100 if (defined(my $capath = $config->{capath})) {
101 if (-d $capath) {
102 $ad_args{capath} = $capath;
103 } else {
104 $ad_args{cafile} = $capath;
105 }
106 }
107 } elsif (defined($config->{verify})) {
108 $ad_args{verify} = 'none';
109 }
110
111 my $ldap = Net::LDAP->new($conn_string, %ad_args) || die "$@\n";
112
113 $username = "$username\@$config->{domain}"
5bb4e06a
DM
114 if $username !~ m/@/ && $config->{domain};
115
116 my $res = $ldap->bind($username, password => $password);
117
118 my $code = $res->code();
119 my $err = $res->error;
120
121 $ldap->unbind();
122
123 die "$err\n" if ($code);
124};
125
126sub authenticate_user {
127 my ($class, $config, $realm, $username, $password) = @_;
128
129 eval { &$authenticate_user_ad($config, $config->{server1}, $username, $password); };
130 my $err = $@;
131 return 1 if !$err;
132 die $err if !$config->{server2};
133 &$authenticate_user_ad($config, $config->{server2}, $username, $password);
134 return 1;
135}
136
1371;