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