]>
Commit | Line | Data |
---|---|---|
ca02e0ec | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
ca02e0ec AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
b1218840 AJ |
9 | /* |
10 | * ----------------------------------------------------------------------------- | |
11 | * | |
12 | * Author: Markus Moeller (markus_moeller at compuserve.com) | |
13 | * | |
14 | * Copyright (C) 2007 Markus Moeller. All rights reserved. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. | |
29 | * | |
30 | * ----------------------------------------------------------------------------- | |
31 | */ | |
32 | ||
0d0698bb AJ |
33 | /* get_attributes is partly from OpenLDAP Software <http://www.openldap.org/>. |
34 | * | |
35 | * Copyright 1998-2009 The OpenLDAP Foundation. | |
36 | * All rights reserved. | |
37 | * | |
38 | * Redistribution and use in source and binary forms, with or without | |
39 | * modification, are permitted only as authorized by the OpenLDAP | |
40 | * Public License. | |
41 | * | |
42 | * A copy of this license is available in the file LICENSE in the | |
43 | * top-level directory of the distribution or, alternatively, at | |
44 | * <http://www.OpenLDAP.org/license.html>. | |
45 | */ | |
46 | ||
f7f3304a | 47 | #include "squid.h" |
b1218840 AJ |
48 | #include "util.h" |
49 | ||
7451e5ad | 50 | #if HAVE_LDAP |
b1218840 AJ |
51 | |
52 | #include "support.h" | |
1a30fdf5 | 53 | #include <cerrno> |
b1218840 AJ |
54 | |
55 | char *convert_domain_to_bind_path(char *domain); | |
56 | char *escape_filter(char *filter); | |
57 | int check_AD(struct main_args *margs, LDAP * ld); | |
4ebcf1ce | 58 | int ldap_set_defaults(LDAP * ld); |
b1218840 AJ |
59 | int ldap_set_ssl_defaults(struct main_args *margs); |
60 | LDAP *tool_ldap_open(struct main_args *margs, char *host, int port, char *ssl); | |
61 | ||
62 | #define CONNECT_TIMEOUT 2 | |
63 | #define SEARCH_TIMEOUT 30 | |
64 | ||
65 | #define FILTER "(memberuid=%s)" | |
66 | #define ATTRIBUTE "cn" | |
1a22a39e | 67 | #define ATTRIBUTE_DN "distinguishedName" |
b1218840 AJ |
68 | #define FILTER_UID "(uid=%s)" |
69 | #define FILTER_GID "(&(gidNumber=%s)(objectclass=posixgroup))" | |
70 | #define ATTRIBUTE_GID "gidNumber" | |
1a22a39e MM |
71 | #define ATTRIBUTE_GID_AD "primaryGroupID" |
72 | #define ATTRIBUTE_SID "objectSID" | |
b1218840 AJ |
73 | |
74 | #define FILTER_AD "(samaccountname=%s)" | |
75 | #define ATTRIBUTE_AD "memberof" | |
76 | ||
0604b6b3 | 77 | size_t get_attributes(LDAP * ld, LDAPMessage * res, |
9e167fa2 | 78 | const char *attribute /* IN */, char ***out_val /* OUT (caller frees) */ ); |
0604b6b3 | 79 | size_t get_bin_attributes(LDAP * ld, LDAPMessage * res, |
9e167fa2 | 80 | const char *attribute /* IN */, char ***out_val, |
0604b6b3 MM |
81 | int **out_len /* OUT (caller frees) */ ); |
82 | int search_group_tree(struct main_args *margs, LDAP * ld, char *bindp, | |
83 | char *ldap_group, char *group, int depth); | |
b1218840 | 84 | |
1a22a39e MM |
85 | #if HAVE_SUN_LDAP_SDK || HAVE_MOZILLA_LDAP_SDK |
86 | #if HAVE_LDAP_REBINDPROC_CALLBACK | |
b1218840 | 87 | |
388d024e | 88 | #if HAVE_SASL_H || HAVE_SASL_SASL_H |
b1218840 AJ |
89 | static LDAP_REBINDPROC_CALLBACK ldap_sasl_rebind; |
90 | ||
91 | static int LDAP_CALL LDAP_CALLBACK | |
0604b6b3 MM |
92 | ldap_sasl_rebind(LDAP * ld, |
93 | char **whop, char **credp, int *methodp, int freeit, void *params) | |
b1218840 AJ |
94 | { |
95 | struct ldap_creds *cp = (struct ldap_creds *) params; | |
96 | whop = whop; | |
97 | credp = credp; | |
98 | methodp = methodp; | |
99 | freeit = freeit; | |
100 | return tool_sasl_bind(ld, cp->dn, cp->pw); | |
101 | } | |
102 | #endif | |
103 | ||
104 | static LDAP_REBINDPROC_CALLBACK ldap_simple_rebind; | |
105 | ||
106 | static int LDAP_CALL LDAP_CALLBACK | |
0604b6b3 MM |
107 | ldap_simple_rebind(LDAP * ld, |
108 | char **whop, char **credp, int *methodp, int freeit, void *params) | |
b1218840 AJ |
109 | { |
110 | struct ldap_creds *cp = (struct ldap_creds *) params; | |
75f3c557 MM |
111 | struct berval cred; |
112 | if (cp->pw) { | |
0604b6b3 MM |
113 | cred.bv_val = cp->pw; |
114 | cred.bv_len = strlen(cp->pw); | |
75f3c557 | 115 | } |
b1218840 AJ |
116 | whop = whop; |
117 | credp = credp; | |
118 | methodp = methodp; | |
119 | freeit = freeit; | |
a1b1756c AJ |
120 | return ldap_sasl_bind_s(ld, cp->dn, LDAP_SASL_SIMPLE, &cred, nullptr, nullptr, |
121 | nullptr); | |
b1218840 | 122 | } |
1a22a39e | 123 | #elif HAVE_LDAP_REBIND_PROC |
388d024e | 124 | #if HAVE_SASL_H || HAVE_SASL_SASL_H |
b1218840 AJ |
125 | static LDAP_REBIND_PROC ldap_sasl_rebind; |
126 | ||
127 | static int | |
0604b6b3 MM |
128 | ldap_sasl_rebind(LDAP * ld, |
129 | LDAP_CONST char *url, ber_tag_t request, ber_int_t msgid, void *params) | |
b1218840 AJ |
130 | { |
131 | struct ldap_creds *cp = (struct ldap_creds *) params; | |
b1218840 AJ |
132 | return tool_sasl_bind(ld, cp->dn, cp->pw); |
133 | } | |
388d024e | 134 | #endif /* HAVE_SASL_H || HAVE_SASL_SASL_H */ |
b1218840 AJ |
135 | |
136 | static LDAP_REBIND_PROC ldap_simple_rebind; | |
137 | ||
138 | static int | |
0604b6b3 MM |
139 | ldap_simple_rebind(LDAP * ld, |
140 | LDAP_CONST char *url, ber_tag_t request, ber_int_t msgid, void *params) | |
b1218840 AJ |
141 | { |
142 | struct ldap_creds *cp = (struct ldap_creds *) params; | |
75f3c557 MM |
143 | struct berval cred; |
144 | if (cp->pw) { | |
0604b6b3 MM |
145 | cred.bv_val = cp->pw; |
146 | cred.bv_len = strlen(cp->pw); | |
75f3c557 | 147 | } |
a1b1756c AJ |
148 | return ldap_sasl_bind_s(ld, cp->dn, LDAP_SASL_SIMPLE, &cred, nullptr, nullptr, |
149 | nullptr); | |
b1218840 AJ |
150 | } |
151 | ||
1a22a39e | 152 | #elif HAVE_LDAP_REBIND_FUNCTION |
b1218840 AJ |
153 | #ifndef LDAP_REFERRALS |
154 | #define LDAP_REFERRALS | |
8b082ed9 | 155 | #endif /* LDAP_REFERRALS */ |
388d024e | 156 | #if HAVE_SASL_H || HAVE_SASL_SASL_H |
b1218840 AJ |
157 | static LDAP_REBIND_FUNCTION ldap_sasl_rebind; |
158 | ||
159 | static int | |
0604b6b3 MM |
160 | ldap_sasl_rebind(LDAP * ld, |
161 | char **whop, char **credp, int *methodp, int freeit, void *params) | |
b1218840 AJ |
162 | { |
163 | struct ldap_creds *cp = (struct ldap_creds *) params; | |
164 | whop = whop; | |
165 | credp = credp; | |
166 | methodp = methodp; | |
167 | freeit = freeit; | |
168 | return tool_sasl_bind(ld, cp->dn, cp->pw); | |
169 | } | |
170 | #endif | |
171 | ||
172 | static LDAP_REBIND_FUNCTION ldap_simple_rebind; | |
173 | ||
174 | static int | |
0604b6b3 MM |
175 | ldap_simple_rebind(LDAP * ld, |
176 | char **whop, char **credp, int *methodp, int freeit, void *params) | |
b1218840 AJ |
177 | { |
178 | struct ldap_creds *cp = (struct ldap_creds *) params; | |
75f3c557 MM |
179 | struct berval cred; |
180 | if (cp->pw) { | |
0604b6b3 MM |
181 | cred.bv_val = cp->pw; |
182 | cred.bv_len = strlen(cp->pw); | |
75f3c557 | 183 | } |
b1218840 AJ |
184 | whop = whop; |
185 | credp = credp; | |
186 | methodp = methodp; | |
187 | freeit = freeit; | |
a1b1756c AJ |
188 | return ldap_sasl_bind_s(ld, cp->dn, LDAP_SASL_SIMPLE, &cred, nullptr, nullptr, |
189 | nullptr); | |
b1218840 AJ |
190 | } |
191 | #else | |
192 | #error "No rebind functione defined" | |
193 | #endif | |
194 | #else /* HAVE_SUN_LDAP_SDK */ | |
388d024e | 195 | #if HAVE_SASL_H || HAVE_SASL_SASL_H |
b1218840 AJ |
196 | static LDAP_REBIND_PROC ldap_sasl_rebind; |
197 | ||
198 | static int | |
8b082ed9 FC |
199 | ldap_sasl_rebind(LDAP * ld, LDAP_CONST char *, ber_tag_t, |
200 | ber_int_t, void *params) | |
b1218840 AJ |
201 | { |
202 | struct ldap_creds *cp = (struct ldap_creds *) params; | |
b1218840 AJ |
203 | return tool_sasl_bind(ld, cp->dn, cp->pw); |
204 | } | |
205 | #endif | |
206 | ||
207 | static LDAP_REBIND_PROC ldap_simple_rebind; | |
208 | ||
209 | static int | |
8b082ed9 FC |
210 | ldap_simple_rebind(LDAP * ld, LDAP_CONST char *, ber_tag_t, |
211 | ber_int_t, void *params) | |
b1218840 AJ |
212 | { |
213 | ||
214 | struct ldap_creds *cp = (struct ldap_creds *) params; | |
75f3c557 MM |
215 | struct berval cred; |
216 | if (cp->pw) { | |
0604b6b3 MM |
217 | cred.bv_val = cp->pw; |
218 | cred.bv_len = strlen(cp->pw); | |
75f3c557 | 219 | } |
aee3523a AR |
220 | return ldap_sasl_bind_s(ld, cp->dn, LDAP_SASL_SIMPLE, &cred, nullptr, nullptr, |
221 | nullptr); | |
b1218840 AJ |
222 | } |
223 | ||
224 | #endif | |
225 | char * | |
226 | convert_domain_to_bind_path(char *domain) | |
227 | { | |
aee3523a | 228 | char *dp, *bindp = nullptr, *bp = nullptr; |
4ebcf1ce | 229 | size_t i = 0; |
b1218840 AJ |
230 | |
231 | if (!domain) | |
aee3523a | 232 | return nullptr; |
b1218840 | 233 | |
a2f5277a | 234 | for (dp = domain; *dp; ++dp) { |
2e881a6f | 235 | if (*dp == '.') |
755494da | 236 | ++i; |
b1218840 | 237 | } |
2e881a6f A |
238 | /* |
239 | * add dc= and | |
240 | * replace . with ,dc= => new length = old length + #dots * 3 + 3 | |
b1218840 AJ |
241 | */ |
242 | bindp = (char *) xmalloc(strlen(domain) + 3 + i * 3 + 1); | |
243 | bp = bindp; | |
244 | strcpy(bp, "dc="); | |
245 | bp += 3; | |
a2f5277a | 246 | for (dp = domain; *dp; ++dp) { |
2e881a6f A |
247 | if (*dp == '.') { |
248 | strcpy(bp, ",dc="); | |
249 | bp += 4; | |
f207fe64 FC |
250 | } else { |
251 | *bp = *dp; | |
252 | ++bp; | |
253 | } | |
b1218840 AJ |
254 | } |
255 | *bp = '\0'; | |
256 | return bindp; | |
257 | } | |
258 | ||
259 | char * | |
260 | escape_filter(char *filter) | |
261 | { | |
b1218840 | 262 | char *ldap_filter_esc, *ldf; |
4ebcf1ce | 263 | size_t i; |
b1218840 AJ |
264 | |
265 | i = 0; | |
a2f5277a | 266 | for (ldap_filter_esc = filter; *ldap_filter_esc; ++ldap_filter_esc) { |
2e881a6f A |
267 | if ((*ldap_filter_esc == '*') || |
268 | (*ldap_filter_esc == '(') || | |
0604b6b3 | 269 | (*ldap_filter_esc == ')') || (*ldap_filter_esc == '\\')) |
2e881a6f | 270 | i = i + 3; |
b1218840 AJ |
271 | } |
272 | ||
273 | ldap_filter_esc = (char *) xcalloc(strlen(filter) + i + 1, sizeof(char)); | |
274 | ldf = ldap_filter_esc; | |
a2f5277a | 275 | for (; *filter; ++filter) { |
2e881a6f A |
276 | if (*filter == '*') { |
277 | strcpy(ldf, "\\2a"); | |
278 | ldf = ldf + 3; | |
279 | } else if (*filter == '(') { | |
280 | strcpy(ldf, "\\28"); | |
281 | ldf = ldf + 3; | |
282 | } else if (*filter == ')') { | |
283 | strcpy(ldf, "\\29"); | |
284 | ldf = ldf + 3; | |
285 | } else if (*filter == '\\') { | |
286 | strcpy(ldf, "\\5c"); | |
287 | ldf = ldf + 3; | |
288 | } else { | |
289 | *ldf = *filter; | |
755494da | 290 | ++ldf; |
2e881a6f | 291 | } |
b1218840 AJ |
292 | } |
293 | *ldf = '\0'; | |
294 | ||
295 | return ldap_filter_esc; | |
4ebcf1ce | 296 | } |
b1218840 AJ |
297 | |
298 | int | |
299 | check_AD(struct main_args *margs, LDAP * ld) | |
300 | { | |
301 | LDAPMessage *res; | |
aee3523a | 302 | char **attr_value = nullptr; |
b1218840 | 303 | struct timeval searchtime; |
4ebcf1ce MM |
304 | size_t max_attr = 0; |
305 | int rc = 0; | |
b1218840 AJ |
306 | |
307 | #define FILTER_SCHEMA "(objectclass=*)" | |
308 | #define ATTRIBUTE_SCHEMA "schemaNamingContext" | |
309 | #define FILTER_SAM "(ldapdisplayname=samaccountname)" | |
310 | ||
311 | searchtime.tv_sec = SEARCH_TIMEOUT; | |
312 | searchtime.tv_usec = 0; | |
313 | ||
0604b6b3 MM |
314 | debug((char *) |
315 | "%s| %s: DEBUG: Search ldap server with bind path \"\" and filter: %s\n", | |
316 | LogTime(), PROGRAM, FILTER_SCHEMA); | |
317 | rc = ldap_search_ext_s(ld, (char *) "", LDAP_SCOPE_BASE, | |
aee3523a | 318 | (char *) FILTER_SCHEMA, nullptr, 0, nullptr, nullptr, &searchtime, 0, &res); |
b1218840 AJ |
319 | |
320 | if (rc == LDAP_SUCCESS) | |
4ebcf1ce | 321 | max_attr = get_attributes(ld, res, ATTRIBUTE_SCHEMA, &attr_value); |
b1218840 AJ |
322 | |
323 | if (max_attr == 1) { | |
2e881a6f | 324 | ldap_msgfree(res); |
0604b6b3 MM |
325 | debug((char *) |
326 | "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", | |
327 | LogTime(), PROGRAM, attr_value[0], FILTER_SAM); | |
328 | rc = ldap_search_ext_s(ld, attr_value[0], LDAP_SCOPE_SUBTREE, | |
aee3523a | 329 | (char *) FILTER_SAM, nullptr, 0, nullptr, nullptr, &searchtime, 0, &res); |
0604b6b3 MM |
330 | debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), |
331 | PROGRAM, ldap_count_entries(ld, res), ldap_count_entries(ld, | |
332 | res) > 1 || ldap_count_entries(ld, res) == 0 ? "ies" : "y"); | |
2e881a6f A |
333 | if (ldap_count_entries(ld, res) > 0) |
334 | margs->AD = 1; | |
b1218840 | 335 | } else |
0604b6b3 MM |
336 | debug((char *) |
337 | "%s| %s: DEBUG: Did not find ldap entry for subschemasubentry\n", | |
338 | LogTime(), PROGRAM); | |
339 | debug((char *) | |
340 | "%s| %s: DEBUG: Determined ldap server %sas an Active Directory server\n", | |
341 | LogTime(), PROGRAM, margs->AD ? "" : "not "); | |
b1218840 AJ |
342 | /* |
343 | * Cleanup | |
344 | */ | |
345 | if (attr_value) { | |
4ebcf1ce | 346 | size_t j; |
a2f5277a | 347 | for (j = 0; j < max_attr; ++j) { |
2e881a6f A |
348 | xfree(attr_value[j]); |
349 | } | |
4ebcf1ce | 350 | safe_free(attr_value); |
b1218840 AJ |
351 | } |
352 | ldap_msgfree(res); | |
353 | return rc; | |
354 | } | |
0604b6b3 | 355 | |
b1218840 | 356 | int |
0604b6b3 MM |
357 | search_group_tree(struct main_args *margs, LDAP * ld, char *bindp, |
358 | char *ldap_group, char *group, int depth) | |
b1218840 | 359 | { |
aee3523a AR |
360 | LDAPMessage *res = nullptr; |
361 | char **attr_value = nullptr; | |
4ebcf1ce | 362 | size_t max_attr = 0; |
aee3523a AR |
363 | char *filter = nullptr; |
364 | char *search_exp = nullptr; | |
1a22a39e | 365 | size_t se_len = 0; |
4ebcf1ce | 366 | int rc = 0, retval = 0; |
b1218840 | 367 | int ldepth; |
aee3523a | 368 | char *ldap_filter_esc = nullptr; |
b1218840 AJ |
369 | struct timeval searchtime; |
370 | ||
371 | #define FILTER_GROUP_AD "(&(%s)(objectclass=group))" | |
372 | #define FILTER_GROUP "(&(memberuid=%s)(objectclass=posixgroup))" | |
373 | ||
374 | searchtime.tv_sec = SEARCH_TIMEOUT; | |
375 | searchtime.tv_usec = 0; | |
376 | ||
377 | if (margs->AD) | |
2e881a6f | 378 | filter = (char *) FILTER_GROUP_AD; |
b1218840 | 379 | else |
2e881a6f | 380 | filter = (char *) FILTER_GROUP; |
b1218840 AJ |
381 | |
382 | ldap_filter_esc = escape_filter(ldap_group); | |
383 | ||
1a22a39e MM |
384 | se_len = strlen(filter) + strlen(ldap_filter_esc) + 1; |
385 | search_exp = (char *) xmalloc(se_len); | |
386 | snprintf(search_exp, se_len, filter, ldap_filter_esc); | |
b1218840 | 387 | |
4ad7aabf | 388 | xfree(ldap_filter_esc); |
b1218840 AJ |
389 | |
390 | if (depth > margs->mdepth) { | |
0604b6b3 MM |
391 | debug((char *) "%s| %s: DEBUG: Max search depth reached %d>%d\n", |
392 | LogTime(), PROGRAM, depth, margs->mdepth); | |
4ad7aabf | 393 | xfree(search_exp); |
2e881a6f | 394 | return 0; |
b1218840 | 395 | } |
0604b6b3 MM |
396 | debug((char *) |
397 | "%s| %s: DEBUG: Search ldap server with bind path %s and filter : %s\n", | |
398 | LogTime(), PROGRAM, bindp, search_exp); | |
aee3523a AR |
399 | rc = ldap_search_ext_s(ld, bindp, LDAP_SCOPE_SUBTREE, search_exp, nullptr, 0, |
400 | nullptr, nullptr, &searchtime, 0, &res); | |
4ad7aabf | 401 | xfree(search_exp); |
b1218840 AJ |
402 | |
403 | if (rc != LDAP_SUCCESS) { | |
0604b6b3 MM |
404 | error((char *) "%s| %s: ERROR: Error searching ldap server: %s\n", |
405 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
2e881a6f | 406 | return 0; |
b1218840 | 407 | } |
0604b6b3 MM |
408 | debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM, |
409 | ldap_count_entries(ld, res), ldap_count_entries(ld, res) > 1 | |
410 | || ldap_count_entries(ld, res) == 0 ? "ies" : "y"); | |
b1218840 AJ |
411 | |
412 | if (margs->AD) | |
4ebcf1ce | 413 | max_attr = get_attributes(ld, res, ATTRIBUTE_AD, &attr_value); |
b1218840 | 414 | else |
4ebcf1ce | 415 | max_attr = get_attributes(ld, res, ATTRIBUTE, &attr_value); |
b1218840 AJ |
416 | |
417 | /* | |
418 | * Compare group names | |
419 | */ | |
420 | retval = 0; | |
421 | ldepth = depth + 1; | |
365642d3 | 422 | for (size_t j = 0; j < max_attr; ++j) { |
aee3523a | 423 | char *av = nullptr; |
b1218840 | 424 | |
2e881a6f A |
425 | /* Compare first CN= value assuming it is the same as the group name itself */ |
426 | av = attr_value[j]; | |
427 | if (!strncasecmp("CN=", av, 3)) { | |
aee3523a | 428 | char *avp = nullptr; |
2e881a6f A |
429 | av += 3; |
430 | if ((avp = strchr(av, ','))) { | |
431 | *avp = '\0'; | |
432 | } | |
433 | } | |
434 | if (debug_enabled) { | |
435 | int n; | |
a20724e4 | 436 | debug((char *) "%s| %s: DEBUG: Entry %zu \"%s\" in hex UTF-8 is ", LogTime(), PROGRAM, j + 1, av); |
a2f5277a | 437 | for (n = 0; av[n] != '\0'; ++n) |
2e881a6f A |
438 | fprintf(stderr, "%02x", (unsigned char) av[n]); |
439 | fprintf(stderr, "\n"); | |
440 | } | |
441 | if (!strcasecmp(group, av)) { | |
442 | retval = 1; | |
a20724e4 | 443 | debug((char *) "%s| %s: DEBUG: Entry %zu \"%s\" matches group name \"%s\"\n", LogTime(), PROGRAM, |
0604b6b3 | 444 | j + 1, av, group); |
2e881a6f A |
445 | break; |
446 | } else | |
a20724e4 | 447 | debug((char *) "%s| %s: DEBUG: Entry %zu \"%s\" does not match group name \"%s\"\n", LogTime(), |
0604b6b3 | 448 | PROGRAM, j + 1, av, group); |
2e881a6f A |
449 | /* |
450 | * Do recursive group search | |
451 | */ | |
0604b6b3 MM |
452 | debug((char *) |
453 | "%s| %s: DEBUG: Perform recursive group search for group \"%s\"\n", | |
454 | LogTime(), PROGRAM, av); | |
2e881a6f A |
455 | av = attr_value[j]; |
456 | if (search_group_tree(margs, ld, bindp, av, group, ldepth)) { | |
457 | retval = 1; | |
458 | if (!strncasecmp("CN=", av, 3)) { | |
aee3523a | 459 | char *avp = nullptr; |
2e881a6f A |
460 | av += 3; |
461 | if ((avp = strchr(av, ','))) { | |
462 | *avp = '\0'; | |
463 | } | |
464 | } | |
465 | if (debug_enabled) | |
a20724e4 | 466 | debug((char *) "%s| %s: DEBUG: Entry %zu \"%s\" is member of group named \"%s\"\n", LogTime(), |
0604b6b3 | 467 | PROGRAM, j + 1, av, group); |
2e881a6f A |
468 | else |
469 | break; | |
470 | ||
471 | } | |
b1218840 AJ |
472 | } |
473 | ||
474 | /* | |
475 | * Cleanup | |
476 | */ | |
477 | if (attr_value) { | |
365642d3 | 478 | for (size_t j = 0; j < max_attr; ++j) { |
2e881a6f A |
479 | xfree(attr_value[j]); |
480 | } | |
4ebcf1ce | 481 | safe_free(attr_value); |
b1218840 AJ |
482 | } |
483 | ldap_msgfree(res); | |
484 | ||
485 | return retval; | |
486 | } | |
487 | ||
488 | int | |
4ebcf1ce | 489 | ldap_set_defaults(LDAP * ld) |
b1218840 AJ |
490 | { |
491 | int val, rc = 0; | |
1a22a39e | 492 | #if LDAP_OPT_NETWORK_TIMEOUT |
b1218840 AJ |
493 | struct timeval tv; |
494 | #endif | |
495 | val = LDAP_VERSION3; | |
496 | rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &val); | |
497 | if (rc != LDAP_SUCCESS) { | |
0604b6b3 MM |
498 | debug((char *) |
499 | "%s| %s: DEBUG: Error while setting protocol version: %s\n", | |
500 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
2e881a6f | 501 | return rc; |
b1218840 AJ |
502 | } |
503 | rc = ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); | |
504 | if (rc != LDAP_SUCCESS) { | |
0604b6b3 MM |
505 | debug((char *) "%s| %s: DEBUG: Error while setting referrals off: %s\n", |
506 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
2e881a6f | 507 | return rc; |
b1218840 | 508 | } |
1a22a39e | 509 | #if LDAP_OPT_NETWORK_TIMEOUT |
b1218840 AJ |
510 | tv.tv_sec = CONNECT_TIMEOUT; |
511 | tv.tv_usec = 0; | |
512 | rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv); | |
513 | if (rc != LDAP_SUCCESS) { | |
0604b6b3 MM |
514 | debug((char *) |
515 | "%s| %s: DEBUG: Error while setting network timeout: %s\n", | |
516 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
2e881a6f | 517 | return rc; |
b1218840 AJ |
518 | } |
519 | #endif /* LDAP_OPT_NETWORK_TIMEOUT */ | |
520 | return LDAP_SUCCESS; | |
521 | } | |
522 | ||
523 | int | |
524 | ldap_set_ssl_defaults(struct main_args *margs) | |
525 | { | |
1a22a39e | 526 | #if HAVE_OPENLDAP || HAVE_LDAPSSL_CLIENT_INIT |
b1218840 AJ |
527 | int rc = 0; |
528 | #endif | |
1a22a39e | 529 | #if HAVE_OPENLDAP |
b1218840 | 530 | int val; |
1a22a39e | 531 | #elif HAVE_LDAPSSL_CLIENT_INIT |
a1b1756c | 532 | char *ssl_certdbpath = nullptr; |
b1218840 AJ |
533 | #endif |
534 | ||
1a22a39e | 535 | #if HAVE_OPENLDAP |
b1218840 | 536 | if (!margs->rc_allow) { |
aee3523a AR |
537 | char *ssl_cacertfile = nullptr; |
538 | char *ssl_cacertdir = nullptr; | |
0604b6b3 MM |
539 | debug((char *) |
540 | "%s| %s: DEBUG: Enable server certificate check for ldap server.\n", | |
541 | LogTime(), PROGRAM); | |
2e881a6f | 542 | val = LDAP_OPT_X_TLS_DEMAND; |
aee3523a | 543 | rc = ldap_set_option(nullptr, LDAP_OPT_X_TLS_REQUIRE_CERT, &val); |
2e881a6f | 544 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
545 | error((char *) |
546 | "%s| %s: ERROR: Error while setting LDAP_OPT_X_TLS_REQUIRE_CERT DEMAND for ldap server: %s\n", | |
547 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
2e881a6f A |
548 | return rc; |
549 | } | |
0604b6b3 | 550 | ssl_cacertfile = xstrdup(getenv("TLS_CACERTFILE")); |
2e881a6f A |
551 | if (!ssl_cacertfile) { |
552 | ssl_cacertfile = xstrdup("/etc/ssl/certs/cert.pem"); | |
2e881a6f | 553 | } |
0604b6b3 MM |
554 | if (access(ssl_cacertfile, R_OK) == 0) { |
555 | debug((char *) | |
556 | "%s| %s: DEBUG: Set certificate file for ldap server to %s. (Changeable through setting environment variable TLS_CACERTFILE)\n", | |
557 | LogTime(), PROGRAM, ssl_cacertfile); | |
aee3523a | 558 | rc = ldap_set_option(nullptr, LDAP_OPT_X_TLS_CACERTFILE, |
0604b6b3 | 559 | ssl_cacertfile); |
2e881a6f | 560 | xfree(ssl_cacertfile); |
0604b6b3 MM |
561 | if (rc != LDAP_OPT_SUCCESS) { |
562 | error((char *) | |
563 | "%s| %s: ERROR: Error while setting LDAP_OPT_X_TLS_CACERTFILE for ldap server: %s\n", | |
564 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
565 | return rc; | |
566 | } | |
567 | } else { | |
568 | debug((char *) | |
569 | "%s| %s: DEBUG: Set certificate file for ldap server to %s failed (%s). (Changeable through setting environment variable TLS_CACERTFILE) Trying db certificate directory\n", | |
570 | LogTime(), PROGRAM, ssl_cacertfile, strerror(errno)); | |
571 | xfree(ssl_cacertfile); | |
572 | ssl_cacertdir = xstrdup(getenv("TLS_CACERTDIR")); | |
573 | if (!ssl_cacertdir) { | |
574 | ssl_cacertdir = xstrdup("/etc/ssl/certs"); | |
575 | } | |
576 | if (access(ssl_cacertdir, R_OK) == 0) { | |
577 | debug((char *) | |
578 | "%s| %s: DEBUG: Set certificate database path for ldap server to %s. (Changeable through setting environment variable TLS_CACERTDIR)\n", | |
579 | LogTime(), PROGRAM, ssl_cacertdir); | |
aee3523a | 580 | rc = ldap_set_option(nullptr, LDAP_OPT_X_TLS_CACERTDIR, |
0604b6b3 MM |
581 | ssl_cacertdir); |
582 | xfree(ssl_cacertdir); | |
583 | if (rc != LDAP_OPT_SUCCESS) { | |
584 | error((char *) | |
585 | "%s| %s: ERROR: Error while setting LDAP_OPT_X_TLS_CACERTDIR for ldap server: %s\n", | |
586 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
587 | return rc; | |
588 | } | |
589 | } else { | |
590 | debug((char *) | |
591 | "%s| %s: DEBUG: Set certificate database path for ldap server to %s failed (%s). (Changeable through setting environment variable TLS_CACERTDIR)\n", | |
592 | LogTime(), PROGRAM, ssl_cacertdir, strerror(errno)); | |
593 | xfree(ssl_cacertdir); | |
594 | return errno; | |
595 | } | |
2e881a6f | 596 | } |
b1218840 | 597 | } else { |
0604b6b3 MM |
598 | debug((char *) |
599 | "%s| %s: DEBUG: Disable server certificate check for ldap server.\n", | |
600 | LogTime(), PROGRAM); | |
2e881a6f | 601 | val = LDAP_OPT_X_TLS_ALLOW; |
aee3523a | 602 | rc = ldap_set_option(nullptr, LDAP_OPT_X_TLS_REQUIRE_CERT, &val); |
2e881a6f | 603 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
604 | error((char *) |
605 | "%s| %s: ERROR: Error while setting LDAP_OPT_X_TLS_REQUIRE_CERT ALLOW for ldap server: %s\n", | |
606 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
2e881a6f A |
607 | return rc; |
608 | } | |
b1218840 | 609 | } |
1a22a39e | 610 | #elif HAVE_LDAPSSL_CLIENT_INIT |
2e881a6f | 611 | /* |
b1218840 AJ |
612 | * Solaris SSL ldap calls require path to certificate database |
613 | */ | |
2e881a6f | 614 | /* |
a1b1756c AJ |
615 | * rc = ldapssl_client_init( ssl_certdbpath, nullptr); |
616 | * rc = ldapssl_advclientauth_init( ssl_certdbpath, nullptr, 0 , nullptr, nullptr, 0, nullptr, 2); | |
2e881a6f | 617 | */ |
b1218840 AJ |
618 | ssl_certdbpath = getenv("SSL_CERTDBPATH"); |
619 | if (!ssl_certdbpath) { | |
2e881a6f | 620 | ssl_certdbpath = xstrdup("/etc/certs"); |
b1218840 | 621 | } |
0604b6b3 MM |
622 | debug((char *) |
623 | "%s| %s: DEBUG: Set certificate database path for ldap server to %s. (Changeable through setting environment variable SSL_CERTDBPATH)\n", | |
624 | LogTime(), PROGRAM, ssl_certdbpath); | |
b1218840 | 625 | if (!margs->rc_allow) { |
a1b1756c AJ |
626 | rc = ldapssl_advclientauth_init(ssl_certdbpath, nullptr, 0, nullptr, nullptr, 0, |
627 | nullptr, 2); | |
b1218840 | 628 | } else { |
a1b1756c AJ |
629 | rc = ldapssl_advclientauth_init(ssl_certdbpath, nullptr, 0, nullptr, nullptr, 0, |
630 | nullptr, 0); | |
0604b6b3 MM |
631 | debug((char *) |
632 | "%s| %s: DEBUG: Disable server certificate check for ldap server.\n", | |
633 | LogTime(), PROGRAM); | |
b1218840 | 634 | } |
4ebcf1ce | 635 | xfree(ssl_certdbpath); |
b1218840 | 636 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
637 | error((char *) |
638 | "%s| %s: ERROR: Error while setting SSL for ldap server: %s\n", | |
639 | LogTime(), PROGRAM, ldapssl_err2string(rc)); | |
2e881a6f | 640 | return rc; |
b1218840 AJ |
641 | } |
642 | #else | |
7e1c9aa7 | 643 | (void)margs; |
0604b6b3 MM |
644 | error((char *) "%s| %s: ERROR: SSL not supported by ldap library\n", |
645 | LogTime(), PROGRAM); | |
b1218840 AJ |
646 | #endif |
647 | return LDAP_SUCCESS; | |
648 | } | |
649 | ||
4ebcf1ce | 650 | size_t |
0604b6b3 MM |
651 | get_attributes(LDAP * ld, LDAPMessage * res, const char *attribute, |
652 | char ***ret_value) | |
b1218840 AJ |
653 | { |
654 | ||
1a22a39e | 655 | char **attr_value = *ret_value; |
4ebcf1ce | 656 | size_t max_attr = 0; |
b1218840 | 657 | |
b1218840 AJ |
658 | /* |
659 | * loop over attributes | |
660 | */ | |
0604b6b3 MM |
661 | debug((char *) "%s| %s: DEBUG: Search ldap entries for attribute : %s\n", |
662 | LogTime(), PROGRAM, attribute); | |
663 | for (LDAPMessage * msg = ldap_first_entry(ld, res); msg; | |
664 | msg = ldap_next_entry(ld, msg)) { | |
2e881a6f A |
665 | |
666 | switch (ldap_msgtype(msg)) { | |
667 | ||
1a22a39e | 668 | case LDAP_RES_SEARCH_ENTRY: { |
aee3523a | 669 | BerElement *b = nullptr; |
1a22a39e | 670 | for (char *attr = ldap_first_attribute(ld, msg, &b); attr; |
2e881a6f A |
671 | attr = ldap_next_attribute(ld, msg, b)) { |
672 | if (strcasecmp(attr, attribute) == 0) { | |
673 | struct berval **values; | |
2e881a6f | 674 | |
0604b6b3 | 675 | if ((values = |
aee3523a AR |
676 | ldap_get_values_len(ld, msg, attr)) != nullptr) { |
677 | for (int il = 0; values[il] != nullptr; ++il) { | |
2e881a6f | 678 | |
0604b6b3 MM |
679 | attr_value = |
680 | (char **) xrealloc(attr_value, | |
681 | (max_attr + 1) * sizeof(char *)); | |
2e881a6f A |
682 | if (!attr_value) |
683 | break; | |
684 | ||
0604b6b3 MM |
685 | attr_value[max_attr] = |
686 | (char *) xmalloc(values[il]->bv_len + 1); | |
687 | memcpy(attr_value[max_attr], values[il]->bv_val, | |
688 | values[il]->bv_len); | |
4ebcf1ce MM |
689 | attr_value[max_attr][values[il]->bv_len] = 0; |
690 | max_attr++; | |
2e881a6f | 691 | } |
2e881a6f A |
692 | } |
693 | ber_bvecfree(values); | |
694 | } | |
695 | ldap_memfree(attr); | |
696 | } | |
697 | ber_free(b, 0); | |
1a22a39e MM |
698 | } |
699 | break; | |
700 | case LDAP_RES_SEARCH_REFERENCE: | |
0604b6b3 MM |
701 | debug((char *) |
702 | "%s| %s: DEBUG: Received a search reference message\n", | |
703 | LogTime(), PROGRAM); | |
1a22a39e MM |
704 | break; |
705 | case LDAP_RES_SEARCH_RESULT: | |
0604b6b3 MM |
706 | debug((char *) "%s| %s: DEBUG: Received a search result message\n", |
707 | LogTime(), PROGRAM); | |
1a22a39e MM |
708 | break; |
709 | default: | |
2e881a6f | 710 | break; |
1a22a39e MM |
711 | } |
712 | } | |
713 | ||
a20724e4 | 714 | debug((char *) "%s| %s: DEBUG: %zu ldap entr%s found with attribute : %s\n", LogTime(), PROGRAM, |
0604b6b3 | 715 | max_attr, max_attr > 1 || max_attr == 0 ? "ies" : "y", attribute); |
1a22a39e MM |
716 | |
717 | *ret_value = attr_value; | |
718 | return max_attr; | |
719 | } | |
720 | ||
721 | size_t | |
0604b6b3 MM |
722 | get_bin_attributes(LDAP * ld, LDAPMessage * res, const char *attribute, |
723 | char ***ret_value, int **ret_len) | |
1a22a39e MM |
724 | { |
725 | ||
726 | char **attr_value = *ret_value; | |
727 | int *attr_len = *ret_len; | |
728 | size_t max_attr = 0; | |
729 | ||
730 | /* | |
731 | * loop over attributes | |
732 | */ | |
0604b6b3 MM |
733 | debug((char *) "%s| %s: DEBUG: Search ldap entries for attribute : %s\n", |
734 | LogTime(), PROGRAM, attribute); | |
735 | for (LDAPMessage * msg = ldap_first_entry(ld, res); msg; | |
736 | msg = ldap_next_entry(ld, msg)) { | |
1a22a39e MM |
737 | |
738 | switch (ldap_msgtype(msg)) { | |
739 | ||
740 | case LDAP_RES_SEARCH_ENTRY: { | |
aee3523a | 741 | BerElement *b = nullptr; |
1a22a39e MM |
742 | for (char *attr = ldap_first_attribute(ld, msg, &b); attr; |
743 | attr = ldap_next_attribute(ld, msg, b)) { | |
744 | if (strcasecmp(attr, attribute) == 0) { | |
745 | struct berval **values; | |
746 | ||
0604b6b3 | 747 | if ((values = |
aee3523a AR |
748 | ldap_get_values_len(ld, msg, attr)) != nullptr) { |
749 | for (int il = 0; values[il] != nullptr; ++il) { | |
1a22a39e | 750 | |
0604b6b3 MM |
751 | attr_value = |
752 | (char **) xrealloc(attr_value, | |
753 | (max_attr + 1) * sizeof(char *)); | |
1a22a39e MM |
754 | if (!attr_value) |
755 | break; | |
756 | ||
0604b6b3 MM |
757 | attr_len = |
758 | (int *) xrealloc(attr_len, | |
759 | (max_attr + 1) * sizeof(int)); | |
1a22a39e MM |
760 | if (!attr_len) |
761 | break; | |
762 | ||
0604b6b3 MM |
763 | attr_value[max_attr] = |
764 | (char *) xmalloc(values[il]->bv_len + 1); | |
765 | memcpy(attr_value[max_attr], values[il]->bv_val, | |
766 | values[il]->bv_len); | |
1a22a39e | 767 | attr_value[max_attr][values[il]->bv_len] = 0; |
0604b6b3 | 768 | attr_len[max_attr] = values[il]->bv_len; |
1a22a39e MM |
769 | max_attr++; |
770 | } | |
771 | } | |
772 | ber_bvecfree(values); | |
773 | } | |
774 | ldap_memfree(attr); | |
775 | } | |
776 | ber_free(b, 0); | |
777 | } | |
778 | break; | |
2e881a6f | 779 | case LDAP_RES_SEARCH_REFERENCE: |
0604b6b3 MM |
780 | debug((char *) |
781 | "%s| %s: DEBUG: Received a search reference message\n", | |
782 | LogTime(), PROGRAM); | |
2e881a6f A |
783 | break; |
784 | case LDAP_RES_SEARCH_RESULT: | |
0604b6b3 MM |
785 | debug((char *) "%s| %s: DEBUG: Received a search result message\n", |
786 | LogTime(), PROGRAM); | |
2e881a6f A |
787 | break; |
788 | default: | |
789 | break; | |
790 | } | |
b1218840 AJ |
791 | } |
792 | ||
a20724e4 | 793 | debug((char *) "%s| %s: DEBUG: %zu ldap entr%s found with attribute : %s\n", LogTime(), PROGRAM, |
0604b6b3 | 794 | max_attr, max_attr > 1 || max_attr == 0 ? "ies" : "y", attribute); |
b1218840 AJ |
795 | |
796 | *ret_value = attr_value; | |
1a22a39e | 797 | *ret_len = attr_len; |
b1218840 AJ |
798 | return max_attr; |
799 | } | |
800 | ||
801 | /* | |
802 | * call to open ldap server with or without SSL | |
803 | */ | |
804 | LDAP * | |
805 | tool_ldap_open(struct main_args * margs, char *host, int port, char *ssl) | |
806 | { | |
807 | LDAP *ld; | |
1a22a39e | 808 | #if HAVE_OPENLDAP |
aee3523a AR |
809 | LDAPURLDesc *url = nullptr; |
810 | char *ldapuri = nullptr; | |
b1218840 AJ |
811 | #endif |
812 | int rc = 0; | |
813 | ||
2e881a6f A |
814 | /* |
815 | * Use ldap open here to check if TCP connection is possible. If possible use it. | |
b1218840 AJ |
816 | * (Not sure if this is the best way) |
817 | */ | |
1a22a39e | 818 | #if HAVE_OPENLDAP |
b1218840 AJ |
819 | url = (LDAPURLDesc *) xmalloc(sizeof(*url)); |
820 | memset(url, 0, sizeof(*url)); | |
1a22a39e | 821 | #if HAVE_LDAP_URL_LUD_SCHEME |
b1218840 | 822 | if (ssl) |
a667f09e | 823 | url->lud_scheme = xstrdup("ldaps"); |
b1218840 | 824 | else |
a667f09e | 825 | url->lud_scheme = xstrdup("ldap"); |
b1218840 | 826 | #endif |
a667f09e | 827 | url->lud_host = xstrdup(host); |
b1218840 | 828 | url->lud_port = port; |
1a22a39e | 829 | #if HAVE_LDAP_SCOPE_DEFAULT |
b1218840 AJ |
830 | url->lud_scope = LDAP_SCOPE_DEFAULT; |
831 | #else | |
832 | url->lud_scope = LDAP_SCOPE_SUBTREE; | |
833 | #endif | |
1a22a39e | 834 | #if HAVE_LDAP_URL_DESC2STR |
b1218840 | 835 | ldapuri = ldap_url_desc2str(url); |
1a22a39e | 836 | #elif HAVE_LDAP_URL_PARSE |
b1218840 AJ |
837 | rc = ldap_url_parse(ldapuri, &url); |
838 | if (rc != LDAP_SUCCESS) { | |
0604b6b3 MM |
839 | error((char *) "%s| %s: ERROR: Error while parsing url: %s\n", |
840 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
b656d212 | 841 | xfree(ldapuri); |
4ebcf1ce | 842 | ldap_free_urldesc(url); |
a1b1756c | 843 | return nullptr; |
b1218840 AJ |
844 | } |
845 | #else | |
846 | #error "No URL parsing function" | |
847 | #endif | |
4ebcf1ce | 848 | ldap_free_urldesc(url); |
b1218840 | 849 | rc = ldap_initialize(&ld, ldapuri); |
b656d212 | 850 | xfree(ldapuri); |
b1218840 | 851 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
852 | error((char *) |
853 | "%s| %s: ERROR: Error while initialising connection to ldap server: %s\n", | |
854 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
aee3523a AR |
855 | ldap_unbind_ext(ld, nullptr, nullptr); |
856 | ld = nullptr; | |
857 | return nullptr; | |
b1218840 AJ |
858 | } |
859 | #else | |
860 | ld = ldap_init(host, port); | |
861 | #endif | |
4ebcf1ce | 862 | rc = ldap_set_defaults(ld); |
b1218840 | 863 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
864 | error((char *) |
865 | "%s| %s: ERROR: Error while setting default options for ldap server: %s\n", | |
866 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
aee3523a AR |
867 | ldap_unbind_ext(ld, nullptr, nullptr); |
868 | ld = nullptr; | |
869 | return nullptr; | |
b1218840 AJ |
870 | } |
871 | if (ssl) { | |
2e881a6f A |
872 | /* |
873 | * Try Start TLS first | |
874 | */ | |
875 | debug((char *) "%s| %s: DEBUG: Set SSL defaults\n", LogTime(), PROGRAM); | |
876 | rc = ldap_set_ssl_defaults(margs); | |
877 | if (rc != LDAP_SUCCESS) { | |
0604b6b3 MM |
878 | error((char *) |
879 | "%s| %s: ERROR: Error while setting SSL default options for ldap server: %s\n", | |
880 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
aee3523a AR |
881 | ldap_unbind_ext(ld, nullptr, nullptr); |
882 | ld = nullptr; | |
883 | return nullptr; | |
2e881a6f | 884 | } |
1a22a39e | 885 | #if HAVE_OPENLDAP |
2e881a6f A |
886 | /* |
887 | * Use tls if possible | |
888 | */ | |
aee3523a | 889 | rc = ldap_start_tls_s(ld, nullptr, nullptr); |
2e881a6f | 890 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
891 | debug((char *) |
892 | "%s| %s: WARNING: Error while setting start_tls for ldap server: %s\n", | |
893 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
aee3523a AR |
894 | ldap_unbind_ext(ld, nullptr, nullptr); |
895 | ld = nullptr; | |
2e881a6f A |
896 | url = (LDAPURLDesc *) xmalloc(sizeof(*url)); |
897 | memset(url, 0, sizeof(*url)); | |
1a22a39e | 898 | #if HAVE_LDAP_URL_LUD_SCHEME |
a667f09e | 899 | url->lud_scheme = xstrdup("ldaps"); |
b1218840 | 900 | #endif |
a667f09e | 901 | url->lud_host = xstrdup(host); |
2e881a6f | 902 | url->lud_port = port; |
1a22a39e | 903 | #if HAVE_LDAP_SCOPE_DEFAULT |
2e881a6f | 904 | url->lud_scope = LDAP_SCOPE_DEFAULT; |
b1218840 | 905 | #else |
2e881a6f | 906 | url->lud_scope = LDAP_SCOPE_SUBTREE; |
b1218840 | 907 | #endif |
1a22a39e | 908 | #if HAVE_LDAP_URL_DESC2STR |
2e881a6f | 909 | ldapuri = ldap_url_desc2str(url); |
1a22a39e | 910 | #elif HAVE_LDAP_URL_PARSE |
2e881a6f A |
911 | rc = ldap_url_parse(ldapuri, &url); |
912 | if (rc != LDAP_SUCCESS) { | |
0604b6b3 MM |
913 | error((char *) "%s| %s: ERROR: Error while parsing url: %s\n", |
914 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
4ad7aabf | 915 | xfree(ldapuri); |
4ebcf1ce | 916 | ldap_free_urldesc(url); |
a1b1756c | 917 | return nullptr; |
2e881a6f | 918 | } |
b1218840 AJ |
919 | #else |
920 | #error "No URL parsing function" | |
921 | #endif | |
4ebcf1ce | 922 | ldap_free_urldesc(url); |
2e881a6f | 923 | rc = ldap_initialize(&ld, ldapuri); |
4ad7aabf | 924 | xfree(ldapuri); |
2e881a6f | 925 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
926 | error((char *) |
927 | "%s| %s: ERROR: Error while initialising connection to ldap server: %s\n", | |
928 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
aee3523a AR |
929 | ldap_unbind_ext(ld, nullptr, nullptr); |
930 | ld = nullptr; | |
931 | return nullptr; | |
2e881a6f | 932 | } |
4ebcf1ce | 933 | rc = ldap_set_defaults(ld); |
2e881a6f | 934 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
935 | error((char *) |
936 | "%s| %s: ERROR: Error while setting default options for ldap server: %s\n", | |
937 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
aee3523a AR |
938 | ldap_unbind_ext(ld, nullptr, nullptr); |
939 | ld = nullptr; | |
940 | return nullptr; | |
2e881a6f A |
941 | } |
942 | } | |
1a22a39e | 943 | #elif HAVE_LDAPSSL_CLIENT_INIT |
2e881a6f A |
944 | ld = ldapssl_init(host, port, 1); |
945 | if (!ld) { | |
0604b6b3 MM |
946 | error((char *) |
947 | "%s| %s: ERROR: Error while setting SSL for ldap server: %s\n", | |
948 | LogTime(), PROGRAM, ldapssl_err2string(rc)); | |
a1b1756c AJ |
949 | ldap_unbind_ext(ld, nullptr, nullptr); |
950 | ld = nullptr; | |
951 | return nullptr; | |
2e881a6f | 952 | } |
4ebcf1ce | 953 | rc = ldap_set_defaults(ld); |
2e881a6f | 954 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
955 | error((char *) |
956 | "%s| %s: ERROR: Error while setting default options for ldap server: %s\n", | |
957 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
a1b1756c AJ |
958 | ldap_unbind_ext(ld, nullptr, nullptr); |
959 | ld = nullptr; | |
960 | return nullptr; | |
2e881a6f | 961 | } |
b1218840 | 962 | #else |
0604b6b3 MM |
963 | error((char *) "%s| %s: ERROR: SSL not supported by ldap library\n", |
964 | LogTime(), PROGRAM); | |
b1218840 AJ |
965 | #endif |
966 | } | |
967 | return ld; | |
968 | } | |
969 | ||
970 | /* | |
971 | * ldap calls to get attribute from Ldap Directory Server | |
972 | */ | |
973 | int | |
974 | get_memberof(struct main_args *margs, char *user, char *domain, char *group) | |
975 | { | |
aee3523a | 976 | LDAP *ld = nullptr; |
b1218840 | 977 | LDAPMessage *res; |
1a22a39e | 978 | #if !HAVE_SUN_LDAP_SDK |
b1218840 AJ |
979 | int ldap_debug = 0; |
980 | #endif | |
aee3523a AR |
981 | struct ldap_creds *lcreds = nullptr; |
982 | char *bindp = nullptr; | |
983 | char *filter = nullptr; | |
b1218840 | 984 | char *search_exp; |
1a22a39e | 985 | size_t se_len = 0; |
b1218840 | 986 | struct timeval searchtime; |
4ebcf1ce | 987 | int rc = 0, kc = 1; |
b1218840 | 988 | int retval; |
aee3523a | 989 | char **attr_value = nullptr; |
4ebcf1ce | 990 | size_t max_attr = 0; |
aee3523a | 991 | struct hstruct *hlist = nullptr; |
4ebcf1ce | 992 | size_t nhosts = 0; |
aee3523a | 993 | char *ldap_filter_esc = nullptr; |
b1218840 | 994 | |
b1218840 AJ |
995 | searchtime.tv_sec = SEARCH_TIMEOUT; |
996 | searchtime.tv_usec = 0; | |
997 | /* | |
998 | * Fill Kerberos memory cache with credential from keytab for SASL/GSSAPI | |
999 | */ | |
1000 | if (domain) { | |
0604b6b3 MM |
1001 | debug((char *) "%s| %s: DEBUG: Setup Kerberos credential cache\n", |
1002 | LogTime(), PROGRAM); | |
b1218840 | 1003 | |
1a22a39e | 1004 | #if HAVE_KRB5 |
7451e5ad MM |
1005 | if (margs->nokerberos) { |
1006 | kc = 1; | |
0604b6b3 MM |
1007 | debug((char *) |
1008 | "%s| %s: DEBUG: Kerberos is disabled. Use username/password with ldap url instead\n", | |
1009 | LogTime(), PROGRAM); | |
7451e5ad | 1010 | } else { |
40f1fd09 | 1011 | kc = krb5_create_cache(domain, margs->principal); |
7451e5ad | 1012 | if (kc) { |
0604b6b3 MM |
1013 | error((char *) |
1014 | "%s| %s: ERROR: Error during setup of Kerberos credential cache\n", | |
1015 | LogTime(), PROGRAM); | |
7451e5ad | 1016 | } |
2e881a6f | 1017 | } |
bec91ba0 MM |
1018 | #else |
1019 | kc = 1; | |
0604b6b3 MM |
1020 | debug((char *) |
1021 | "%s| %s: DEBUG: Kerberos is not supported. Use username/password with ldap url instead\n", | |
1022 | LogTime(), PROGRAM); | |
bec91ba0 | 1023 | #endif |
b1218840 | 1024 | } |
bec91ba0 | 1025 | |
1a22a39e | 1026 | if (kc && (!margs->lurl || !margs->luser || !margs->lpass)) { |
2e881a6f A |
1027 | /* |
1028 | * If Kerberos fails and no url given exit here | |
1029 | */ | |
1030 | retval = 0; | |
1031 | goto cleanup; | |
b1218840 | 1032 | } |
1a22a39e | 1033 | #if !HAVE_SUN_LDAP_SDK |
b1218840 AJ |
1034 | /* |
1035 | * Initialise ldap | |
1036 | */ | |
ce835b37 AJ |
1037 | // ldap_debug = 127 /* LDAP_DEBUG_TRACE */ ; |
1038 | // ldap_debug = -1 /* LDAP_DEBUG_ANY */ ; | |
b1218840 | 1039 | ldap_debug = 0; |
aee3523a | 1040 | (void) ldap_set_option(nullptr, LDAP_OPT_DEBUG_LEVEL, &ldap_debug); |
b1218840 | 1041 | #endif |
0604b6b3 MM |
1042 | debug((char *) "%s| %s: DEBUG: Initialise ldap connection\n", LogTime(), |
1043 | PROGRAM); | |
b1218840 AJ |
1044 | |
1045 | if (domain && !kc) { | |
2e881a6f | 1046 | if (margs->ssl) { |
0604b6b3 MM |
1047 | debug((char *) "%s| %s: DEBUG: Enable SSL to ldap servers\n", |
1048 | LogTime(), PROGRAM); | |
2e881a6f | 1049 | } |
0604b6b3 MM |
1050 | debug((char *) |
1051 | "%s| %s: DEBUG: Canonicalise ldap server name for domain %s\n", | |
1052 | LogTime(), PROGRAM, domain); | |
2e881a6f A |
1053 | /* |
1054 | * Loop over list of ldap servers of users domain | |
1055 | */ | |
1056 | nhosts = get_ldap_hostname_list(margs, &hlist, 0, domain); | |
365642d3 | 1057 | for (size_t i = 0; i < nhosts; ++i) { |
4ebcf1ce | 1058 | int port = 389; |
2e881a6f A |
1059 | if (hlist[i].port != -1) |
1060 | port = hlist[i].port; | |
0604b6b3 MM |
1061 | debug((char *) |
1062 | "%s| %s: DEBUG: Setting up connection to ldap server %s:%d\n", | |
1063 | LogTime(), PROGRAM, hlist[i].host, port); | |
2e881a6f A |
1064 | |
1065 | ld = tool_ldap_open(margs, hlist[i].host, port, margs->ssl); | |
1066 | if (!ld) | |
1067 | continue; | |
1068 | ||
1069 | /* | |
1070 | * ldap bind with SASL/GSSAPI authentication (only possible if a domain was part of the username) | |
1071 | */ | |
b1218840 | 1072 | |
388d024e | 1073 | #if HAVE_SASL_H || HAVE_SASL_SASL_H |
0604b6b3 MM |
1074 | debug((char *) |
1075 | "%s| %s: DEBUG: Bind to ldap server with SASL/GSSAPI\n", | |
1076 | LogTime(), PROGRAM); | |
2e881a6f A |
1077 | |
1078 | rc = tool_sasl_bind(ld, bindp, margs->ssl); | |
1079 | if (rc != LDAP_SUCCESS) { | |
0604b6b3 MM |
1080 | error((char *) |
1081 | "%s| %s: ERROR: Error while binding to ldap server with SASL/GSSAPI: %s\n", | |
1082 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
aee3523a AR |
1083 | ldap_unbind_ext(ld, nullptr, nullptr); |
1084 | ld = nullptr; | |
2e881a6f A |
1085 | continue; |
1086 | } | |
1a22a39e | 1087 | lcreds = (struct ldap_creds *) xmalloc(sizeof(struct ldap_creds)); |
aee3523a AR |
1088 | lcreds->dn = nullptr; |
1089 | lcreds->pw = margs->ssl ? xstrdup(margs->ssl) : nullptr; | |
2e881a6f | 1090 | ldap_set_rebind_proc(ld, ldap_sasl_rebind, (char *) lcreds); |
aee3523a | 1091 | if (ld != nullptr) { |
0604b6b3 MM |
1092 | debug((char *) |
1093 | "%s| %s: DEBUG: %s initialised %sconnection to ldap server %s:%d\n", | |
1094 | LogTime(), PROGRAM, ld ? "Successfully" : "Failed to", | |
1095 | margs->ssl ? "SSL protected " : "", hlist[i].host, port); | |
2e881a6f A |
1096 | break; |
1097 | } | |
b1218840 | 1098 | #else |
a1b1756c AJ |
1099 | ldap_unbind_ext(ld, nullptr, nullptr); |
1100 | ld = nullptr; | |
0604b6b3 MM |
1101 | error((char *) "%s| %s: ERROR: SASL not supported on system\n", |
1102 | LogTime(), PROGRAM); | |
2e881a6f | 1103 | continue; |
b1218840 | 1104 | #endif |
2e881a6f A |
1105 | } |
1106 | nhosts = free_hostname_list(&hlist, nhosts); | |
aee3523a | 1107 | if (ld == nullptr) { |
0604b6b3 MM |
1108 | debug((char *) |
1109 | "%s| %s: DEBUG: Error during initialisation of ldap connection: %s\n", | |
1110 | LogTime(), PROGRAM, strerror(errno)); | |
2e881a6f | 1111 | } |
71f62e86 AB |
1112 | if (margs->lbind) { |
1113 | bindp = xstrdup(margs->lbind); | |
1114 | } else { | |
1115 | bindp = convert_domain_to_bind_path(domain); | |
1116 | } | |
b1218840 AJ |
1117 | } |
1118 | if ((!domain || !ld) && margs->lurl && strstr(margs->lurl, "://")) { | |
4ebcf1ce MM |
1119 | char *hostname; |
1120 | char *host; | |
1121 | int port; | |
aee3523a | 1122 | char *ssl = nullptr; |
4ebcf1ce | 1123 | char *p; |
2e881a6f A |
1124 | /* |
1125 | * If username does not contain a domain and a url was given then try it | |
1126 | */ | |
1127 | hostname = strstr(margs->lurl, "://") + 3; | |
1128 | ssl = strstr(margs->lurl, "ldaps://"); | |
1129 | if (ssl) { | |
0604b6b3 MM |
1130 | debug((char *) "%s| %s: DEBUG: Enable SSL to ldap servers\n", |
1131 | LogTime(), PROGRAM); | |
2e881a6f | 1132 | } |
0604b6b3 MM |
1133 | debug((char *) "%s| %s: DEBUG: Canonicalise ldap server name %s\n", |
1134 | LogTime(), PROGRAM, hostname); | |
2e881a6f A |
1135 | /* |
1136 | * Loop over list of ldap servers | |
1137 | */ | |
1138 | host = xstrdup(hostname); | |
1139 | port = 389; | |
1140 | if ((p = strchr(host, ':'))) { | |
1141 | *p = '\0'; | |
755494da | 1142 | ++p; |
2e881a6f A |
1143 | port = atoi(p); |
1144 | } | |
4ebcf1ce MM |
1145 | nhosts = get_hostname_list(&hlist, 0, host); |
1146 | xfree(host); | |
365642d3 | 1147 | for (size_t i = 0; i < nhosts; ++i) { |
5f4daa47 SM |
1148 | struct berval cred; |
1149 | if (margs->lpass) { | |
0604b6b3 MM |
1150 | cred.bv_val = margs->lpass; |
1151 | cred.bv_len = strlen(margs->lpass); | |
5f4daa47 | 1152 | } |
2e881a6f A |
1153 | ld = tool_ldap_open(margs, hlist[i].host, port, ssl); |
1154 | if (!ld) | |
1155 | continue; | |
1156 | /* | |
1157 | * ldap bind with username/password authentication | |
1158 | */ | |
1159 | ||
0604b6b3 MM |
1160 | debug((char *) |
1161 | "%s| %s: DEBUG: Bind to ldap server with Username/Password\n", | |
1162 | LogTime(), PROGRAM); | |
1163 | rc = ldap_sasl_bind_s(ld, margs->luser, LDAP_SASL_SIMPLE, &cred, | |
aee3523a | 1164 | nullptr, nullptr, nullptr); |
2e881a6f | 1165 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
1166 | error((char *) |
1167 | "%s| %s: ERROR: Error while binding to ldap server with Username/Password: %s\n", | |
1168 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
aee3523a AR |
1169 | ldap_unbind_ext(ld, nullptr, nullptr); |
1170 | ld = nullptr; | |
2e881a6f A |
1171 | continue; |
1172 | } | |
1a22a39e | 1173 | lcreds = (struct ldap_creds *) xmalloc(sizeof(struct ldap_creds)); |
2e881a6f A |
1174 | lcreds->dn = xstrdup(margs->luser); |
1175 | lcreds->pw = xstrdup(margs->lpass); | |
1176 | ldap_set_rebind_proc(ld, ldap_simple_rebind, (char *) lcreds); | |
0604b6b3 MM |
1177 | debug((char *) |
1178 | "%s| %s: DEBUG: %s set up %sconnection to ldap server %s:%d\n", | |
1179 | LogTime(), PROGRAM, ld ? "Successfully" : "Failed to", | |
1180 | ssl ? "SSL protected " : "", hlist[i].host, port); | |
2e881a6f A |
1181 | break; |
1182 | ||
1183 | } | |
1184 | nhosts = free_hostname_list(&hlist, nhosts); | |
4ad7aabf | 1185 | xfree(bindp); |
2e881a6f A |
1186 | if (margs->lbind) { |
1187 | bindp = xstrdup(margs->lbind); | |
1188 | } else { | |
1189 | bindp = convert_domain_to_bind_path(domain); | |
1190 | } | |
b1218840 | 1191 | } |
aee3523a | 1192 | if (ld == nullptr) { |
0604b6b3 MM |
1193 | debug((char *) |
1194 | "%s| %s: DEBUG: Error during initialisation of ldap connection: %s\n", | |
1195 | LogTime(), PROGRAM, strerror(errno)); | |
2e881a6f A |
1196 | retval = 0; |
1197 | goto cleanup; | |
b1218840 AJ |
1198 | } |
1199 | /* | |
1200 | * ldap search for user | |
1201 | */ | |
2e881a6f | 1202 | /* |
b1218840 AJ |
1203 | * Check if server is AD by querying for attribute samaccountname |
1204 | */ | |
1205 | margs->AD = 0; | |
1206 | rc = check_AD(margs, ld); | |
1207 | if (rc != LDAP_SUCCESS) { | |
0604b6b3 MM |
1208 | error((char *) |
1209 | "%s| %s: ERROR: Error determining ldap server type: %s\n", | |
1210 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
aee3523a AR |
1211 | ldap_unbind_ext(ld, nullptr, nullptr); |
1212 | ld = nullptr; | |
2e881a6f A |
1213 | retval = 0; |
1214 | goto cleanup; | |
b1218840 AJ |
1215 | } |
1216 | if (margs->AD) | |
2e881a6f | 1217 | filter = (char *) FILTER_AD; |
b1218840 | 1218 | else |
2e881a6f | 1219 | filter = (char *) FILTER; |
b1218840 AJ |
1220 | |
1221 | ldap_filter_esc = escape_filter(user); | |
1222 | ||
1a22a39e MM |
1223 | se_len = strlen(filter) + strlen(ldap_filter_esc) + 1; |
1224 | search_exp = (char *) xmalloc(se_len); | |
1225 | snprintf(search_exp, se_len, filter, ldap_filter_esc); | |
b1218840 | 1226 | |
4ad7aabf | 1227 | xfree(ldap_filter_esc); |
b1218840 | 1228 | |
0604b6b3 MM |
1229 | debug((char *) |
1230 | "%s| %s: DEBUG: Search ldap server with bind path %s and filter : %s\n", | |
1231 | LogTime(), PROGRAM, bindp, search_exp); | |
aee3523a AR |
1232 | rc = ldap_search_ext_s(ld, bindp, LDAP_SCOPE_SUBTREE, search_exp, nullptr, 0, |
1233 | nullptr, nullptr, &searchtime, 0, &res); | |
4ad7aabf | 1234 | xfree(search_exp); |
b1218840 AJ |
1235 | |
1236 | if (rc != LDAP_SUCCESS) { | |
0604b6b3 MM |
1237 | error((char *) "%s| %s: ERROR: Error searching ldap server: %s\n", |
1238 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
aee3523a AR |
1239 | ldap_unbind_ext(ld, nullptr, nullptr); |
1240 | ld = nullptr; | |
2e881a6f A |
1241 | retval = 0; |
1242 | goto cleanup; | |
b1218840 | 1243 | } |
0604b6b3 MM |
1244 | debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM, |
1245 | ldap_count_entries(ld, res), ldap_count_entries(ld, res) > 1 | |
1246 | || ldap_count_entries(ld, res) == 0 ? "ies" : "y"); | |
b1218840 AJ |
1247 | |
1248 | if (ldap_count_entries(ld, res) != 0) { | |
1249 | ||
2e881a6f | 1250 | if (margs->AD) |
4ebcf1ce | 1251 | max_attr = get_attributes(ld, res, ATTRIBUTE_AD, &attr_value); |
2e881a6f | 1252 | else { |
4ebcf1ce | 1253 | max_attr = get_attributes(ld, res, ATTRIBUTE, &attr_value); |
2e881a6f A |
1254 | } |
1255 | ||
1256 | /* | |
1257 | * Compare group names | |
1258 | */ | |
1259 | retval = 0; | |
365642d3 | 1260 | for (size_t k = 0; k < max_attr; ++k) { |
aee3523a | 1261 | char *av = nullptr; |
2e881a6f A |
1262 | |
1263 | /* Compare first CN= value assuming it is the same as the group name itself */ | |
4ebcf1ce | 1264 | av = attr_value[k]; |
2e881a6f | 1265 | if (!strncasecmp("CN=", av, 3)) { |
aee3523a | 1266 | char *avp = nullptr; |
2e881a6f A |
1267 | av += 3; |
1268 | if ((avp = strchr(av, ','))) { | |
1269 | *avp = '\0'; | |
1270 | } | |
1271 | } | |
1272 | if (debug_enabled) { | |
a20724e4 | 1273 | debug((char *) "%s| %s: DEBUG: Entry %zu \"%s\" in hex UTF-8 is ", LogTime(), PROGRAM, k + 1, av); |
365642d3 | 1274 | for (unsigned int n = 0; av[n] != '\0'; ++n) |
2e881a6f A |
1275 | fprintf(stderr, "%02x", (unsigned char) av[n]); |
1276 | fprintf(stderr, "\n"); | |
1277 | } | |
1278 | if (!strcasecmp(group, av)) { | |
1279 | retval = 1; | |
1280 | if (debug_enabled) | |
a20724e4 | 1281 | debug((char *) "%s| %s: DEBUG: Entry %zu \"%s\" matches group name \"%s\"\n", LogTime(), |
0604b6b3 | 1282 | PROGRAM, k + 1, av, group); |
2e881a6f A |
1283 | else |
1284 | break; | |
1285 | } else | |
a20724e4 | 1286 | debug((char *) "%s| %s: DEBUG: Entry %zu \"%s\" does not match group name \"%s\"\n", LogTime(), |
0604b6b3 | 1287 | PROGRAM, k + 1, av, group); |
2e881a6f A |
1288 | } |
1289 | /* | |
1290 | * Do recursive group search for AD only since posixgroups can not contain other groups | |
1291 | */ | |
1292 | if (!retval && margs->AD) { | |
1293 | if (debug_enabled && max_attr > 0) { | |
0604b6b3 MM |
1294 | debug((char *) |
1295 | "%s| %s: DEBUG: Perform recursive group search\n", | |
1296 | LogTime(), PROGRAM); | |
2e881a6f | 1297 | } |
365642d3 | 1298 | for (size_t j = 0; j < max_attr; ++j) { |
aee3523a | 1299 | char *av = nullptr; |
2e881a6f A |
1300 | |
1301 | av = attr_value[j]; | |
1302 | if (search_group_tree(margs, ld, bindp, av, group, 1)) { | |
1303 | retval = 1; | |
1304 | if (!strncasecmp("CN=", av, 3)) { | |
aee3523a | 1305 | char *avp = nullptr; |
2e881a6f A |
1306 | av += 3; |
1307 | if ((avp = strchr(av, ','))) { | |
1308 | *avp = '\0'; | |
1309 | } | |
1310 | } | |
1311 | if (debug_enabled) | |
a20724e4 | 1312 | debug((char *) "%s| %s: DEBUG: Entry %zu group \"%s\" is (in)direct member of group \"%s\"\n", |
0604b6b3 | 1313 | LogTime(), PROGRAM, j + 1, av, group); |
2e881a6f A |
1314 | else |
1315 | break; | |
1316 | } | |
1317 | } | |
1318 | } | |
1319 | /* | |
1320 | * Cleanup | |
1321 | */ | |
1322 | if (attr_value) { | |
365642d3 | 1323 | for (size_t j = 0; j < max_attr; ++j) { |
2e881a6f A |
1324 | xfree(attr_value[j]); |
1325 | } | |
4ebcf1ce | 1326 | safe_free(attr_value); |
2e881a6f A |
1327 | } |
1328 | ldap_msgfree(res); | |
b1218840 | 1329 | } else if (ldap_count_entries(ld, res) == 0 && margs->AD) { |
2e881a6f | 1330 | ldap_msgfree(res); |
aee3523a AR |
1331 | ldap_unbind_ext(ld, nullptr, nullptr); |
1332 | ld = nullptr; | |
2e881a6f A |
1333 | retval = 0; |
1334 | goto cleanup; | |
b1218840 | 1335 | } else { |
2e881a6f A |
1336 | ldap_msgfree(res); |
1337 | retval = 0; | |
b1218840 AJ |
1338 | } |
1339 | ||
1a22a39e | 1340 | if (retval == 0) { |
2e881a6f A |
1341 | /* |
1342 | * Check for primary Group membership | |
1343 | */ | |
0604b6b3 MM |
1344 | debug((char *) |
1345 | "%s| %s: DEBUG: Search for primary group membership: \"%s\"\n", | |
1346 | LogTime(), PROGRAM, group); | |
1a22a39e MM |
1347 | if (margs->AD) |
1348 | filter = (char *) FILTER_AD; | |
1349 | else | |
1350 | filter = (char *) FILTER_UID; | |
2e881a6f A |
1351 | |
1352 | ldap_filter_esc = escape_filter(user); | |
1353 | ||
1a22a39e MM |
1354 | se_len = strlen(filter) + strlen(ldap_filter_esc) + 1; |
1355 | search_exp = (char *) xmalloc(se_len); | |
1356 | snprintf(search_exp, se_len, filter, ldap_filter_esc); | |
2e881a6f | 1357 | |
4ad7aabf | 1358 | xfree(ldap_filter_esc); |
2e881a6f | 1359 | |
0604b6b3 MM |
1360 | debug((char *) |
1361 | "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", | |
1362 | LogTime(), PROGRAM, bindp, search_exp); | |
aee3523a AR |
1363 | rc = ldap_search_ext_s(ld, bindp, LDAP_SCOPE_SUBTREE, search_exp, nullptr, |
1364 | 0, nullptr, nullptr, &searchtime, 0, &res); | |
4ad7aabf | 1365 | xfree(search_exp); |
2e881a6f | 1366 | |
0604b6b3 MM |
1367 | debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), |
1368 | PROGRAM, ldap_count_entries(ld, res), ldap_count_entries(ld, | |
1369 | res) > 1 || ldap_count_entries(ld, res) == 0 ? "ies" : "y"); | |
2e881a6f | 1370 | |
1a22a39e MM |
1371 | max_attr = 0; |
1372 | if (!rc) { | |
1373 | if (margs->AD) | |
0604b6b3 MM |
1374 | max_attr = |
1375 | get_attributes(ld, res, ATTRIBUTE_GID_AD, &attr_value); | |
1a22a39e MM |
1376 | else |
1377 | max_attr = get_attributes(ld, res, ATTRIBUTE_GID, &attr_value); | |
1378 | } | |
2e881a6f A |
1379 | |
1380 | if (max_attr == 1) { | |
aee3523a | 1381 | char **attr_value_2 = nullptr; |
4ebcf1ce | 1382 | size_t max_attr_2 = 0; |
2e881a6f | 1383 | |
1a22a39e | 1384 | if (margs->AD) { |
aee3523a AR |
1385 | char **attr_value_3 = nullptr; |
1386 | int *attr_len_3 = nullptr; | |
1a22a39e | 1387 | size_t max_attr_3 = 0; |
0604b6b3 | 1388 | uint32_t gid = atoi(attr_value[0]); |
1a22a39e MM |
1389 | |
1390 | /* Get objectsid and search for group | |
1391 | * with objectsid = domain(objectsid) + primarygroupid */ | |
0604b6b3 MM |
1392 | debug((char *) "%s| %s: DEBUG: Got primaryGroupID %u\n", |
1393 | LogTime(), PROGRAM, gid); | |
1394 | max_attr_3 = | |
1395 | get_bin_attributes(ld, res, ATTRIBUTE_SID, &attr_value_3, | |
1396 | &attr_len_3); | |
1a22a39e MM |
1397 | ldap_msgfree(res); |
1398 | if (max_attr_3 == 1) { | |
0604b6b3 | 1399 | int len = attr_len_3[0]; |
1a22a39e | 1400 | if (len < 4) { |
0604b6b3 MM |
1401 | debug((char *) |
1402 | "%s| %s: ERROR: Length %d is too short for objectSID\n", | |
1403 | LogTime(), PROGRAM, len); | |
1a22a39e MM |
1404 | rc = 1; |
1405 | } else { | |
aee3523a | 1406 | char *se = nullptr; |
0604b6b3 MM |
1407 | attr_value_3[0][len - 1] = ((gid >> 24) & 0xff); |
1408 | attr_value_3[0][len - 2] = ((gid >> 16) & 0xff); | |
1409 | attr_value_3[0][len - 3] = ((gid >> 8) & 0xff); | |
1410 | attr_value_3[0][len - 4] = ((gid >> 0) & 0xff); | |
1a22a39e MM |
1411 | |
1412 | #define FILTER_SID_1 "(objectSID=" | |
1413 | #define FILTER_SID_2 ")" | |
1414 | ||
0604b6b3 MM |
1415 | se_len = |
1416 | strlen(FILTER_SID_1) + len * 3 + | |
1417 | strlen(FILTER_SID_2) + 1; | |
1a22a39e | 1418 | search_exp = (char *) xmalloc(se_len); |
0604b6b3 | 1419 | snprintf(search_exp, se_len, "%s", FILTER_SID_1); |
1a22a39e | 1420 | |
0604b6b3 MM |
1421 | for (int j = 0; j < len; j++) { |
1422 | se = xstrdup(search_exp); | |
1423 | snprintf(search_exp, se_len, "%s\\%02x", se, | |
1424 | attr_value_3[0][j] & 0xFF); | |
1a22a39e MM |
1425 | xfree(se); |
1426 | } | |
0604b6b3 MM |
1427 | se = xstrdup(search_exp); |
1428 | snprintf(search_exp, se_len, "%s%s", se, FILTER_SID_2); | |
1a22a39e | 1429 | xfree(se); |
2e881a6f | 1430 | |
0604b6b3 MM |
1431 | debug((char *) |
1432 | "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", | |
1433 | LogTime(), PROGRAM, bindp, search_exp); | |
1a22a39e | 1434 | rc = ldap_search_ext_s(ld, bindp, LDAP_SCOPE_SUBTREE, |
aee3523a | 1435 | search_exp, nullptr, 0, nullptr, nullptr, &searchtime, 0, |
0604b6b3 | 1436 | &res); |
1a22a39e | 1437 | xfree(search_exp); |
2e881a6f | 1438 | |
0604b6b3 MM |
1439 | debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", |
1440 | LogTime(), PROGRAM, ldap_count_entries(ld, res), | |
1441 | ldap_count_entries(ld, res) > 1 | |
1442 | || ldap_count_entries(ld, res) == 0 ? "ies" : "y"); | |
2e881a6f | 1443 | |
1a22a39e MM |
1444 | } |
1445 | } else { | |
1446 | rc = 1; | |
1447 | } | |
1448 | if (attr_value_3) { | |
1449 | size_t j; | |
1450 | for (j = 0; j < max_attr_3; ++j) { | |
1451 | xfree(attr_value_3[j]); | |
1452 | } | |
1453 | safe_free(attr_value_3); | |
1454 | } | |
1455 | if (attr_len_3) { | |
1456 | xfree(attr_len_3); | |
1457 | } | |
1458 | } else { | |
1459 | ldap_msgfree(res); | |
1460 | filter = (char *) FILTER_GID; | |
1461 | ||
1462 | ldap_filter_esc = escape_filter(attr_value[0]); | |
1463 | ||
1464 | se_len = strlen(filter) + strlen(ldap_filter_esc) + 1; | |
1465 | search_exp = (char *) xmalloc(se_len); | |
1466 | snprintf(search_exp, se_len, filter, ldap_filter_esc); | |
2e881a6f | 1467 | |
1a22a39e | 1468 | xfree(ldap_filter_esc); |
2e881a6f | 1469 | |
0604b6b3 MM |
1470 | debug((char *) |
1471 | "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", | |
1472 | LogTime(), PROGRAM, bindp, search_exp); | |
1a22a39e | 1473 | rc = ldap_search_ext_s(ld, bindp, LDAP_SCOPE_SUBTREE, |
aee3523a | 1474 | search_exp, nullptr, 0, nullptr, nullptr, &searchtime, 0, &res); |
1a22a39e MM |
1475 | xfree(search_exp); |
1476 | } | |
1477 | ||
1478 | if (!rc) { | |
1479 | if (margs->AD) | |
0604b6b3 MM |
1480 | max_attr_2 = |
1481 | get_attributes(ld, res, ATTRIBUTE_DN, &attr_value_2); | |
1a22a39e | 1482 | else |
0604b6b3 MM |
1483 | max_attr_2 = |
1484 | get_attributes(ld, res, ATTRIBUTE, &attr_value_2); | |
1a22a39e MM |
1485 | ldap_msgfree(res); |
1486 | } else { | |
1487 | ldap_msgfree(res); | |
1488 | } | |
2e881a6f A |
1489 | /* |
1490 | * Compare group names | |
1491 | */ | |
1492 | retval = 0; | |
1493 | if (max_attr_2 == 1) { | |
2e881a6f | 1494 | /* Compare first CN= value assuming it is the same as the group name itself */ |
4ebcf1ce | 1495 | char *av = attr_value_2[0]; |
1a22a39e | 1496 | if (!strncasecmp("CN=", av, 3)) { |
aee3523a | 1497 | char *avp = nullptr; |
1a22a39e MM |
1498 | av += 3; |
1499 | if ((avp = strchr(av, ','))) { | |
1500 | *avp = '\0'; | |
1501 | } | |
1502 | } | |
2e881a6f A |
1503 | if (!strcasecmp(group, av)) { |
1504 | retval = 1; | |
0604b6b3 MM |
1505 | debug((char *) |
1506 | "%s| %s: DEBUG: \"%s\" matches group name \"%s\"\n", | |
1507 | LogTime(), PROGRAM, av, group); | |
2e881a6f | 1508 | } else |
0604b6b3 MM |
1509 | debug((char *) |
1510 | "%s| %s: DEBUG: \"%s\" does not match group name \"%s\"\n", | |
1511 | LogTime(), PROGRAM, av, group); | |
2e881a6f A |
1512 | |
1513 | } | |
1a22a39e MM |
1514 | /* |
1515 | * Do recursive group search for AD only since posixgroups can not contain other groups | |
1516 | */ | |
1517 | if (!retval && margs->AD) { | |
1518 | if (debug_enabled && max_attr_2 > 0) { | |
0604b6b3 MM |
1519 | debug((char *) |
1520 | "%s| %s: DEBUG: Perform recursive group search\n", | |
1521 | LogTime(), PROGRAM); | |
1a22a39e MM |
1522 | } |
1523 | for (size_t j = 0; j < max_attr_2; ++j) { | |
aee3523a | 1524 | char *av = nullptr; |
1a22a39e MM |
1525 | |
1526 | av = attr_value_2[j]; | |
1527 | if (search_group_tree(margs, ld, bindp, av, group, 1)) { | |
1528 | retval = 1; | |
1529 | if (!strncasecmp("CN=", av, 3)) { | |
aee3523a | 1530 | char *avp = nullptr; |
1a22a39e MM |
1531 | av += 3; |
1532 | if ((avp = strchr(av, ','))) { | |
1533 | *avp = '\0'; | |
1534 | } | |
1535 | } | |
1536 | if (debug_enabled) { | |
a20724e4 | 1537 | debug((char *) "%s| %s: DEBUG: Entry %zu group \"%s\" is (in)direct member of group \"%s\"\n", |
0604b6b3 | 1538 | LogTime(), PROGRAM, j + 1, av, group); |
1a22a39e MM |
1539 | } else { |
1540 | break; | |
1541 | } | |
1542 | } | |
1543 | } | |
1544 | } | |
2e881a6f A |
1545 | /* |
1546 | * Cleanup | |
1547 | */ | |
1548 | if (attr_value_2) { | |
4ebcf1ce | 1549 | size_t j; |
a2f5277a | 1550 | for (j = 0; j < max_attr_2; ++j) { |
2e881a6f A |
1551 | xfree(attr_value_2[j]); |
1552 | } | |
4ebcf1ce | 1553 | safe_free(attr_value_2); |
2e881a6f | 1554 | } |
2e881a6f | 1555 | |
0604b6b3 MM |
1556 | debug((char *) "%s| %s: DEBUG: Users primary group %s %s\n", |
1557 | LogTime(), PROGRAM, retval ? "matches" : "does not match", | |
1558 | group); | |
2e881a6f | 1559 | |
96072b37 | 1560 | } else { |
4f10fd9b | 1561 | ldap_msgfree(res); |
0604b6b3 MM |
1562 | debug((char *) |
1563 | "%s| %s: DEBUG: Did not find ldap entry for group %s\n", | |
1564 | LogTime(), PROGRAM, group); | |
96072b37 | 1565 | } |
2e881a6f A |
1566 | /* |
1567 | * Cleanup | |
1568 | */ | |
1569 | if (attr_value) { | |
365642d3 | 1570 | for (size_t j = 0; j < max_attr; ++j) { |
2e881a6f A |
1571 | xfree(attr_value[j]); |
1572 | } | |
4ebcf1ce | 1573 | safe_free(attr_value); |
2e881a6f | 1574 | } |
b1218840 | 1575 | } |
aee3523a AR |
1576 | rc = ldap_unbind_ext(ld, nullptr, nullptr); |
1577 | ld = nullptr; | |
b1218840 | 1578 | if (rc != LDAP_SUCCESS) { |
0604b6b3 MM |
1579 | error((char *) "%s| %s: ERROR: Error unbind ldap server: %s\n", |
1580 | LogTime(), PROGRAM, ldap_err2string(rc)); | |
b1218840 AJ |
1581 | } |
1582 | debug((char *) "%s| %s: DEBUG: Unbind ldap server\n", LogTime(), PROGRAM); | |
2e881a6f | 1583 | cleanup: |
b1218840 | 1584 | if (lcreds) { |
4ad7aabf AJ |
1585 | xfree(lcreds->dn); |
1586 | xfree(lcreds->pw); | |
2e881a6f | 1587 | xfree(lcreds); |
b1218840 | 1588 | } |
4ad7aabf | 1589 | xfree(bindp); |
b1218840 | 1590 | return (retval); |
b1218840 AJ |
1591 | } |
1592 | #endif | |
f53969cc | 1593 |