2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
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.
10 * -----------------------------------------------------------------------------
12 * Author: Markus Moeller (markus_moeller at compuserve.com)
14 * Copyright (C) 2007 Markus Moeller. All rights reserved.
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.
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.
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.
30 * -----------------------------------------------------------------------------
33 /* get_attributes is partly from OpenLDAP Software <http://www.openldap.org/>.
35 * Copyright 1998-2009 The OpenLDAP Foundation.
36 * All rights reserved.
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted only as authorized by the OpenLDAP
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>.
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
);
62 #define CONNECT_TIMEOUT 2
63 #define SEARCH_TIMEOUT 30
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"
74 #define FILTER_AD "(samaccountname=%s)"
75 #define ATTRIBUTE_AD "memberof"
77 size_t get_attributes(LDAP
* ld
, LDAPMessage
* res
, const char *attribute
/* IN */ , char ***out_val
/* OUT (caller frees) */ );
78 size_t get_bin_attributes(LDAP
* ld
, LDAPMessage
* res
, const char *attribute
/* IN */ , char ***out_val
, int **out_len
/* OUT (caller frees) */ );
79 int search_group_tree(struct main_args
*margs
, LDAP
* ld
, char *bindp
, char *ldap_group
, char *group
, int depth
);
81 #if HAVE_SUN_LDAP_SDK || HAVE_MOZILLA_LDAP_SDK
82 #if HAVE_LDAP_REBINDPROC_CALLBACK
84 #if HAVE_SASL_H || HAVE_SASL_SASL_H || HAVE_SASL_DARWIN
85 static LDAP_REBINDPROC_CALLBACK ldap_sasl_rebind
;
87 static int LDAP_CALL LDAP_CALLBACK
96 struct ldap_creds
*cp
= (struct ldap_creds
*) params
;
101 return tool_sasl_bind(ld
, cp
->dn
, cp
->pw
);
105 static LDAP_REBINDPROC_CALLBACK ldap_simple_rebind
;
107 static int LDAP_CALL LDAP_CALLBACK
116 struct ldap_creds
*cp
= (struct ldap_creds
*) params
;
121 return ldap_bind_s(ld
, cp
->dn
, cp
->pw
, LDAP_AUTH_SIMPLE
);
123 #elif HAVE_LDAP_REBIND_PROC
124 #if HAVE_SASL_H || HAVE_SASL_SASL_H || HAVE_SASL_DARWIN
125 static LDAP_REBIND_PROC ldap_sasl_rebind
;
130 LDAP_CONST
char *url
,
135 struct ldap_creds
*cp
= (struct ldap_creds
*) params
;
136 return tool_sasl_bind(ld
, cp
->dn
, cp
->pw
);
140 static LDAP_REBIND_PROC ldap_simple_rebind
;
145 LDAP_CONST
char *url
,
150 struct ldap_creds
*cp
= (struct ldap_creds
*) params
;
151 return ldap_bind_s(ld
, cp
->dn
, cp
->pw
, LDAP_AUTH_SIMPLE
);
154 #elif HAVE_LDAP_REBIND_FUNCTION
155 #ifndef LDAP_REFERRALS
156 #define LDAP_REFERRALS
158 #if HAVE_SASL_H || HAVE_SASL_SASL_H || HAVE_SASL_DARWIN
159 static LDAP_REBIND_FUNCTION ldap_sasl_rebind
;
170 struct ldap_creds
*cp
= (struct ldap_creds
*) params
;
175 return tool_sasl_bind(ld
, cp
->dn
, cp
->pw
);
179 static LDAP_REBIND_FUNCTION ldap_simple_rebind
;
190 struct ldap_creds
*cp
= (struct ldap_creds
*) params
;
195 return ldap_bind_s(ld
, cp
->dn
, cp
->pw
, LDAP_AUTH_SIMPLE
);
198 #error "No rebind functione defined"
200 #else /* HAVE_SUN_LDAP_SDK */
201 #if HAVE_SASL_H || HAVE_SASL_SASL_H || HAVE_SASL_DARWIN
202 static LDAP_REBIND_PROC ldap_sasl_rebind
;
205 ldap_sasl_rebind(LDAP
*ld
, LDAP_CONST
char *, ber_tag_t
, ber_int_t
, void *params
)
207 struct ldap_creds
*cp
= (struct ldap_creds
*) params
;
208 return tool_sasl_bind(ld
, cp
->dn
, cp
->pw
);
212 static LDAP_REBIND_PROC ldap_simple_rebind
;
215 ldap_simple_rebind(LDAP
* ld
, LDAP_CONST
char *, ber_tag_t
, ber_int_t
, void *params
)
218 struct ldap_creds
*cp
= (struct ldap_creds
*) params
;
219 return ldap_bind_s(ld
, cp
->dn
, cp
->pw
, LDAP_AUTH_SIMPLE
);
224 convert_domain_to_bind_path(char *domain
)
226 char *dp
, *bindp
= NULL
, *bp
= NULL
;
232 for (dp
= domain
; *dp
; ++dp
) {
238 * replace . with ,dc= => new length = old length + #dots * 3 + 3
240 bindp
= (char *) xmalloc(strlen(domain
) + 3 + i
* 3 + 1);
244 for (dp
= domain
; *dp
; ++dp
) {
258 escape_filter(char *filter
)
260 char *ldap_filter_esc
, *ldf
;
264 for (ldap_filter_esc
= filter
; *ldap_filter_esc
; ++ldap_filter_esc
) {
265 if ((*ldap_filter_esc
== '*') ||
266 (*ldap_filter_esc
== '(') ||
267 (*ldap_filter_esc
== ')') ||
268 (*ldap_filter_esc
== '\\'))
272 ldap_filter_esc
= (char *) xcalloc(strlen(filter
) + i
+ 1, sizeof(char));
273 ldf
= ldap_filter_esc
;
274 for (; *filter
; ++filter
) {
275 if (*filter
== '*') {
278 } else if (*filter
== '(') {
281 } else if (*filter
== ')') {
284 } else if (*filter
== '\\') {
294 return ldap_filter_esc
;
298 check_AD(struct main_args
*margs
, LDAP
* ld
)
301 char **attr_value
= NULL
;
302 struct timeval searchtime
;
306 #define FILTER_SCHEMA "(objectclass=*)"
307 #define ATTRIBUTE_SCHEMA "schemaNamingContext"
308 #define FILTER_SAM "(ldapdisplayname=samaccountname)"
310 searchtime
.tv_sec
= SEARCH_TIMEOUT
;
311 searchtime
.tv_usec
= 0;
313 debug((char *) "%s| %s: DEBUG: Search ldap server with bind path \"\" and filter: %s\n", LogTime(), PROGRAM
, FILTER_SCHEMA
);
314 rc
= ldap_search_ext_s(ld
, (char *) "", LDAP_SCOPE_BASE
, (char *) FILTER_SCHEMA
, NULL
, 0,
315 NULL
, NULL
, &searchtime
, 0, &res
);
317 if (rc
== LDAP_SUCCESS
)
318 max_attr
= get_attributes(ld
, res
, ATTRIBUTE_SCHEMA
, &attr_value
);
322 debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", LogTime(), PROGRAM
, attr_value
[0], FILTER_SAM
);
323 rc
= ldap_search_ext_s(ld
, attr_value
[0], LDAP_SCOPE_SUBTREE
, (char *) FILTER_SAM
, NULL
, 0,
324 NULL
, NULL
, &searchtime
, 0, &res
);
325 debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM
, ldap_count_entries(ld
, res
), ldap_count_entries(ld
, res
) > 1 || ldap_count_entries(ld
, res
) == 0 ? "ies" : "y");
326 if (ldap_count_entries(ld
, res
) > 0)
329 debug((char *) "%s| %s: DEBUG: Did not find ldap entry for subschemasubentry\n", LogTime(), PROGRAM
);
330 debug((char *) "%s| %s: DEBUG: Determined ldap server %sas an Active Directory server\n", LogTime(), PROGRAM
, margs
->AD
? "" : "not ");
336 for (j
= 0; j
< max_attr
; ++j
) {
337 xfree(attr_value
[j
]);
339 safe_free(attr_value
);
345 search_group_tree(struct main_args
*margs
, LDAP
* ld
, char *bindp
, char *ldap_group
, char *group
, int depth
)
347 LDAPMessage
*res
= NULL
;
348 char **attr_value
= NULL
;
351 char *search_exp
= NULL
;
353 int rc
= 0, retval
= 0;
355 char *ldap_filter_esc
= NULL
;
356 struct timeval searchtime
;
358 #define FILTER_GROUP_AD "(&(%s)(objectclass=group))"
359 #define FILTER_GROUP "(&(memberuid=%s)(objectclass=posixgroup))"
361 searchtime
.tv_sec
= SEARCH_TIMEOUT
;
362 searchtime
.tv_usec
= 0;
365 filter
= (char *) FILTER_GROUP_AD
;
367 filter
= (char *) FILTER_GROUP
;
369 ldap_filter_esc
= escape_filter(ldap_group
);
371 se_len
= strlen(filter
) + strlen(ldap_filter_esc
) + 1;
372 search_exp
= (char *) xmalloc(se_len
);
373 snprintf(search_exp
, se_len
, filter
, ldap_filter_esc
);
375 xfree(ldap_filter_esc
);
377 if (depth
> margs
->mdepth
) {
378 debug((char *) "%s| %s: DEBUG: Max search depth reached %d>%d\n", LogTime(), PROGRAM
, depth
, margs
->mdepth
);
382 debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter : %s\n", LogTime(), PROGRAM
, bindp
, search_exp
);
383 rc
= ldap_search_ext_s(ld
, bindp
, LDAP_SCOPE_SUBTREE
,
385 NULL
, NULL
, &searchtime
, 0, &res
);
388 if (rc
!= LDAP_SUCCESS
) {
389 error((char *) "%s| %s: ERROR: Error searching ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
392 debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM
, ldap_count_entries(ld
, res
), ldap_count_entries(ld
, res
) > 1 || ldap_count_entries(ld
, res
) == 0 ? "ies" : "y");
395 max_attr
= get_attributes(ld
, res
, ATTRIBUTE_AD
, &attr_value
);
397 max_attr
= get_attributes(ld
, res
, ATTRIBUTE
, &attr_value
);
400 * Compare group names
404 for (size_t j
= 0; j
< max_attr
; ++j
) {
407 /* Compare first CN= value assuming it is the same as the group name itself */
409 if (!strncasecmp("CN=", av
, 3)) {
412 if ((avp
= strchr(av
, ','))) {
418 debug((char *) "%s| %s: DEBUG: Entry %" PRIuSIZE
" \"%s\" in hex UTF-8 is ", LogTime(), PROGRAM
, j
+ 1, av
);
419 for (n
= 0; av
[n
] != '\0'; ++n
)
420 fprintf(stderr
, "%02x", (unsigned char) av
[n
]);
421 fprintf(stderr
, "\n");
423 if (!strcasecmp(group
, av
)) {
425 debug((char *) "%s| %s: DEBUG: Entry %" PRIuSIZE
" \"%s\" matches group name \"%s\"\n", LogTime(), PROGRAM
, j
+ 1, av
, group
);
428 debug((char *) "%s| %s: DEBUG: Entry %" PRIuSIZE
" \"%s\" does not match group name \"%s\"\n", LogTime(), PROGRAM
, j
+ 1, av
, group
);
430 * Do recursive group search
432 debug((char *) "%s| %s: DEBUG: Perform recursive group search for group \"%s\"\n", LogTime(), PROGRAM
, av
);
434 if (search_group_tree(margs
, ld
, bindp
, av
, group
, ldepth
)) {
436 if (!strncasecmp("CN=", av
, 3)) {
439 if ((avp
= strchr(av
, ','))) {
444 debug((char *) "%s| %s: DEBUG: Entry %" PRIuSIZE
" \"%s\" is member of group named \"%s\"\n", LogTime(), PROGRAM
, j
+ 1, av
, group
);
455 for (size_t j
= 0; j
< max_attr
; ++j
) {
456 xfree(attr_value
[j
]);
458 safe_free(attr_value
);
466 ldap_set_defaults(LDAP
* ld
)
469 #if LDAP_OPT_NETWORK_TIMEOUT
473 rc
= ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &val
);
474 if (rc
!= LDAP_SUCCESS
) {
475 debug((char *) "%s| %s: DEBUG: Error while setting protocol version: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
478 rc
= ldap_set_option(ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
479 if (rc
!= LDAP_SUCCESS
) {
480 debug((char *) "%s| %s: DEBUG: Error while setting referrals off: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
483 #if LDAP_OPT_NETWORK_TIMEOUT
484 tv
.tv_sec
= CONNECT_TIMEOUT
;
486 rc
= ldap_set_option(ld
, LDAP_OPT_NETWORK_TIMEOUT
, &tv
);
487 if (rc
!= LDAP_SUCCESS
) {
488 debug((char *) "%s| %s: DEBUG: Error while setting network timeout: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
491 #endif /* LDAP_OPT_NETWORK_TIMEOUT */
496 ldap_set_ssl_defaults(struct main_args
*margs
)
498 #if HAVE_OPENLDAP || HAVE_LDAPSSL_CLIENT_INIT
503 #elif HAVE_LDAPSSL_CLIENT_INIT
504 char *ssl_certdbpath
= NULL
;
508 if (!margs
->rc_allow
) {
509 char *ssl_cacertfile
= NULL
;
511 debug((char *) "%s| %s: DEBUG: Enable server certificate check for ldap server.\n", LogTime(), PROGRAM
);
512 val
= LDAP_OPT_X_TLS_DEMAND
;
513 rc
= ldap_set_option(NULL
, LDAP_OPT_X_TLS_REQUIRE_CERT
, &val
);
514 if (rc
!= LDAP_SUCCESS
) {
515 error((char *) "%s| %s: ERROR: Error while setting LDAP_OPT_X_TLS_REQUIRE_CERT DEMAND for ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
518 ssl_cacertfile
= getenv("TLS_CACERTFILE");
520 if (!ssl_cacertfile
) {
521 ssl_cacertfile
= xstrdup("/etc/ssl/certs/cert.pem");
524 debug((char *) "%s| %s: DEBUG: Set certificate file for ldap server to %s.(Changeable through setting environment variable TLS_CACERTFILE)\n", LogTime(), PROGRAM
, ssl_cacertfile
);
525 rc
= ldap_set_option(NULL
, LDAP_OPT_X_TLS_CACERTFILE
, ssl_cacertfile
);
526 if (ssl_cacertfile
&& free_path
) {
527 xfree(ssl_cacertfile
);
529 if (rc
!= LDAP_OPT_SUCCESS
) {
530 error((char *) "%s| %s: ERROR: Error while setting LDAP_OPT_X_TLS_CACERTFILE for ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
534 debug((char *) "%s| %s: DEBUG: Disable server certificate check for ldap server.\n", LogTime(), PROGRAM
);
535 val
= LDAP_OPT_X_TLS_ALLOW
;
536 rc
= ldap_set_option(NULL
, LDAP_OPT_X_TLS_REQUIRE_CERT
, &val
);
537 if (rc
!= LDAP_SUCCESS
) {
538 error((char *) "%s| %s: ERROR: Error while setting LDAP_OPT_X_TLS_REQUIRE_CERT ALLOW for ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
542 #elif HAVE_LDAPSSL_CLIENT_INIT
544 * Solaris SSL ldap calls require path to certificate database
547 * rc = ldapssl_client_init( ssl_certdbpath, NULL );
548 * rc = ldapssl_advclientauth_init( ssl_certdbpath, NULL , 0 , NULL, NULL, 0, NULL, 2);
550 ssl_certdbpath
= getenv("SSL_CERTDBPATH");
551 if (!ssl_certdbpath
) {
552 ssl_certdbpath
= xstrdup("/etc/certs");
554 debug((char *) "%s| %s: DEBUG: Set certificate database path for ldap server to %s.(Changeable through setting environment variable SSL_CERTDBPATH)\n", LogTime(), PROGRAM
, ssl_certdbpath
);
555 if (!margs
->rc_allow
) {
556 rc
= ldapssl_advclientauth_init(ssl_certdbpath
, NULL
, 0, NULL
, NULL
, 0, NULL
, 2);
558 rc
= ldapssl_advclientauth_init(ssl_certdbpath
, NULL
, 0, NULL
, NULL
, 0, NULL
, 0);
559 debug((char *) "%s| %s: DEBUG: Disable server certificate check for ldap server.\n", LogTime(), PROGRAM
);
561 xfree(ssl_certdbpath
);
562 if (rc
!= LDAP_SUCCESS
) {
563 error((char *) "%s| %s: ERROR: Error while setting SSL for ldap server: %s\n", LogTime(), PROGRAM
, ldapssl_err2string(rc
));
567 error((char *) "%s| %s: ERROR: SSL not supported by ldap library\n", LogTime(), PROGRAM
);
573 get_attributes(LDAP
* ld
, LDAPMessage
* res
, const char *attribute
, char ***ret_value
)
576 char **attr_value
= *ret_value
;
580 * loop over attributes
582 debug((char *) "%s| %s: DEBUG: Search ldap entries for attribute : %s\n", LogTime(), PROGRAM
, attribute
);
583 for (LDAPMessage
*msg
= ldap_first_entry(ld
, res
); msg
; msg
= ldap_next_entry(ld
, msg
)) {
585 switch (ldap_msgtype(msg
)) {
587 case LDAP_RES_SEARCH_ENTRY
: {
588 BerElement
*b
= NULL
;
589 for (char *attr
= ldap_first_attribute(ld
, msg
, &b
); attr
;
590 attr
= ldap_next_attribute(ld
, msg
, b
)) {
591 if (strcasecmp(attr
, attribute
) == 0) {
592 struct berval
**values
;
594 if ((values
= ldap_get_values_len(ld
, msg
, attr
)) != NULL
) {
595 for (int il
= 0; values
[il
] != NULL
; ++il
) {
597 attr_value
= (char **) xrealloc(attr_value
, (max_attr
+ 1) * sizeof(char *));
601 attr_value
[max_attr
] = (char *) xmalloc(values
[il
]->bv_len
+ 1);
602 memcpy(attr_value
[max_attr
], values
[il
]->bv_val
, values
[il
]->bv_len
);
603 attr_value
[max_attr
][values
[il
]->bv_len
] = 0;
607 ber_bvecfree(values
);
614 case LDAP_RES_SEARCH_REFERENCE
:
615 debug((char *) "%s| %s: DEBUG: Received a search reference message\n", LogTime(), PROGRAM
);
617 case LDAP_RES_SEARCH_RESULT
:
618 debug((char *) "%s| %s: DEBUG: Received a search result message\n", LogTime(), PROGRAM
);
625 debug((char *) "%s| %s: DEBUG: %" PRIuSIZE
" ldap entr%s found with attribute : %s\n", LogTime(), PROGRAM
, max_attr
, max_attr
> 1 || max_attr
== 0 ? "ies" : "y", attribute
);
627 *ret_value
= attr_value
;
632 get_bin_attributes(LDAP
* ld
, LDAPMessage
* res
, const char *attribute
, char ***ret_value
, int **ret_len
)
635 char **attr_value
= *ret_value
;
636 int *attr_len
= *ret_len
;
640 * loop over attributes
642 debug((char *) "%s| %s: DEBUG: Search ldap entries for attribute : %s\n", LogTime(), PROGRAM
, attribute
);
643 for ( LDAPMessage
*msg
= ldap_first_entry(ld
, res
); msg
; msg
= ldap_next_entry(ld
, msg
)) {
645 switch (ldap_msgtype(msg
)) {
647 case LDAP_RES_SEARCH_ENTRY
: {
648 BerElement
*b
= NULL
;
649 for (char *attr
= ldap_first_attribute(ld
, msg
, &b
); attr
;
650 attr
= ldap_next_attribute(ld
, msg
, b
)) {
651 if (strcasecmp(attr
, attribute
) == 0) {
652 struct berval
**values
;
654 if ((values
= ldap_get_values_len(ld
, msg
, attr
)) != NULL
) {
655 for (int il
= 0; values
[il
] != NULL
; ++il
) {
657 attr_value
= (char **) xrealloc(attr_value
, (max_attr
+ 1) * sizeof(char *));
661 attr_len
= (int *) xrealloc(attr_len
, (max_attr
+ 1) * sizeof(int));
665 attr_value
[max_attr
] = (char *) xmalloc(values
[il
]->bv_len
+ 1);
666 memcpy(attr_value
[max_attr
], values
[il
]->bv_val
, values
[il
]->bv_len
);
667 attr_value
[max_attr
][values
[il
]->bv_len
] = 0;
668 attr_len
[max_attr
]=values
[il
]->bv_len
;
672 ber_bvecfree(values
);
679 case LDAP_RES_SEARCH_REFERENCE
:
680 debug((char *) "%s| %s: DEBUG: Received a search reference message\n", LogTime(), PROGRAM
);
682 case LDAP_RES_SEARCH_RESULT
:
683 debug((char *) "%s| %s: DEBUG: Received a search result message\n", LogTime(), PROGRAM
);
690 debug((char *) "%s| %s: DEBUG: %" PRIuSIZE
" ldap entr%s found with attribute : %s\n", LogTime(), PROGRAM
, max_attr
, max_attr
> 1 || max_attr
== 0 ? "ies" : "y", attribute
);
692 *ret_value
= attr_value
;
698 * call to open ldap server with or without SSL
701 tool_ldap_open(struct main_args
* margs
, char *host
, int port
, char *ssl
)
705 LDAPURLDesc
*url
= NULL
;
706 char *ldapuri
= NULL
;
711 * Use ldap open here to check if TCP connection is possible. If possible use it.
712 * (Not sure if this is the best way)
715 url
= (LDAPURLDesc
*) xmalloc(sizeof(*url
));
716 memset(url
, 0, sizeof(*url
));
717 #if HAVE_LDAP_URL_LUD_SCHEME
719 url
->lud_scheme
= xstrdup("ldaps");
721 url
->lud_scheme
= xstrdup("ldap");
723 url
->lud_host
= xstrdup(host
);
724 url
->lud_port
= port
;
725 #if HAVE_LDAP_SCOPE_DEFAULT
726 url
->lud_scope
= LDAP_SCOPE_DEFAULT
;
728 url
->lud_scope
= LDAP_SCOPE_SUBTREE
;
730 #if HAVE_LDAP_URL_DESC2STR
731 ldapuri
= ldap_url_desc2str(url
);
732 #elif HAVE_LDAP_URL_PARSE
733 rc
= ldap_url_parse(ldapuri
, &url
);
734 if (rc
!= LDAP_SUCCESS
) {
735 error((char *) "%s| %s: ERROR: Error while parsing url: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
737 ldap_free_urldesc(url
);
741 #error "No URL parsing function"
743 ldap_free_urldesc(url
);
744 rc
= ldap_initialize(&ld
, ldapuri
);
746 if (rc
!= LDAP_SUCCESS
) {
747 error((char *) "%s| %s: ERROR: Error while initialising connection to ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
753 ld
= ldap_init(host
, port
);
755 rc
= ldap_set_defaults(ld
);
756 if (rc
!= LDAP_SUCCESS
) {
757 error((char *) "%s| %s: ERROR: Error while setting default options for ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
764 * Try Start TLS first
766 debug((char *) "%s| %s: DEBUG: Set SSL defaults\n", LogTime(), PROGRAM
);
767 rc
= ldap_set_ssl_defaults(margs
);
768 if (rc
!= LDAP_SUCCESS
) {
769 error((char *) "%s| %s: ERROR: Error while setting SSL default options for ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
776 * Use tls if possible
778 rc
= ldap_start_tls_s(ld
, NULL
, NULL
);
779 if (rc
!= LDAP_SUCCESS
) {
780 error((char *) "%s| %s: ERROR: Error while setting start_tls for ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
783 url
= (LDAPURLDesc
*) xmalloc(sizeof(*url
));
784 memset(url
, 0, sizeof(*url
));
785 #if HAVE_LDAP_URL_LUD_SCHEME
786 url
->lud_scheme
= xstrdup("ldaps");
788 url
->lud_host
= xstrdup(host
);
789 url
->lud_port
= port
;
790 #if HAVE_LDAP_SCOPE_DEFAULT
791 url
->lud_scope
= LDAP_SCOPE_DEFAULT
;
793 url
->lud_scope
= LDAP_SCOPE_SUBTREE
;
795 #if HAVE_LDAP_URL_DESC2STR
796 ldapuri
= ldap_url_desc2str(url
);
797 #elif HAVE_LDAP_URL_PARSE
798 rc
= ldap_url_parse(ldapuri
, &url
);
799 if (rc
!= LDAP_SUCCESS
) {
800 error((char *) "%s| %s: ERROR: Error while parsing url: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
802 ldap_free_urldesc(url
);
806 #error "No URL parsing function"
808 ldap_free_urldesc(url
);
809 rc
= ldap_initialize(&ld
, ldapuri
);
811 if (rc
!= LDAP_SUCCESS
) {
812 error((char *) "%s| %s: ERROR: Error while initialising connection to ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
817 rc
= ldap_set_defaults(ld
);
818 if (rc
!= LDAP_SUCCESS
) {
819 error((char *) "%s| %s: ERROR: Error while setting default options for ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
825 #elif HAVE_LDAPSSL_CLIENT_INIT
826 ld
= ldapssl_init(host
, port
, 1);
828 error((char *) "%s| %s: ERROR: Error while setting SSL for ldap server: %s\n", LogTime(), PROGRAM
, ldapssl_err2string(rc
));
833 rc
= ldap_set_defaults(ld
);
834 if (rc
!= LDAP_SUCCESS
) {
835 error((char *) "%s| %s: ERROR: Error while setting default options for ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
841 error((char *) "%s| %s: ERROR: SSL not supported by ldap library\n", LogTime(), PROGRAM
);
848 * ldap calls to get attribute from Ldap Directory Server
851 get_memberof(struct main_args
*margs
, char *user
, char *domain
, char *group
)
855 #if !HAVE_SUN_LDAP_SDK
858 struct ldap_creds
*lcreds
= NULL
;
863 struct timeval searchtime
;
866 char **attr_value
= NULL
;
868 struct hstruct
*hlist
= NULL
;
870 char *ldap_filter_esc
= NULL
;
872 searchtime
.tv_sec
= SEARCH_TIMEOUT
;
873 searchtime
.tv_usec
= 0;
875 * Fill Kerberos memory cache with credential from keytab for SASL/GSSAPI
878 debug((char *) "%s| %s: DEBUG: Setup Kerberos credential cache\n", LogTime(), PROGRAM
);
881 if (margs
->nokerberos
) {
883 debug((char *) "%s| %s: DEBUG: Kerberos is disabled. Use username/password with ldap url instead\n", LogTime(), PROGRAM
);
885 kc
= krb5_create_cache(domain
);
887 error((char *) "%s| %s: ERROR: Error during setup of Kerberos credential cache\n", LogTime(), PROGRAM
);
892 debug((char *) "%s| %s: DEBUG: Kerberos is not supported. Use username/password with ldap url instead\n", LogTime(), PROGRAM
);
896 if (kc
&& (!margs
->lurl
|| !margs
->luser
|| !margs
->lpass
)) {
898 * If Kerberos fails and no url given exit here
903 #if !HAVE_SUN_LDAP_SDK
907 ldap_debug
= 127 /* LDAP_DEBUG_TRACE */ ;
908 ldap_debug
= -1 /* LDAP_DEBUG_ANY */ ;
910 (void) ldap_set_option(NULL
, LDAP_OPT_DEBUG_LEVEL
, &ldap_debug
);
912 debug((char *) "%s| %s: DEBUG: Initialise ldap connection\n", LogTime(), PROGRAM
);
916 debug((char *) "%s| %s: DEBUG: Enable SSL to ldap servers\n", LogTime(), PROGRAM
);
918 debug((char *) "%s| %s: DEBUG: Canonicalise ldap server name for domain %s\n", LogTime(), PROGRAM
, domain
);
920 * Loop over list of ldap servers of users domain
922 nhosts
= get_ldap_hostname_list(margs
, &hlist
, 0, domain
);
923 for (size_t i
= 0; i
< nhosts
; ++i
) {
925 if (hlist
[i
].port
!= -1)
926 port
= hlist
[i
].port
;
927 debug((char *) "%s| %s: DEBUG: Setting up connection to ldap server %s:%d\n", LogTime(), PROGRAM
, hlist
[i
].host
, port
);
929 ld
= tool_ldap_open(margs
, hlist
[i
].host
, port
, margs
->ssl
);
934 * ldap bind with SASL/GSSAPI authentication (only possible if a domain was part of the username)
937 #if HAVE_SASL_H || HAVE_SASL_SASL_H || HAVE_SASL_DARWIN
938 debug((char *) "%s| %s: DEBUG: Bind to ldap server with SASL/GSSAPI\n", LogTime(), PROGRAM
);
940 rc
= tool_sasl_bind(ld
, bindp
, margs
->ssl
);
941 if (rc
!= LDAP_SUCCESS
) {
942 error((char *) "%s| %s: ERROR: Error while binding to ldap server with SASL/GSSAPI: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
947 lcreds
= (struct ldap_creds
*) xmalloc(sizeof(struct ldap_creds
));
949 lcreds
->pw
= margs
->ssl
? xstrdup(margs
->ssl
) : NULL
;
950 ldap_set_rebind_proc(ld
, ldap_sasl_rebind
, (char *) lcreds
);
952 debug((char *) "%s| %s: DEBUG: %s initialised %sconnection to ldap server %s:%d\n", LogTime(), PROGRAM
, ld
? "Successfully" : "Failed to", margs
->ssl
? "SSL protected " : "", hlist
[i
].host
, port
);
958 error((char *) "%s| %s: ERROR: SASL not supported on system\n", LogTime(), PROGRAM
);
962 nhosts
= free_hostname_list(&hlist
, nhosts
);
964 debug((char *) "%s| %s: DEBUG: Error during initialisation of ldap connection: %s\n", LogTime(), PROGRAM
, strerror(errno
));
966 bindp
= convert_domain_to_bind_path(domain
);
968 if ((!domain
|| !ld
) && margs
->lurl
&& strstr(margs
->lurl
, "://")) {
975 * If username does not contain a domain and a url was given then try it
977 hostname
= strstr(margs
->lurl
, "://") + 3;
978 ssl
= strstr(margs
->lurl
, "ldaps://");
980 debug((char *) "%s| %s: DEBUG: Enable SSL to ldap servers\n", LogTime(), PROGRAM
);
982 debug((char *) "%s| %s: DEBUG: Canonicalise ldap server name %s\n", LogTime(), PROGRAM
, hostname
);
984 * Loop over list of ldap servers
986 host
= xstrdup(hostname
);
988 if ((p
= strchr(host
, ':'))) {
993 nhosts
= get_hostname_list(&hlist
, 0, host
);
995 for (size_t i
= 0; i
< nhosts
; ++i
) {
997 ld
= tool_ldap_open(margs
, hlist
[i
].host
, port
, ssl
);
1001 * ldap bind with username/password authentication
1004 debug((char *) "%s| %s: DEBUG: Bind to ldap server with Username/Password\n", LogTime(), PROGRAM
);
1005 rc
= ldap_simple_bind_s(ld
, margs
->luser
, margs
->lpass
);
1006 if (rc
!= LDAP_SUCCESS
) {
1007 error((char *) "%s| %s: ERROR: Error while binding to ldap server with Username/Password: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
1012 lcreds
= (struct ldap_creds
*) xmalloc(sizeof(struct ldap_creds
));
1013 lcreds
->dn
= xstrdup(margs
->luser
);
1014 lcreds
->pw
= xstrdup(margs
->lpass
);
1015 ldap_set_rebind_proc(ld
, ldap_simple_rebind
, (char *) lcreds
);
1016 debug((char *) "%s| %s: DEBUG: %s set up %sconnection to ldap server %s:%d\n", LogTime(), PROGRAM
, ld
? "Successfully" : "Failed to", ssl
? "SSL protected " : "", hlist
[i
].host
, port
);
1020 nhosts
= free_hostname_list(&hlist
, nhosts
);
1023 bindp
= xstrdup(margs
->lbind
);
1025 bindp
= convert_domain_to_bind_path(domain
);
1029 debug((char *) "%s| %s: DEBUG: Error during initialisation of ldap connection: %s\n", LogTime(), PROGRAM
, strerror(errno
));
1034 * ldap search for user
1037 * Check if server is AD by querying for attribute samaccountname
1040 rc
= check_AD(margs
, ld
);
1041 if (rc
!= LDAP_SUCCESS
) {
1042 error((char *) "%s| %s: ERROR: Error determining ldap server type: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
1049 filter
= (char *) FILTER_AD
;
1051 filter
= (char *) FILTER
;
1053 ldap_filter_esc
= escape_filter(user
);
1055 se_len
= strlen(filter
) + strlen(ldap_filter_esc
) + 1;
1056 search_exp
= (char *) xmalloc(se_len
);
1057 snprintf(search_exp
, se_len
, filter
, ldap_filter_esc
);
1059 xfree(ldap_filter_esc
);
1061 debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter : %s\n", LogTime(), PROGRAM
, bindp
, search_exp
);
1062 rc
= ldap_search_ext_s(ld
, bindp
, LDAP_SCOPE_SUBTREE
,
1063 search_exp
, NULL
, 0,
1064 NULL
, NULL
, &searchtime
, 0, &res
);
1067 if (rc
!= LDAP_SUCCESS
) {
1068 error((char *) "%s| %s: ERROR: Error searching ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
1074 debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM
, ldap_count_entries(ld
, res
), ldap_count_entries(ld
, res
) > 1 || ldap_count_entries(ld
, res
) == 0 ? "ies" : "y");
1076 if (ldap_count_entries(ld
, res
) != 0) {
1079 max_attr
= get_attributes(ld
, res
, ATTRIBUTE_AD
, &attr_value
);
1081 max_attr
= get_attributes(ld
, res
, ATTRIBUTE
, &attr_value
);
1085 * Compare group names
1088 for (size_t k
= 0; k
< max_attr
; ++k
) {
1091 /* Compare first CN= value assuming it is the same as the group name itself */
1093 if (!strncasecmp("CN=", av
, 3)) {
1096 if ((avp
= strchr(av
, ','))) {
1100 if (debug_enabled
) {
1101 debug((char *) "%s| %s: DEBUG: Entry %" PRIuSIZE
" \"%s\" in hex UTF-8 is ", LogTime(), PROGRAM
, k
+ 1, av
);
1102 for (unsigned int n
= 0; av
[n
] != '\0'; ++n
)
1103 fprintf(stderr
, "%02x", (unsigned char) av
[n
]);
1104 fprintf(stderr
, "\n");
1106 if (!strcasecmp(group
, av
)) {
1109 debug((char *) "%s| %s: DEBUG: Entry %" PRIuSIZE
" \"%s\" matches group name \"%s\"\n", LogTime(), PROGRAM
, k
+ 1, av
, group
);
1113 debug((char *) "%s| %s: DEBUG: Entry %" PRIuSIZE
" \"%s\" does not match group name \"%s\"\n", LogTime(), PROGRAM
, k
+ 1, av
, group
);
1116 * Do recursive group search for AD only since posixgroups can not contain other groups
1118 if (!retval
&& margs
->AD
) {
1119 if (debug_enabled
&& max_attr
> 0) {
1120 debug((char *) "%s| %s: DEBUG: Perform recursive group search\n", LogTime(), PROGRAM
);
1122 for (size_t j
= 0; j
< max_attr
; ++j
) {
1126 if (search_group_tree(margs
, ld
, bindp
, av
, group
, 1)) {
1128 if (!strncasecmp("CN=", av
, 3)) {
1131 if ((avp
= strchr(av
, ','))) {
1136 debug((char *) "%s| %s: DEBUG: Entry %" PRIuSIZE
" group \"%s\" is (in)direct member of group \"%s\"\n", LogTime(), PROGRAM
, j
+ 1, av
, group
);
1146 for (size_t j
= 0; j
< max_attr
; ++j
) {
1147 xfree(attr_value
[j
]);
1149 safe_free(attr_value
);
1152 } else if (ldap_count_entries(ld
, res
) == 0 && margs
->AD
) {
1165 * Check for primary Group membership
1167 debug((char *) "%s| %s: DEBUG: Search for primary group membership: \"%s\"\n", LogTime(), PROGRAM
, group
);
1169 filter
= (char *) FILTER_AD
;
1171 filter
= (char *) FILTER_UID
;
1173 ldap_filter_esc
= escape_filter(user
);
1175 se_len
= strlen(filter
) + strlen(ldap_filter_esc
) + 1;
1176 search_exp
= (char *) xmalloc(se_len
);
1177 snprintf(search_exp
, se_len
, filter
, ldap_filter_esc
);
1179 xfree(ldap_filter_esc
);
1181 debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", LogTime(), PROGRAM
, bindp
, search_exp
);
1182 rc
= ldap_search_ext_s(ld
, bindp
, LDAP_SCOPE_SUBTREE
,
1183 search_exp
, NULL
, 0,
1184 NULL
, NULL
, &searchtime
, 0, &res
);
1187 debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM
, ldap_count_entries(ld
, res
), ldap_count_entries(ld
, res
) > 1 || ldap_count_entries(ld
, res
) == 0 ? "ies" : "y");
1192 max_attr
= get_attributes(ld
, res
, ATTRIBUTE_GID_AD
, &attr_value
);
1194 max_attr
= get_attributes(ld
, res
, ATTRIBUTE_GID
, &attr_value
);
1197 if (max_attr
== 1) {
1198 char **attr_value_2
= NULL
;
1199 size_t max_attr_2
= 0;
1202 char **attr_value_3
= NULL
;
1203 int *attr_len_3
= NULL
;
1204 size_t max_attr_3
= 0;
1205 uint32_t gid
=atoi(attr_value
[0]);
1207 /* Get objectsid and search for group
1208 * with objectsid = domain(objectsid) + primarygroupid */
1209 debug((char *) "%s| %s: DEBUG: Got primaryGroupID %u\n", LogTime(), PROGRAM
, gid
);
1210 max_attr_3
= get_bin_attributes(ld
, res
, ATTRIBUTE_SID
, &attr_value_3
, &attr_len_3
);
1212 if (max_attr_3
== 1) {
1213 int len
=attr_len_3
[0];
1215 debug((char *) "%s| %s: ERROR: Length %d is too short for objectSID\n", LogTime(), PROGRAM
, len
);
1219 attr_value_3
[0][len
-1]=((gid
>>24) & 0xff);
1220 attr_value_3
[0][len
-2]=((gid
>>16) & 0xff);
1221 attr_value_3
[0][len
-3]=((gid
>>8) & 0xff);
1222 attr_value_3
[0][len
-4]=((gid
>>0) & 0xff);
1224 #define FILTER_SID_1 "(objectSID="
1225 #define FILTER_SID_2 ")"
1227 se_len
= strlen(FILTER_SID_1
) + len
*3 + strlen(FILTER_SID_2
) + 1;
1228 search_exp
= (char *) xmalloc(se_len
);
1229 snprintf(search_exp
, se_len
, "%s", FILTER_SID_1
);
1231 for (int j
=0; j
<len
; j
++) {
1232 se
=xstrdup(search_exp
);
1233 snprintf(search_exp
, se_len
, "%s\\%02x", se
, attr_value_3
[0][j
] & 0xFF);
1236 se
=xstrdup(search_exp
);
1237 snprintf(search_exp
, se_len
, "%s%s", se
, FILTER_SID_2
);
1240 debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", LogTime(), PROGRAM
, bindp
, search_exp
);
1241 rc
= ldap_search_ext_s(ld
, bindp
, LDAP_SCOPE_SUBTREE
,
1242 search_exp
, NULL
, 0,
1243 NULL
, NULL
, &searchtime
, 0, &res
);
1246 debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM
, ldap_count_entries(ld
, res
), ldap_count_entries(ld
, res
) > 1 || ldap_count_entries(ld
, res
) == 0 ? "ies" : "y");
1254 for (j
= 0; j
< max_attr_3
; ++j
) {
1255 xfree(attr_value_3
[j
]);
1257 safe_free(attr_value_3
);
1264 filter
= (char *) FILTER_GID
;
1266 ldap_filter_esc
= escape_filter(attr_value
[0]);
1268 se_len
= strlen(filter
) + strlen(ldap_filter_esc
) + 1;
1269 search_exp
= (char *) xmalloc(se_len
);
1270 snprintf(search_exp
, se_len
, filter
, ldap_filter_esc
);
1272 xfree(ldap_filter_esc
);
1274 debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", LogTime(), PROGRAM
, bindp
, search_exp
);
1275 rc
= ldap_search_ext_s(ld
, bindp
, LDAP_SCOPE_SUBTREE
,
1276 search_exp
, NULL
, 0,
1277 NULL
, NULL
, &searchtime
, 0, &res
);
1283 max_attr_2
= get_attributes(ld
, res
, ATTRIBUTE_DN
, &attr_value_2
);
1285 max_attr_2
= get_attributes(ld
, res
, ATTRIBUTE
, &attr_value_2
);
1291 * Compare group names
1294 if (max_attr_2
== 1) {
1295 /* Compare first CN= value assuming it is the same as the group name itself */
1296 char *av
= attr_value_2
[0];
1297 if (!strncasecmp("CN=", av
, 3)) {
1300 if ((avp
= strchr(av
, ','))) {
1304 if (!strcasecmp(group
, av
)) {
1306 debug((char *) "%s| %s: DEBUG: \"%s\" matches group name \"%s\"\n", LogTime(), PROGRAM
, av
, group
);
1308 debug((char *) "%s| %s: DEBUG: \"%s\" does not match group name \"%s\"\n", LogTime(), PROGRAM
, av
, group
);
1312 * Do recursive group search for AD only since posixgroups can not contain other groups
1314 if (!retval
&& margs
->AD
) {
1315 if (debug_enabled
&& max_attr_2
> 0) {
1316 debug((char *) "%s| %s: DEBUG: Perform recursive group search\n", LogTime(), PROGRAM
);
1318 for (size_t j
= 0; j
< max_attr_2
; ++j
) {
1321 av
= attr_value_2
[j
];
1322 if (search_group_tree(margs
, ld
, bindp
, av
, group
, 1)) {
1324 if (!strncasecmp("CN=", av
, 3)) {
1327 if ((avp
= strchr(av
, ','))) {
1331 if (debug_enabled
) {
1332 debug((char *) "%s| %s: DEBUG: Entry %" PRIuSIZE
" group \"%s\" is (in)direct member of group \"%s\"\n", LogTime(), PROGRAM
, j
+ 1, av
, group
);
1344 for (j
= 0; j
< max_attr_2
; ++j
) {
1345 xfree(attr_value_2
[j
]);
1347 safe_free(attr_value_2
);
1350 debug((char *) "%s| %s: DEBUG: Users primary group %s %s\n", LogTime(), PROGRAM
, retval
? "matches" : "does not match", group
);
1354 debug((char *) "%s| %s: DEBUG: Did not find ldap entry for group %s\n", LogTime(), PROGRAM
, group
);
1360 for (size_t j
= 0; j
< max_attr
; ++j
) {
1361 xfree(attr_value
[j
]);
1363 safe_free(attr_value
);
1366 rc
= ldap_unbind(ld
);
1368 if (rc
!= LDAP_SUCCESS
) {
1369 error((char *) "%s| %s: ERROR: Error unbind ldap server: %s\n", LogTime(), PROGRAM
, ldap_err2string(rc
));
1371 debug((char *) "%s| %s: DEBUG: Unbind ldap server\n", LogTime(), PROGRAM
);