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

 

Introduction

Documentation

Support

Miscellaneous