"Fossies" - the Fresh Open Source Software Archive 
Member "nss-pam-ldapd-0.9.12/nslcd/myldap.c" (15 Nov 2021, 85840 Bytes) of package /linux/privat/nss-pam-ldapd-0.9.12.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
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-2020 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
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
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
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
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
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
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() */
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)
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
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
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 /* parse the result from the bind operation (frees result, gets controls) */
636 responsectrls = NULL;
637 parserc = ldap_parse_result(ld, result, &rc, NULL, NULL, NULL, &responsectrls, 1);
638 if (parserc != LDAP_SUCCESS)
639 {
640 myldap_err(LOG_ERR, ld, parserc, "ldap_parse_result() failed");
641 if (responsectrls != NULL)
642 ldap_controls_free(responsectrls);
643 return parserc;
644 }
645 /* handle any returned controls */
646 if (responsectrls != NULL)
647 {
648 if (nslcd_cfg->pam_authc_ppolicy)
649 handle_ppolicy_controls(session, ld, responsectrls);
650 ldap_controls_free(responsectrls);
651 }
652 /* return the result of the BIND operation */
653 if (rc != LDAP_SUCCESS)
654 {
655 myldap_err(LOG_DEBUG, ld, rc, "ldap_parse_result() result");
656 return rc;
657 }
658 return LDAP_SUCCESS;
659 }
660 #endif /* no SASL, so no ppolicy */
661
662 /* This function performs the authentication phase of opening a connection.
663 The binddn and bindpw parameters may be used to override the authentication
664 mechanism defined in the configuration. This returns an LDAP result
665 code. */
666 static int do_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri)
667 {
668 int rc;
669 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
670 #ifndef HAVE_SASL_INTERACT_T
671 struct berval cred;
672 #endif /* not HAVE_SASL_INTERACT_T */
673 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
674 #ifdef LDAP_OPT_X_TLS
675 /* check if StartTLS is requested */
676 if (nslcd_cfg->ssl == SSL_START_TLS)
677 {
678 log_log(LOG_DEBUG, "ldap_start_tls_s()");
679 errno = 0;
680 rc = ldap_start_tls_s(ld, NULL, NULL);
681 if (rc != LDAP_SUCCESS)
682 {
683 myldap_err(LOG_WARNING, ld, rc, "ldap_start_tls_s() failed (uri=%s)",
684 uri);
685 return rc;
686 }
687 }
688 #endif /* LDAP_OPT_X_TLS */
689 /* check if the binddn and bindpw are overwritten in the session */
690 if (session->binddn[0] != '\0')
691 {
692 #if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE)
693 return do_ppolicy_bind(session, ld, uri);
694 #else /* no SASL, so no ppolicy */
695 /* do a simple bind */
696 log_log(LOG_DEBUG, "ldap_simple_bind_s(\"%s\",%s) (uri=\"%s\")",
697 session->binddn,
698 (session->bindpw[0] != '\0') ? "\"***\"" : "\"\"",
699 uri);
700 return ldap_simple_bind_s(ld, session->binddn, session->bindpw);
701 #endif
702 }
703 /* perform SASL bind if requested and available on platform */
704 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
705 /* TODO: store this information in the session */
706 if (nslcd_cfg->sasl_mech != NULL)
707 {
708 /* do a SASL bind */
709 if (nslcd_cfg->sasl_secprops != NULL)
710 {
711 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_SASL_SECPROPS,\"%s\")",
712 nslcd_cfg->sasl_secprops);
713 LDAP_SET_OPTION(ld, LDAP_OPT_X_SASL_SECPROPS, (void *)nslcd_cfg->sasl_secprops);
714 }
715 #ifdef HAVE_SASL_INTERACT_T
716 if (nslcd_cfg->binddn != NULL)
717 log_log(LOG_DEBUG, "ldap_sasl_interactive_bind_s(\"%s\",\"%s\") (uri=\"%s\")",
718 nslcd_cfg->binddn, nslcd_cfg->sasl_mech, uri);
719 else
720 log_log(LOG_DEBUG, "ldap_sasl_interactive_bind_s(NULL,\"%s\") (uri=\"%s\")",
721 nslcd_cfg->sasl_mech, uri);
722 return ldap_sasl_interactive_bind_s(ld, nslcd_cfg->binddn,
723 nslcd_cfg->sasl_mech, NULL, NULL,
724 LDAP_SASL_QUIET, do_sasl_interact,
725 (void *)nslcd_cfg);
726 #else /* HAVE_SASL_INTERACT_T */
727 if (nslcd_cfg->bindpw != NULL)
728 {
729 cred.bv_val = nslcd_cfg->bindpw;
730 cred.bv_len = strlen(nslcd_cfg->bindpw);
731 }
732 else
733 {
734 cred.bv_val = "";
735 cred.bv_len = 0;
736 }
737 if (nslcd_cfg->binddn != NULL)
738 log_log(LOG_DEBUG, "ldap_sasl_bind_s(\"%s\",\"%s\",%s) (uri=\"%s\")",
739 nslcd_cfg->binddn, nslcd_cfg->sasl_mech,
740 nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
741 else
742 log_log(LOG_DEBUG, "ldap_sasl_bind_s(NULL,\"%s\",%s) (uri=\"%s\")",
743 nslcd_cfg->sasl_mech,
744 nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
745 return ldap_sasl_bind_s(ld, nslcd_cfg->binddn,
746 nslcd_cfg->sasl_mech, &cred, NULL, NULL, NULL);
747 #endif /* not HAVE_SASL_INTERACT_T */
748 }
749 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
750 /* do a simple bind */
751 if (nslcd_cfg->binddn)
752 log_log(LOG_DEBUG, "ldap_simple_bind_s(\"%s\",%s) (uri=\"%s\")",
753 nslcd_cfg->binddn, nslcd_cfg->bindpw ? "\"***\"" : "NULL",
754 uri);
755 else
756 log_log(LOG_DEBUG, "ldap_simple_bind_s(NULL,%s) (uri=\"%s\")",
757 nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
758 return ldap_simple_bind_s(ld, nslcd_cfg->binddn, nslcd_cfg->bindpw);
759 }
760
761 #ifdef HAVE_LDAP_SET_REBIND_PROC
762 /* This function is called by the LDAP library when chasing referrals.
763 It is configured with the ldap_set_rebind_proc() below. */
764 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
765 static int do_rebind(LDAP *ld, LDAP_CONST char *url,
766 ber_tag_t UNUSED(request),
767 ber_int_t UNUSED(msgid), void *arg)
768 {
769 MYLDAP_SESSION *session = (MYLDAP_SESSION *)arg;
770 log_log(LOG_DEBUG, "rebinding to %s", url);
771 return do_bind(session, ld, url);
772 }
773 #else /* not recent OpenLDAP */
774 static int do_rebind(LDAP *ld, char **dnp, char **passwdp, int *authmethodp,
775 int freeit, void *arg)
776 {
777 MYLDAP_SESSION *session = (MYLDAP_SESSION *)arg;
778 if (freeit)
779 {
780 free(*dnp);
781 memset(*passwdp, 0, strlen(*passwdp));
782 free(*passwdp);
783 }
784 else
785 {
786 log_log(LOG_DEBUG, "rebinding");
787 *dnp = strdup(session->binddn);
788 *passwdp = strdup(session->bindpw);
789 *authmethodp = LDAP_AUTH_SIMPLE;
790 if ((*dnp == NULL) || (*passwdp == NULL))
791 {
792 if (*dnp != NULL)
793 free(*dnp);
794 log_log(LOG_CRIT, "do_rebind(): strdup() failed to allocate memory");
795 return LDAP_NO_MEMORY;
796 }
797 }
798 return LDAP_SUCCESS;
799 }
800 #endif /* not recent OpenLDAP */
801 #endif /* HAVE_LDAP_SET_REBIND_PROC */
802
803 /* set a recieve and send timeout on a socket */
804 static int set_socket_timeout(LDAP *ld, time_t sec, suseconds_t usec)
805 {
806 struct timeval tv;
807 int rc = LDAP_SUCCESS;
808 int sd;
809 log_log(LOG_DEBUG, "set_socket_timeout(%lu,%lu)",
810 (unsigned long)sec, (unsigned long)usec);
811 /* get the socket */
812 if ((rc = ldap_get_option(ld, LDAP_OPT_DESC, &sd)) != LDAP_SUCCESS)
813 {
814 myldap_err(LOG_ERR, ld, rc, "ldap_get_option(LDAP_OPT_DESC) failed");
815 return rc;
816 }
817 /* ignore invalid (probably closed) file descriptors */
818 if (sd <= 0)
819 return LDAP_SUCCESS;
820 /* set timeouts */
821 memset(&tv, 0, sizeof(tv));
822 tv.tv_sec = sec;
823 tv.tv_usec = usec;
824 if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv)))
825 {
826 log_log(LOG_ERR, "setsockopt(%d,SO_RCVTIMEO) failed: %s",
827 sd, strerror(errno));
828 rc = LDAP_LOCAL_ERROR;
829 }
830 if (setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, (void *)&tv, sizeof(tv)))
831 {
832 log_log(LOG_ERR, "setsockopt(%d,SO_RCVTIMEO) failed: %s",
833 sd, strerror(errno));
834 rc = LDAP_LOCAL_ERROR;
835 }
836 return rc;
837 }
838
839 #ifdef LDAP_OPT_CONNECT_CB
840 /* This function is called by the LDAP library once a connection was made to the server. We
841 set a timeout on the socket here, to catch network timeouts during the ssl
842 handshake phase. It is configured with LDAP_OPT_CONNECT_CB. */
843 static int connect_cb(LDAP *ld, Sockbuf UNUSED(*sb),
844 LDAPURLDesc UNUSED(*srv), struct sockaddr UNUSED(*addr),
845 struct ldap_conncb UNUSED(*ctx))
846 {
847 /* set timeout options on socket to avoid hang in some cases (a little
848 more than the normal timeout so this should only be triggered in cases
849 where the library behaves incorrectly) */
850 if (nslcd_cfg->timelimit)
851 set_socket_timeout(ld, nslcd_cfg->timelimit, 500000);
852 return LDAP_SUCCESS;
853 }
854
855 /* We have an empty disconnect callback because LDAP_OPT_CONNECT_CB expects
856 both functions to be available. */
857 static void disconnect_cb(LDAP UNUSED(*ld), Sockbuf UNUSED(*sb),
858 struct ldap_conncb UNUSED(*ctx))
859 {
860 }
861 #endif /* LDAP_OPT_CONNECT_CB */
862
863 /* This function sets a number of properties on the connection, based
864 what is configured in the configfile. This function returns an
865 LDAP status code. */
866 static int do_set_options(MYLDAP_SESSION *session)
867 {
868 int rc;
869 struct timeval tv;
870 #ifdef LDAP_OPT_CONNECT_CB
871 /* make this static because OpenLDAP doesn't make its own copy */
872 static struct ldap_conncb cb;
873 #endif /* LDAP_OPT_CONNECT_CB */
874 #ifdef LDAP_OPT_X_TLS
875 int i;
876 #endif /* LDAP_OPT_X_TLS */
877 #ifdef HAVE_LDAP_SET_REBIND_PROC
878 /* the rebind function that is called when chasing referrals, see
879 http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/apis/ldap_set_rebind_proc.htm
880 http://www.openldap.org/software/man.cgi?query=ldap_set_rebind_proc&manpath=OpenLDAP+2.4-Release */
881 /* TODO: probably only set this if we should chase referrals */
882 log_log(LOG_DEBUG, "ldap_set_rebind_proc()");
883 #ifndef LDAP_SET_REBIND_PROC_RETURNS_VOID /* it returns int */
884 rc = ldap_set_rebind_proc(session->ld, do_rebind, session);
885 if (rc != LDAP_SUCCESS)
886 {
887 myldap_err(LOG_ERR, session->ld, rc, "ldap_set_rebind_proc() failed");
888 return rc;
889 }
890 #else /* ldap_set_rebind_proc() returns void */
891 ldap_set_rebind_proc(session->ld, do_rebind, session);
892 #endif
893 #endif /* HAVE_LDAP_SET_REBIND_PROC */
894 /* set the protocol version to use */
895 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,%d)",
896 nslcd_cfg->ldap_version);
897 LDAP_SET_OPTION(session->ld, LDAP_OPT_PROTOCOL_VERSION,
898 &nslcd_cfg->ldap_version);
899 /* set some other options */
900 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_DEREF,%d)",
901 nslcd_cfg->deref);
902 LDAP_SET_OPTION(session->ld, LDAP_OPT_DEREF, &nslcd_cfg->deref);
903 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_TIMELIMIT,%d)",
904 nslcd_cfg->timelimit);
905 LDAP_SET_OPTION(session->ld, LDAP_OPT_TIMELIMIT, &nslcd_cfg->timelimit);
906 tv.tv_sec = nslcd_cfg->bind_timelimit;
907 tv.tv_usec = 0;
908 #ifdef LDAP_OPT_TIMEOUT
909 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_TIMEOUT,%d)",
910 nslcd_cfg->bind_timelimit);
911 LDAP_SET_OPTION(session->ld, LDAP_OPT_TIMEOUT, &tv);
912 #endif /* LDAP_OPT_TIMEOUT */
913 #ifdef LDAP_OPT_NETWORK_TIMEOUT
914 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,%d)",
915 nslcd_cfg->bind_timelimit);
916 LDAP_SET_OPTION(session->ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
917 #endif /* LDAP_OPT_NETWORK_TIMEOUT */
918 #ifdef LDAP_X_OPT_CONNECT_TIMEOUT
919 log_log(LOG_DEBUG, "ldap_set_option(LDAP_X_OPT_CONNECT_TIMEOUT,%d)",
920 nslcd_cfg->bind_timelimit);
921 LDAP_SET_OPTION(session->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &tv);
922 #endif /* LDAP_X_OPT_CONNECT_TIMEOUT */
923 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_REFERRALS,%s)",
924 nslcd_cfg->referrals ? "LDAP_OPT_ON" : "LDAP_OPT_OFF");
925 LDAP_SET_OPTION(session->ld, LDAP_OPT_REFERRALS,
926 nslcd_cfg->referrals ? LDAP_OPT_ON : LDAP_OPT_OFF);
927 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)");
928 LDAP_SET_OPTION(session->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
929 #ifdef LDAP_OPT_CONNECT_CB
930 /* register a connection callback */
931 cb.lc_add = connect_cb;
932 cb.lc_del = disconnect_cb;
933 cb.lc_arg = NULL;
934 LDAP_SET_OPTION(session->ld, LDAP_OPT_CONNECT_CB, (void *)&cb);
935 #endif /* LDAP_OPT_CONNECT_CB */
936 #ifdef LDAP_OPT_X_TLS
937 /* if SSL is desired, then enable it */
938 if ((nslcd_cfg->ssl == SSL_LDAPS) ||
939 (strncasecmp(nslcd_cfg->uris[session->current_uri].uri, "ldaps://", 8) == 0))
940 {
941 /* use tls */
942 i = LDAP_OPT_X_TLS_HARD;
943 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_TLS,LDAP_OPT_X_TLS_HARD)");
944 LDAP_SET_OPTION(session->ld, LDAP_OPT_X_TLS, &i);
945 }
946 #endif /* LDAP_OPT_X_TLS */
947 #ifdef LDAP_OPT_X_SASL_NOCANON
948 if (nslcd_cfg->sasl_canonicalize >= 0)
949 {
950 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_SASL_NOCANON,%s)",
951 nslcd_cfg->sasl_canonicalize ? "LDAP_OPT_OFF" : "LDAP_OPT_ON");
952 LDAP_SET_OPTION(session->ld, LDAP_OPT_X_SASL_NOCANON,
953 nslcd_cfg->sasl_canonicalize ? LDAP_OPT_OFF : LDAP_OPT_ON);
954 }
955 #endif /* LDAP_OPT_X_SASL_NOCANON */
956 /* if nothing above failed, everything should be fine */
957 return LDAP_SUCCESS;
958 }
959
960 /* close the connection to the server and invalidate any running searches */
961 static void do_close(MYLDAP_SESSION *session)
962 {
963 int i;
964 int rc;
965 time_t sec;
966 /* if we had reachability problems with the server close the connection */
967 if (session->ld != NULL)
968 {
969 /* set timeout options on socket to avoid hang in some cases
970 (we set a short timeout because we don't care too much about properly
971 shutting down the connection) */
972 if (nslcd_cfg->timelimit)
973 {
974 sec = nslcd_cfg->timelimit / 2;
975 if (!sec)
976 sec = 1;
977 set_socket_timeout(session->ld, sec, 0);
978 }
979 /* go over the other searches and partially close them */
980 for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
981 {
982 if (session->searches[i] != NULL)
983 {
984 /* free any messages (because later ld is no longer valid) */
985 if (session->searches[i]->msg != NULL)
986 {
987 ldap_msgfree(session->searches[i]->msg);
988 session->searches[i]->msg = NULL;
989 }
990 /* abandon the search if there were more results to fetch */
991 if (session->searches[i]->msgid != -1)
992 {
993 log_log(LOG_DEBUG, "ldap_abandon()");
994 if (ldap_abandon(session->searches[i]->session->ld, session->searches[i]->msgid))
995 {
996 if (ldap_get_option(session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
997 rc = LDAP_OTHER;
998 myldap_err(LOG_WARNING, session->ld, rc,
999 "ldap_abandon() failed to abandon search");
1000 }
1001 session->searches[i]->msgid = -1;
1002 }
1003 /* flag the search as invalid */
1004 session->searches[i]->valid = 0;
1005 }
1006 }
1007 /* close the connection to the server */
1008 log_log(LOG_DEBUG, "ldap_unbind()");
1009 rc = ldap_unbind(session->ld);
1010 session->ld = NULL;
1011 if (rc != LDAP_SUCCESS)
1012 myldap_err(LOG_WARNING, session->ld, rc, "ldap_unbind() failed");
1013 }
1014 }
1015
1016 void myldap_session_check(MYLDAP_SESSION *session)
1017 {
1018 int i;
1019 time_t current_time;
1020 int sd;
1021 int rc;
1022 struct sockaddr sa;
1023 socklen_t salen = sizeof(sa);
1024 /* check parameters */
1025 if (session == NULL)
1026 {
1027 log_log(LOG_ERR, "myldap_session_check(): invalid parameter passed");
1028 errno = EINVAL;
1029 return;
1030 }
1031 if (session->ld != NULL)
1032 {
1033 rc = ldap_get_option(session->ld, LDAP_OPT_DESC, &sd);
1034 if (rc != LDAP_SUCCESS)
1035 {
1036 myldap_err(LOG_WARNING, session->ld, rc,
1037 "ldap_get_option(LDAP_OPT_DESC) failed (ignored)");
1038 }
1039 else
1040 {
1041 /* check if the connection was closed by the peer */
1042 if (getpeername(sd, &sa, &salen) == -1)
1043 {
1044 if (errno == ENOTCONN)
1045 {
1046 log_log(LOG_DEBUG, "myldap_session_check(): connection reset by peer");
1047 do_close(session);
1048 return;
1049 }
1050 }
1051 }
1052 /* check if we should time out the connection */
1053 if (nslcd_cfg->idle_timelimit > 0)
1054 {
1055 /* if we have any running searches, don't time out */
1056 for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
1057 if ((session->searches[i] != NULL) && (session->searches[i]->valid))
1058 return;
1059 /* consider timeout (there are no running searches) */
1060 time(¤t_time);
1061 if ((session->lastactivity + nslcd_cfg->idle_timelimit) < current_time)
1062 {
1063 log_log(LOG_DEBUG, "myldap_session_check(): idle_timelimit reached");
1064 do_close(session);
1065 /* try to use the first URI from the list again */
1066 session->current_uri = 0;
1067 }
1068 }
1069 }
1070 }
1071
1072 /* This opens connection to an LDAP server, sets all connection options
1073 and binds to the server. This returns an LDAP status code. */
1074 static int do_open(MYLDAP_SESSION *session)
1075 {
1076 int rc;
1077 /* if the connection is still there (ie. ldap_unbind() wasn't
1078 called) then we can return the cached connection */
1079 if (session->ld != NULL)
1080 return LDAP_SUCCESS;
1081 /* we should build a new session now */
1082 session->ld = NULL;
1083 session->lastactivity = 0;
1084 /* open the connection */
1085 log_log(LOG_DEBUG, "ldap_initialize(%s)",
1086 nslcd_cfg->uris[session->current_uri].uri);
1087 errno = 0;
1088 rc = ldap_initialize(&(session->ld), nslcd_cfg->uris[session->current_uri].uri);
1089 if (rc != LDAP_SUCCESS)
1090 {
1091 myldap_err(LOG_WARNING, session->ld, rc, "ldap_initialize(%s) failed",
1092 nslcd_cfg->uris[session->current_uri].uri);
1093 if (session->ld != NULL)
1094 do_close(session);
1095 return rc;
1096 }
1097 else if (session->ld == NULL)
1098 {
1099 log_log(LOG_WARNING, "ldap_initialize() returned NULL");
1100 return LDAP_LOCAL_ERROR;
1101 }
1102 /* set the options for the connection */
1103 rc = do_set_options(session);
1104 if (rc != LDAP_SUCCESS)
1105 {
1106 do_close(session);
1107 return rc;
1108 }
1109 /* bind to the server */
1110 errno = 0;
1111 rc = do_bind(session, session->ld, nslcd_cfg->uris[session->current_uri].uri);
1112 if (rc != LDAP_SUCCESS)
1113 {
1114 /* log actual LDAP error code */
1115 myldap_err((session->binddn[0] == '\0') ? LOG_WARNING : LOG_DEBUG,
1116 session->ld, rc, "failed to bind to LDAP server %s",
1117 nslcd_cfg->uris[session->current_uri].uri);
1118 do_close(session);
1119 return rc;
1120 }
1121 /* update last activity and finish off state */
1122 time(&(session->lastactivity));
1123 return LDAP_SUCCESS;
1124 }
1125
1126 /* Perform a simple bind operation and return the ppolicy results. */
1127 int myldap_bind(MYLDAP_SESSION *session, const char *dn, const char *password,
1128 int *response, const char **message)
1129 {
1130 MYLDAP_SEARCH *search;
1131 static const char *attrs[2];
1132 int rc;
1133 /* error out when buffers are too small */
1134 if (strlen(dn) >= sizeof(session->binddn))
1135 {
1136 log_log(LOG_ERR, "myldap_bind(): binddn buffer too small (%lu required)",
1137 (unsigned long) strlen(dn));
1138 return LDAP_LOCAL_ERROR;
1139 }
1140 if (strlen(password) >= sizeof(session->bindpw))
1141 {
1142 log_log(LOG_ERR, "myldap_bind(): bindpw buffer too small (%lu required)",
1143 (unsigned long) strlen(password));
1144 return LDAP_LOCAL_ERROR;
1145 }
1146 /* copy dn and password into session */
1147 strncpy(session->binddn, dn, sizeof(session->binddn));
1148 session->binddn[sizeof(session->binddn) - 1] = '\0';
1149 strncpy(session->bindpw, password, sizeof(session->bindpw));
1150 session->bindpw[sizeof(session->bindpw) - 1] = '\0';
1151 /* construct a fake search to trigger the BIND operation */
1152 attrs[0] = "dn";
1153 attrs[1] = NULL;
1154 search = myldap_search(session, session->binddn, MYLDAP_SCOPE_BINDONLY,
1155 "(objectClass=*)", attrs, &rc);
1156 if (search != NULL)
1157 myldap_search_close(search);
1158 /* return ppolicy results */
1159 if (response != NULL)
1160 *response = session->policy_response;
1161 if (message != NULL)
1162 *message = session->policy_message;
1163 return rc;
1164 }
1165
1166 /* perform a search operation, the connection is assumed to be open */
1167 static int do_try_search(MYLDAP_SEARCH *search)
1168 {
1169 int ctrlidx = 0;
1170 int rc;
1171 LDAPControl *serverctrls[3];
1172 #ifdef HAVE_LDAP_CREATE_DEREF_CONTROL
1173 int i;
1174 struct LDAPDerefSpec ds[2];
1175 char *deref_attrs[2];
1176 #endif /* HAVE_LDAP_CREATE_DEREF_CONTROL */
1177 int msgid;
1178 /* if we're using paging, build a page control */
1179 if ((nslcd_cfg->pagesize > 0) && (search->scope != LDAP_SCOPE_BASE))
1180 {
1181 rc = ldap_create_page_control(search->session->ld, nslcd_cfg->pagesize,
1182 search->cookie, 0, &serverctrls[ctrlidx]);
1183 if (rc == LDAP_SUCCESS)
1184 ctrlidx++;
1185 else
1186 {
1187 myldap_err(LOG_WARNING, search->session->ld, rc,
1188 "ldap_create_page_control() failed");
1189 serverctrls[ctrlidx] = NULL;
1190 /* if we were paging, failure building the second control is fatal */
1191 if (search->cookie != NULL)
1192 return rc;
1193 }
1194 }
1195 #ifdef HAVE_LDAP_CREATE_DEREF_CONTROL
1196 /* if doing group searches, add deref control to search request
1197 (this is currently a bit of a hack and hard-coded for group searches
1198 which are detected by requesting the attmap_group_member member
1199 attribute) */
1200 for (i = 0; search->attrs[i] != NULL; i++)
1201 if (strcasecmp(search->attrs[i], attmap_group_member) == 0)
1202 {
1203 /* attributes from dereff'd entries */
1204 deref_attrs[0] = (void *)attmap_passwd_uid;
1205 deref_attrs[1] = NULL;
1206 /* build deref control */
1207 ds[0].derefAttr = (void *)attmap_group_member;
1208 ds[0].attributes = deref_attrs;
1209 ds[1].derefAttr = NULL;
1210 ds[1].attributes = NULL;
1211 rc = ldap_create_deref_control(search->session->ld, ds, 0, &serverctrls[ctrlidx]);
1212 if (rc == LDAP_SUCCESS)
1213 ctrlidx++;
1214 else
1215 {
1216 myldap_err(LOG_WARNING, search->session->ld, rc,
1217 "ldap_create_deref_control() failed");
1218 serverctrls[ctrlidx] = NULL;
1219 }
1220 }
1221 #endif /* HAVE_LDAP_CREATE_DEREF_CONTROL */
1222 /* NULL terminate control list */
1223 serverctrls[ctrlidx] = NULL;
1224 /* clear error flag (perhaps control setting failed) */
1225 if (ctrlidx > 0)
1226 {
1227 rc = LDAP_SUCCESS;
1228 if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
1229 log_log(LOG_WARNING, "failed to clear the error flag");
1230 }
1231 /* perform the search */
1232 rc = ldap_search_ext(search->session->ld, search->base, search->scope,
1233 search->filter, (char **)(search->attrs),
1234 0, serverctrls[0] == NULL ? NULL : serverctrls,
1235 NULL, NULL, LDAP_NO_LIMIT, &msgid);
1236 /* free the controls if we had them */
1237 for (ctrlidx = 0; serverctrls[ctrlidx] != NULL; ctrlidx++)
1238 ldap_control_free(serverctrls[ctrlidx]);
1239 /* handle errors */
1240 if (rc != LDAP_SUCCESS)
1241 {
1242 myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_search_ext() failed");
1243 return rc;
1244 }
1245 /* update the last activity on the connection */
1246 time(&(search->session->lastactivity));
1247 /* save msgid */
1248 search->msgid = msgid;
1249 /* return the new search */
1250 return LDAP_SUCCESS;
1251 }
1252
1253 MYLDAP_SESSION *myldap_create_session(void)
1254 {
1255 return myldap_session_new();
1256 }
1257
1258 void myldap_session_cleanup(MYLDAP_SESSION *session)
1259 {
1260 int i;
1261 /* check parameter */
1262 if (session == NULL)
1263 {
1264 log_log(LOG_ERR, "myldap_session_cleanup(): invalid session passed");
1265 return;
1266 }
1267 /* go over all searches in the session and close them */
1268 for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
1269 {
1270 if (session->searches[i] != NULL)
1271 {
1272 myldap_search_close(session->searches[i]);
1273 session->searches[i] = NULL;
1274 }
1275 }
1276 }
1277
1278 void myldap_session_close(MYLDAP_SESSION *session)
1279 {
1280 /* check parameter */
1281 if (session == NULL)
1282 {
1283 log_log(LOG_ERR, "myldap_session_cleanup(): invalid session passed");
1284 return;
1285 }
1286 /* close pending searches */
1287 myldap_session_cleanup(session);
1288 /* close any open connections */
1289 do_close(session);
1290 /* free allocated memory */
1291 memset(session->bindpw, 0, sizeof(session->bindpw));
1292 free(session);
1293 }
1294
1295 /* mutex for updating the times in the uri */
1296 pthread_mutex_t uris_mutex = PTHREAD_MUTEX_INITIALIZER;
1297
1298 static int do_retry_search(MYLDAP_SEARCH *search)
1299 {
1300 int sleeptime = 0;
1301 int start_uri;
1302 time_t endtime;
1303 time_t nexttry;
1304 time_t t;
1305 int rc = LDAP_UNAVAILABLE;
1306 struct myldap_uri *current_uri;
1307 int dotry[NSS_LDAP_CONFIG_MAX_URIS];
1308 int do_invalidate = 0;
1309 /* clear time stamps */
1310 for (start_uri = 0; start_uri < NSS_LDAP_CONFIG_MAX_URIS; start_uri++)
1311 dotry[start_uri] = 1;
1312 /* keep trying until we time out */
1313 endtime = time(NULL) + nslcd_cfg->reconnect_retrytime;
1314 while (1)
1315 {
1316 nexttry = endtime;
1317 /* try each configured URL once */
1318 pthread_mutex_lock(&uris_mutex);
1319 start_uri = search->session->current_uri;
1320 do
1321 {
1322 current_uri = &(nslcd_cfg->uris[search->session->current_uri]);
1323 /* only try this URI if we should */
1324 if (!dotry[search->session->current_uri])
1325 { /* skip this URI */ }
1326 else if ((current_uri->lastfail > (current_uri->firstfail + nslcd_cfg->reconnect_retrytime)) &&
1327 ((t = time(NULL)) < (current_uri->lastfail + nslcd_cfg->reconnect_retrytime)))
1328 {
1329 /* we are in a hard fail state and have retried not long ago */
1330 log_log(LOG_DEBUG, "not retrying server %s which failed just %d second(s) ago and has been failing for %d seconds",
1331 current_uri->uri, (int)(t - current_uri->lastfail),
1332 (int)(t - current_uri->firstfail));
1333 dotry[search->session->current_uri] = 0;
1334 }
1335 else
1336 {
1337 /* try to start the search */
1338 pthread_mutex_unlock(&uris_mutex);
1339 /* ensure that we have an open connection and start a search */
1340 rc = do_open(search->session);
1341 /* perform the actual search, unless we were only binding */
1342 if ((rc == LDAP_SUCCESS) && (search->scope != MYLDAP_SCOPE_BINDONLY))
1343 rc = do_try_search(search);
1344 /* if we are authenticating a user and get an error regarding failed
1345 password we should error out instead of trying all servers */
1346 if ((search->session->binddn[0] != '\0') && (rc == LDAP_INVALID_CREDENTIALS))
1347 {
1348 do_close(search->session);
1349 return rc;
1350 }
1351 if (rc == LDAP_SUCCESS)
1352 {
1353 pthread_mutex_lock(&uris_mutex);
1354 /* check if we are coming back from an error */
1355 if ((current_uri->lastfail > 0) || (search->session->current_uri != start_uri))
1356 {
1357 log_log(LOG_INFO, "connected to LDAP server %s", current_uri->uri);
1358 do_invalidate = 1;
1359 }
1360 if (first_search)
1361 {
1362 do_invalidate = 1;
1363 first_search = 0;
1364 }
1365 /* update ok time */
1366 current_uri->firstfail = 0;
1367 current_uri->lastfail = 0;
1368 pthread_mutex_unlock(&uris_mutex);
1369 /* flag the search as valid */
1370 search->valid = 1;
1371 /* signal external invalidation of configured caches */
1372 if (do_invalidate)
1373 invalidator_do(LM_NONE);
1374 return LDAP_SUCCESS;
1375 }
1376 /* close the current connection */
1377 do_close(search->session);
1378 /* update time of failure and figure out when we should retry */
1379 pthread_mutex_lock(&uris_mutex);
1380 t = time(NULL);
1381 /* update timestamps unless we are doing an authentication search */
1382 if (search->session->binddn[0] == '\0')
1383 {
1384 if (current_uri->firstfail == 0)
1385 current_uri->firstfail = t;
1386 current_uri->lastfail = t;
1387 }
1388 /* if it is one of these, retrying this URI is not going to help */
1389 if ((rc == LDAP_INVALID_CREDENTIALS) || (rc == LDAP_INSUFFICIENT_ACCESS) ||
1390 (rc == LDAP_AUTH_METHOD_NOT_SUPPORTED))
1391 dotry[search->session->current_uri] = 0;
1392 /* check when we should try this URI again */
1393 else if (t <= (current_uri->firstfail + nslcd_cfg->reconnect_retrytime))
1394 {
1395 t += nslcd_cfg->reconnect_sleeptime;
1396 if (t < nexttry)
1397 nexttry = t;
1398 }
1399 }
1400 /* try the next URI (with wrap-around) */
1401 search->session->current_uri++;
1402 if (nslcd_cfg->uris[search->session->current_uri].uri == NULL)
1403 search->session->current_uri = 0;
1404 }
1405 while (search->session->current_uri != start_uri);
1406 pthread_mutex_unlock(&uris_mutex);
1407 /* see if it is any use sleeping */
1408 if (nexttry >= endtime)
1409 {
1410 if (search->session->binddn[0] == '\0')
1411 myldap_err(LOG_ERR, search->session->ld, rc, "no available LDAP server found");
1412 return rc;
1413 }
1414 /* sleep between tries */
1415 sleeptime = nexttry - time(NULL);
1416 if (sleeptime > 0)
1417 {
1418 log_log(LOG_WARNING, "no available LDAP server found, sleeping %d seconds",
1419 sleeptime);
1420 (void)sleep(sleeptime);
1421 }
1422 }
1423 }
1424
1425 /* force quick retries of all failing LDAP servers */
1426 void myldap_immediate_reconnect(void)
1427 {
1428 int i;
1429 time_t t;
1430 t = time(NULL) - nslcd_cfg->reconnect_retrytime;
1431 pthread_mutex_lock(&uris_mutex);
1432 for (i = 0; i < (NSS_LDAP_CONFIG_MAX_URIS + 1); i++)
1433 {
1434 /* only adjust failing connections that are in a hard fail state */
1435 if ((nslcd_cfg->uris[i].lastfail > t) &&
1436 (nslcd_cfg->uris[i].lastfail > (nslcd_cfg->uris[i].firstfail + nslcd_cfg->reconnect_retrytime)))
1437 {
1438 /* move lastfail back to ensure quick retry */
1439 log_log(LOG_DEBUG, "moving lastfail of %s %d second(s) back to force retry",
1440 nslcd_cfg->uris[i].uri, (int)(nslcd_cfg->uris[i].lastfail - t));
1441 nslcd_cfg->uris[i].lastfail = t;
1442 }
1443 }
1444 pthread_mutex_unlock(&uris_mutex);
1445 }
1446
1447 MYLDAP_SEARCH *myldap_search(MYLDAP_SESSION *session,
1448 const char *base, int scope, const char *filter,
1449 const char **attrs, int *rcp)
1450 {
1451 MYLDAP_SEARCH *search;
1452 int i;
1453 int rc;
1454 /* check parameters */
1455 if ((session == NULL) || (base == NULL) || (filter == NULL) || (attrs == NULL))
1456 {
1457 log_log(LOG_ERR, "myldap_search(): invalid parameter passed");
1458 errno = EINVAL;
1459 if (rcp != NULL)
1460 *rcp = LDAP_OPERATIONS_ERROR;
1461 return NULL;
1462 }
1463 /* log the call */
1464 log_log(LOG_DEBUG, "myldap_search(base=\"%s\", filter=\"%s\")",
1465 base, filter);
1466 /* check if the idle time for the connection has expired */
1467 myldap_session_check(session);
1468 /* allocate a new search entry */
1469 search = myldap_search_new(session, base, scope, filter, attrs);
1470 /* find a place in the session where we can register our search */
1471 for (i = 0; (i < MAX_SEARCHES_IN_SESSION) && (session->searches[i] != NULL); i++)
1472 /* nothing */ ;
1473 if (i >= MAX_SEARCHES_IN_SESSION)
1474 {
1475 log_log(LOG_ERR, "myldap_search(): too many searches registered with session (max %d)",
1476 MAX_SEARCHES_IN_SESSION);
1477 myldap_search_close(search);
1478 if (rcp != NULL)
1479 *rcp = LDAP_OPERATIONS_ERROR;
1480 return NULL;
1481 }
1482 /* register search with the session so we can free it later on */
1483 session->searches[i] = search;
1484 /* do the search with retries to all configured servers */
1485 rc = do_retry_search(search);
1486 if (rc != LDAP_SUCCESS)
1487 {
1488 myldap_search_close(search);
1489 if (rcp != NULL)
1490 *rcp = rc;
1491 return NULL;
1492 }
1493 if (rcp != NULL)
1494 *rcp = LDAP_SUCCESS;
1495 return search;
1496 }
1497
1498 void myldap_search_close(MYLDAP_SEARCH *search)
1499 {
1500 int i;
1501 if (search == NULL)
1502 return;
1503 /* free any messages */
1504 if (search->msg != NULL)
1505 {
1506 ldap_msgfree(search->msg);
1507 search->msg = NULL;
1508 }
1509 /* abandon the search if there were more results to fetch */
1510 if ((search->session->ld != NULL) && (search->msgid != -1))
1511 {
1512 ldap_abandon(search->session->ld, search->msgid);
1513 search->msgid = -1;
1514 }
1515 /* find the reference to this search in the session */
1516 for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
1517 {
1518 if (search->session->searches[i] == search)
1519 search->session->searches[i] = NULL;
1520 }
1521 /* free any search entries */
1522 if (search->entry != NULL)
1523 myldap_entry_free(search->entry);
1524 /* clean up cookie */
1525 if (search->cookie != NULL)
1526 ber_bvfree(search->cookie);
1527 /* free read messages */
1528 if (search->msg != NULL)
1529 ldap_msgfree(search->msg);
1530 /* free the storage we allocated */
1531 free(search);
1532 }
1533
1534 MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search, int *rcp)
1535 {
1536 int rc;
1537 int parserc;
1538 struct timeval tv, *tvp;
1539 LDAPControl **resultcontrols;
1540 ber_int_t count;
1541 /* check parameters */
1542 if ((search == NULL) || (search->session == NULL) || (search->session->ld == NULL))
1543 {
1544 log_log(LOG_ERR, "myldap_get_entry(): invalid search passed");
1545 errno = EINVAL;
1546 if (rcp != NULL)
1547 *rcp = LDAP_OPERATIONS_ERROR;
1548 return NULL;
1549 }
1550 /* check if the connection wasn't closed in another search */
1551 if (!search->valid)
1552 {
1553 log_log(LOG_WARNING, "myldap_get_entry(): connection was closed");
1554 /* retry the search */
1555 if (search->may_retry_search)
1556 {
1557 log_log(LOG_DEBUG, "myldap_get_entry(): retry search");
1558 search->may_retry_search = 0;
1559 if (do_retry_search(search) == LDAP_SUCCESS)
1560 return myldap_get_entry(search, rcp);
1561 }
1562 myldap_search_close(search);
1563 if (rcp != NULL)
1564 *rcp = LDAP_SERVER_DOWN;
1565 return NULL;
1566 }
1567 /* set up a timelimit value for operations */
1568 if (nslcd_cfg->timelimit == LDAP_NO_LIMIT)
1569 tvp = NULL;
1570 else
1571 {
1572 tv.tv_sec = nslcd_cfg->timelimit;
1573 tv.tv_usec = 0;
1574 tvp = &tv;
1575 }
1576 /* if we have an existing result entry, free it */
1577 if (search->entry != NULL)
1578 {
1579 myldap_entry_free(search->entry);
1580 search->entry = NULL;
1581 }
1582 /* try to parse results until we have a final error or ok */
1583 while (1)
1584 {
1585 /* free the previous message if there was any */
1586 if (search->msg != NULL)
1587 {
1588 ldap_msgfree(search->msg);
1589 search->msg = NULL;
1590 }
1591 /* get the next result */
1592 rc = ldap_result(search->session->ld, search->msgid, LDAP_MSG_ONE, tvp,
1593 &(search->msg));
1594 /* handle result */
1595 switch (rc)
1596 {
1597 case LDAP_RES_SEARCH_ENTRY:
1598 /* we have a normal search entry, update timestamp and return result */
1599 time(&(search->session->lastactivity));
1600 search->entry = myldap_entry_new(search);
1601 if (rcp != NULL)
1602 *rcp = LDAP_SUCCESS;
1603 /* log the first couple of dns in the result (but not all, to
1604 prevent swamping the log) */
1605 if (search->count < MAX_DEBUG_LOG_DNS)
1606 log_log(LOG_DEBUG, "ldap_result(): %s", myldap_get_dn(search->entry));
1607 search->count++;
1608 search->may_retry_search = 0;
1609 return search->entry;
1610 case LDAP_RES_SEARCH_RESULT:
1611 /* we have a search result, parse it */
1612 resultcontrols = NULL;
1613 if (search->cookie != NULL)
1614 {
1615 ber_bvfree(search->cookie);
1616 search->cookie = NULL;
1617 }
1618 /* NB: this frees search->msg */
1619 parserc = ldap_parse_result(search->session->ld, search->msg, &rc,
1620 NULL, NULL, NULL, &resultcontrols, 1);
1621 search->msg = NULL;
1622 /* check for errors during parsing */
1623 if ((parserc != LDAP_SUCCESS) && (parserc != LDAP_MORE_RESULTS_TO_RETURN))
1624 {
1625 if (resultcontrols != NULL)
1626 ldap_controls_free(resultcontrols);
1627 myldap_err(LOG_ERR, search->session->ld, parserc, "ldap_parse_result() failed");
1628 myldap_search_close(search);
1629 if (rcp != NULL)
1630 *rcp = parserc;
1631 return NULL;
1632 }
1633 /* check for errors in message */
1634 if ((rc != LDAP_SUCCESS) && (rc != LDAP_MORE_RESULTS_TO_RETURN))
1635 {
1636 if (resultcontrols != NULL)
1637 ldap_controls_free(resultcontrols);
1638 myldap_err(LOG_ERR, search->session->ld, rc, "ldap_result() failed");
1639 /* close connection on connection problems */
1640 if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
1641 do_close(search->session);
1642 myldap_search_close(search);
1643 if (rcp != NULL)
1644 *rcp = rc;
1645 return NULL;
1646 }
1647 /* handle result controls */
1648 if (resultcontrols != NULL)
1649 {
1650 /* see if there are any more pages to come */
1651 rc = ldap_parse_page_control(search->session->ld, resultcontrols,
1652 &count, &(search->cookie));
1653 if (rc != LDAP_SUCCESS)
1654 {
1655 if (rc != LDAP_CONTROL_NOT_FOUND)
1656 myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_parse_page_control() failed");
1657 /* clear error flag */
1658 rc = LDAP_SUCCESS;
1659 if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER,
1660 &rc) != LDAP_SUCCESS)
1661 log_log(LOG_WARNING, "failed to clear the error flag");
1662 }
1663 /* TODO: handle the above return code?? */
1664 ldap_controls_free(resultcontrols);
1665 }
1666 search->msgid = -1;
1667 /* check if there are more pages to come */
1668 if ((search->cookie == NULL) || (search->cookie->bv_len == 0))
1669 {
1670 if (search->count > MAX_DEBUG_LOG_DNS)
1671 log_log(LOG_DEBUG, "ldap_result(): ... %d more results",
1672 search->count - MAX_DEBUG_LOG_DNS);
1673 log_log(LOG_DEBUG, "ldap_result(): end of results (%d total)",
1674 search->count);
1675 /* we are at the end of the search, no more results */
1676 myldap_search_close(search);
1677 if (rcp != NULL)
1678 *rcp = LDAP_SUCCESS;
1679 return NULL;
1680 }
1681 /* try the next page */
1682 rc = do_try_search(search);
1683 if (rc != LDAP_SUCCESS)
1684 {
1685 /* close connection on connection problems */
1686 if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
1687 do_close(search->session);
1688 myldap_search_close(search);
1689 if (rcp != NULL)
1690 *rcp = rc;
1691 return NULL;
1692 }
1693 /* we continue with another pass */
1694 break;
1695 case LDAP_RES_SEARCH_REFERENCE:
1696 break; /* just ignore search references */
1697 default:
1698 /* we have some error condition, find out which */
1699 switch (rc)
1700 {
1701 case -1:
1702 /* try to get error code */
1703 if (ldap_get_option(search->session->ld, LDAP_OPT_ERROR_NUMBER,
1704 &rc) != LDAP_SUCCESS)
1705 rc = LDAP_UNAVAILABLE;
1706 myldap_err(LOG_ERR, search->session->ld, rc, "ldap_result() failed");
1707 break;
1708 case 0:
1709 /* the timeout expired */
1710 log_log(LOG_ERR, "ldap_result() timed out");
1711 rc = LDAP_TIMELIMIT_EXCEEDED;
1712 break;
1713 default:
1714 /* unknown code */
1715 log_log(LOG_WARNING, "ldap_result() returned unexpected result type");
1716 rc = LDAP_PROTOCOL_ERROR;
1717 }
1718 /* close connection on some connection problems */
1719 if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN) ||
1720 (rc == LDAP_SUCCESS) || (rc == LDAP_TIMELIMIT_EXCEEDED) ||
1721 (rc == LDAP_OPERATIONS_ERROR) || (rc == LDAP_PROTOCOL_ERROR) ||
1722 (rc == LDAP_BUSY) || (rc == LDAP_UNWILLING_TO_PERFORM) ||
1723 (rc == LDAP_TIMEOUT) || (rc == LDAP_CONNECT_ERROR) ||
1724 (rc == LDAP_NOT_SUPPORTED))
1725 {
1726 do_close(search->session);
1727 /* retry once if no data has been received yet */
1728 if (search->may_retry_search)
1729 {
1730 log_log(LOG_DEBUG, "myldap_get_entry(): retry search");
1731 search->may_retry_search = 0;
1732 if (do_retry_search(search) == LDAP_SUCCESS)
1733 return myldap_get_entry(search, rcp);
1734 }
1735 }
1736 /* close search */
1737 myldap_search_close(search);
1738 if (rcp != NULL)
1739 *rcp = rc;
1740 return NULL;
1741 }
1742 }
1743 }
1744
1745 /* Get the DN from the entry. This function only returns NULL (and sets
1746 errno) if an incorrect entry is passed. If the DN value cannot be
1747 retrieved "unknown" is returned instead. */
1748 const char *myldap_get_dn(MYLDAP_ENTRY *entry)
1749 {
1750 int rc;
1751 /* check parameters */
1752 if (!is_valid_entry(entry))
1753 {
1754 log_log(LOG_ERR, "myldap_get_dn(): invalid result entry passed");
1755 errno = EINVAL;
1756 return "unknown";
1757 }
1758 /* if we don't have it yet, retrieve it */
1759 if ((entry->dn == NULL) && (entry->search->valid))
1760 {
1761 entry->dn = ldap_get_dn(entry->search->session->ld, entry->search->msg);
1762 if (entry->dn == NULL)
1763 {
1764 if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER,
1765 &rc) != LDAP_SUCCESS)
1766 rc = LDAP_UNAVAILABLE;
1767 myldap_err(LOG_WARNING, entry->search->session->ld, rc, "ldap_get_dn() returned NULL");
1768 /* close connection on connection problems */
1769 if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
1770 do_close(entry->search->session);
1771 }
1772 }
1773 /* if we still don't have it, return unknown */
1774 if (entry->dn == NULL)
1775 return "unknown";
1776 /* return it */
1777 return entry->dn;
1778 }
1779
1780 char *myldap_cpy_dn(MYLDAP_ENTRY *entry, char *buf, size_t buflen)
1781 {
1782 const char *dn;
1783 /* get the dn */
1784 dn = myldap_get_dn(entry);
1785 /* copy into buffer */
1786 if (strlen(dn) < buflen)
1787 strcpy(buf, dn);
1788 else
1789 buf = NULL;
1790 return buf;
1791 }
1792
1793 /* Perform ranged retrieval of attributes.
1794 http://msdn.microsoft.com/en-us/library/aa367017(vs.85).aspx
1795 http://www.tkk.fi/cc/docs/kerberos/draft-kashi-incremental-00.txt */
1796 static char **myldap_get_ranged_values(MYLDAP_ENTRY *entry, const char *attr)
1797 {
1798 char **values;
1799 char *attn;
1800 const char *attrs[2];
1801 BerElement *ber;
1802 int i;
1803 int startat = 0, nxt = 0;
1804 char attbuf[80];
1805 const char *dn = myldap_get_dn(entry);
1806 MYLDAP_SESSION *session = entry->search->session;
1807 MYLDAP_SEARCH *search = NULL;
1808 SET *set = NULL;
1809 /* build the attribute name to find */
1810 if (mysnprintf(attbuf, sizeof(attbuf), "%s;range=0-*", attr))
1811 {
1812 log_log(LOG_ERR, "myldap_get_ranged_values(): attbuf buffer too small (%lu required)",
1813 (unsigned long) strlen(attr) + 10);
1814 return NULL;
1815 }
1816 /* keep doing lookups untul we can't get any more results */
1817 while (1)
1818 {
1819 /* go over all attributes to find the ranged attribute */
1820 ber = NULL;
1821 attn = ldap_first_attribute(entry->search->session->ld, entry->search->msg, &ber);
1822 values = NULL;
1823 while (attn != NULL)
1824 {
1825 if (strncasecmp(attn, attbuf, strlen(attbuf) - 1) == 0)
1826 {
1827 log_log(LOG_DEBUG, "found ranged results %s", attn);
1828 nxt = atoi(attn + strlen(attbuf) - 1) + 1;
1829 values = ldap_get_values(entry->search->session->ld, entry->search->msg, attn);
1830 ldap_memfree(attn);
1831 break;
1832 }
1833 /* free old attribute name and get next one */
1834 ldap_memfree(attn);
1835 attn = ldap_next_attribute(entry->search->session->ld, entry->search->msg, ber);
1836 }
1837 ber_free(ber, 0);
1838 /* see if we found any values */
1839 if ((values == NULL) || (*values == NULL))
1840 break;
1841 /* allocate memory */
1842 if (set == NULL)
1843 {
1844 set = set_new();
1845 if (set == NULL)
1846 {
1847 ldap_value_free(values);
1848 log_log(LOG_CRIT, "myldap_get_ranged_values(): set_new() failed to allocate memory");
1849 return NULL;
1850 }
1851 }
1852 /* add to the set */
1853 for (i = 0; values[i] != NULL; i++)
1854 set_add(set, values[i]);
1855 /* free results */
1856 ldap_value_free(values);
1857 /* check if we should start a new search */
1858 if (nxt <= startat)
1859 break;
1860 startat = nxt;
1861 /* build attributes for a new search */
1862 if (mysnprintf(attbuf, sizeof(attbuf), "%s;range=%d-*", attr, startat))
1863 {
1864 log_log(LOG_ERR, "myldap_get_ranged_values(): attbuf buffer too small");
1865 break;
1866 }
1867 attrs[0] = attbuf;
1868 attrs[1] = NULL;
1869 /* close the previous search, if any */
1870 if (search != NULL)
1871 myldap_search_close(search);
1872 /* start the new search */
1873 search = myldap_search(session, dn, LDAP_SCOPE_BASE, "(objectClass=*)", attrs, NULL);
1874 if (search == NULL)
1875 break;
1876 entry = myldap_get_entry(search, NULL);
1877 if (entry == NULL)
1878 break;
1879 }
1880 /* close any started searches */
1881 if (search != NULL)
1882 myldap_search_close(search);
1883 /* return the contents of the set as a list */
1884 if (set == NULL)
1885 return NULL;
1886 values = (char **)set_tolist(set);
1887 set_free(set);
1888 if (values == NULL)
1889 log_log(LOG_CRIT, "myldap_get_ranged_values(): malloc() failed to allocate memory");
1890 return values;
1891 }
1892
1893 /* Simple wrapper around ldap_get_values(). */
1894 const char **myldap_get_values(MYLDAP_ENTRY *entry, const char *attr)
1895 {
1896 char **values;
1897 int rc;
1898 int i;
1899 /* check parameters */
1900 if (!is_valid_entry(entry))
1901 {
1902 log_log(LOG_ERR, "myldap_get_values(): invalid result entry passed");
1903 errno = EINVAL;
1904 return NULL;
1905 }
1906 else if (attr == NULL)
1907 {
1908 log_log(LOG_ERR, "myldap_get_values(): invalid attribute name passed");
1909 errno = EINVAL;
1910 return NULL;
1911 }
1912 if (!entry->search->valid)
1913 return NULL; /* search has been stopped */
1914 /* get from LDAP */
1915 values = ldap_get_values(entry->search->session->ld, entry->search->msg, attr);
1916 if (values == NULL)
1917 {
1918 if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
1919 rc = LDAP_UNAVAILABLE;
1920 /* ignore decoding errors as they are just non-existing attribute values */
1921 if (rc == LDAP_DECODING_ERROR)
1922 {
1923 rc = LDAP_SUCCESS;
1924 if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
1925 log_log(LOG_WARNING, "failed to clear the error flag");
1926 }
1927 else if (rc == LDAP_SUCCESS)
1928 {
1929 /* we have a success code but no values, let's try to get ranged
1930 values */
1931 values = myldap_get_ranged_values(entry, attr);
1932 if (values == NULL)
1933 return NULL;
1934 /* store values entry so we can free it later on */
1935 for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
1936 if (entry->buffers[i] == NULL)
1937 {
1938 entry->buffers[i] = values;
1939 return (const char **)entry->buffers[i];
1940 }
1941 /* we found no room to store the values */
1942 log_log(LOG_ERR, "ldap_get_values() couldn't store results, increase MAX_BUFFERS_PER_ENTRY");
1943 free(values);
1944 return NULL;
1945 }
1946 else
1947 myldap_err(LOG_WARNING, entry->search->session->ld, rc,
1948 "ldap_get_values() of attribute \"%s\" on entry \"%s\" returned NULL",
1949 attr, myldap_get_dn(entry));
1950 return NULL;
1951 }
1952 /* store values entry so we can free it later on */
1953 for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
1954 if (entry->attributevalues[i] == NULL)
1955 {
1956 entry->attributevalues[i] = values;
1957 return (const char **)values;
1958 }
1959 /* we found no room to store the entry */
1960 log_log(LOG_ERR, "ldap_get_values() couldn't store results, increase MAX_ATTRIBUTES_PER_ENTRY");
1961 ldap_value_free(values);
1962 return NULL;
1963 }
1964
1965 /* Convert the bervalues to a simple list of strings that can be freed
1966 with one call to free(). */
1967 static const char **bervalues_to_values(struct berval **bvalues)
1968 {
1969 int num_values;
1970 int i;
1971 size_t sz;
1972 char *buf;
1973 char **values;
1974 /* figure out how much memory to allocate */
1975 num_values = ldap_count_values_len(bvalues);
1976 sz = (num_values + 1) * sizeof(char *);
1977 for (i = 0; i < num_values; i++)
1978 sz += bvalues[i]->bv_len + 1;
1979 /* allocate the needed memory */
1980 values = (char **)malloc(sz);
1981 if (values == NULL)
1982 {
1983 log_log(LOG_CRIT, "bervalues_to_values(): malloc() failed to allocate memory");
1984 return NULL;
1985 }
1986 buf = (char *)values;
1987 buf += (num_values + 1) * sizeof(char *);
1988 /* copy from bvalues */
1989 for (i = 0; i < num_values; i++)
1990 {
1991 values[i] = buf;
1992 memcpy(values[i], bvalues[i]->bv_val, bvalues[i]->bv_len);
1993 values[i][bvalues[i]->bv_len] = '\0';
1994 buf += bvalues[i]->bv_len + 1;
1995 }
1996 values[i] = NULL;
1997 return (const char **)values;
1998 }
1999
2000 /* Simple wrapper around ldap_get_values(). */
2001 const char **myldap_get_values_len(MYLDAP_ENTRY *entry, const char *attr)
2002 {
2003 const char **values;
2004 struct berval **bvalues;
2005 int rc;
2006 int i;
2007 /* check parameters */
2008 if (!is_valid_entry(entry))
2009 {
2010 log_log(LOG_ERR, "myldap_get_values_len(): invalid result entry passed");
2011 errno = EINVAL;
2012 return NULL;
2013 }
2014 else if (attr == NULL)
2015 {
2016 log_log(LOG_ERR, "myldap_get_values_len(): invalid attribute name passed");
2017 errno = EINVAL;
2018 return NULL;
2019 }
2020 if (!entry->search->valid)
2021 return NULL; /* search has been stopped */
2022 /* get from LDAP */
2023 bvalues = ldap_get_values_len(entry->search->session->ld, entry->search->msg, attr);
2024 if (bvalues == NULL)
2025 {
2026 if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
2027 rc = LDAP_UNAVAILABLE;
2028 /* ignore decoding errors as they are just non-existing attribute values */
2029 if (rc == LDAP_DECODING_ERROR)
2030 {
2031 rc = LDAP_SUCCESS;
2032 if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
2033 log_log(LOG_WARNING, "failed to clear the error flag");
2034 return NULL;
2035 }
2036 else if (rc == LDAP_SUCCESS)
2037 {
2038 /* we have a success code but no values, let's try to get ranged
2039 values */
2040 values = (const char **)myldap_get_ranged_values(entry, attr);
2041 }
2042 else
2043 {
2044 myldap_err(LOG_WARNING, entry->search->session->ld, rc,
2045 "myldap_get_values_len() of attribute \"%s\" on entry \"%s\" returned NULL",
2046 attr, myldap_get_dn(entry));
2047 return NULL;
2048 }
2049 }
2050 else
2051 {
2052 values = bervalues_to_values(bvalues);
2053 ldap_value_free_len(bvalues);
2054 }
2055 /* check if we got allocated memory */
2056 if (values == NULL)
2057 return NULL;
2058 /* store values entry so we can free it later on */
2059 for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
2060 if (entry->buffers[i] == NULL)
2061 {
2062 entry->buffers[i] = (char **)values;
2063 return values;
2064 }
2065 /* we found no room to store the values */
2066 log_log(LOG_ERR, "myldap_get_values_len() couldn't store results, increase MAX_BUFFERS_PER_ENTRY");
2067 free(values);
2068 return NULL;
2069 }
2070
2071 /* Go over the entries in exploded_rdn and see if any start with
2072 the requested attribute. Return a reference to the value part of
2073 the DN (does not modify exploded_rdn). */
2074 static const char *find_rdn_value(char **exploded_rdn, const char *attr)
2075 {
2076 int i, j;
2077 int l;
2078 if (exploded_rdn == NULL)
2079 return NULL;
2080 /* go over all RDNs */
2081 l = strlen(attr);
2082 for (i = 0; exploded_rdn[i] != NULL; i++)
2083 {
2084 /* check that RDN starts with attr */
2085 if (strncasecmp(exploded_rdn[i], attr, l) != 0)
2086 continue;
2087 j = l;
2088 /* skip spaces */
2089 while (isspace(exploded_rdn[i][j]))
2090 j++;
2091 /* ensure that we found an equals sign now */
2092 if (exploded_rdn[i][j] != '=')
2093 continue;
2094 j++;
2095 /* skip more spaces */
2096 while (isspace(exploded_rdn[i][j]))
2097 j++;
2098 /* ensure that we're not at the end of the string */
2099 if (exploded_rdn[i][j] == '\0')
2100 continue;
2101 /* we found our value */
2102 return exploded_rdn[i] + j;
2103 }
2104 /* fail */
2105 return NULL;
2106 }
2107
2108 /* explode the first part of DN into parts
2109 (e.g. "cn=Test", "uid=test")
2110 The returned value should be freed with ldap_value_free(). */
2111 static char **get_exploded_rdn(const char *dn)
2112 {
2113 char **exploded_dn;
2114 char **exploded_rdn;
2115 /* check if we have a DN */
2116 if ((dn == NULL) || (strcasecmp(dn, "unknown") == 0))
2117 return NULL;
2118 /* explode dn into { "uid=test", "ou=people", ..., NULL } */
2119 exploded_dn = ldap_explode_dn(dn, 0);
2120 if ((exploded_dn == NULL) || (exploded_dn[0] == NULL))
2121 {
2122 log_log(LOG_WARNING, "ldap_explode_dn(%s) returned NULL: %s",
2123 dn, strerror(errno));
2124 return NULL;
2125 }
2126 /* explode rdn (first part of exploded_dn),
2127 e.g. "cn=Test User+uid=testusr" into
2128 { "cn=Test User", "uid=testusr", NULL } */
2129 errno = 0;
2130 exploded_rdn = ldap_explode_rdn(exploded_dn[0], 0);
2131 if ((exploded_rdn == NULL) || (exploded_rdn[0] == NULL))
2132 {
2133 log_log(LOG_WARNING, "ldap_explode_rdn(%s) returned NULL: %s",
2134 exploded_dn[0], strerror(errno));
2135 if (exploded_rdn != NULL)
2136 ldap_value_free(exploded_rdn);
2137 ldap_value_free(exploded_dn);
2138 return NULL;
2139 }
2140 ldap_value_free(exploded_dn);
2141 return exploded_rdn;
2142 }
2143
2144 const char *myldap_get_rdn_value(MYLDAP_ENTRY *entry, const char *attr)
2145 {
2146 /* check parameters */
2147 if (!is_valid_entry(entry))
2148 {
2149 log_log(LOG_ERR, "myldap_get_rdn_value(): invalid result entry passed");
2150 errno = EINVAL;
2151 return NULL;
2152 }
2153 else if (attr == NULL)
2154 {
2155 log_log(LOG_ERR, "myldap_get_rdn_value(): invalid attribute name passed");
2156 errno = EINVAL;
2157 return NULL;
2158 }
2159 /* check if entry contains exploded_rdn */
2160 if (entry->exploded_rdn == NULL)
2161 {
2162 entry->exploded_rdn = get_exploded_rdn(myldap_get_dn(entry));
2163 if (entry->exploded_rdn == NULL)
2164 return NULL;
2165 }
2166 /* find rnd value */
2167 return find_rdn_value(entry->exploded_rdn, attr);
2168 }
2169
2170 const char *myldap_cpy_rdn_value(const char *dn, const char *attr,
2171 char *buf, size_t buflen)
2172 {
2173 char **exploded_rdn;
2174 const char *value;
2175 /* explode dn into { "cn=Test", "uid=test", NULL } */
2176 exploded_rdn = get_exploded_rdn(dn);
2177 if (exploded_rdn == NULL)
2178 return NULL;
2179 /* see if we have a match */
2180 value = find_rdn_value(exploded_rdn, attr);
2181 /* if we have something store it in the buffer */
2182 if ((value != NULL) && (strlen(value) < buflen))
2183 strcpy(buf, value);
2184 else
2185 value = NULL;
2186 /* free allocated stuff */
2187 ldap_value_free(exploded_rdn);
2188 /* check if we have something to return */
2189 return (value != NULL) ? buf : NULL;
2190 }
2191
2192 int myldap_has_objectclass(MYLDAP_ENTRY *entry, const char *objectclass)
2193 {
2194 const char **values;
2195 int i;
2196 if ((!is_valid_entry(entry)) || (objectclass == NULL))
2197 {
2198 log_log(LOG_ERR, "myldap_has_objectclass(): invalid argument passed");
2199 errno = EINVAL;
2200 return 0;
2201 }
2202 values = myldap_get_values(entry, "objectClass");
2203 if (values == NULL)
2204 return 0;
2205 for (i = 0; values[i] != NULL; i++)
2206 {
2207 if (strcasecmp(values[i], objectclass) == 0)
2208 return -1;
2209 }
2210 return 0;
2211 }
2212
2213 #ifdef HAVE_LDAP_PARSE_DEREF_CONTROL
2214 const char ***myldap_get_deref_values(MYLDAP_ENTRY *entry,
2215 const char *derefattr, const char *getattr)
2216 {
2217 LDAPControl **entryctrls;
2218 LDAPDerefRes *deref, *d;
2219 LDAPDerefVal *a;
2220 int i, pass;
2221 int rc;
2222 int found;
2223 int counts[2];
2224 size_t sizes[2], size;
2225 char *buffer = NULL;
2226 char ***results = NULL;
2227 rc = ldap_get_entry_controls(entry->search->session->ld, entry->search->msg,
2228 &entryctrls);
2229 if (rc != LDAP_SUCCESS)
2230 {
2231 myldap_err(LOG_WARNING, entry->search->session->ld, rc,
2232 "ldap_get_entry_controls() failed");
2233 return NULL;
2234 }
2235 if (entryctrls == NULL)
2236 return NULL;
2237 /* see if we can find a deref control */
2238 rc = ldap_parse_deref_control(entry->search->session->ld, entryctrls,
2239 &deref);
2240 if ((rc != LDAP_SUCCESS) || (deref == NULL))
2241 {
2242 if ((rc != LDAP_SUCCESS) && (rc != LDAP_CONTROL_NOT_FOUND))
2243 myldap_err(LOG_WARNING, entry->search->session->ld, rc,
2244 "ldap_parse_deref_control() failed");
2245 /* clear error flag */
2246 rc = LDAP_SUCCESS;
2247 if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER,
2248 &rc) != LDAP_SUCCESS)
2249 log_log(LOG_WARNING, "failed to clear the error flag");
2250 ldap_controls_free(entryctrls);
2251 return NULL;
2252 }
2253 /* two passes: one to calculate size, one to store data */
2254 for (pass=0; pass < 2; pass++)
2255 {
2256 /* reset counters and size */
2257 for (i = 0; i < 2; i++)
2258 {
2259 counts[i] = 0;
2260 sizes[i] = 0;
2261 }
2262 /* go over all deref'd attributes and find the one we're looking for */
2263 for (d = deref; d != NULL; d = d->next)
2264 if ((d->derefAttr != NULL) && (d->derefVal.bv_val != NULL) &&
2265 (strcasecmp(derefattr, d->derefAttr) == 0))
2266 {
2267 /* we should have one d per original attribute value */
2268 found = 0;
2269 /* go over deref'd attribute values to find the ones we're looking for */
2270 for (a = d->attrVals; a != NULL; a = a->next)
2271 if ((a->type != NULL) && (a->vals != NULL) &&
2272 (strcasecmp(getattr, a->type) == 0))
2273 for (i=0; a->vals[i].bv_val != NULL; i++)
2274 {
2275 found = 1;
2276 if (results == NULL)
2277 {
2278 log_log(LOG_DEBUG, "deref %s %s=%s -> %s=%s",
2279 myldap_get_dn(entry), d->derefAttr, d->derefVal.bv_val,
2280 a->type, a->vals[i].bv_val);
2281 counts[0]++;
2282 sizes[0] += strlen(a->vals[i].bv_val) + 1;
2283 }
2284 else
2285 {
2286 strcpy(buffer, a->vals[i].bv_val);
2287 results[0][counts[0]++] = buffer;
2288 buffer += strlen(buffer) + 1;
2289 }
2290 }
2291 if (!found)
2292 {
2293 if (results == NULL)
2294 {
2295 log_log(LOG_DEBUG, "no %s deref %s %s=%s", getattr,
2296 myldap_get_dn(entry), d->derefAttr, d->derefVal.bv_val);
2297 counts[1]++;
2298 sizes[1] += strlen(d->derefVal.bv_val) + 1;
2299 }
2300 else
2301 {
2302 strcpy(buffer, d->derefVal.bv_val);
2303 results[1][counts[1]++] = buffer;
2304 buffer += strlen(buffer) + 1;
2305 }
2306 }
2307 }
2308 /* allocate memory after first pass */
2309 if (results == NULL)
2310 {
2311 size = sizeof(char **) * 3;
2312 for (i = 0; i < 2; i++)
2313 size += sizeof(char *) * (counts[i] + 1);
2314 for (i = 0; i < 2; i++)
2315 size += sizeof(char) * sizes[i];
2316 buffer = (char *)malloc(size);
2317 if (buffer == NULL)
2318 {
2319 log_log(LOG_CRIT, "myldap_get_deref_values(): malloc() failed to allocate memory");
2320 return NULL;
2321 }
2322 /* allocate the list of lists */
2323 results = (void *)buffer;
2324 buffer += sizeof(char **) * 3;
2325 /* allocate the lists */
2326 for (i = 0; i < 2; i++)
2327 {
2328 results[i] = (char **)buffer;
2329 buffer += sizeof(char *) * (counts[i] + 1);
2330 }
2331 results[i] = NULL;
2332 }
2333 }
2334 /* NULL terminate the lists */
2335 results[0][counts[0]] = NULL;
2336 results[1][counts[1]] = NULL;
2337 /* free control data */
2338 ldap_derefresponse_free(deref);
2339 ldap_controls_free(entryctrls);
2340 /* store results so we can free it later on */
2341 for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
2342 if (entry->buffers[i] == NULL)
2343 {
2344 entry->buffers[i] = (void *)results;
2345 return (const char ***)results;
2346 }
2347 /* we found no room to store the values */
2348 log_log(LOG_ERR, "myldap_get_deref_values() couldn't store results, "
2349 "increase MAX_BUFFERS_PER_ENTRY");
2350 free(results);
2351 return NULL;
2352 }
2353 #else /* not HAVE_LDAP_PARSE_DEREF_CONTROL */
2354 const char ***myldap_get_deref_values(MYLDAP_ENTRY UNUSED(*entry),
2355 const char UNUSED(*derefattr), const char UNUSED(*getattr))
2356 {
2357 return NULL;
2358 }
2359 #endif /* not HAVE_LDAP_PARSE_DEREF_CONTROL */
2360
2361 int myldap_escape(const char *src, char *buffer, size_t buflen)
2362 {
2363 size_t pos = 0;
2364 /* go over all characters in source string */
2365 for (; *src != '\0'; src++)
2366 {
2367 /* check if char will fit */
2368 if ((pos + 4) >= buflen)
2369 return -1;
2370 /* do escaping for some characters */
2371 switch (*src)
2372 {
2373 case '*':
2374 strcpy(buffer + pos, "\\2a");
2375 pos += 3;
2376 break;
2377 case '(':
2378 strcpy(buffer + pos, "\\28");
2379 pos += 3;
2380 break;
2381 case ')':
2382 strcpy(buffer + pos, "\\29");
2383 pos += 3;
2384 break;
2385 case '\\':
2386 strcpy(buffer + pos, "\\5c");
2387 pos += 3;
2388 break;
2389 default:
2390 /* just copy character */
2391 buffer[pos++] = *src;
2392 break;
2393 }
2394 }
2395 /* terminate destination string */
2396 buffer[pos] = '\0';
2397 return 0;
2398 }
2399
2400 int myldap_set_debuglevel(int level)
2401 {
2402 int i;
2403 int rc;
2404 /* turn on debugging */
2405 if (level > 1)
2406 {
2407 #ifdef LBER_OPT_LOG_PRINT_FILE
2408 log_log(LOG_DEBUG, "ber_set_option(LBER_OPT_LOG_PRINT_FILE)");
2409 rc = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FILE, stderr);
2410 if (rc != LDAP_SUCCESS)
2411 {
2412 myldap_err(LOG_ERR, NULL, rc, "ber_set_option(LBER_OPT_LOG_PRINT_FILE) failed");
2413 return rc;
2414 }
2415 #endif /* LBER_OPT_LOG_PRINT_FILE */
2416 #ifdef LBER_OPT_DEBUG_LEVEL
2417 if (level > 2)
2418 {
2419 i = -1;
2420 log_log(LOG_DEBUG, "ber_set_option(LBER_OPT_DEBUG_LEVEL,-1)");
2421 rc = ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &i);
2422 if (rc != LDAP_SUCCESS)
2423 {
2424 myldap_err(LOG_ERR, NULL, rc, "ber_set_option(LBER_OPT_DEBUG_LEVEL) failed");
2425 return rc;
2426 }
2427 }
2428 #endif /* LBER_OPT_DEBUG_LEVEL */
2429 #ifdef LDAP_OPT_DEBUG_LEVEL
2430 i = -1;
2431 log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_DEBUG_LEVEL,-1)");
2432 rc = ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &i);
2433 if (rc != LDAP_SUCCESS)
2434 {
2435 myldap_err(LOG_ERR, NULL, rc, "ldap_set_option(LDAP_OPT_DEBUG_LEVEL) failed");
2436 return rc;
2437 }
2438 #endif /* LDAP_OPT_DEBUG_LEVEL */
2439 }
2440 return LDAP_SUCCESS;
2441 }
2442
2443 int myldap_passwd(MYLDAP_SESSION *session,
2444 const char *userdn, const char *oldpassword,
2445 const char *newpasswd)
2446 {
2447 int rc;
2448 struct berval ber_userdn, ber_oldpassword, ber_newpassword, ber_retpassword;
2449 /* check parameters */
2450 if ((session == NULL) || (userdn == NULL) || (newpasswd == NULL))
2451 {
2452 log_log(LOG_ERR, "myldap_passwd(): invalid parameter passed");
2453 errno = EINVAL;
2454 return LDAP_OTHER;
2455 }
2456 /* log the call */
2457 log_log(LOG_DEBUG, "myldap_passwd(userdn=\"%s\",oldpasswd=%s,newpasswd=\"***\")",
2458 userdn, oldpassword ? "\"***\"" : "NULL");
2459 /* translate to ber stuff */
2460 ber_userdn.bv_val = (char *)userdn;
2461 ber_userdn.bv_len = strlen(userdn);
2462 ber_newpassword.bv_val = (char *)newpasswd;
2463 ber_newpassword.bv_len = strlen(newpasswd);
2464 ber_retpassword.bv_val = NULL;
2465 ber_retpassword.bv_len = 0;
2466 /* perform request */
2467 log_log(LOG_DEBUG, "myldap_passwd(): try ldap_passwd_s() without old password");
2468 rc = ldap_passwd_s(session->ld, &ber_userdn, NULL, &ber_newpassword,
2469 &ber_retpassword, NULL, NULL);
2470 if (rc != LDAP_SUCCESS)
2471 myldap_err(LOG_ERR, session->ld, rc, "ldap_passwd_s() without old password failed");
2472 /* free returned data if needed */
2473 if (ber_retpassword.bv_val != NULL)
2474 ldap_memfree(ber_retpassword.bv_val);
2475 if ((rc != LDAP_SUCCESS) && (oldpassword != NULL))
2476 {
2477 /* retry with old password */
2478 log_log(LOG_DEBUG, "myldap_passwd(): try ldap_passwd_s() with old password");
2479 ber_oldpassword.bv_val = (char *)oldpassword;
2480 ber_oldpassword.bv_len = strlen(oldpassword);
2481 /* perform request */
2482 rc = ldap_passwd_s(session->ld, &ber_userdn, &ber_oldpassword,
2483 &ber_newpassword, &ber_retpassword, NULL, NULL);
2484 if (rc != LDAP_SUCCESS)
2485 myldap_err(LOG_ERR, session->ld, rc, "ldap_passwd_s() with old password failed");
2486 /* free returned data if needed */
2487 if (ber_retpassword.bv_val != NULL)
2488 ldap_memfree(ber_retpassword.bv_val);
2489 }
2490 return rc;
2491 }
2492
2493 int myldap_modify(MYLDAP_SESSION *session, const char *dn, LDAPMod * mods[])
2494 {
2495 if ((session == NULL) || (dn == NULL))
2496 {
2497 log_log(LOG_ERR, "myldap_passwd(): invalid parameter passed");
2498 errno = EINVAL;
2499 return LDAP_OTHER;
2500 }
2501 return ldap_modify_ext_s(session->ld, dn, mods, NULL, NULL);
2502 }
2503
2504 int myldap_error_message(MYLDAP_SESSION *session, int rc,
2505 char *buffer, size_t buflen)
2506 {
2507 char *msg_diag = NULL;
2508 if ((session == NULL) || (buffer == NULL) || (buflen <= 0))
2509 {
2510 log_log(LOG_ERR, "myldap_error_message(): invalid parameter passed");
2511 errno = EINVAL;
2512 return LDAP_OTHER;
2513 }
2514 /* clear buffer */
2515 buffer[0] = '\0';
2516 #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
2517 if (session->ld != NULL)
2518 ldap_get_option(session->ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg_diag);
2519 #endif /* LDAP_OPT_DIAGNOSTIC_MESSAGE */
2520 /* return msg_diag or generic error message */
2521 mysnprintf(buffer, buflen - 1, "%s",
2522 ((msg_diag != NULL) && (msg_diag[0]!='\0')) ?
2523 msg_diag : ldap_err2string(rc));
2524 /* free diagnostic message */
2525 if (msg_diag != NULL)
2526 ldap_memfree(msg_diag);
2527 return LDAP_SUCCESS;
2528 }