1 | <?php |
||||
2 | /* |
||||
3 | * This program is free software: you can redistribute it and/or modify |
||||
4 | * it under the terms of the GNU General Public License as published by |
||||
5 | * the Free Software Foundation, either version 3 of the License, or |
||||
6 | * (at your option) any later version. |
||||
7 | * |
||||
8 | * This program is distributed in the hope that it will be useful, |
||||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the |
||||
11 | * GNU General Public License for more details. |
||||
12 | * |
||||
13 | * You should have received a copy of the GNU General Public License |
||||
14 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
15 | */ |
||||
16 | |||||
17 | /** |
||||
18 | * libreNMS HTTP-Authentication and LDAP Authorization Library |
||||
19 | * |
||||
20 | * @author Maximilian Wilhelm <[email protected]> |
||||
21 | * @copyright 2016 LibreNMS, Barbarossa |
||||
22 | * @license GPL |
||||
23 | */ |
||||
24 | |||||
25 | namespace LibreNMS\Authentication; |
||||
26 | |||||
27 | use App\Models\User; |
||||
28 | use LibreNMS\Config; |
||||
29 | use LibreNMS\Exceptions\AuthenticationException; |
||||
30 | use LibreNMS\Exceptions\LdapMissingException; |
||||
31 | |||||
32 | class LdapAuthorizationAuthorizer extends AuthorizerBase |
||||
33 | { |
||||
34 | use LdapSessionCache; |
||||
35 | |||||
36 | protected $ldap_connection; |
||||
37 | protected static $AUTH_IS_EXTERNAL = true; |
||||
38 | |||||
39 | public function __construct() |
||||
40 | { |
||||
41 | if (! function_exists('ldap_connect')) { |
||||
42 | throw new LdapMissingException(); |
||||
43 | } |
||||
44 | |||||
45 | /** |
||||
46 | * Set up connection to LDAP server |
||||
47 | */ |
||||
48 | $this->ldap_connection = @ldap_connect(Config::get('auth_ldap_server'), Config::get('auth_ldap_port')); |
||||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
49 | if (! $this->ldap_connection) { |
||||
50 | throw new AuthenticationException('Fatal error while connecting to LDAP server ' . Config::get('auth_ldap_server') . ':' . Config::get('auth_ldap_port') . ': ' . ldap_error($this->ldap_connection)); |
||||
51 | } |
||||
52 | if (Config::get('auth_ldap_version')) { |
||||
53 | ldap_set_option($this->ldap_connection, LDAP_OPT_PROTOCOL_VERSION, Config::get('auth_ldap_version')); |
||||
54 | } |
||||
55 | |||||
56 | if (Config::get('auth_ldap_starttls') && (Config::get('auth_ldap_starttls') == 'optional' || Config::get('auth_ldap_starttls') == 'require')) { |
||||
57 | $tls = ldap_start_tls($this->ldap_connection); |
||||
58 | if (Config::get('auth_ldap_starttls') == 'require' && $tls === false) { |
||||
59 | throw new AuthenticationException('Fatal error: LDAP TLS required but not successfully negotiated:' . ldap_error($this->ldap_connection)); |
||||
60 | } |
||||
61 | } |
||||
62 | } |
||||
63 | |||||
64 | public function authenticate($credentials) |
||||
65 | { |
||||
66 | if (isset($credentials['username']) && $this->userExists($credentials['username'])) { |
||||
67 | return true; |
||||
68 | } |
||||
69 | |||||
70 | $guest = Config::get('http_auth_guest'); |
||||
71 | if ($guest && User::thisAuth()->where('username', $guest)->exists()) { |
||||
72 | return true; |
||||
73 | } |
||||
74 | |||||
75 | throw new AuthenticationException(); |
||||
76 | } |
||||
77 | |||||
78 | public function userExists($username, $throw_exception = false) |
||||
79 | { |
||||
80 | if ($this->authLdapSessionCacheGet('user_exists')) { |
||||
81 | return true; |
||||
82 | } |
||||
83 | |||||
84 | $filter = '(' . Config::get('auth_ldap_prefix') . $username . ')'; |
||||
85 | $search = ldap_search($this->ldap_connection, trim(Config::get('auth_ldap_suffix'), ','), $filter); |
||||
86 | $entries = ldap_get_entries($this->ldap_connection, $search); |
||||
87 | if ($entries['count']) { |
||||
88 | /* |
||||
89 | * Cache positiv result as this will result in more queries which we |
||||
90 | * want to speed up. |
||||
91 | */ |
||||
92 | $this->authLdapSessionCacheSet('user_exists', 1); |
||||
93 | |||||
94 | return true; |
||||
95 | } |
||||
96 | |||||
97 | /* |
||||
98 | * Don't cache that user doesn't exists as this might be a misconfiguration |
||||
99 | * on some end and the user will be happy if it "just works" after the user |
||||
100 | * has been added to LDAP. |
||||
101 | */ |
||||
102 | return false; |
||||
103 | } |
||||
104 | |||||
105 | public function getUserlevel($username) |
||||
106 | { |
||||
107 | $userlevel = $this->authLdapSessionCacheGet('userlevel'); |
||||
108 | if ($userlevel) { |
||||
109 | return $userlevel; |
||||
110 | } else { |
||||
111 | $userlevel = 0; |
||||
112 | } |
||||
113 | |||||
114 | // Find all defined groups $username is in |
||||
115 | $filter = '(&(|(cn=' . join(')(cn=', array_keys(Config::get('auth_ldap_groups'))) . '))(' . Config::get('auth_ldap_groupmemberattr') . '=' . $this->getMembername($username) . '))'; |
||||
116 | $search = ldap_search($this->ldap_connection, Config::get('auth_ldap_groupbase'), $filter); |
||||
117 | $entries = ldap_get_entries($this->ldap_connection, $search); |
||||
118 | |||||
119 | // Loop the list and find the highest level |
||||
120 | foreach ($entries as $entry) { |
||||
121 | $groupname = $entry['cn'][0]; |
||||
122 | $authLdapGroups = Config::get('auth_ldap_groups'); |
||||
123 | if ($authLdapGroups[$groupname]['level'] > $userlevel) { |
||||
124 | $userlevel = $authLdapGroups[$groupname]['level']; |
||||
125 | } |
||||
126 | } |
||||
127 | |||||
128 | $this->authLdapSessionCacheSet('userlevel', $userlevel); |
||||
129 | |||||
130 | return $userlevel; |
||||
131 | } |
||||
132 | |||||
133 | public function getUserid($username) |
||||
134 | { |
||||
135 | $user_id = $this->authLdapSessionCacheGet('userid'); |
||||
136 | if (isset($user_id)) { |
||||
137 | return $user_id; |
||||
138 | } |
||||
139 | |||||
140 | $guest_username = Config::get('http_auth_guest'); |
||||
141 | $user_id = User::thisAuth()->where('username', $guest_username)->value('auth_id') ?: -1; |
||||
142 | |||||
143 | $filter = '(' . Config::get('auth_ldap_prefix') . $username . ')'; |
||||
144 | $search = ldap_search($this->ldap_connection, trim(Config::get('auth_ldap_suffix'), ','), $filter); |
||||
145 | $entries = ldap_get_entries($this->ldap_connection, $search); |
||||
146 | |||||
147 | if ($entries['count']) { |
||||
148 | $user_id = (int) $entries[0]['uidnumber'][0]; |
||||
149 | } |
||||
150 | |||||
151 | if ($user_id === -1) { |
||||
152 | // no user or guest user, don't allow |
||||
153 | if ($guest_username) { |
||||
154 | throw new AuthenticationException(); |
||||
155 | } else { |
||||
156 | throw new AuthenticationException('Guest login allowed.'); |
||||
157 | } |
||||
158 | } |
||||
159 | |||||
160 | $this->authLdapSessionCacheSet('userid', $user_id); |
||||
161 | |||||
162 | return $user_id; |
||||
0 ignored issues
–
show
|
|||||
163 | } |
||||
164 | |||||
165 | public function getUserlist() |
||||
166 | { |
||||
167 | $userlist = []; |
||||
168 | |||||
169 | $filter = '(' . Config::get('auth_ldap_prefix') . '*)'; |
||||
170 | |||||
171 | $search = ldap_search($this->ldap_connection, trim(Config::get('auth_ldap_suffix'), ','), $filter); |
||||
172 | $entries = ldap_get_entries($this->ldap_connection, $search); |
||||
173 | |||||
174 | if ($entries['count']) { |
||||
175 | foreach ($entries as $entry) { |
||||
176 | $username = $entry['uid'][0]; |
||||
177 | $realname = $entry['cn'][0]; |
||||
178 | $user_id = $entry['uidnumber'][0]; |
||||
179 | $email = $entry[Config::get('auth_ldap_emailattr')][0]; |
||||
180 | $ldap_groups = $this->getGroupList(); |
||||
181 | foreach ($ldap_groups as $ldap_group) { |
||||
182 | $ldap_comparison = ldap_compare( |
||||
183 | $this->ldap_connection, |
||||
184 | $ldap_group, |
||||
185 | Config::get('auth_ldap_groupmemberattr'), |
||||
0 ignored issues
–
show
It seems like
LibreNMS\Config::get('auth_ldap_groupmemberattr') can also be of type null ; however, parameter $attribute of ldap_compare() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
186 | $this->getMembername($username) |
||||
187 | ); |
||||
188 | if (! Config::has('auth_ldap_group') || $ldap_comparison === true) { |
||||
189 | $userlist[] = [ |
||||
190 | 'username' => $username, |
||||
191 | 'realname' => $realname, |
||||
192 | 'user_id' => $user_id, |
||||
193 | 'email' => $email, |
||||
194 | ]; |
||||
195 | } |
||||
196 | } |
||||
197 | } |
||||
198 | } |
||||
199 | |||||
200 | return $userlist; |
||||
201 | } |
||||
202 | |||||
203 | public function getUser($user_id) |
||||
204 | { |
||||
205 | foreach ($this->getUserlist() as $user) { |
||||
206 | if ((int) $user['user_id'] === (int) $user_id) { |
||||
207 | $user['level'] = $this->getUserlevel($user['username']); |
||||
208 | |||||
209 | return $user; |
||||
210 | } |
||||
211 | } |
||||
212 | |||||
213 | return false; |
||||
214 | } |
||||
215 | |||||
216 | protected function getMembername($username) |
||||
217 | { |
||||
218 | if (Config::get('auth_ldap_groupmembertype') == 'fulldn') { |
||||
219 | $membername = Config::get('auth_ldap_prefix') . $username . Config::get('auth_ldap_suffix'); |
||||
220 | } elseif (Config::get('auth_ldap_groupmembertype') == 'puredn') { |
||||
221 | $filter = '(' . Config::get('auth_ldap_attr.uid') . '=' . $username . ')'; |
||||
222 | $search = ldap_search($this->ldap_connection, Config::get('auth_ldap_groupbase'), $filter); |
||||
223 | $entries = ldap_get_entries($this->ldap_connection, $search); |
||||
224 | $membername = $entries[0]['dn']; |
||||
225 | } else { |
||||
226 | $membername = $username; |
||||
227 | } |
||||
228 | |||||
229 | return $membername; |
||||
230 | } |
||||
231 | |||||
232 | public function getGroupList() |
||||
233 | { |
||||
234 | $ldap_groups = []; |
||||
235 | $default_group = 'cn=groupname,ou=groups,dc=example,dc=com'; |
||||
236 | if (Config::has('auth_ldap_group')) { |
||||
237 | if (Config::get('auth_ldap_group') !== $default_group) { |
||||
238 | $ldap_groups[] = Config::get('auth_ldap_group'); |
||||
239 | } |
||||
240 | } |
||||
241 | |||||
242 | foreach (Config::get('auth_ldap_groups') as $key => $value) { |
||||
243 | $dn = "cn=$key," . Config::get('auth_ldap_groupbase'); |
||||
244 | $ldap_groups[] = $dn; |
||||
245 | } |
||||
246 | |||||
247 | return $ldap_groups; |
||||
248 | } |
||||
249 | } |
||||
250 |