[sudo-workers] LDAP sudoers search Performance
Thomas, Matthew (GE Tech Infra, US)
matthewt.thomas at ge.com
Wed Mar 16 13:19:05 EDT 2011
With the current ldap search that sudoers performs there can be performance
issues when netgroups are used as the value for sudoUser. I have seen delays
over 10 minutes with just 1000 entries in the sudoers OU. This can be
optimized to some degree with support for a search filter, similar to the
service search descriptor functionality in a DUAConfigProfile for Solaris.
I have modified the ldap.c which came with 1.7.5rc1 to implement a
sudoers_search_filter in /etc/ldap.c. The Patch for the ldap.c and sudoers
man page is below.
Could this change be incorporated in the a future version of sudo?
Thanks,
Matt Thomas
*** ../sudo-1.7.5rc1/sudoers.ldap.pod Tue Feb 1 14:28:56 2011
--- ./sudoers.ldap.pod Thu Mar 3 10:28:03 2011
***************
*** 369,374 ****
--- 369,380 ----
C<example.com>. Multiple B<SUDOERS_BASE> lines may be specified,
in which case they are queried in the order specified.
+ =item B<SUDOERS_SEARCH_FILTER> ldap_filter
+
+ A ldap filter which is used when performing B<sudo> LDAP queries.
+ Typically this is of the form C<attribute=value> or
+ C<(&(attribute=value)(attribute2=value2))>.
+
=item B<SUDOERS_TIMED> on/true/yes/off/false/no
Whether or not to evaluate the C<sudoNotBefore> and C<sudoNotAfter>
*** ldap.c.orig Thu Feb 24 15:25:15 2011
--- ldap.c Thu Mar 3 10:54:43 2011
***************
*** 201,206 ****
--- 201,207 ----
char *bindpw;
char *rootbinddn;
struct ldap_config_list_str *base;
+ char *search_filter;
char *ssl;
char *tls_cacertfile;
char *tls_cacertdir;
***************
*** 285,290 ****
--- 286,292 ----
{ "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn },
{ "sudoers_base", CONF_LIST_STR, FALSE, -1, &ldap_conf.base },
{ "sudoers_timed", CONF_BOOL, FALSE, -1, &ldap_conf.timed },
+ { "sudoers_search_filter", CONF_STR, FALSE, -1,
&ldap_conf.search_filter },
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
{ "use_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.use_sasl },
{ "sasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.sasl_auth_id },
***************
*** 976,982 ****
--- 978,1029 ----
return bytes;
}
+
/*
+ * Builds up a filter to search for default settings
+ */
+ static char *
+ sudo_ldap_build_default_filter()
+ {
+ char *buf;
+ size_t sz;
+
+ sz = 14;
+
+ if(ldap_conf.search_filter){
+ sz += strlen(ldap_conf.search_filter) + 3;
+ if(ldap_conf.search_filter[0] != '('){
+ sz += 2;
+ }
+ }
+
+ buf = emalloc(sz);
+ *buf = '\0';
+
+ if (ldap_conf.search_filter)
+ (void) strlcpy(buf, "(&", sz);
+
+ if(ldap_conf.search_filter){
+ if(ldap_conf.search_filter[0] != '('){
+ (void) strlcat(buf, "(", sz);
+ }
+ (void) strlcat(buf, ldap_conf.search_filter, sz);
+
+ if(ldap_conf.search_filter[0] != '('){
+ (void) strlcat(buf, ")", sz);
+ }
+ }
+
+ (void) strlcat(buf,"(cn=defaults)",sz);
+
+ if (ldap_conf.search_filter){
+ strlcat(buf, ")", sz); /* closes the global AND */
+ }
+
+ return buf;
+ }
+
+ /*
* Builds up a filter to check against LDAP.
*/
static char *
***************
*** 988,995 ****
size_t sz;
int i;
/* Start with (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */
! sz = 29 + strlen(pw->pw_name);
/* Add space for groups */
if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
--- 1035,1051 ----
size_t sz;
int i;
+ sz = 0;
+ /* Start with LDAP Search Descriptor length + 3 */
+ if(ldap_conf.search_filter){
+ sz += strlen(ldap_conf.search_filter) + 3;
+ if(ldap_conf.search_filter[0] != '('){
+ sz += 2;
+ }
+ }
+
/* Start with (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */
! sz += 29 + strlen(pw->pw_name);
/* Add space for groups */
if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
***************
*** 1014,1023 ****
/*
* If timed, start a global AND clause that will have the time limits
* as the second leg.
*/
! if (ldap_conf.timed)
(void) strlcpy(buf, "(&", sz);
/* Global OR + sudoUser=user_name filter */
(void) strlcat(buf, "(|(sudoUser=", sz);
(void) strlcat(buf, pw->pw_name, sz);
--- 1070,1091 ----
/*
* If timed, start a global AND clause that will have the time limits
* as the second leg.
+ * check for searchdescriptor as well
*/
! if (ldap_conf.timed || ldap_conf.search_filter)
(void) strlcpy(buf, "(&", sz);
+ if(ldap_conf.search_filter){
+ if(ldap_conf.search_filter[0] != '('){
+ (void) strlcat(buf, "(", sz);
+ }
+ (void) strlcat(buf, ldap_conf.search_filter, sz);
+
+ if(ldap_conf.search_filter[0] != '('){
+ (void) strlcat(buf, ")", sz);
+ }
+ }
+
/* Global OR + sudoUser=user_name filter */
(void) strlcat(buf, "(|(sudoUser=", sz);
(void) strlcat(buf, pw->pw_name, sz);
***************
*** 1053,1058 ****
--- 1121,1129 ----
sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
strlcat(buf, timebuffer, sz);
}
+ else if (ldap_conf.search_filter){
+ strlcat(buf, ")", sz); /* closes the global OR */
+ }
strlcat(buf, ")", sz); /* closes the global OR or the global AND */
return buf;
***************
*** 1065,1071 ****
--- 1136,1179 ----
sudo_ldap_build_pass2()
{
char *buf, timebuffer[TIMEFILTER_LENGTH];
+ size_t sz;
+ sz = 14;
+ /* Start with LDAP Search Descriptor length + 3 */
+ if(ldap_conf.search_filter){
+ sz += strlen(ldap_conf.search_filter) + 3;
+ if(ldap_conf.search_filter[0] != '('){
+ sz += 2;
+ }
+ }
+
+ /* If timed, add space for time limits. */
+ if (ldap_conf.timed)
+ sz += TIMEFILTER_LENGTH;
+
+ buf = emalloc(sz);
+ *buf = '\0';
+
+ /*
+ * If timed, start a global AND clause that will have the time limits
+ * as the second leg.
+ * check for searchdescriptor as well
+ */
+ if (ldap_conf.timed || ldap_conf.search_filter)
+ (void) strlcpy(buf, "(&", sz);
+
+ if(ldap_conf.search_filter){
+ if(ldap_conf.search_filter[0] != '('){
+ (void) strlcat(buf, "(", sz);
+ }
+ (void) strlcat(buf, ldap_conf.search_filter, sz);
+ if(ldap_conf.search_filter[0] != '('){
+ (void) strlcat(buf, ")", sz);
+ }
+ }
+
+ (void) strlcat(buf, "(sudoUser=+*)", sz);
+
if (ldap_conf.timed) {
/*
* If timed, use a global AND clause that has the time limit as
***************
*** 1072,1083 ****
* as the second leg.
*/
sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
! easprintf(&buf, "(&(sudoUser=+*)%s)", timebuffer);
! } else {
! /* No time limit, just the netgroup selection. */
! buf = estrdup("sudoUser=+*");
}
return buf;
}
--- 1180,1191 ----
* as the second leg.
*/
sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
! strlcat(buf, timebuffer, sz);
}
+ if (ldap_conf.timed || ldap_conf.search_filter)
+ strlcat(buf, ")", sz);
+
return buf;
}
***************
*** 1242,1247 ****
--- 1350,1358 ----
fprintf(stderr, "sudoers_base %s\n",
"(NONE) <---Sudo will ignore ldap)");
}
+ if (ldap_conf.search_filter){
+ fprintf(stderr,"Search_Filter %s\n",ldap_conf.search_filter);
+ }
fprintf(stderr, "binddn %s\n", ldap_conf.binddn ?
ldap_conf.binddn : "(anonymous)");
fprintf(stderr, "bindpw %s\n", ldap_conf.bindpw ?
***************
*** 1414,1419 ****
--- 1525,1531 ----
LDAP *ld;
LDAPMessage *entry, *result;
char *prefix;
+ char *filt;
int rc, count = 0;
if (handle == NULL || handle->ld == NULL)
***************
*** 1420,1425 ****
--- 1532,1539 ----
goto done;
ld = handle->ld;
+ filt = sudo_ldap_build_default_filter();
+
for (base = ldap_conf.base; base != NULL; base = base->next) {
if (ldap_conf.timeout > 0) {
tv.tv_sec = ldap_conf.timeout;
***************
*** 1428,1434 ****
}
result = NULL;
rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
! "cn=defaults", NULL, 0, NULL, NULL, tvp, 0, &result);
if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
bv = ldap_get_values_len(ld, entry, "sudoOption");
if (bv != NULL) {
--- 1542,1548 ----
}
result = NULL;
rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
! filt,NULL, 0, NULL, NULL, tvp, 0, &result);
if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
bv = ldap_get_values_len(ld, entry, "sudoOption");
if (bv != NULL) {
***************
*** 1447,1452 ****
--- 1561,1567 ----
if (result)
ldap_msgfree(result);
}
+ efree(filt);
done:
return count;
}
***************
*** 2080,2090 ****
--- 2195,2209 ----
LDAP *ld;
LDAPMessage *entry, *result;
int rc;
+ char *filt;
if (handle == NULL || handle->ld == NULL)
return -1;
ld = handle->ld;
+ filt = sudo_ldap_build_default_filter();
+ DPRINTF(("Looking for cn=defaults: %s", filt), 1);
+
for (base = ldap_conf.base; base != NULL; base = base->next) {
if (ldap_conf.timeout > 0) {
tv.tv_sec = ldap_conf.timeout;
***************
*** 2093,2099 ****
}
result = NULL;
rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
! "cn=defaults", NULL, 0, NULL, NULL, NULL, 0, &result);
if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
sudo_ldap_parse_options(ld, entry);
--- 2212,2218 ----
}
result = NULL;
rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
! filt,NULL, 0, NULL, NULL, tvp, 0, &result);
if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
sudo_ldap_parse_options(ld, entry);
***************
*** 2103,2108 ****
--- 2222,2228 ----
if (result)
ldap_msgfree(result);
}
+ efree(filt);
return 0;
}
More information about the sudo-workers
mailing list