]>
Commit | Line | Data |
---|---|---|
5bb4e06a DM |
1 | package PVE::Auth::LDAP; |
2 | ||
3 | use strict; | |
7c410d63 DM |
4 | use warnings; |
5 | ||
b5040b42 | 6 | use PVE::Tools; |
5bb4e06a | 7 | use PVE::Auth::Plugin; |
d9e93d2e | 8 | use PVE::LDAP; |
5bb4e06a DM |
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 | }, | |
b5040b42 WB |
31 | bind_dn => { |
32 | description => "LDAP bind domain name", | |
33 | type => 'string', | |
34 | pattern => '\w+=[^,]+(,\s*\w+=[^,]+)*', | |
35 | optional => 1, | |
36 | maxLength => 256, | |
37 | }, | |
e03c2aef WB |
38 | verify => { |
39 | description => "Verify the server's SSL certificate", | |
40 | type => 'boolean', | |
41 | optional => 1, | |
42 | default => 0, | |
43 | }, | |
44 | capath => { | |
45 | description => "Path to the CA certificate store", | |
46 | type => 'string', | |
47 | optional => 1, | |
48 | default => '/etc/ssl/certs', | |
49 | }, | |
50 | cert => { | |
51 | description => "Path to the client certificate", | |
52 | type => 'string', | |
53 | optional => 1, | |
54 | }, | |
55 | certkey => { | |
56 | description => "Path to the client certificate key", | |
57 | type => 'string', | |
58 | optional => 1, | |
59 | }, | |
eba326d2 DC |
60 | filter => { |
61 | description => "LDAP filter for user sync.", | |
62 | type => 'string', | |
63 | optional => 1, | |
64 | maxLength => 2048, | |
65 | }, | |
66 | sync_attributes => { | |
67 | description => "Comma separated list of key=value pairs for specifying" | |
68 | ." which LDAP attributes map to which PVE user field. For example," | |
69 | ." to map the LDAP attribute 'mail' to PVEs 'email', write " | |
70 | ." 'email=mail'. By default, each PVE user field is represented " | |
71 | ." by an LDAP attribute of the same name.", | |
72 | optional => 1, | |
73 | type => 'string', | |
74 | pattern => '\w+=[^,]+(,\s*\w+=[^,]+)*', | |
75 | }, | |
76 | user_classes => { | |
77 | description => "The objectclasses for users.", | |
78 | type => 'string', | |
79 | default => 'inetorgperson, posixaccount, person, user', | |
80 | format => 'ldap-simple-attr-list', | |
81 | optional => 1, | |
82 | }, | |
83 | group_dn => { | |
84 | description => "LDAP base domain name for group sync. If not set, the" | |
85 | ." base_dn will be used.", | |
86 | type => 'string', | |
87 | pattern => '\w+=[^,]+(,\s*\w+=[^,]+)*', | |
88 | optional => 1, | |
89 | maxLength => 256, | |
90 | }, | |
91 | group_name_attr => { | |
92 | description => "LDAP attribute representing a groups name. If not set" | |
93 | ." or found, the first value of the DN will be used as name.", | |
94 | type => 'string', | |
95 | format => 'ldap-simple-attr', | |
96 | optional => 1, | |
97 | maxLength => 256, | |
98 | }, | |
99 | group_filter => { | |
100 | description => "LDAP filter for group sync.", | |
101 | type => 'string', | |
102 | optional => 1, | |
103 | maxLength => 2048, | |
104 | }, | |
105 | group_classes => { | |
106 | description => "The objectclasses for groups.", | |
107 | type => 'string', | |
108 | default => 'groupOfNames, group, univentionGroup, ipausergroup', | |
109 | format => 'ldap-simple-attr-list', | |
110 | optional => 1, | |
111 | }, | |
5bb4e06a DM |
112 | }; |
113 | } | |
114 | ||
115 | sub options { | |
116 | return { | |
117 | server1 => {}, | |
118 | server2 => { optional => 1 }, | |
119 | base_dn => {}, | |
b5040b42 | 120 | bind_dn => { optional => 1 }, |
5bb4e06a DM |
121 | user_attr => {}, |
122 | port => { optional => 1 }, | |
123 | secure => { optional => 1 }, | |
07dd90d7 | 124 | sslversion => { optional => 1 }, |
5bb4e06a DM |
125 | default => { optional => 1 }, |
126 | comment => { optional => 1 }, | |
96f8ebd6 | 127 | tfa => { optional => 1 }, |
e03c2aef WB |
128 | verify => { optional => 1 }, |
129 | capath => { optional => 1 }, | |
130 | cert => { optional => 1 }, | |
131 | certkey => { optional => 1 }, | |
eba326d2 DC |
132 | filter => { optional => 1 }, |
133 | sync_attributes => { optional => 1 }, | |
134 | user_classes => { optional => 1 }, | |
135 | group_dn => { optional => 1 }, | |
136 | group_name_attr => { optional => 1 }, | |
137 | group_filter => { optional => 1 }, | |
138 | group_classes => { optional => 1 }, | |
5bb4e06a DM |
139 | }; |
140 | } | |
141 | ||
30aad017 DC |
142 | sub connect_and_bind { |
143 | my ($class, $config, $realm) = @_; | |
d9e93d2e DC |
144 | |
145 | my $servers = [$config->{server1}]; | |
146 | push @$servers, $config->{server2} if $config->{server2}; | |
5bb4e06a DM |
147 | |
148 | my $default_port = $config->{secure} ? 636: 389; | |
d9e93d2e | 149 | my $port = $config->{port} // $default_port; |
5bb4e06a | 150 | my $scheme = $config->{secure} ? 'ldaps' : 'ldap'; |
5bb4e06a | 151 | |
e03c2aef WB |
152 | my %ldap_args; |
153 | if ($config->{verify}) { | |
154 | $ldap_args{verify} = 'require'; | |
d9e93d2e DC |
155 | $ldap_args{clientcert} = $config->{cert} if $config->{cert}; |
156 | $ldap_args{clientkey} = $config->{certkey} if $config->{certkey}; | |
e03c2aef WB |
157 | if (defined(my $capath = $config->{capath})) { |
158 | if (-d $capath) { | |
159 | $ldap_args{capath} = $capath; | |
160 | } else { | |
161 | $ldap_args{cafile} = $capath; | |
162 | } | |
163 | } | |
164 | } else { | |
165 | $ldap_args{verify} = 'none'; | |
166 | } | |
167 | ||
07dd90d7 | 168 | if ($config->{secure}) { |
3b7eaef1 | 169 | $ldap_args{sslversion} = $config->{sslversion} || 'tlsv1_2'; |
07dd90d7 AD |
170 | } |
171 | ||
d9e93d2e DC |
172 | my $ldap = PVE::LDAP::ldap_connect($servers, $scheme, $port, \%ldap_args); |
173 | ||
174 | my $bind_dn; | |
175 | my $bind_pass; | |
b5040b42 | 176 | |
d9e93d2e DC |
177 | if ($config->{bind_dn}) { |
178 | $bind_dn = $config->{bind_dn}; | |
179 | $bind_pass = PVE::Tools::file_read_firstline("/etc/pve/priv/ldap/${realm}.pw"); | |
b5040b42 | 180 | die "missing password for realm $realm\n" if !defined($bind_pass); |
b5040b42 WB |
181 | } |
182 | ||
d9e93d2e | 183 | PVE::LDAP::ldap_bind($ldap, $bind_dn, $bind_pass); |
30aad017 DC |
184 | |
185 | if (!$config->{base_dn}) { | |
186 | my $root = $ldap->root_dse(attrs => [ 'defaultNamingContext' ]); | |
187 | $config->{base_dn} = $root->get_value('defaultNamingContext'); | |
188 | } | |
189 | ||
190 | return $ldap; | |
191 | } | |
192 | ||
193 | sub authenticate_user { | |
194 | my ($class, $config, $realm, $username, $password) = @_; | |
195 | ||
196 | my $ldap = $class->connect_and_bind($config, $realm); | |
197 | ||
d9e93d2e DC |
198 | my $user_dn = PVE::LDAP::get_user_dn($ldap, $username, $config->{user_attr}, $config->{base_dn}); |
199 | PVE::LDAP::auth_user_dn($ldap, $user_dn, $password); | |
5bb4e06a DM |
200 | |
201 | $ldap->unbind(); | |
d9e93d2e | 202 | return 1; |
5bb4e06a DM |
203 | } |
204 | ||
205 | 1; |