1 /*
2 myldap.c - simple interface to do LDAP requests
3 Parts of this file were part of the nss_ldap library (as ldap-nss.c)
4 which has been forked into the nss-pam-ldapd library.
5
6 Copyright (C) 1997-2006 Luke Howard
7 Copyright (C) 2006-2007 West Consulting
8 Copyright (C) 2006-2017 Arthur de Jong
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 02110-1301 USA
24 */
25
26 /*
27 This library expects to use an LDAP library to provide the real
28 functionality and only provides a convenient wrapper.
29 Some pointers for more information on the LDAP API:
30 http://tools.ietf.org/id/draft-ietf-ldapext-ldap-c-api-05.txt
31 http://www.mozilla.org/directory/csdk-docs/function.htm
32 http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/apis/dirserv1.htm
33 http://www.openldap.org/software/man.cgi?query=ldap
34 */
35
36 #include "config.h"
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <sys/time.h>
44 #include <time.h>
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <errno.h>
48 #include <lber.h>
49 #include <ldap.h>
50 #ifdef HAVE_LDAP_SSL_H
51 #include <ldap_ssl.h>
52 #endif
53 #ifdef HAVE_GSSLDAP_H
54 #include <gssldap.h>
55 #endif
56 #ifdef HAVE_GSSSASL_H
57 #include <gsssasl.h>
58 #endif
59 #ifdef HAVE_SASL_SASL_H
60 #include <sasl/sasl.h>
61 #endif
62 #ifdef HAVE_SASL_H
63 #include <sasl.h>
64 #endif
65 #include <ctype.h>
66 #include <pthread.h>
67 #include <stdarg.h>
68
69 #include "myldap.h"
70 #include "common.h"
71 #include "log.h"
72 #include "cfg.h"
73 #include "common/set.h"
74 #include "compat/ldap_compat.h"
75 #include "attmap.h"
76
77 /* the maximum number of searches per session */
78 #define MAX_SEARCHES_IN_SESSION 4
79
80 /* the maximum number of dn's to log to the debug log for each search */
81 #define MAX_DEBUG_LOG_DNS 10
82
83 /* a fake scope that is used to not perform an actual search but only
84 simulate the handling of the search (used for authentication) */
85 #define MYLDAP_SCOPE_BINDONLY 0x1972 /* magic number: should never be a real scope */
86
87 /* This refers to a current LDAP session that contains the connection
88 information. */
89 struct ldap_session {
90 /* the connection */
91 LDAP *ld;
92 /* timestamp of last activity */
93 time_t lastactivity;
94 /* index into uris: currently connected LDAP uri */
95 int current_uri;
96 /* a list of searches registered with this session */
97 struct myldap_search *searches[MAX_SEARCHES_IN_SESSION];
98 /* the username to bind with */
99 char binddn[BUFLEN_DN];
100 /* the password to bind with if any */
101 char bindpw[BUFLEN_PASSWORD];
102 /* the authentication result (NSLCD_PAM_* code) */
103 int policy_response;
104 /* the authentication message */
105 char policy_message[BUFLEN_MESSAGE];
106 };
107
108 /* A search description set as returned by myldap_search(). */
109 struct myldap_search {
110 /* reference to the session */
111 MYLDAP_SESSION *session;
112 /* indicator that the search is still valid */
113 int valid;
114 /* the parameters describing the search */
115 const char *base;
116 int scope;
117 const char *filter;
118 char **attrs;
119 /* a pointer to the current result entry, used for
120 freeing resource allocated with that entry */
121 MYLDAP_ENTRY *entry;
122 /* LDAP message id for the search, -1 indicates absence of an active search */
123 int msgid;
124 /* the last result that was returned by ldap_result() */
125 LDAPMessage *msg;
126 /* cookie for paged searches */
127 struct berval *cookie;
128 /* to indicate that we can retry the search from myldap_get_entry() */
129 int may_retry_search;
130 /* the number of results returned so far */
131 int count;
132 };
133
134 /* The maximum number of calls to myldap_get_values() that may be
135 done per returned entry. */
136 #define MAX_ATTRIBUTES_PER_ENTRY 16
137
138 /* The maximum number of buffers (used for ranged attribute values and
139 values returned by bervalues_to_values()) that may be stored per entry. */
140 #define MAX_BUFFERS_PER_ENTRY 8
141
142 /* A single entry from the LDAP database as returned by
143 myldap_get_entry(). */
144 struct myldap_entry {
145 /* reference to the search to be used to get parameters
146 (e.g. LDAP connection) for other calls */
147 MYLDAP_SEARCH *search;
148 /* the DN */
149 const char *dn;
150 /* a cached version of the exploded rdn */
151 char **exploded_rdn;
152 /* a cache of attribute to value list */
153 char **attributevalues[MAX_ATTRIBUTES_PER_ENTRY];
154 /* a reference to buffers so we can free() them later on */
155 char **buffers[MAX_BUFFERS_PER_ENTRY];
156 };
157
158 /* Flag to record first search operation */
159 int first_search = 1;
160
myldap_err(int pri,LDAP * ld,int rc,const char * format,...)161 static void myldap_err(int pri, LDAP *ld, int rc, const char *format, ...)
162 {
163 char message[BUFLEN_MESSAGE];
164 char *msg_ldap = NULL;
165 char *msg_diag = NULL;
166 char *msg_errno = NULL;
167 va_list ap;
168 /* make the message */
169 va_start(ap, format);
170 vsnprintf(message, sizeof(message), format, ap);
171 message[sizeof(message) - 1] = '\0';
172 va_end(ap);
173 /* get the various error message */
174 if (rc != LDAP_SUCCESS)
175 {
176 msg_ldap = ldap_err2string(rc);
177 /* get the diagnostic information */
178 #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
179 if (ld != NULL)
180 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg_diag);
181 #endif /* LDAP_OPT_DIAGNOSTIC_MESSAGE */
182 }
183 if (errno != 0)
184 msg_errno = strerror(errno);
185 /* log the message */
186 log_log(pri, "%s%s%s%s%s%s%s", message,
187 (msg_ldap == NULL) ? "" : ": ", (msg_ldap == NULL) ? "" : msg_ldap,
188 (msg_diag == NULL) ? "" : ": ", (msg_diag == NULL) ? "" : msg_diag,
189 (msg_errno == NULL) ? "" : ": ", (msg_errno == NULL) ? "" : msg_errno);
190 /* free diagnostic message */
191 if (msg_diag != NULL)
192 ldap_memfree(msg_diag);
193 }
194
myldap_entry_new(MYLDAP_SEARCH * search)195 static MYLDAP_ENTRY *myldap_entry_new(MYLDAP_SEARCH *search)
196 {
197 MYLDAP_ENTRY *entry;
198 int i;
199 /* Note: as an alternative we could embed the myldap_entry into the
200 myldap_search struct to save on malloc() and free() calls. */
201 /* allocate new entry */
202 entry = (MYLDAP_ENTRY *)malloc(sizeof(struct myldap_entry));
203 if (entry == NULL)
204 {
205 log_log(LOG_CRIT, "myldap_entry_new(): malloc() failed to allocate memory");
206 exit(EXIT_FAILURE);
207 }
208 /* fill in fields */
209 entry->search = search;
210 entry->dn = NULL;
211 entry->exploded_rdn = NULL;
212 for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
213 entry->attributevalues[i] = NULL;
214 for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
215 entry->buffers[i] = NULL;
216 /* return the fresh entry */
217 return entry;
218 }
219
myldap_entry_free(MYLDAP_ENTRY * entry)220 static void myldap_entry_free(MYLDAP_ENTRY *entry)
221 {
222 int i;
223 /* free the DN */
224 if (entry->dn != NULL)
225 ldap_memfree((char *)entry->dn);
226 /* free the exploded RDN */
227 if (entry->exploded_rdn != NULL)
228 ldap_value_free(entry->exploded_rdn);
229 /* free all attribute values */
230 for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
231 if (entry->attributevalues[i] != NULL)
232 ldap_value_free(entry->attributevalues[i]);
233 /* free all buffers */
234 for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
235 if (entry->buffers[i] != NULL)
236 free(entry->buffers[i]);
237 /* we don't need the result anymore, ditch it. */
238 ldap_msgfree(entry->search->msg);
239 entry->search->msg = NULL;
240 /* free the actual memory for the struct */
241 free(entry);
242 }
243
myldap_search_new(MYLDAP_SESSION * session,const char * base,int scope,const char * filter,const char ** attrs)244 static MYLDAP_SEARCH *myldap_search_new(MYLDAP_SESSION *session,
245 const char *base, int scope,
246 const char *filter,
247 const char **attrs)
248 {
249 char *buffer;
250 MYLDAP_SEARCH *search;
251 int i;
252 size_t sz;
253 /* figure out size for new memory block to allocate
254 this has the advantage that we can free the whole lot with one call */
255 sz = sizeof(struct myldap_search);
256 sz += strlen(base) + 1 + strlen(filter) + 1;
257 for (i = 0; attrs[i] != NULL; i++)
258 sz += strlen(attrs[i]) + 1;
259 sz += (i + 1) * sizeof(char *);
260 /* allocate new results memory region */
261 buffer = (char *)malloc(sz);
262 if (buffer == NULL)
263 {
264 log_log(LOG_CRIT, "myldap_search_new(): malloc() failed to allocate memory");
265 exit(EXIT_FAILURE);
266 }
267 /* initialize struct */
268 search = (MYLDAP_SEARCH *)(void *)(buffer);
269 buffer += sizeof(struct myldap_search);
270 /* save pointer to session */
271 search->session = session;
272 /* flag as valid search */
273 search->valid = 1;
274 /* initialize array of attributes */
275 search->attrs = (char **)(void *)buffer;
276 buffer += (i + 1) * sizeof(char *);
277 /* copy base */
278 strcpy(buffer, base);
279 search->base = buffer;
280 buffer += strlen(base) + 1;
281 /* just plainly store scope */
282 search->scope = scope;
283 /* copy filter */
284 strcpy(buffer, filter);
285 search->filter = buffer;
286 buffer += strlen(filter) + 1;
287 /* copy attributes themselves */
288 for (i = 0; attrs[i] != NULL; i++)
289 {
290 strcpy(buffer, attrs[i]);
291 search->attrs[i] = buffer;
292 buffer += strlen(attrs[i]) + 1;
293 }
294 search->attrs[i] = NULL;
295 /* initialize context */
296 search->cookie = NULL;
297 search->msg = NULL;
298 search->msgid = -1;
299 search->may_retry_search = 1;
300 /* clear result entry */
301 search->entry = NULL;
302 search->count = 0;
303 /* return the new search struct */
304 return search;
305 }
306
myldap_session_new(void)307 static MYLDAP_SESSION *myldap_session_new(void)
308 {
309 MYLDAP_SESSION *session;
310 int i;
311 /* allocate memory for the session storage */
312 session = (struct ldap_session *)malloc(sizeof(struct ldap_session));
313 if (session == NULL)
314 {
315 log_log(LOG_CRIT, "myldap_session_new(): malloc() failed to allocate memory");
316 exit(EXIT_FAILURE);
317 }
318 /* initialize the session */
319 session->ld = NULL;
320 session->lastactivity = 0;
321 session->current_uri = 0;
322 for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
323 session->searches[i] = NULL;
324 session->binddn[0] = '\0';
325 memset(session->bindpw, 0, sizeof(session->bindpw));
326 session->bindpw[0] = '\0';
327 session->policy_response = NSLCD_PAM_SUCCESS;
328 session->policy_message[0] = '\0';
329 /* return the new session */
330 return session;
331 }
332
is_valid_entry(MYLDAP_ENTRY * entry)333 PURE static inline int is_valid_entry(MYLDAP_ENTRY *entry)
334 {
335 return (entry != NULL) && (entry->search != NULL) &&
336 (entry->search->session != NULL) && (entry->search->session->ld != NULL) &&
337 (entry->search->msg != NULL);
338 }
339
340 #ifdef HAVE_SASL_INTERACT_T
341 /* this is registered with ldap_sasl_interactive_bind_s() in do_bind() */
do_sasl_interact(LDAP UNUSED (* ld),unsigned UNUSED (flags),void * defaults,void * _interact)342 static int do_sasl_interact(LDAP UNUSED(*ld), unsigned UNUSED(flags),
343 void *defaults, void *_interact)
344 {
345 struct ldap_config *cfg = defaults;
346 sasl_interact_t *interact = _interact;
347 while (interact->id != SASL_CB_LIST_END)
348 {
349 switch (interact->id)
350 {
351 case SASL_CB_GETREALM:
352 if (cfg->sasl_realm)
353 {
354 log_log(LOG_DEBUG, "do_sasl_interact(): returning sasl_realm \"%s\"",
355 cfg->sasl_realm);
356 interact->result = cfg->sasl_realm;
357 interact->len = strlen(cfg->sasl_realm);
358 }
359 else
360 log_log(LOG_DEBUG, "do_sasl_interact(): were asked for sasl_realm but we don't have any");
361 break;
362 case SASL_CB_AUTHNAME:
363 if (cfg->sasl_authcid)
364 {
365 log_log(LOG_DEBUG, "do_sasl_interact(): returning sasl_authcid \"%s\"",
366 cfg->sasl_authcid);
367 interact->result = cfg->sasl_authcid;
368 interact->len = strlen(cfg->sasl_authcid);
369 }
370 else
371 log_log(LOG_DEBUG, "do_sasl_interact(): were asked for sasl_authcid but we don't have any");
372 break;
373 case SASL_CB_USER:
374 if (cfg->sasl_authzid)
375 {
376 log_log(LOG_DEBUG, "do_sasl_interact(): returning sasl_authzid \"%s\"",
377 cfg->sasl_authzid);
378 interact->result = cfg->sasl_authzid;
379 interact->len = strlen(cfg->sasl_authzid);
380 }
381 else
382 log_log(LOG_DEBUG, "do_sasl_interact(): were asked for sasl_authzid but we don't have any");
383 break;
384 case SASL_CB_PASS:
385 if (cfg->bindpw)
386 {
387 log_log(LOG_DEBUG, "do_sasl_interact(): returning bindpw \"***\"");
388 interact->result = cfg->bindpw;
389 interact->len = strlen(cfg->bindpw);
390 }
391 else
392 log_log(LOG_DEBUG, "do_sasl_interact(): were asked for bindpw but we don't have any");
393 break;
394 default:
395 /* just ignore */
396 break;
397 }
398 interact++;
399 }
400 return LDAP_SUCCESS;
401 }
402 #endif /* HAVE_SASL_INTERACT_T */
403
404 #define LDAP_SET_OPTION(ld, option, invalue) \
405 rc = ldap_set_option(ld, option, invalue); \
406 if (rc != LDAP_SUCCESS) \
407 { \
408 myldap_err(LOG_ERR, ld, rc, "ldap_set_option(" #option ") failed"); \
409 return rc; \
410 }
411
412 #if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE)
print_ppolicy_expiry(MYLDAP_SESSION * session,unsigned int sec)413 static void print_ppolicy_expiry(MYLDAP_SESSION *session, unsigned int sec)
414 {
415 unsigned int days = 0;
416 unsigned int hours = 0;
417 unsigned int minutes = 0;
418 /* return this warning so PAM can present it to the user */
419 if (strlen(session->policy_message) != 0)
420 return;
421 if (sec > 24 * 3600)
422 {
423 days = sec / (24 * 3600);
424 sec -= days * 24 * 3600;
425 }
426 if (sec > 3600)
427 {
428 hours = sec / 3600;
429 sec -= (hours * 3600);
430 }
431 if (sec > 60)
432 {
433 minutes = sec / 60;
434 sec -= minutes * 60;
435 }
436 if (days > 1)
437 mysnprintf(session->policy_message, sizeof(session->policy_message),
438 "Password will expire in %u days", days);
439 else if (days > 0)
440 mysnprintf(session->policy_message, sizeof(session->policy_message),
441 "Password will expire in %u hours", hours + 24);
442 else if (hours > 1)
443 {
444 if (minutes > 1)
445 mysnprintf(session->policy_message, sizeof(session->policy_message),
446 "Password will expire in %u hours and %u minutes",
447 hours, minutes);
448 else
449 mysnprintf(session->policy_message, sizeof(session->policy_message),
450 "Password will expire in %u hours", hours);
451 }
452 else if (hours > 0)
453 mysnprintf(session->policy_message, sizeof(session->policy_message),
454 "Password will expire in %u minutes", minutes + 60);
455 else if (minutes > 1)
456 {
457 if (sec > 1)
458 mysnprintf(session->policy_message, sizeof(session->policy_message),
459 "Password will expire in %u minutes and %u seconds",
460 minutes, sec);
461 else
462 mysnprintf(session->policy_message, sizeof(session->policy_message),
463 "Password will expire in %u minutes", minutes);
464 }
465 else
466 mysnprintf(session->policy_message, sizeof(session->policy_message),
467 "Password will expire in %u seconds", sec);
468 }
469
handle_ppolicy_controls(MYLDAP_SESSION * session,LDAP * ld,LDAPControl ** ctrls)470 static void handle_ppolicy_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPControl **ctrls)
471 {
472 int i;
473 int rc;
474 /* clear policy response information in session */
475 session->policy_response = NSLCD_PAM_SUCCESS;
476 strncpy(session->policy_message, "", sizeof(session->policy_message));
477 for (i = 0; ctrls[i] != NULL; i++)
478 {
479 if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PWEXPIRED) == 0)
480 {
481 /* check for expired control: force the user to change their password */
482 log_log(LOG_DEBUG, "got LDAP_CONTROL_PWEXPIRED (password expired, user should change)");
483 if (session->policy_response == NSLCD_PAM_SUCCESS)
484 session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD;
485 }
486 else if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PWEXPIRING) == 0)
487 {
488 /* check for password expiration warning control: the password is about
489 to expire (returns the number of seconds remaining until the password
490 expires) */
491 char seconds[32];
492 long int sec;
493 mysnprintf(seconds, sizeof(seconds), "%.*s", (int)ctrls[i]->ldctl_value.bv_len,
494 ctrls[i]->ldctl_value.bv_val);
495 sec = atol(seconds);
496 log_log(LOG_DEBUG, "got LDAP_CONTROL_PWEXPIRING (password will expire in %ld seconds)",
497 sec);
498 print_ppolicy_expiry(session, (unsigned int)sec);
499 }
500 else if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0)
501 {
502 /* check for password policy control */
503 int expire = 0, grace = 0;
504 LDAPPasswordPolicyError error = -1;
505 rc = ldap_parse_passwordpolicy_control(ld, ctrls[i], &expire, &grace, &error);
506 if (rc != LDAP_SUCCESS)
507 myldap_err(LOG_WARNING, ld, rc, "ldap_parse_passwordpolicy_control() failed (ignored)");
508 else
509 {
510 /* log returned control information */
511 log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (%s)",
512 ldap_passwordpolicy_err2txt(error));
513 if (expire >= 0)
514 log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (password will expire in %d seconds)",
515 expire);
516 if (grace >= 0)
517 log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (%d grace logins left)",
518 grace);
519 /* return this information to PAM */
520 if ((error == PP_passwordExpired) &&
521 ((session->policy_response == NSLCD_PAM_SUCCESS) ||
522 (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD)))
523 {
524 /* this means that the password has expired and must be reset */
525 session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD;
526 mysnprintf(session->policy_message, sizeof(session->policy_message),
527 "%s", ldap_passwordpolicy_err2txt(error));
528 }
529 else if ((error == PP_accountLocked) &&
530 ((session->policy_response == NSLCD_PAM_SUCCESS) ||
531 (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD)))
532 {
533 /* this means that the account is locked and the user cannot log
534 in (the bind probably failed already) */
535 session->policy_response = NSLCD_PAM_ACCT_EXPIRED;
536 mysnprintf(session->policy_message, sizeof(session->policy_message),
537 "%s", ldap_passwordpolicy_err2txt(error));
538 }
539 else if ((error == PP_changeAfterReset) &&
540 (session->policy_response == NSLCD_PAM_SUCCESS))
541 {
542 /* this indicates that the password must be changed before the
543 user is allowed to perform any other operation */
544 session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD;
545 mysnprintf(session->policy_message, sizeof(session->policy_message),
546 "%s", ldap_passwordpolicy_err2txt(error));
547 }
548 else if ((error != PP_noError) &&
549 ((session->policy_response == NSLCD_PAM_SUCCESS) ||
550 (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD)))
551 {
552 /* any other error is assumed to mean that the operation failed */
553 session->policy_response = NSLCD_PAM_PERM_DENIED;
554 mysnprintf(session->policy_message, sizeof(session->policy_message),
555 "%s", ldap_passwordpolicy_err2txt(error));
556 }
557 /* both expire and grace should just be warnings to the user */
558 if ((expire >= 0) && (strlen(session->policy_message) == 0))
559 {
560 /* if no other error has happened, this indicates that the password
561 will soon expire (number of seconds) */
562 print_ppolicy_expiry(session, (unsigned int)expire);
563 }
564 else if ((grace >= 0) && (strlen(session->policy_message) == 0))
565 {
566 /* this indicates the number of grace logins that are left before
567 no further login attempts will be allowed */
568 mysnprintf(session->policy_message, sizeof(session->policy_message),
569 "Password expired, %d grace logins left", grace);
570 }
571 }
572 }
573 /* ignore any other controls */
574 }
575 }
576
do_ppolicy_bind(MYLDAP_SESSION * session,LDAP * ld,const char * uri)577 static int do_ppolicy_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri)
578 {
579 int rc, parserc;
580 struct berval cred;
581 LDAPControl passwd_policy_req;
582 LDAPControl *requestctrls[2];
583 LDAPControl **responsectrls;
584 int msgid;
585 struct timeval timeout;
586 LDAPMessage *result;
587 /* build policy request if pam_authc_ppolicy is set */
588 if (nslcd_cfg->pam_authc_ppolicy)
589 {
590 passwd_policy_req.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
591 passwd_policy_req.ldctl_value.bv_val = NULL; /* none */
592 passwd_policy_req.ldctl_value.bv_len = 0;
593 passwd_policy_req.ldctl_iscritical = 0; /* not critical */
594 requestctrls[0] = &passwd_policy_req;
595 }
596 else
597 requestctrls[0] = NULL;
598 requestctrls[1] = NULL;
599 /* build password berval */
600 cred.bv_val = (char *)session->bindpw;
601 cred.bv_len = strlen(session->bindpw);
602 /* do a SASL simple bind with the binddn and bindpw */
603 log_log(LOG_DEBUG, "ldap_sasl_bind(\"%s\",%s) (uri=\"%s\") (ppolicy=%s)",
604 session->binddn, (session->bindpw[0] != '\0') ? "\"***\"" : "\"\"",
605 uri, (requestctrls[0] == NULL) ? "no" : "yes");
606 rc = ldap_sasl_bind(ld, session->binddn, LDAP_SASL_SIMPLE, &cred, requestctrls, NULL, &msgid);
607 if (rc != LDAP_SUCCESS)
608 return rc;
609 if (msgid == -1)
610 {
611 myldap_err(LOG_WARNING, ld, rc,"ldap_sasl_bind() failed (msgid=-1, uri=%s)", uri);
612 return LDAP_OPERATIONS_ERROR;
613 }
614 /* get the result from the bind operation */
615 timeout.tv_sec = nslcd_cfg->bind_timelimit;
616 timeout.tv_usec = 0;
617 result = NULL;
618 rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &timeout, &result);
619 if (rc == -1) /* some error */
620 {
621 if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
622 rc = LDAP_UNAVAILABLE;
623 myldap_err(LOG_ERR, ld, rc, "ldap_result() failed");
624 if (result != NULL)
625 ldap_msgfree(result);
626 return LDAP_LOCAL_ERROR;
627 }
628 if (rc == 0) /* the timeout expired */
629 {
630 log_log(LOG_ERR, "ldap_result() timed out");
631 if (result != NULL)
632 ldap_msgfree(result);
633 return LDAP_TIMEOUT;
634 }
635 responsectrls = NULL;
636 /* ignore any response controls unless we're interested in ppolicy */
637 if (nslcd_cfg->pam_authc_ppolicy)
638 {
639 /* parse the result from the bind operation (frees result, gets controls) */
640 parserc = ldap_parse_result(ld, result, &rc, NULL, NULL, NULL, &responsectrls, 1);
641 if (parserc != LDAP_SUCCESS)
642 {
643 myldap_err(LOG_ERR, ld, parserc, "ldap_parse_result() failed");
644 if (responsectrls != NULL)
645 ldap_controls_free(responsectrls);
646 return parserc;
647 }
648 /* handle any returned controls */
649 if (responsectrls != NULL)
650 {
651 handle_ppolicy_controls(session, ld, responsectrls);
652 ldap_controls_free(responsectrls);
653 }
654 }
655 /* return the result of the BIND operation */
656 if (rc != LDAP_SUCCESS)
657 {
658 myldap_err(LOG_DEBUG, ld, rc, "ldap_parse_result() result");
659 return rc;
660 }
661 /* check the returned controls */
662 return LDAP_SUCCESS;
663 }
664 #endif /* no SASL, so no ppolicy */
665
666 /* This function performs the authentication phase of opening a connection.
667 The binddn and bindpw parameters may be used to override the authentication
668 mechanism defined in the configuration. This returns an LDAP result
669 code. */
do_bind(MYLDAP_SESSION * session,LDAP * ld,const char * uri)670 static int do_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri)
671 {
672 int rc;
673 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
674 #ifndef HAVE_SASL_INTERACT_T
675 struct berval cred;
676 #endif /* not HAVE_SASL_INTERACT_T */
677 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
678 #ifdef LDAP_OPT_X_TLS
679 /* check if StartTLS is requested */
680 if (nslcd_cfg->ssl == SSL_START_TLS)
681 {
682 log_log(LOG_DEBUG, "ldap_start_tls_s()");
683 errno = 0;
684 rc = ldap_start_tls_s(ld, NULL, NULL);
685 if (rc != LDAP_SUCCESS)
686 {
687 myldap_err(LOG_WARNING, ld, rc, "ldap_start_tls_s() failed (uri=%s)",
688 uri);
689 return rc;
690 }
691 }
692 #endif /* LDAP_OPT_X_TLS */
693 /* check if the binddn and bindpw are overwritten in the session */
694 if (session->binddn[0] != '\0')
695 {
696 #if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE)
697 return do_ppolicy_bind(session, ld, uri);
698 #else /* no SASL, so no ppolicy */
699 /* do a simple bind */
700 log_log(LOG_DEBUG, "ldap_simple_bind_s(\"%s\",%s) (uri=\"%s\")",
701 session->binddn,
702 (session->bindpw[0] != '\0') ? "\"***\"" : "\"\"",
703 uri);
704 return ldap_simple_bind_s(ld, session->binddn, session->bindpw);
705 #endif
706 }
707 /* perform SASL bind if requested and available on platform */
708 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
709 /* TODO: store this information in the session */
710 if (nslcd_cfg->sasl_mech != NULL)
711 {
712 /* do a SASL bind */
713 if (nslcd_cfg->sasl_secprops != NULL)
714 {
715 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_SASL_SECPROPS,\"%s\")",
716 nslcd_cfg->sasl_secprops);
717 LDAP_SET_OPTION(ld, LDAP_OPT_X_SASL_SECPROPS, (void *)nslcd_cfg->sasl_secprops);
718 }
719 #ifdef HAVE_SASL_INTERACT_T
720 if (nslcd_cfg->binddn != NULL)
721 log_log(LOG_DEBUG, "ldap_sasl_interactive_bind_s(\"%s\",\"%s\") (uri=\"%s\")",
722 nslcd_cfg->binddn, nslcd_cfg->sasl_mech, uri);
723 else
724 log_log(LOG_DEBUG, "ldap_sasl_interactive_bind_s(NULL,\"%s\") (uri=\"%s\")",
725 nslcd_cfg->sasl_mech, uri);
726 return ldap_sasl_interactive_bind_s(ld, nslcd_cfg->binddn,
727 nslcd_cfg->sasl_mech, NULL, NULL,
728 LDAP_SASL_QUIET, do_sasl_interact,
729 (void *)nslcd_cfg);
730 #else /* HAVE_SASL_INTERACT_T */
731 if (nslcd_cfg->bindpw != NULL)
732 {
733 cred.bv_val = nslcd_cfg->bindpw;
734 cred.bv_len = strlen(nslcd_cfg->bindpw);
735 }
736 else
737 {
738 cred.bv_val = "";
739 cred.bv_len = 0;
740 }
741 if (nslcd_cfg->binddn != NULL)
742 log_log(LOG_DEBUG, "ldap_sasl_bind_s(\"%s\",\"%s\",%s) (uri=\"%s\")",
743 nslcd_cfg->binddn, nslcd_cfg->sasl_mech,
744 nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
745 else
746 log_log(LOG_DEBUG, "ldap_sasl_bind_s(NULL,\"%s\",%s) (uri=\"%s\")",
747 nslcd_cfg->sasl_mech,
748 nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
749 return ldap_sasl_bind_s(ld, nslcd_cfg->binddn,
750 nslcd_cfg->sasl_mech, &cred, NULL, NULL, NULL);
751 #endif /* not HAVE_SASL_INTERACT_T */
752 }
753 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
754 /* do a simple bind */
755 if (nslcd_cfg->binddn)
756 log_log(LOG_DEBUG, "ldap_simple_bind_s(\"%s\",%s) (uri=\"%s\")",
757 nslcd_cfg->binddn, nslcd_cfg->bindpw ? "\"***\"" : "NULL",
758 uri);
759 else
760 log_log(LOG_DEBUG, "ldap_simple_bind_s(NULL,%s) (uri=\"%s\")",
761 nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
762 return ldap_simple_bind_s(ld, nslcd_cfg->binddn, nslcd_cfg->bindpw);
763 }
764
765 #ifdef HAVE_LDAP_SET_REBIND_PROC
766 /* This function is called by the LDAP library when chasing referrals.
767 It is configured with the ldap_set_rebind_proc() below. */
768 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
do_rebind(LDAP * ld,LDAP_CONST char * url,ber_tag_t UNUSED (request),ber_int_t UNUSED (msgid),void * arg)769 static int do_rebind(LDAP *ld, LDAP_CONST char *url,
770 ber_tag_t UNUSED(request),
771 ber_int_t UNUSED(msgid), void *arg)
772 {
773 MYLDAP_SESSION *session = (MYLDAP_SESSION *)arg;
774 log_log(LOG_DEBUG, "rebinding to %s", url);
775 return do_bind(session, ld, url);
776 }
777 #else /* not recent OpenLDAP */
do_rebind(LDAP * ld,char ** dnp,char ** passwdp,int * authmethodp,int freeit,void * arg)778 static int do_rebind(LDAP *ld, char **dnp, char **passwdp, int *authmethodp,
779 int freeit, void *arg)
780 {
781 MYLDAP_SESSION *session = (MYLDAP_SESSION *)arg;
782 if (freeit)
783 {
784 free(*dnp);
785 memset(*passwdp, 0, strlen(*passwdp));
786 free(*passwdp);
787 }
788 else
789 {
790 log_log(LOG_DEBUG, "rebinding");
791 *dnp = strdup(session->binddn);
792 *passwdp = strdup(session->bindpw);
793 *authmethodp = LDAP_AUTH_SIMPLE;
794 if ((*dnp == NULL) || (*passwdp == NULL))
795 {
796 if (*dnp != NULL)
797 free(*dnp);
798 log_log(LOG_CRIT, "do_rebind(): strdup() failed to allocate memory");
799 return LDAP_NO_MEMORY;
800 }
801 }
802 return LDAP_SUCCESS;
803 }
804 #endif /* not recent OpenLDAP */
805 #endif /* HAVE_LDAP_SET_REBIND_PROC */
806
807 /* set a recieve and send timeout on a socket */
set_socket_timeout(LDAP * ld,time_t sec,suseconds_t usec)808 static int set_socket_timeout(LDAP *ld, time_t sec, suseconds_t usec)
809 {
810 struct timeval tv;
811 int rc = LDAP_SUCCESS;
812 int sd;
813 log_log(LOG_DEBUG, "set_socket_timeout(%lu,%lu)",
814 (unsigned long)sec, (unsigned long)usec);
815 /* get the socket */
816 if ((rc = ldap_get_option(ld, LDAP_OPT_DESC, &sd)) != LDAP_SUCCESS)
817 {
818 myldap_err(LOG_ERR, ld, rc, "ldap_get_option(LDAP_OPT_DESC) failed");
819 return rc;
820 }
821 /* ignore invalid (probably closed) file descriptors */
822 if (sd <= 0)
823 return LDAP_SUCCESS;
824 /* set timeouts */
825 memset(&tv, 0, sizeof(tv));
826 tv.tv_sec = sec;
827 tv.tv_usec = usec;
828 if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv)))
829 {
830 log_log(LOG_ERR, "setsockopt(%d,SO_RCVTIMEO) failed: %s",
831 sd, strerror(errno));
832 rc = LDAP_LOCAL_ERROR;
833 }
834 if (setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, (void *)&tv, sizeof(tv)))
835 {
836 log_log(LOG_ERR, "setsockopt(%d,SO_RCVTIMEO) failed: %s",
837 sd, strerror(errno));
838 rc = LDAP_LOCAL_ERROR;
839 }
840 return rc;
841 }
842
843 #ifdef LDAP_OPT_CONNECT_CB
844 /* This function is called by the LDAP library once a connection was made to the server. We
845 set a timeout on the socket here, to catch network timeouts during the ssl
846 handshake phase. It is configured with LDAP_OPT_CONNECT_CB. */
connect_cb(LDAP * ld,Sockbuf UNUSED (* sb),LDAPURLDesc UNUSED (* srv),struct sockaddr UNUSED (* addr),struct ldap_conncb UNUSED (* ctx))847 static int connect_cb(LDAP *ld, Sockbuf UNUSED(*sb),
848 LDAPURLDesc UNUSED(*srv), struct sockaddr UNUSED(*addr),
849 struct ldap_conncb UNUSED(*ctx))
850 {
851 /* set timeout options on socket to avoid hang in some cases (a little
852 more than the normal timeout so this should only be triggered in cases
853 where the library behaves incorrectly) */
854 if (nslcd_cfg->timelimit)
855 set_socket_timeout(ld, nslcd_cfg->timelimit, 500000);
856 return LDAP_SUCCESS;
857 }
858
859 /* We have an empty disconnect callback because LDAP_OPT_CONNECT_CB expects
860 both functions to be available. */
disconnect_cb(LDAP UNUSED (* ld),Sockbuf UNUSED (* sb),struct ldap_conncb UNUSED (* ctx))861 static void disconnect_cb(LDAP UNUSED(*ld), Sockbuf UNUSED(*sb),
862 struct ldap_conncb UNUSED(*ctx))
863 {
864 }
865 #endif /* LDAP_OPT_CONNECT_CB */
866
867 /* This function sets a number of properties on the connection, based
868 what is configured in the configfile. This function returns an
869 LDAP status code. */
do_set_options(MYLDAP_SESSION * session)870 static int do_set_options(MYLDAP_SESSION *session)
871 {
872 int rc;
873 struct timeval tv;
874 #ifdef LDAP_OPT_CONNECT_CB
875 /* make this static because OpenLDAP doesn't make its own copy */
876 static struct ldap_conncb cb;
877 #endif /* LDAP_OPT_CONNECT_CB */
878 #ifdef LDAP_OPT_X_TLS
879 int i;
880 #endif /* LDAP_OPT_X_TLS */
881 #ifdef HAVE_LDAP_SET_REBIND_PROC
882 /* the rebind function that is called when chasing referrals, see
883 http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/apis/ldap_set_rebind_proc.htm
884 http://www.openldap.org/software/man.cgi?query=ldap_set_rebind_proc&manpath=OpenLDAP+2.4-Release */
885 /* TODO: probably only set this if we should chase referrals */
886 log_log(LOG_DEBUG, "ldap_set_rebind_proc()");
887 #ifndef LDAP_SET_REBIND_PROC_RETURNS_VOID /* it returns int */
888 rc = ldap_set_rebind_proc(session->ld, do_rebind, session);
889 if (rc != LDAP_SUCCESS)
890 {
891 myldap_err(LOG_ERR, session->ld, rc, "ldap_set_rebind_proc() failed");
892 return rc;
893 }
894 #else /* ldap_set_rebind_proc() returns void */
895 ldap_set_rebind_proc(session->ld, do_rebind, session);
896 #endif
897 #endif /* HAVE_LDAP_SET_REBIND_PROC */
898 /* set the protocol version to use */
899 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,%d)",
900 nslcd_cfg->ldap_version);
901 LDAP_SET_OPTION(session->ld, LDAP_OPT_PROTOCOL_VERSION,
902 &nslcd_cfg->ldap_version);
903 /* set some other options */
904 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_DEREF,%d)",
905 nslcd_cfg->deref);
906 LDAP_SET_OPTION(session->ld, LDAP_OPT_DEREF, &nslcd_cfg->deref);
907 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_TIMELIMIT,%d)",
908 nslcd_cfg->timelimit);
909 LDAP_SET_OPTION(session->ld, LDAP_OPT_TIMELIMIT, &nslcd_cfg->timelimit);
910 tv.tv_sec = nslcd_cfg->bind_timelimit;
911 tv.tv_usec = 0;
912 #ifdef LDAP_OPT_TIMEOUT
913 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_TIMEOUT,%d)",
914 nslcd_cfg->timelimit);
915 LDAP_SET_OPTION(session->ld, LDAP_OPT_TIMEOUT, &tv);
916 #endif /* LDAP_OPT_TIMEOUT */
917 #ifdef LDAP_OPT_NETWORK_TIMEOUT
918 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,%d)",
919 nslcd_cfg->timelimit);
920 LDAP_SET_OPTION(session->ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
921 #endif /* LDAP_OPT_NETWORK_TIMEOUT */
922 #ifdef LDAP_X_OPT_CONNECT_TIMEOUT
923 log_log(LOG_DEBUG, "ldap_set_option(LDAP_X_OPT_CONNECT_TIMEOUT,%d)",
924 nslcd_cfg->timelimit);
925 LDAP_SET_OPTION(session->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &tv);
926 #endif /* LDAP_X_OPT_CONNECT_TIMEOUT */
927 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_REFERRALS,%s)",
928 nslcd_cfg->referrals ? "LDAP_OPT_ON" : "LDAP_OPT_OFF");
929 LDAP_SET_OPTION(session->ld, LDAP_OPT_REFERRALS,
930 nslcd_cfg->referrals ? LDAP_OPT_ON : LDAP_OPT_OFF);
931 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)");
932 LDAP_SET_OPTION(session->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
933 #ifdef LDAP_OPT_CONNECT_CB
934 /* register a connection callback */
935 cb.lc_add = connect_cb;
936 cb.lc_del = disconnect_cb;
937 cb.lc_arg = NULL;
938 LDAP_SET_OPTION(session->ld, LDAP_OPT_CONNECT_CB, (void *)&cb);
939 #endif /* LDAP_OPT_CONNECT_CB */
940 #ifdef LDAP_OPT_X_TLS
941 /* if SSL is desired, then enable it */
942 if ((nslcd_cfg->ssl == SSL_LDAPS) ||
943 (strncasecmp(nslcd_cfg->uris[session->current_uri].uri, "ldaps://", 8) == 0))
944 {
945 /* use tls */
946 i = LDAP_OPT_X_TLS_HARD;
947 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_TLS,LDAP_OPT_X_TLS_HARD)");
948 LDAP_SET_OPTION(session->ld, LDAP_OPT_X_TLS, &i);
949 }
950 #endif /* LDAP_OPT_X_TLS */
951 #ifdef LDAP_OPT_X_SASL_NOCANON
952 if (nslcd_cfg->sasl_canonicalize >= 0)
953 {
954 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_SASL_NOCANON,%s)",
955 nslcd_cfg->sasl_canonicalize ? "LDAP_OPT_OFF" : "LDAP_OPT_ON");
956 LDAP_SET_OPTION(session->ld, LDAP_OPT_X_SASL_NOCANON,
957 nslcd_cfg->sasl_canonicalize ? LDAP_OPT_OFF : LDAP_OPT_ON);
958 }
959 #endif /* LDAP_OPT_X_SASL_NOCANON */
960 /* if nothing above failed, everything should be fine */
961 return LDAP_SUCCESS;
962 }
963
964 /* close the connection to the server and invalidate any running searches */
do_close(MYLDAP_SESSION * session)965 static void do_close(MYLDAP_SESSION *session)
966 {
967 int i;
968 int rc;
969 time_t sec;
970 /* if we had reachability problems with the server close the connection */
971 if (session->ld != NULL)
972 {
973 /* set timeout options on socket to avoid hang in some cases
974 (we set a short timeout because we don't care too much about properly
975 shutting down the connection) */
976 if (nslcd_cfg->timelimit)
977 {
978 sec = nslcd_cfg->timelimit / 2;
979 if (!sec)
980 sec = 1;
981 set_socket_timeout(session->ld, sec, 0);
982 }
983 /* go over the other searches and partially close them */
984 for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
985 {
986 if (session->searches[i] != NULL)
987 {
988 /* free any messages (because later ld is no longer valid) */
989 if (session->searches[i]->msg != NULL)
990 {
991 ldap_msgfree(session->searches[i]->msg);
992 session->searches[i]->msg = NULL;
993 }
994 /* abandon the search if there were more results to fetch */
995 if (session->searches[i]->msgid != -1)
996 {
997 log_log(LOG_DEBUG, "ldap_abandon()");
998 if (ldap_abandon(session->searches[i]->session->ld, session->searches[i]->msgid))
999 {
1000 if (ldap_get_option(session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
1001 rc = LDAP_OTHER;
1002 myldap_err(LOG_WARNING, session->ld, rc,
1003 "ldap_abandon() failed to abandon search");
1004 }
1005 session->searches[i]->msgid = -1;
1006 }
1007 /* flag the search as invalid */
1008 session->searches[i]->valid = 0;
1009 }
1010 }
1011 /* close the connection to the server */
1012 log_log(LOG_DEBUG, "ldap_unbind()");
1013 rc = ldap_unbind(session->ld);
1014 session->ld = NULL;
1015 if (rc != LDAP_SUCCESS)
1016 myldap_err(LOG_WARNING, session->ld, rc, "ldap_unbind() failed");
1017 }
1018 }
1019
myldap_session_check(MYLDAP_SESSION * session)1020 void myldap_session_check(MYLDAP_SESSION *session)
1021 {
1022 int i;
1023 time_t current_time;
1024 int sd;
1025 int rc;
1026 struct sockaddr sa;
1027 socklen_t salen = sizeof(sa);
1028 /* check parameters */
1029 if (session == NULL)
1030 {
1031 log_log(LOG_ERR, "myldap_session_check(): invalid parameter passed");
1032 errno = EINVAL;
1033 return;
1034 }
1035 if (session->ld != NULL)
1036 {
1037 rc = ldap_get_option(session->ld, LDAP_OPT_DESC, &sd);
1038 if (rc != LDAP_SUCCESS)
1039 {
1040 myldap_err(LOG_WARNING, session->ld, rc,
1041 "ldap_get_option(LDAP_OPT_DESC) failed (ignored)");
1042 }
1043 else
1044 {
1045 /* check if the connection was closed by the peer */
1046 if (getpeername(sd, &sa, &salen) == -1)
1047 {
1048 if (errno == ENOTCONN)
1049 {
1050 log_log(LOG_DEBUG, "myldap_session_check(): connection reset by peer");
1051 do_close(session);
1052 return;
1053 }
1054 }
1055 }
1056 /* check if we should time out the connection */
1057 if (nslcd_cfg->idle_timelimit > 0)
1058 {
1059 /* if we have any running searches, don't time out */
1060 for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
1061 if ((session->searches[i] != NULL) && (session->searches[i]->valid))
1062 return;
1063 /* consider timeout (there are no running searches) */
1064 time(¤t_time);
1065 if ((session->lastactivity + nslcd_cfg->idle_timelimit) < current_time)
1066 {
1067 log_log(LOG_DEBUG, "myldap_session_check(): idle_timelimit reached");
1068 do_close(session);
1069 }
1070 }
1071 }
1072 }
1073
1074 /* This opens connection to an LDAP server, sets all connection options
1075 and binds to the server. This returns an LDAP status code. */
do_open(MYLDAP_SESSION * session)1076 static int do_open(MYLDAP_SESSION *session)
1077 {
1078 int rc;
1079 /* if the connection is still there (ie. ldap_unbind() wasn't
1080 called) then we can return the cached connection */
1081 if (session->ld != NULL)
1082 return LDAP_SUCCESS;
1083 /* we should build a new session now */
1084 session->ld = NULL;
1085 session->lastactivity = 0;
1086 /* open the connection */
1087 log_log(LOG_DEBUG, "ldap_initialize(%s)",
1088 nslcd_cfg->uris[session->current_uri].uri);
1089 errno = 0;
1090 rc = ldap_initialize(&(session->ld), nslcd_cfg->uris[session->current_uri].uri);
1091 if (rc != LDAP_SUCCESS)
1092 {
1093 myldap_err(LOG_WARNING, session->ld, rc, "ldap_initialize(%s) failed",
1094 nslcd_cfg->uris[session->current_uri].uri);
1095 if (session->ld != NULL)
1096 do_close(session);
1097 return rc;
1098 }
1099 else if (session->ld == NULL)
1100 {
1101 log_log(LOG_WARNING, "ldap_initialize() returned NULL");
1102 return LDAP_LOCAL_ERROR;
1103 }
1104 /* set the options for the connection */
1105 rc = do_set_options(session);
1106 if (rc != LDAP_SUCCESS)
1107 {
1108 do_close(session);
1109 return rc;
1110 }
1111 /* bind to the server */
1112 errno = 0;
1113 rc = do_bind(session, session->ld, nslcd_cfg->uris[session->current_uri].uri);
1114 if (rc != LDAP_SUCCESS)
1115 {
1116 /* log actual LDAP error code */
1117 myldap_err((session->binddn[0] == '\0') ? LOG_WARNING : LOG_DEBUG,
1118 session->ld, rc, "failed to bind to LDAP server %s",
1119 nslcd_cfg->uris[session->current_uri].uri);
1120 do_close(session);
1121 return rc;
1122 }
1123 /* update last activity and finish off state */
1124 time(&(session->lastactivity));
1125 return LDAP_SUCCESS;
1126 }
1127
1128 /* Perform a simple bind operation and return the ppolicy results. */
myldap_bind(MYLDAP_SESSION * session,const char * dn,const char * password,int * response,const char ** message)1129 int myldap_bind(MYLDAP_SESSION *session, const char *dn, const char *password,
1130 int *response, const char **message)
1131 {
1132 MYLDAP_SEARCH *search;
1133 static const char *attrs[2];
1134 int rc;
1135 /* error out when buffers are too small */
1136 if (strlen(dn) >= sizeof(session->binddn))
1137 {
1138 log_log(LOG_ERR, "myldap_bind(): binddn buffer too small (%lu required)",
1139 (unsigned long) strlen(dn));
1140 return LDAP_LOCAL_ERROR;
1141 }
1142 if (strlen(password) >= sizeof(session->bindpw))
1143 {
1144 log_log(LOG_ERR, "myldap_bind(): bindpw buffer too small (%lu required)",
1145 (unsigned long) strlen(password));
1146 return LDAP_LOCAL_ERROR;
1147 }
1148 /* copy dn and password into session */
1149 strncpy(session->binddn, dn, sizeof(session->binddn));
1150 session->binddn[sizeof(session->binddn) - 1] = '\0';
1151 strncpy(session->bindpw, password, sizeof(session->bindpw));
1152 session->bindpw[sizeof(session->bindpw) - 1] = '\0';
1153 /* construct a fake search to trigger the BIND operation */
1154 attrs[0] = "dn";
1155 attrs[1] = NULL;
1156 search = myldap_search(session, session->binddn, MYLDAP_SCOPE_BINDONLY,
1157 "(objectClass=*)", attrs, &rc);
1158 if (search != NULL)
1159 myldap_search_close(search);
1160 /* return ppolicy results */
1161 if (response != NULL)
1162 *response = session->policy_response;
1163 if (message != NULL)
1164 *message = session->policy_message;
1165 return rc;
1166 }
1167
1168 /* perform a search operation, the connection is assumed to be open */
do_try_search(MYLDAP_SEARCH * search)1169 static int do_try_search(MYLDAP_SEARCH *search)
1170 {
1171 int ctrlidx = 0;
1172 int rc;
1173 LDAPControl *serverctrls[3];
1174 #ifdef HAVE_LDAP_CREATE_DEREF_CONTROL
1175 int i;
1176 struct LDAPDerefSpec ds[2];
1177 char *deref_attrs[2];
1178 #endif /* HAVE_LDAP_CREATE_DEREF_CONTROL */
1179 int msgid;
1180 /* if we're using paging, build a page control */
1181 if ((nslcd_cfg->pagesize > 0) && (search->scope != LDAP_SCOPE_BASE))
1182 {
1183 rc = ldap_create_page_control(search->session->ld, nslcd_cfg->pagesize,
1184 search->cookie, 0, &serverctrls[ctrlidx]);
1185 if (rc == LDAP_SUCCESS)
1186 ctrlidx++;
1187 else
1188 {
1189 myldap_err(LOG_WARNING, search->session->ld, rc,
1190 "ldap_create_page_control() failed");
1191 serverctrls[ctrlidx] = NULL;
1192 /* if we were paging, failure building the second control is fatal */
1193 if (search->cookie != NULL)
1194 return rc;
1195 }
1196 }
1197 #ifdef HAVE_LDAP_CREATE_DEREF_CONTROL
1198 /* if doing group searches, add deref control to search request
1199 (this is currently a bit of a hack and hard-coded for group searches
1200 which are detected by requesting the attmap_group_member member
1201 attribute) */
1202 for (i = 0; search->attrs[i] != NULL; i++)
1203 if (strcasecmp(search->attrs[i], attmap_group_member) == 0)
1204 {
1205 /* attributes from dereff'd entries */
1206 deref_attrs[0] = (void *)attmap_passwd_uid;
1207 deref_attrs[1] = NULL;
1208 /* build deref control */
1209 ds[0].derefAttr = (void *)attmap_group_member;
1210 ds[0].attributes = deref_attrs;
1211 ds[1].derefAttr = NULL;
1212 ds[1].attributes = NULL;
1213 rc = ldap_create_deref_control(search->session->ld, ds, 0, &serverctrls[ctrlidx]);
1214 if (rc == LDAP_SUCCESS)
1215 ctrlidx++;
1216 else
1217 {
1218 myldap_err(LOG_WARNING, search->session->ld, rc,
1219 "ldap_create_deref_control() failed");
1220 serverctrls[ctrlidx] = NULL;
1221 }
1222 }
1223 #endif /* HAVE_LDAP_CREATE_DEREF_CONTROL */
1224 /* NULL terminate control list */
1225 serverctrls[ctrlidx] = NULL;
1226 /* clear error flag (perhaps control setting failed) */
1227 if (ctrlidx > 0)
1228 {
1229 rc = LDAP_SUCCESS;
1230 if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
1231 log_log(LOG_WARNING, "failed to clear the error flag");
1232 }
1233 /* perform the search */
1234 rc = ldap_search_ext(search->session->ld, search->base, search->scope,
1235 search->filter, (char **)(search->attrs),
1236 0, serverctrls[0] == NULL ? NULL : serverctrls,
1237 NULL, NULL, LDAP_NO_LIMIT, &msgid);
1238 /* free the controls if we had them */
1239 for (ctrlidx = 0; serverctrls[ctrlidx] != NULL; ctrlidx++)
1240 ldap_control_free(serverctrls[ctrlidx]);
1241 /* handle errors */
1242 if (rc != LDAP_SUCCESS)
1243 {
1244 myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_search_ext() failed");
1245 return rc;
1246 }
1247 /* update the last activity on the connection */
1248 time(&(search->session->lastactivity));
1249 /* save msgid */
1250 search->msgid = msgid;
1251 /* return the new search */
1252 return LDAP_SUCCESS;
1253 }
1254
myldap_create_session(void)1255 MYLDAP_SESSION *myldap_create_session(void)
1256 {
1257 return myldap_session_new();
1258 }
1259
myldap_session_cleanup(MYLDAP_SESSION * session)1260 void myldap_session_cleanup(MYLDAP_SESSION *session)
1261 {
1262 int i;
1263 /* check parameter */
1264 if (session == NULL)
1265 {
1266 log_log(LOG_ERR, "myldap_session_cleanup(): invalid session passed");
1267 return;
1268 }
1269 /* go over all searches in the session and close them */
1270 for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
1271 {
1272 if (session->searches[i] != NULL)
1273 {
1274 myldap_search_close(session->searches[i]);
1275 session->searches[i] = NULL;
1276 }
1277 }
1278 }
1279
myldap_session_close(MYLDAP_SESSION * session)1280 void myldap_session_close(MYLDAP_SESSION *session)
1281 {
1282 /* check parameter */
1283 if (session == NULL)
1284 {
1285 log_log(LOG_ERR, "myldap_session_cleanup(): invalid session passed");
1286 return;
1287 }
1288 /* close pending searches */
1289 myldap_session_cleanup(session);
1290 /* close any open connections */
1291 do_close(session);
1292 /* free allocated memory */
1293 memset(session->bindpw, 0, sizeof(session->bindpw));
1294 free(session);
1295 }
1296
1297 /* mutex for updating the times in the uri */
1298 pthread_mutex_t uris_mutex = PTHREAD_MUTEX_INITIALIZER;
1299
do_retry_search(MYLDAP_SEARCH * search)1300 static int do_retry_search(MYLDAP_SEARCH *search)
1301 {
1302 int sleeptime = 0;
1303 int start_uri;
1304 time_t endtime;
1305 time_t nexttry;
1306 time_t t;
1307 int rc = LDAP_UNAVAILABLE;
1308 struct myldap_uri *current_uri;
1309 int dotry[NSS_LDAP_CONFIG_MAX_URIS];
1310 int do_invalidate = 0;
1311 /* clear time stamps */
1312 for (start_uri = 0; start_uri < NSS_LDAP_CONFIG_MAX_URIS; start_uri++)
1313 dotry[start_uri] = 1;
1314 /* keep trying until we time out */
1315 endtime = time(NULL) + nslcd_cfg->reconnect_retrytime;
1316 while (1)
1317 {
1318 nexttry = endtime;
1319 /* try each configured URL once */
1320 pthread_mutex_lock(&uris_mutex);
1321 start_uri = search->session->current_uri;
1322 do
1323 {
1324 current_uri = &(nslcd_cfg->uris[search->session->current_uri]);
1325 /* only try this URI if we should */
1326 if (!dotry[search->session->current_uri])
1327 { /* skip this URI */ }
1328 else if ((current_uri->lastfail > (current_uri->firstfail + nslcd_cfg->reconnect_retrytime)) &&
1329 ((t = time(NULL)) < (current_uri->lastfail + nslcd_cfg->reconnect_retrytime)))
1330 {
1331 /* we are in a hard fail state and have retried not long ago */
1332 log_log(LOG_DEBUG, "not retrying server %s which failed just %d second(s) ago and has been failing for %d seconds",
1333 current_uri->uri, (int)(t - current_uri->lastfail),
1334 (int)(t - current_uri->firstfail));
1335 dotry[search->session->current_uri] = 0;
1336 }
1337 else
1338 {
1339 /* try to start the search */
1340 pthread_mutex_unlock(&uris_mutex);
1341 /* ensure that we have an open connection and start a search */
1342 rc = do_open(search->session);
1343 /* perform the actual search, unless we were only binding */
1344 if ((rc == LDAP_SUCCESS) && (search->scope != MYLDAP_SCOPE_BINDONLY))
1345 rc = do_try_search(search);
1346 /* if we are authenticating a user and get an error regarding failed
1347 password we should error out instead of trying all servers */
1348 if ((search->session->binddn[0] != '\0') && (rc == LDAP_INVALID_CREDENTIALS))
1349 {
1350 do_close(search->session);
1351 return rc;
1352 }
1353 if (rc == LDAP_SUCCESS)
1354 {
1355 pthread_mutex_lock(&uris_mutex);
1356 /* check if we are coming back from an error */
1357 if ((current_uri->lastfail > 0) || (search->session->current_uri != start_uri))
1358 {
1359 log_log(LOG_INFO, "connected to LDAP server %s", current_uri->uri);
1360 do_invalidate = 1;
1361 }
1362 if (first_search)
1363 {
1364 do_invalidate = 1;
1365 first_search = 0;
1366 }
1367 /* update ok time */
1368 current_uri->firstfail = 0;
1369 current_uri->lastfail = 0;
1370 pthread_mutex_unlock(&uris_mutex);
1371 /* flag the search as valid */
1372 search->valid = 1;
1373 /* signal external invalidation of configured caches */
1374 if (do_invalidate)
1375 invalidator_do(LM_NONE);
1376 return LDAP_SUCCESS;
1377 }
1378 /* close the current connection */
1379 do_close(search->session);
1380 /* update time of failure and figure out when we should retry */
1381 pthread_mutex_lock(&uris_mutex);
1382 t = time(NULL);
1383 /* update timestamps unless we are doing an authentication search */
1384 if (search->session->binddn[0] == '\0')
1385 {
1386 if (current_uri->firstfail == 0)
1387 current_uri->firstfail = t;
1388 current_uri->lastfail = t;
1389 }
1390 /* if it is one of these, retrying this URI is not going to help */
1391 if ((rc == LDAP_INVALID_CREDENTIALS) || (rc == LDAP_INSUFFICIENT_ACCESS) ||
1392 (rc == LDAP_AUTH_METHOD_NOT_SUPPORTED))
1393 dotry[search->session->current_uri] = 0;
1394 /* check when we should try this URI again */
1395 else if (t <= (current_uri->firstfail + nslcd_cfg->reconnect_retrytime))
1396 {
1397 t += nslcd_cfg->reconnect_sleeptime;
1398 if (t < nexttry)
1399 nexttry = t;
1400 }
1401 }
1402 /* try the next URI (with wrap-around) */
1403 search->session->current_uri++;
1404 if (nslcd_cfg->uris[search->session->current_uri].uri == NULL)
1405 search->session->current_uri = 0;
1406 }
1407 while (search->session->current_uri != start_uri);
1408 pthread_mutex_unlock(&uris_mutex);
1409 /* see if it is any use sleeping */
1410 if (nexttry >= endtime)
1411 {
1412 if (search->session->binddn[0] == '\0')
1413 myldap_err(LOG_ERR, search->session->ld, rc, "no available LDAP server found");
1414 return rc;
1415 }
1416 /* sleep between tries */
1417 sleeptime = nexttry - time(NULL);
1418 if (sleeptime > 0)
1419 {
1420 log_log(LOG_WARNING, "no available LDAP server found, sleeping %d seconds",
1421 sleeptime);
1422 (void)sleep(sleeptime);
1423 }
1424 }
1425 }
1426
1427 /* force quick retries of all failing LDAP servers */
myldap_immediate_reconnect(void)1428 void myldap_immediate_reconnect(void)
1429 {
1430 int i;
1431 time_t t;
1432 t = time(NULL) - nslcd_cfg->reconnect_retrytime;
1433 pthread_mutex_lock(&uris_mutex);
1434 for (i = 0; i < (NSS_LDAP_CONFIG_MAX_URIS + 1); i++)
1435 {
1436 /* only adjust failing connections that are in a hard fail state */
1437 if ((nslcd_cfg->uris[i].lastfail > t) &&
1438 (nslcd_cfg->uris[i].lastfail > (nslcd_cfg->uris[i].firstfail + nslcd_cfg->reconnect_retrytime)))
1439 {
1440 /* move lastfail back to ensure quick retry */
1441 log_log(LOG_DEBUG, "moving lastfail of %s %d second(s) back to force retry",
1442 nslcd_cfg->uris[i].uri, (int)(nslcd_cfg->uris[i].lastfail - t));
1443 nslcd_cfg->uris[i].lastfail = t;
1444 }
1445 }
1446 pthread_mutex_unlock(&uris_mutex);
1447 }
1448
myldap_search(MYLDAP_SESSION * session,const char * base,int scope,const char * filter,const char ** attrs,int * rcp)1449 MYLDAP_SEARCH *myldap_search(MYLDAP_SESSION *session,
1450 const char *base, int scope, const char *filter,
1451 const char **attrs, int *rcp)
1452 {
1453 MYLDAP_SEARCH *search;
1454 int i;
1455 int rc;
1456 /* check parameters */
1457 if ((session == NULL) || (base == NULL) || (filter == NULL) || (attrs == NULL))
1458 {
1459 log_log(LOG_ERR, "myldap_search(): invalid parameter passed");
1460 errno = EINVAL;
1461 if (rcp != NULL)
1462 *rcp = LDAP_OPERATIONS_ERROR;
1463 return NULL;
1464 }
1465 /* log the call */
1466 log_log(LOG_DEBUG, "myldap_search(base=\"%s\", filter=\"%s\")",
1467 base, filter);
1468 /* check if the idle time for the connection has expired */
1469 myldap_session_check(session);
1470 /* allocate a new search entry */
1471 search = myldap_search_new(session, base, scope, filter, attrs);
1472 /* find a place in the session where we can register our search */
1473 for (i = 0; (i < MAX_SEARCHES_IN_SESSION) && (session->searches[i] != NULL); i++)
1474 /* nothing */ ;
1475 if (i >= MAX_SEARCHES_IN_SESSION)
1476 {
1477 log_log(LOG_ERR, "myldap_search(): too many searches registered with session (max %d)",
1478 MAX_SEARCHES_IN_SESSION);
1479 myldap_search_close(search);
1480 if (rcp != NULL)
1481 *rcp = LDAP_OPERATIONS_ERROR;
1482 return NULL;
1483 }
1484 /* register search with the session so we can free it later on */
1485 session->searches[i] = search;
1486 /* do the search with retries to all configured servers */
1487 rc = do_retry_search(search);
1488 if (rc != LDAP_SUCCESS)
1489 {
1490 myldap_search_close(search);
1491 if (rcp != NULL)
1492 *rcp = rc;
1493 return NULL;
1494 }
1495 if (rcp != NULL)
1496 *rcp = LDAP_SUCCESS;
1497 return search;
1498 }
1499
myldap_search_close(MYLDAP_SEARCH * search)1500 void myldap_search_close(MYLDAP_SEARCH *search)
1501 {
1502 int i;
1503 if (search == NULL)
1504 return;
1505 /* free any messages */
1506 if (search->msg != NULL)
1507 {
1508 ldap_msgfree(search->msg);
1509 search->msg = NULL;
1510 }
1511 /* abandon the search if there were more results to fetch */
1512 if ((search->session->ld != NULL) && (search->msgid != -1))
1513 {
1514 ldap_abandon(search->session->ld, search->msgid);
1515 search->msgid = -1;
1516 }
1517 /* find the reference to this search in the session */
1518 for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
1519 {
1520 if (search->session->searches[i] == search)
1521 search->session->searches[i] = NULL;
1522 }
1523 /* free any search entries */
1524 if (search->entry != NULL)
1525 myldap_entry_free(search->entry);
1526 /* clean up cookie */
1527 if (search->cookie != NULL)
1528 ber_bvfree(search->cookie);
1529 /* free read messages */
1530 if (search->msg != NULL)
1531 ldap_msgfree(search->msg);
1532 /* free the storage we allocated */
1533 free(search);
1534 }
1535
myldap_get_entry(MYLDAP_SEARCH * search,int * rcp)1536 MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search, int *rcp)
1537 {
1538 int rc;
1539 int parserc;
1540 struct timeval tv, *tvp;
1541 LDAPControl **resultcontrols;
1542 ber_int_t count;
1543 /* check parameters */
1544 if ((search == NULL) || (search->session == NULL) || (search->session->ld == NULL))
1545 {
1546 log_log(LOG_ERR, "myldap_get_entry(): invalid search passed");
1547 errno = EINVAL;
1548 if (rcp != NULL)
1549 *rcp = LDAP_OPERATIONS_ERROR;
1550 return NULL;
1551 }
1552 /* check if the connection wasn't closed in another search */
1553 if (!search->valid)
1554 {
1555 log_log(LOG_WARNING, "myldap_get_entry(): connection was closed");
1556 /* retry the search */
1557 if (search->may_retry_search)
1558 {
1559 log_log(LOG_DEBUG, "myldap_get_entry(): retry search");
1560 search->may_retry_search = 0;
1561 if (do_retry_search(search) == LDAP_SUCCESS)
1562 return myldap_get_entry(search, rcp);
1563 }
1564 myldap_search_close(search);
1565 if (rcp != NULL)
1566 *rcp = LDAP_SERVER_DOWN;
1567 return NULL;
1568 }
1569 /* set up a timelimit value for operations */
1570 if (nslcd_cfg->timelimit == LDAP_NO_LIMIT)
1571 tvp = NULL;
1572 else
1573 {
1574 tv.tv_sec = nslcd_cfg->timelimit;
1575 tv.tv_usec = 0;
1576 tvp = &tv;
1577 }
1578 /* if we have an existing result entry, free it */
1579 if (search->entry != NULL)
1580 {
1581 myldap_entry_free(search->entry);
1582 search->entry = NULL;
1583 }
1584 /* try to parse results until we have a final error or ok */
1585 while (1)
1586 {
1587 /* free the previous message if there was any */
1588 if (search->msg != NULL)
1589 {
1590 ldap_msgfree(search->msg);
1591 search->msg = NULL;
1592 }
1593 /* get the next result */
1594 rc = ldap_result(search->session->ld, search->msgid, LDAP_MSG_ONE, tvp,
1595 &(search->msg));
1596 /* handle result */
1597 switch (rc)
1598 {
1599 case LDAP_RES_SEARCH_ENTRY:
1600 /* we have a normal search entry, update timestamp and return result */
1601 time(&(search->session->lastactivity));
1602 search->entry = myldap_entry_new(search);
1603 if (rcp != NULL)
1604 *rcp = LDAP_SUCCESS;
1605 /* log the first couple of dns in the result (but not all, to
1606 prevent swamping the log) */
1607 if (search->count < MAX_DEBUG_LOG_DNS)
1608 log_log(LOG_DEBUG, "ldap_result(): %s", myldap_get_dn(search->entry));
1609 search->count++;
1610 search->may_retry_search = 0;
1611 return search->entry;
1612 case LDAP_RES_SEARCH_RESULT:
1613 /* we have a search result, parse it */
1614 resultcontrols = NULL;
1615 if (search->cookie != NULL)
1616 {
1617 ber_bvfree(search->cookie);
1618 search->cookie = NULL;
1619 }
1620 /* NB: this frees search->msg */
1621 parserc = ldap_parse_result(search->session->ld, search->msg, &rc,
1622 NULL, NULL, NULL, &resultcontrols, 1);
1623 search->msg = NULL;
1624 /* check for errors during parsing */
1625 if ((parserc != LDAP_SUCCESS) && (parserc != LDAP_MORE_RESULTS_TO_RETURN))
1626 {
1627 if (resultcontrols != NULL)
1628 ldap_controls_free(resultcontrols);
1629 myldap_err(LOG_ERR, search->session->ld, parserc, "ldap_parse_result() failed");
1630 myldap_search_close(search);
1631 if (rcp != NULL)
1632 *rcp = parserc;
1633 return NULL;
1634 }
1635 /* check for errors in message */
1636 if ((rc != LDAP_SUCCESS) && (rc != LDAP_MORE_RESULTS_TO_RETURN))
1637 {
1638 if (resultcontrols != NULL)
1639 ldap_controls_free(resultcontrols);
1640 myldap_err(LOG_ERR, search->session->ld, rc, "ldap_result() failed");
1641 /* close connection on connection problems */
1642 if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
1643 do_close(search->session);
1644 myldap_search_close(search);
1645 if (rcp != NULL)
1646 *rcp = rc;
1647 return NULL;
1648 }
1649 /* handle result controls */
1650 if (resultcontrols != NULL)
1651 {
1652 /* see if there are any more pages to come */
1653 rc = ldap_parse_page_control(search->session->ld, resultcontrols,
1654 &count, &(search->cookie));
1655 if (rc != LDAP_SUCCESS)
1656 {
1657 if (rc != LDAP_CONTROL_NOT_FOUND)
1658 myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_parse_page_control() failed");
1659 /* clear error flag */
1660 rc = LDAP_SUCCESS;
1661 if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER,
1662 &rc) != LDAP_SUCCESS)
1663 log_log(LOG_WARNING, "failed to clear the error flag");
1664 }
1665 /* TODO: handle the above return code?? */
1666 ldap_controls_free(resultcontrols);
1667 }
1668 search->msgid = -1;
1669 /* check if there are more pages to come */
1670 if ((search->cookie == NULL) || (search->cookie->bv_len == 0))
1671 {
1672 if (search->count > MAX_DEBUG_LOG_DNS)
1673 log_log(LOG_DEBUG, "ldap_result(): ... %d more results",
1674 search->count - MAX_DEBUG_LOG_DNS);
1675 log_log(LOG_DEBUG, "ldap_result(): end of results (%d total)",
1676 search->count);
1677 /* we are at the end of the search, no more results */
1678 myldap_search_close(search);
1679 if (rcp != NULL)
1680 *rcp = LDAP_SUCCESS;
1681 return NULL;
1682 }
1683 /* try the next page */
1684 rc = do_try_search(search);
1685 if (rc != LDAP_SUCCESS)
1686 {
1687 /* close connection on connection problems */
1688 if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
1689 do_close(search->session);
1690 myldap_search_close(search);
1691 if (rcp != NULL)
1692 *rcp = rc;
1693 return NULL;
1694 }
1695 /* we continue with another pass */
1696 break;
1697 case LDAP_RES_SEARCH_REFERENCE:
1698 break; /* just ignore search references */
1699 default:
1700 /* we have some error condition, find out which */
1701 switch (rc)
1702 {
1703 case -1:
1704 /* try to get error code */
1705 if (ldap_get_option(search->session->ld, LDAP_OPT_ERROR_NUMBER,
1706 &rc) != LDAP_SUCCESS)
1707 rc = LDAP_UNAVAILABLE;
1708 myldap_err(LOG_ERR, search->session->ld, rc, "ldap_result() failed");
1709 break;
1710 case 0:
1711 /* the timeout expired */
1712 log_log(LOG_ERR, "ldap_result() timed out");
1713 rc = LDAP_TIMELIMIT_EXCEEDED;
1714 break;
1715 default:
1716 /* unknown code */
1717 log_log(LOG_WARNING, "ldap_result() returned unexpected result type");
1718 rc = LDAP_PROTOCOL_ERROR;
1719 }
1720 /* close connection on some connection problems */
1721 if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN) ||
1722 (rc == LDAP_SUCCESS) || (rc == LDAP_TIMELIMIT_EXCEEDED) ||
1723 (rc == LDAP_OPERATIONS_ERROR) || (rc == LDAP_PROTOCOL_ERROR) ||
1724 (rc == LDAP_BUSY) || (rc == LDAP_UNWILLING_TO_PERFORM) ||
1725 (rc == LDAP_TIMEOUT) || (rc == LDAP_CONNECT_ERROR) ||
1726 (rc == LDAP_NOT_SUPPORTED))
1727 {
1728 do_close(search->session);
1729 /* retry once if no data has been received yet */
1730 if (search->may_retry_search)
1731 {
1732 log_log(LOG_DEBUG, "myldap_get_entry(): retry search");
1733 search->may_retry_search = 0;
1734 if (do_retry_search(search) == LDAP_SUCCESS)
1735 return myldap_get_entry(search, rcp);
1736 }
1737 }
1738 /* close search */
1739 myldap_search_close(search);
1740 if (rcp != NULL)
1741 *rcp = rc;
1742 return NULL;
1743 }
1744 }
1745 }
1746
1747 /* Get the DN from the entry. This function only returns NULL (and sets
1748 errno) if an incorrect entry is passed. If the DN value cannot be
1749 retrieved "unknown" is returned instead. */
myldap_get_dn(MYLDAP_ENTRY * entry)1750 const char *myldap_get_dn(MYLDAP_ENTRY *entry)
1751 {
1752 int rc;
1753 /* check parameters */
1754 if (!is_valid_entry(entry))
1755 {
1756 log_log(LOG_ERR, "myldap_get_dn(): invalid result entry passed");
1757 errno = EINVAL;
1758 return "unknown";
1759 }
1760 /* if we don't have it yet, retrieve it */
1761 if ((entry->dn == NULL) && (entry->search->valid))
1762 {
1763 entry->dn = ldap_get_dn(entry->search->session->ld, entry->search->msg);
1764 if (entry->dn == NULL)
1765 {
1766 if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER,
1767 &rc) != LDAP_SUCCESS)
1768 rc = LDAP_UNAVAILABLE;
1769 myldap_err(LOG_WARNING, entry->search->session->ld, rc, "ldap_get_dn() returned NULL");
1770 /* close connection on connection problems */
1771 if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
1772 do_close(entry->search->session);
1773 }
1774 }
1775 /* if we still don't have it, return unknown */
1776 if (entry->dn == NULL)
1777 return "unknown";
1778 /* return it */
1779 return entry->dn;
1780 }
1781
myldap_cpy_dn(MYLDAP_ENTRY * entry,char * buf,size_t buflen)1782 char *myldap_cpy_dn(MYLDAP_ENTRY *entry, char *buf, size_t buflen)
1783 {
1784 const char *dn;
1785 /* get the dn */
1786 dn = myldap_get_dn(entry);
1787 /* copy into buffer */
1788 if (strlen(dn) < buflen)
1789 strcpy(buf, dn);
1790 else
1791 buf = NULL;
1792 return buf;
1793 }
1794
1795 /* Perform ranged retrieval of attributes.
1796 http://msdn.microsoft.com/en-us/library/aa367017(vs.85).aspx
1797 http://www.tkk.fi/cc/docs/kerberos/draft-kashi-incremental-00.txt */
myldap_get_ranged_values(MYLDAP_ENTRY * entry,const char * attr)1798 static char **myldap_get_ranged_values(MYLDAP_ENTRY *entry, const char *attr)
1799 {
1800 char **values;
1801 char *attn;
1802 const char *attrs[2];
1803 BerElement *ber;
1804 int i;
1805 int startat = 0, nxt = 0;
1806 char attbuf[80];
1807 const char *dn = myldap_get_dn(entry);
1808 MYLDAP_SESSION *session = entry->search->session;
1809 MYLDAP_SEARCH *search = NULL;
1810 SET *set = NULL;
1811 /* build the attribute name to find */
1812 if (mysnprintf(attbuf, sizeof(attbuf), "%s;range=0-*", attr))
1813 {
1814 log_log(LOG_ERR, "myldap_get_ranged_values(): attbuf buffer too small (%lu required)",
1815 (unsigned long) strlen(attr) + 10);
1816 return NULL;
1817 }
1818 /* keep doing lookups untul we can't get any more results */
1819 while (1)
1820 {
1821 /* go over all attributes to find the ranged attribute */
1822 ber = NULL;
1823 attn = ldap_first_attribute(entry->search->session->ld, entry->search->msg, &ber);
1824 values = NULL;
1825 while (attn != NULL)
1826 {
1827 if (strncasecmp(attn, attbuf, strlen(attbuf) - 1) == 0)
1828 {
1829 log_log(LOG_DEBUG, "found ranged results %s", attn);
1830 nxt = atoi(attn + strlen(attbuf) - 1) + 1;
1831 values = ldap_get_values(entry->search->session->ld, entry->search->msg, attn);
1832 ldap_memfree(attn);
1833 break;
1834 }
1835 /* free old attribute name and get next one */
1836 ldap_memfree(attn);
1837 attn = ldap_next_attribute(entry->search->session->ld, entry->search->msg, ber);
1838 }
1839 ber_free(ber, 0);
1840 /* see if we found any values */
1841 if ((values == NULL) || (*values == NULL))
1842 break;
1843 /* allocate memory */
1844 if (set == NULL)
1845 {
1846 set = set_new();
1847 if (set == NULL)
1848 {
1849 ldap_value_free(values);
1850 log_log(LOG_CRIT, "myldap_get_ranged_values(): set_new() failed to allocate memory");
1851 return NULL;
1852 }
1853 }
1854 /* add to the set */
1855 for (i = 0; values[i] != NULL; i++)
1856 set_add(set, values[i]);
1857 /* free results */
1858 ldap_value_free(values);
1859 /* check if we should start a new search */
1860 if (nxt <= startat)
1861 break;
1862 startat = nxt;
1863 /* build attributes for a new search */
1864 if (mysnprintf(attbuf, sizeof(attbuf), "%s;range=%d-*", attr, startat))
1865 {
1866 log_log(LOG_ERR, "myldap_get_ranged_values(): attbuf buffer too small");
1867 break;
1868 }
1869 attrs[0] = attbuf;
1870 attrs[1] = NULL;
1871 /* close the previous search, if any */
1872 if (search != NULL)
1873 myldap_search_close(search);
1874 /* start the new search */
1875 search = myldap_search(session, dn, LDAP_SCOPE_BASE, "(objectClass=*)", attrs, NULL);
1876 if (search == NULL)
1877 break;
1878 entry = myldap_get_entry(search, NULL);
1879 if (entry == NULL)
1880 break;
1881 }
1882 /* close any started searches */
1883 if (search != NULL)
1884 myldap_search_close(search);
1885 /* return the contents of the set as a list */
1886 if (set == NULL)
1887 return NULL;
1888 values = (char **)set_tolist(set);
1889 set_free(set);
1890 if (values == NULL)
1891 log_log(LOG_CRIT, "myldap_get_ranged_values(): malloc() failed to allocate memory");
1892 return values;
1893 }
1894
1895 /* Simple wrapper around ldap_get_values(). */
myldap_get_values(MYLDAP_ENTRY * entry,const char * attr)1896 const char **myldap_get_values(MYLDAP_ENTRY *entry, const char *attr)
1897 {
1898 char **values;
1899 int rc;
1900 int i;
1901 /* check parameters */
1902 if (!is_valid_entry(entry))
1903 {
1904 log_log(LOG_ERR, "myldap_get_values(): invalid result entry passed");
1905 errno = EINVAL;
1906 return NULL;
1907 }
1908 else if (attr == NULL)
1909 {
1910 log_log(LOG_ERR, "myldap_get_values(): invalid attribute name passed");
1911 errno = EINVAL;
1912 return NULL;
1913 }
1914 if (!entry->search->valid)
1915 return NULL; /* search has been stopped */
1916 /* get from LDAP */
1917 values = ldap_get_values(entry->search->session->ld, entry->search->msg, attr);
1918 if (values == NULL)
1919 {
1920 if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
1921 rc = LDAP_UNAVAILABLE;
1922 /* ignore decoding errors as they are just non-existing attribute values */
1923 if (rc == LDAP_DECODING_ERROR)
1924 {
1925 rc = LDAP_SUCCESS;
1926 if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
1927 log_log(LOG_WARNING, "failed to clear the error flag");
1928 }
1929 else if (rc == LDAP_SUCCESS)
1930 {
1931 /* we have a success code but no values, let's try to get ranged
1932 values */
1933 values = myldap_get_ranged_values(entry, attr);
1934 if (values == NULL)
1935 return NULL;
1936 /* store values entry so we can free it later on */
1937 for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
1938 if (entry->buffers[i] == NULL)
1939 {
1940 entry->buffers[i] = values;
1941 return (const char **)entry->buffers[i];
1942 }
1943 /* we found no room to store the values */
1944 log_log(LOG_ERR, "ldap_get_values() couldn't store results, increase MAX_BUFFERS_PER_ENTRY");
1945 free(values);
1946 return NULL;
1947 }
1948 else
1949 myldap_err(LOG_WARNING, entry->search->session->ld, rc,
1950 "ldap_get_values() of attribute \"%s\" on entry \"%s\" returned NULL",
1951 attr, myldap_get_dn(entry));
1952 return NULL;
1953 }
1954 /* store values entry so we can free it later on */
1955 for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
1956 if (entry->attributevalues[i] == NULL)
1957 {
1958 entry->attributevalues[i] = values;
1959 return (const char **)values;
1960 }
1961 /* we found no room to store the entry */
1962 log_log(LOG_ERR, "ldap_get_values() couldn't store results, increase MAX_ATTRIBUTES_PER_ENTRY");
1963 ldap_value_free(values);
1964 return NULL;
1965 }
1966
1967 /* Convert the bervalues to a simple list of strings that can be freed
1968 with one call to free(). */
bervalues_to_values(struct berval ** bvalues)1969 static const char **bervalues_to_values(struct berval **bvalues)
1970 {
1971 int num_values;
1972 int i;
1973 size_t sz;
1974 char *buf;
1975 char **values;
1976 /* figure out how much memory to allocate */
1977 num_values = ldap_count_values_len(bvalues);
1978 sz = (num_values + 1) * sizeof(char *);
1979 for (i = 0; i < num_values; i++)
1980 sz += bvalues[i]->bv_len + 1;
1981 /* allocate the needed memory */
1982 values = (char **)malloc(sz);
1983 if (values == NULL)
1984 {
1985 log_log(LOG_CRIT, "bervalues_to_values(): malloc() failed to allocate memory");
1986 return NULL;
1987 }
1988 buf = (char *)values;
1989 buf += (num_values + 1) * sizeof(char *);
1990 /* copy from bvalues */
1991 for (i = 0; i < num_values; i++)
1992 {
1993 values[i] = buf;
1994 memcpy(values[i], bvalues[i]->bv_val, bvalues[i]->bv_len);
1995 values[i][bvalues[i]->bv_len] = '\0';
1996 buf += bvalues[i]->bv_len + 1;
1997 }
1998 values[i] = NULL;
1999 return (const char **)values;
2000 }
2001
2002 /* Simple wrapper around ldap_get_values(). */
myldap_get_values_len(MYLDAP_ENTRY * entry,const char * attr)2003 const char **myldap_get_values_len(MYLDAP_ENTRY *entry, const char *attr)
2004 {
2005 const char **values;
2006 struct berval **bvalues;
2007 int rc;
2008 int i;
2009 /* check parameters */
2010 if (!is_valid_entry(entry))
2011 {
2012 log_log(LOG_ERR, "myldap_get_values_len(): invalid result entry passed");
2013 errno = EINVAL;
2014 return NULL;
2015 }
2016 else if (attr == NULL)
2017 {
2018 log_log(LOG_ERR, "myldap_get_values_len(): invalid attribute name passed");
2019 errno = EINVAL;
2020 return NULL;
2021 }
2022 if (!entry->search->valid)
2023 return NULL; /* search has been stopped */
2024 /* get from LDAP */
2025 bvalues = ldap_get_values_len(entry->search->session->ld, entry->search->msg, attr);
2026 if (bvalues == NULL)
2027 {
2028 if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
2029 rc = LDAP_UNAVAILABLE;
2030 /* ignore decoding errors as they are just non-existing attribute values */
2031 if (rc == LDAP_DECODING_ERROR)
2032 {
2033 rc = LDAP_SUCCESS;
2034 if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
2035 log_log(LOG_WARNING, "failed to clear the error flag");
2036 return NULL;
2037 }
2038 else if (rc == LDAP_SUCCESS)
2039 {
2040 /* we have a success code but no values, let's try to get ranged
2041 values */
2042 values = (const char **)myldap_get_ranged_values(entry, attr);
2043 }
2044 else
2045 {
2046 myldap_err(LOG_WARNING, entry->search->session->ld, rc,
2047 "myldap_get_values_len() of attribute \"%s\" on entry \"%s\" returned NULL",
2048 attr, myldap_get_dn(entry));
2049 return NULL;
2050 }
2051 }
2052 else
2053 {
2054 values = bervalues_to_values(bvalues);
2055 ldap_value_free_len(bvalues);
2056 }
2057 /* check if we got allocated memory */
2058 if (values == NULL)
2059 return NULL;
2060 /* store values entry so we can free it later on */
2061 for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
2062 if (entry->buffers[i] == NULL)
2063 {
2064 entry->buffers[i] = (char **)values;
2065 return values;
2066 }
2067 /* we found no room to store the values */
2068 log_log(LOG_ERR, "myldap_get_values_len() couldn't store results, increase MAX_BUFFERS_PER_ENTRY");
2069 free(values);
2070 return NULL;
2071 }
2072
2073 /* Go over the entries in exploded_rdn and see if any start with
2074 the requested attribute. Return a reference to the value part of
2075 the DN (does not modify exploded_rdn). */
find_rdn_value(char ** exploded_rdn,const char * attr)2076 static const char *find_rdn_value(char **exploded_rdn, const char *attr)
2077 {
2078 int i, j;
2079 int l;
2080 if (exploded_rdn == NULL)
2081 return NULL;
2082 /* go over all RDNs */
2083 l = strlen(attr);
2084 for (i = 0; exploded_rdn[i] != NULL; i++)
2085 {
2086 /* check that RDN starts with attr */
2087 if (strncasecmp(exploded_rdn[i], attr, l) != 0)
2088 continue;
2089 j = l;
2090 /* skip spaces */
2091 while (isspace(exploded_rdn[i][j]))
2092 j++;
2093 /* ensure that we found an equals sign now */
2094 if (exploded_rdn[i][j] != '=')
2095 continue;
2096 j++;
2097 /* skip more spaces */
2098 while (isspace(exploded_rdn[i][j]))
2099 j++;
2100 /* ensure that we're not at the end of the string */
2101 if (exploded_rdn[i][j] == '\0')
2102 continue;
2103 /* we found our value */
2104 return exploded_rdn[i] + j;
2105 }
2106 /* fail */
2107 return NULL;
2108 }
2109
2110 /* explode the first part of DN into parts
2111 (e.g. "cn=Test", "uid=test")
2112 The returned value should be freed with ldap_value_free(). */
get_exploded_rdn(const char * dn)2113 static char **get_exploded_rdn(const char *dn)
2114 {
2115 char **exploded_dn;
2116 char **exploded_rdn;
2117 /* check if we have a DN */
2118 if ((dn == NULL) || (strcasecmp(dn, "unknown") == 0))
2119 return NULL;
2120 /* explode dn into { "uid=test", "ou=people", ..., NULL } */
2121 exploded_dn = ldap_explode_dn(dn, 0);
2122 if ((exploded_dn == NULL) || (exploded_dn[0] == NULL))
2123 {
2124 log_log(LOG_WARNING, "ldap_explode_dn(%s) returned NULL: %s",
2125 dn, strerror(errno));
2126 return NULL;
2127 }
2128 /* explode rdn (first part of exploded_dn),
2129 e.g. "cn=Test User+uid=testusr" into
2130 { "cn=Test User", "uid=testusr", NULL } */
2131 errno = 0;
2132 exploded_rdn = ldap_explode_rdn(exploded_dn[0], 0);
2133 if ((exploded_rdn == NULL) || (exploded_rdn[0] == NULL))
2134 {
2135 log_log(LOG_WARNING, "ldap_explode_rdn(%s) returned NULL: %s",
2136 exploded_dn[0], strerror(errno));
2137 if (exploded_rdn != NULL)
2138 ldap_value_free(exploded_rdn);
2139 ldap_value_free(exploded_dn);
2140 return NULL;
2141 }
2142 ldap_value_free(exploded_dn);
2143 return exploded_rdn;
2144 }
2145
myldap_get_rdn_value(MYLDAP_ENTRY * entry,const char * attr)2146 const char *myldap_get_rdn_value(MYLDAP_ENTRY *entry, const char *attr)
2147 {
2148 /* check parameters */
2149 if (!is_valid_entry(entry))
2150 {
2151 log_log(LOG_ERR, "myldap_get_rdn_value(): invalid result entry passed");
2152 errno = EINVAL;
2153 return NULL;
2154 }
2155 else if (attr == NULL)
2156 {
2157 log_log(LOG_ERR, "myldap_get_rdn_value(): invalid attribute name passed");
2158 errno = EINVAL;
2159 return NULL;
2160 }
2161 /* check if entry contains exploded_rdn */
2162 if (entry->exploded_rdn == NULL)
2163 {
2164 entry->exploded_rdn = get_exploded_rdn(myldap_get_dn(entry));
2165 if (entry->exploded_rdn == NULL)
2166 return NULL;
2167 }
2168 /* find rnd value */
2169 return find_rdn_value(entry->exploded_rdn, attr);
2170 }
2171
myldap_cpy_rdn_value(const char * dn,const char * attr,char * buf,size_t buflen)2172 const char *myldap_cpy_rdn_value(const char *dn, const char *attr,
2173 char *buf, size_t buflen)
2174 {
2175 char **exploded_rdn;
2176 const char *value;
2177 /* explode dn into { "cn=Test", "uid=test", NULL } */
2178 exploded_rdn = get_exploded_rdn(dn);
2179 if (exploded_rdn == NULL)
2180 return NULL;
2181 /* see if we have a match */
2182 value = find_rdn_value(exploded_rdn, attr);
2183 /* if we have something store it in the buffer */
2184 if ((value != NULL) && (strlen(value) < buflen))
2185 strcpy(buf, value);
2186 else
2187 value = NULL;
2188 /* free allocated stuff */
2189 ldap_value_free(exploded_rdn);
2190 /* check if we have something to return */
2191 return (value != NULL) ? buf : NULL;
2192 }
2193
myldap_has_objectclass(MYLDAP_ENTRY * entry,const char * objectclass)2194 int myldap_has_objectclass(MYLDAP_ENTRY *entry, const char *objectclass)
2195 {
2196 const char **values;
2197 int i;
2198 if ((!is_valid_entry(entry)) || (objectclass == NULL))
2199 {
2200 log_log(LOG_ERR, "myldap_has_objectclass(): invalid argument passed");
2201 errno = EINVAL;
2202 return 0;
2203 }
2204 values = myldap_get_values(entry, "objectClass");
2205 if (values == NULL)
2206 return 0;
2207 for (i = 0; values[i] != NULL; i++)
2208 {
2209 if (strcasecmp(values[i], objectclass) == 0)
2210 return -1;
2211 }
2212 return 0;
2213 }
2214
2215 #ifdef HAVE_LDAP_PARSE_DEREF_CONTROL
myldap_get_deref_values(MYLDAP_ENTRY * entry,const char * derefattr,const char * getattr)2216 const char ***myldap_get_deref_values(MYLDAP_ENTRY *entry,
2217 const char *derefattr, const char *getattr)
2218 {
2219 LDAPControl **entryctrls;
2220 LDAPDerefRes *deref, *d;
2221 LDAPDerefVal *a;
2222 int i, pass;
2223 int rc;
2224 int found;
2225 int counts[2];
2226 size_t sizes[2], size;
2227 char *buffer = NULL;
2228 char ***results = NULL;
2229 rc = ldap_get_entry_controls(entry->search->session->ld, entry->search->msg,
2230 &entryctrls);
2231 if (rc != LDAP_SUCCESS)
2232 {
2233 myldap_err(LOG_WARNING, entry->search->session->ld, rc,
2234 "ldap_get_entry_controls() failed");
2235 return NULL;
2236 }
2237 if (entryctrls == NULL)
2238 return NULL;
2239 /* see if we can find a deref control */
2240 rc = ldap_parse_deref_control(entry->search->session->ld, entryctrls,
2241 &deref);
2242 if ((rc != LDAP_SUCCESS) || (deref == NULL))
2243 {
2244 if ((rc != LDAP_SUCCESS) && (rc != LDAP_CONTROL_NOT_FOUND))
2245 myldap_err(LOG_WARNING, entry->search->session->ld, rc,
2246 "ldap_parse_deref_control() failed");
2247 /* clear error flag */
2248 rc = LDAP_SUCCESS;
2249 if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER,
2250 &rc) != LDAP_SUCCESS)
2251 log_log(LOG_WARNING, "failed to clear the error flag");
2252 ldap_controls_free(entryctrls);
2253 return NULL;
2254 }
2255 /* two passes: one to calculate size, one to store data */
2256 for (pass=0; pass < 2; pass++)
2257 {
2258 /* reset counters and size */
2259 for (i = 0; i < 2; i++)
2260 {
2261 counts[i] = 0;
2262 sizes[i] = 0;
2263 }
2264 /* go over all deref'd attributes and find the one we're looking for */
2265 for (d = deref; d != NULL; d = d->next)
2266 if ((d->derefAttr != NULL) && (d->derefVal.bv_val != NULL) &&
2267 (strcasecmp(derefattr, d->derefAttr) == 0))
2268 {
2269 /* we should have one d per original attribute value */
2270 found = 0;
2271 /* go over deref'd attribute values to find the ones we're looking for */
2272 for (a = d->attrVals; a != NULL; a = a->next)
2273 if ((a->type != NULL) && (a->vals != NULL) &&
2274 (strcasecmp(getattr, a->type) == 0))
2275 for (i=0; a->vals[i].bv_val != NULL; i++)
2276 {
2277 found = 1;
2278 if (results == NULL)
2279 {
2280 log_log(LOG_DEBUG, "deref %s %s=%s -> %s=%s",
2281 myldap_get_dn(entry), d->derefAttr, d->derefVal.bv_val,
2282 a->type, a->vals[i].bv_val);
2283 counts[0]++;
2284 sizes[0] += strlen(a->vals[i].bv_val) + 1;
2285 }
2286 else
2287 {
2288 strcpy(buffer, a->vals[i].bv_val);
2289 results[0][counts[0]++] = buffer;
2290 buffer += strlen(buffer) + 1;
2291 }
2292 }
2293 if (!found)
2294 {
2295 if (results == NULL)
2296 {
2297 log_log(LOG_DEBUG, "no %s deref %s %s=%s", getattr,
2298 myldap_get_dn(entry), d->derefAttr, d->derefVal.bv_val);
2299 counts[1]++;
2300 sizes[1] += strlen(d->derefVal.bv_val) + 1;
2301 }
2302 else
2303 {
2304 strcpy(buffer, d->derefVal.bv_val);
2305 results[1][counts[1]++] = buffer;
2306 buffer += strlen(buffer) + 1;
2307 }
2308 }
2309 }
2310 /* allocate memory after first pass */
2311 if (results == NULL)
2312 {
2313 size = sizeof(char **) * 3;
2314 for (i = 0; i < 2; i++)
2315 size += sizeof(char *) * (counts[i] + 1);
2316 for (i = 0; i < 2; i++)
2317 size += sizeof(char) * sizes[i];
2318 buffer = (char *)malloc(size);
2319 if (buffer == NULL)
2320 {
2321 log_log(LOG_CRIT, "myldap_get_deref_values(): malloc() failed to allocate memory");
2322 return NULL;
2323 }
2324 /* allocate the list of lists */
2325 results = (void *)buffer;
2326 buffer += sizeof(char **) * 3;
2327 /* allocate the lists */
2328 for (i = 0; i < 2; i++)
2329 {
2330 results[i] = (char **)buffer;
2331 buffer += sizeof(char *) * (counts[i] + 1);
2332 }
2333 results[i] = NULL;
2334 }
2335 }
2336 /* NULL terminate the lists */
2337 results[0][counts[0]] = NULL;
2338 results[1][counts[1]] = NULL;
2339 /* free control data */
2340 ldap_derefresponse_free(deref);
2341 ldap_controls_free(entryctrls);
2342 /* store results so we can free it later on */
2343 for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
2344 if (entry->buffers[i] == NULL)
2345 {
2346 entry->buffers[i] = (void *)results;
2347 return (const char ***)results;
2348 }
2349 /* we found no room to store the values */
2350 log_log(LOG_ERR, "myldap_get_deref_values() couldn't store results, "
2351 "increase MAX_BUFFERS_PER_ENTRY");
2352 free(results);
2353 return NULL;
2354 }
2355 #else /* not HAVE_LDAP_PARSE_DEREF_CONTROL */
myldap_get_deref_values(MYLDAP_ENTRY UNUSED (* entry),const char UNUSED (* derefattr),const char UNUSED (* getattr))2356 const char ***myldap_get_deref_values(MYLDAP_ENTRY UNUSED(*entry),
2357 const char UNUSED(*derefattr), const char UNUSED(*getattr))
2358 {
2359 return NULL;
2360 }
2361 #endif /* not HAVE_LDAP_PARSE_DEREF_CONTROL */
2362
myldap_escape(const char * src,char * buffer,size_t buflen)2363 int myldap_escape(const char *src, char *buffer, size_t buflen)
2364 {
2365 size_t pos = 0;
2366 /* go over all characters in source string */
2367 for (; *src != '\0'; src++)
2368 {
2369 /* check if char will fit */
2370 if ((pos + 4) >= buflen)
2371 return -1;
2372 /* do escaping for some characters */
2373 switch (*src)
2374 {
2375 case '*':
2376 strcpy(buffer + pos, "\\2a");
2377 pos += 3;
2378 break;
2379 case '(':
2380 strcpy(buffer + pos, "\\28");
2381 pos += 3;
2382 break;
2383 case ')':
2384 strcpy(buffer + pos, "\\29");
2385 pos += 3;
2386 break;
2387 case '\\':
2388 strcpy(buffer + pos, "\\5c");
2389 pos += 3;
2390 break;
2391 default:
2392 /* just copy character */
2393 buffer[pos++] = *src;
2394 break;
2395 }
2396 }
2397 /* terminate destination string */
2398 buffer[pos] = '\0';
2399 return 0;
2400 }
2401
myldap_set_debuglevel(int level)2402 int myldap_set_debuglevel(int level)
2403 {
2404 int i;
2405 int rc;
2406 /* turn on debugging */
2407 if (level > 1)
2408 {
2409 #ifdef LBER_OPT_LOG_PRINT_FILE
2410 log_log(LOG_DEBUG, "ber_set_option(LBER_OPT_LOG_PRINT_FILE)");
2411 rc = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FILE, stderr);
2412 if (rc != LDAP_SUCCESS)
2413 {
2414 myldap_err(LOG_ERR, NULL, rc, "ber_set_option(LBER_OPT_LOG_PRINT_FILE) failed");
2415 return rc;
2416 }
2417 #endif /* LBER_OPT_LOG_PRINT_FILE */
2418 #ifdef LBER_OPT_DEBUG_LEVEL
2419 if (level > 2)
2420 {
2421 i = -1;
2422 log_log(LOG_DEBUG, "ber_set_option(LBER_OPT_DEBUG_LEVEL,-1)");
2423 rc = ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &i);
2424 if (rc != LDAP_SUCCESS)
2425 {
2426 myldap_err(LOG_ERR, NULL, rc, "ber_set_option(LBER_OPT_DEBUG_LEVEL) failed");
2427 return rc;
2428 }
2429 }
2430 #endif /* LBER_OPT_DEBUG_LEVEL */
2431 #ifdef LDAP_OPT_DEBUG_LEVEL
2432 i = -1;
2433 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_DEBUG_LEVEL,-1)");
2434 rc = ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &i);
2435 if (rc != LDAP_SUCCESS)
2436 {
2437 myldap_err(LOG_ERR, NULL, rc, "ldap_set_option(LDAP_OPT_DEBUG_LEVEL) failed");
2438 return rc;
2439 }
2440 #endif /* LDAP_OPT_DEBUG_LEVEL */
2441 }
2442 return LDAP_SUCCESS;
2443 }
2444
myldap_passwd(MYLDAP_SESSION * session,const char * userdn,const char * oldpassword,const char * newpasswd)2445 int myldap_passwd(MYLDAP_SESSION *session,
2446 const char *userdn, const char *oldpassword,
2447 const char *newpasswd)
2448 {
2449 int rc;
2450 struct berval ber_userdn, ber_oldpassword, ber_newpassword, ber_retpassword;
2451 /* check parameters */
2452 if ((session == NULL) || (userdn == NULL) || (newpasswd == NULL))
2453 {
2454 log_log(LOG_ERR, "myldap_passwd(): invalid parameter passed");
2455 errno = EINVAL;
2456 return LDAP_OTHER;
2457 }
2458 /* log the call */
2459 log_log(LOG_DEBUG, "myldap_passwd(userdn=\"%s\",oldpasswd=%s,newpasswd=\"***\")",
2460 userdn, oldpassword ? "\"***\"" : "NULL");
2461 /* translate to ber stuff */
2462 ber_userdn.bv_val = (char *)userdn;
2463 ber_userdn.bv_len = strlen(userdn);
2464 ber_newpassword.bv_val = (char *)newpasswd;
2465 ber_newpassword.bv_len = strlen(newpasswd);
2466 ber_retpassword.bv_val = NULL;
2467 ber_retpassword.bv_len = 0;
2468 /* perform request */
2469 log_log(LOG_DEBUG, "myldap_passwd(): try ldap_passwd_s() without old password");
2470 rc = ldap_passwd_s(session->ld, &ber_userdn, NULL, &ber_newpassword,
2471 &ber_retpassword, NULL, NULL);
2472 if (rc != LDAP_SUCCESS)
2473 myldap_err(LOG_ERR, session->ld, rc, "ldap_passwd_s() without old password failed");
2474 /* free returned data if needed */
2475 if (ber_retpassword.bv_val != NULL)
2476 ldap_memfree(ber_retpassword.bv_val);
2477 if ((rc != LDAP_SUCCESS) && (oldpassword != NULL))
2478 {
2479 /* retry with old password */
2480 log_log(LOG_DEBUG, "myldap_passwd(): try ldap_passwd_s() with old password");
2481 ber_oldpassword.bv_val = (char *)oldpassword;
2482 ber_oldpassword.bv_len = strlen(oldpassword);
2483 /* perform request */
2484 rc = ldap_passwd_s(session->ld, &ber_userdn, &ber_oldpassword,
2485 &ber_newpassword, &ber_retpassword, NULL, NULL);
2486 if (rc != LDAP_SUCCESS)
2487 myldap_err(LOG_ERR, session->ld, rc, "ldap_passwd_s() with old password failed");
2488 /* free returned data if needed */
2489 if (ber_retpassword.bv_val != NULL)
2490 ldap_memfree(ber_retpassword.bv_val);
2491 }
2492 return rc;
2493 }
2494
myldap_modify(MYLDAP_SESSION * session,const char * dn,LDAPMod * mods[])2495 int myldap_modify(MYLDAP_SESSION *session, const char *dn, LDAPMod * mods[])
2496 {
2497 if ((session == NULL) || (dn == NULL))
2498 {
2499 log_log(LOG_ERR, "myldap_passwd(): invalid parameter passed");
2500 errno = EINVAL;
2501 return LDAP_OTHER;
2502 }
2503 return ldap_modify_ext_s(session->ld, dn, mods, NULL, NULL);
2504 }
2505
myldap_error_message(MYLDAP_SESSION * session,int rc,char * buffer,size_t buflen)2506 int myldap_error_message(MYLDAP_SESSION *session, int rc,
2507 char *buffer, size_t buflen)
2508 {
2509 char *msg_diag = NULL;
2510 if ((session == NULL) || (buffer == NULL) || (buflen <= 0))
2511 {
2512 log_log(LOG_ERR, "myldap_error_message(): invalid parameter passed");
2513 errno = EINVAL;
2514 return LDAP_OTHER;
2515 }
2516 /* clear buffer */
2517 buffer[0] = '\0';
2518 #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
2519 if (session->ld != NULL)
2520 ldap_get_option(session->ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg_diag);
2521 #endif /* LDAP_OPT_DIAGNOSTIC_MESSAGE */
2522 /* return msg_diag or generic error message */
2523 mysnprintf(buffer, buflen - 1, "%s",
2524 ((msg_diag != NULL) && (msg_diag[0]!='\0')) ?
2525 msg_diag : ldap_err2string(rc));
2526 /* free diagnostic message */
2527 if (msg_diag != NULL)
2528 ldap_memfree(msg_diag);
2529 return LDAP_SUCCESS;
2530 }
2531