1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012 Milan Jurik. All rights reserved.
25 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
26 */
27
28 #define __STANDALONE_MODULE__
29
30 #include <stdio.h>
31 #include <sys/types.h>
32 #include <stdlib.h>
33 #include <libintl.h>
34 #include <string.h>
35 #include <ctype.h>
36
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <syslog.h>
41 #include <locale.h>
42 #include <errno.h>
43 #include <sys/time.h>
44
45 #include <arpa/inet.h>
46 #include <netdb.h>
47 #include <strings.h>
48
49 #include <thread.h>
50
51 #include <nsswitch.h>
52 #include <nss_dbdefs.h>
53 #include <nss.h>
54
55 #include "ns_cache_door.h"
56 #include "ns_internal.h"
57 #include "ns_connmgmt.h"
58
59 typedef enum {
60 INFO_SERVER_JUST_INITED = -1,
61 INFO_SERVER_UNKNOWN = 0,
62 INFO_SERVER_CONNECTING = 1,
63 INFO_SERVER_UP = 2,
64 INFO_SERVER_ERROR = 3,
65 INFO_SERVER_REMOVED = 4
66 } dir_server_status_t;
67
68 typedef enum {
69 INFO_STATUS_NEW = 2,
70 INFO_STATUS_OLD = 3
71 } dir_server_info_t;
72
73 typedef struct dir_server {
74 char *ip;
75 char **controls;
76 char **saslMech;
77 dir_server_status_t status;
78 mutex_t updateStatus;
79 dir_server_info_t info;
80 } dir_server_t;
81
82 typedef struct dir_server_list {
83 dir_server_t **nsServers;
84
85 rwlock_t listDestroyLock;
86 } dir_server_list_t;
87
88 struct {
89 /* The local list of the directory servers' root DSEs. */
90 dir_server_list_t *list;
91 /* The flag indicating if libsldap is in the 'Standalone' mode. */
92 int standalone;
93 /*
94 * The mutex ensuring that only one thread performs
95 * the initialization of the list.
96 */
97 mutex_t listReplaceLock;
98 /*
99 * A flag indicating that a particular thread is
100 * in the 'ldap_cachemgr' mode. It is stored by thread as
101 * a thread specific data.
102 */
103 const int initFlag;
104 /*
105 * A thread specific key storing
106 * the the 'ldap_cachemgr' mode indicator.
107 */
108 thread_key_t standaloneInitKey;
109 } dir_servers = {NULL, 0, DEFAULTMUTEX, '1'};
110
111 typedef struct switchDatabase {
112 char *conf;
113 uint32_t alloced;
114 } switch_database_t;
115
116 static thread_key_t switchConfigKey;
117
118 #pragma init(createStandaloneKey)
119
120 #define DONT_INCLUDE_ATTR_NAMES 0
121 #define INCLUDE_ATTR_NAMES 1
122 #define IS_PROFILE 1
123 #define NOT_PROFILE 0
124 /* INET6_ADDRSTRLEN + ":" + <5-digit port> + some round-up */
125 #define MAX_HOSTADDR_LEN (INET6_ADDRSTRLEN + 6 + 12)
126
127 static
128 void
switch_conf_disposer(void * data)129 switch_conf_disposer(void *data)
130 {
131 switch_database_t *localData = (switch_database_t *)data;
132
133 free(localData->conf);
134 free(localData);
135 }
136
137 /*
138 * This function initializes an indication that a thread obtaining a root DSE
139 * will be switched to the 'ldap_cachemgr' mode. Within the thread libsldap
140 * will not invoke the __s_api_requestServer function. Instead, the library
141 * will establish a connection to the server specified by
142 * the __ns_ldap_getRootDSE function.
143 * Since ldap_cachmgr can obtain a DUAProfile and root DSEs at the same time
144 * and we do not want to affect a thread obtaining a DUAProfile,
145 * the 'ldap_cachemgr' mode is thread private.
146 * In addition, this function creates a key holding temporary configuration
147 * for the "hosts" and "ipnodes" databases which is used by the "SKIPDB"
148 * mechanism (__s_api_ip2hostname() & _s_api_hostname2ip()).
149 */
150 static
151 void
createStandaloneKey()152 createStandaloneKey()
153 {
154 if (thr_keycreate(&dir_servers.standaloneInitKey, NULL) != 0) {
155 syslog(LOG_ERR, gettext("libsldap: unable to create a thread "
156 "key needed for sharing ldap connections"));
157 }
158 if (thr_keycreate(&switchConfigKey, switch_conf_disposer) != 0) {
159 syslog(LOG_ERR, gettext("libsldap: unable to create a thread "
160 "key containing current nsswitch configuration"));
161 }
162 }
163
164 /*
165 * This function sets the 'ldap_cachemgr' mode indication.
166 */
167 void
__s_api_setInitMode()168 __s_api_setInitMode()
169 {
170 (void) thr_setspecific(dir_servers.standaloneInitKey,
171 (void *) &dir_servers.initFlag);
172 }
173
174 /*
175 * This function unset the 'ldap_cachemgr' mode indication.
176 */
177 void
__s_api_unsetInitMode()178 __s_api_unsetInitMode()
179 {
180 (void) thr_setspecific(dir_servers.standaloneInitKey, NULL);
181 }
182
183 /*
184 * This function checks if the 'ldap_cachemgr' mode indication is set.
185 */
186 int
__s_api_isInitializing()187 __s_api_isInitializing() {
188 int *flag = NULL;
189
190 (void) thr_getspecific(dir_servers.standaloneInitKey, (void **) &flag);
191
192 return (flag != NULL && *flag == dir_servers.initFlag);
193 }
194
195 /*
196 * This function checks if the process runs in the 'Standalone' mode.
197 * In this mode libsldap will check the local, process private list of root DSEs
198 * instead of requesting them via a door call to ldap_cachemgr.
199 */
200 int
__s_api_isStandalone()201 __s_api_isStandalone()
202 {
203 int mode;
204
205 (void) mutex_lock(&dir_servers.listReplaceLock);
206 mode = dir_servers.standalone;
207 (void) mutex_unlock(&dir_servers.listReplaceLock);
208
209 return (mode);
210 }
211
212
213 static
214 int
remove_ldap(char * dst,char * src,int dst_buf_len)215 remove_ldap(char *dst, char *src, int dst_buf_len)
216 {
217 int i = 0;
218
219 if (strlen(src) >= dst_buf_len)
220 return (0);
221
222 while (*src != '\0') {
223 /* Copy up to one space from source. */
224 if (isspace(*src)) {
225 dst[i++] = *src;
226 while (isspace(*src))
227 src++;
228 }
229
230 /* If not "ldap", just copy. */
231 if (strncmp(src, "ldap", 4) != 0) {
232 while (!isspace(*src)) {
233 dst[i++] = *src++;
234 /* At the end of string? */
235 if (dst[i-1] == '\0')
236 return (1);
237 }
238 /* Copy up to one space from source. */
239 if (isspace(*src)) {
240 dst[i++] = *src;
241 while (isspace(*src))
242 src++;
243 }
244 /* Copy also the criteria section */
245 if (*src == '[')
246 while (*src != ']') {
247 dst[i++] = *src++;
248 /* Shouln't happen if format is right */
249 if (dst[i-1] == '\0')
250 return (1);
251 }
252 }
253
254 /* If next part is ldap, skip over it ... */
255 if (strncmp(src, "ldap", 4) == 0) {
256 if (isspace(*(src+4)) || *(src+4) == '\0') {
257 src += 4;
258 while (isspace(*src))
259 src++;
260 if (*src == '[') {
261 while (*src++ != ']') {
262 /*
263 * See comment above about
264 * correct format.
265 */
266 if (*src == '\0') {
267 dst[i++] = '\0';
268 return (1);
269 }
270 }
271 }
272 while (isspace(*src))
273 src++;
274 }
275 }
276 if (*src == '\0')
277 dst[i++] = '\0';
278 }
279
280 return (1);
281 }
282
283 static
284 char *
get_db(const char * db_name)285 get_db(const char *db_name)
286 {
287 char *ptr;
288 switch_database_t *hostService = NULL;
289 FILE *fp = fopen(__NSW_CONFIG_FILE, "rF");
290 char *linep, line[NSS_BUFSIZ];
291
292 if (fp == NULL) {
293 syslog(LOG_WARNING, gettext("libsldap: can not read %s"),
294 __NSW_CONFIG_FILE);
295 return (NULL);
296 }
297
298 while ((linep = fgets(line, NSS_BUFSIZ, fp)) != NULL) {
299 while (isspace(*linep)) {
300 ++linep;
301 }
302 if (*linep == '#') {
303 continue;
304 }
305 if (strncmp(linep, db_name, strlen(db_name)) != 0) {
306 continue;
307 }
308 if ((linep = strchr(linep, ':')) != NULL) {
309 if (linep[strlen(linep) - 1] == '\n') {
310 linep[strlen(linep) - 1] = '\0';
311 }
312
313 while (isspace(*++linep))
314 ;
315
316 if ((ptr = strchr(linep, '#')) != NULL) {
317 while (--ptr >= linep && isspace(*ptr))
318 ;
319 *(ptr + 1) = '\0';
320 }
321
322 if (strlen(linep) == 0) {
323 continue;
324 }
325 break;
326 }
327 }
328
329 (void) fclose(fp);
330
331 if (linep == NULL) {
332 syslog(LOG_WARNING,
333 gettext("libsldap: the %s database "
334 "is missing from %s"),
335 db_name,
336 __NSW_CONFIG_FILE);
337 return (NULL);
338 }
339
340 (void) thr_getspecific(switchConfigKey, (void **) &hostService);
341 if (hostService == NULL) {
342 hostService = calloc(1, sizeof (switch_database_t));
343 if (hostService == NULL) {
344 return (NULL);
345 }
346 (void) thr_setspecific(switchConfigKey, hostService);
347 }
348
349 /*
350 * In a long-living process threads can perform several
351 * getXbyY requests. And the windows between those requests
352 * can be long. The nsswitch configuration can change from time
353 * to time. So instead of allocating/freeing memory every time
354 * the API is called, reallocate memory only when the current
355 * configuration for the database being used is longer than
356 * the previous one.
357 */
358 if (strlen(linep) >= hostService->alloced) {
359 ptr = (char *)realloc((void *)hostService->conf,
360 strlen(linep) + 1);
361 if (ptr == NULL) {
362 free((void *)hostService->conf);
363 hostService->conf = NULL;
364 hostService->alloced = 0;
365 return (NULL);
366 }
367 bzero(ptr, strlen(linep) + 1);
368 hostService->conf = ptr;
369 hostService->alloced = strlen(linep) + 1;
370 }
371
372 if (remove_ldap(hostService->conf, linep, hostService->alloced))
373 return (hostService->conf);
374 else
375 return (NULL);
376 }
377
378 static
379 void
_initf_ipnodes(nss_db_params_t * p)380 _initf_ipnodes(nss_db_params_t *p)
381 {
382 char *services = get_db("ipnodes");
383
384 p->name = NSS_DBNAM_IPNODES;
385 p->flags |= NSS_USE_DEFAULT_CONFIG;
386 p->default_config = services == NULL ? "" : services;
387 }
388
389 static
390 void
_initf_hosts(nss_db_params_t * p)391 _initf_hosts(nss_db_params_t *p)
392 {
393 char *services = get_db("hosts");
394
395 p->name = NSS_DBNAM_HOSTS;
396 p->flags |= NSS_USE_DEFAULT_CONFIG;
397 p->default_config = services == NULL ? "" : services;
398 }
399
400 /*
401 * This function is an analog of the standard gethostbyaddr_r()
402 * function with an exception that it removes the 'ldap' back-end
403 * (if any) from the host/ipnodes nsswitch's databases and then
404 * looks up using remaining back-ends.
405 */
406 static
407 struct hostent *
_filter_gethostbyaddr_r(const char * addr,int len,int type,struct hostent * result,char * buffer,int buflen,int * h_errnop)408 _filter_gethostbyaddr_r(const char *addr, int len, int type,
409 struct hostent *result, char *buffer, int buflen,
410 int *h_errnop)
411 {
412 DEFINE_NSS_DB_ROOT(db_root_hosts);
413 DEFINE_NSS_DB_ROOT(db_root_ipnodes);
414 nss_XbyY_args_t arg;
415 nss_status_t res;
416 int (*str2ent)();
417 void (*nss_initf)();
418 nss_db_root_t *nss_db_root;
419 int dbop;
420
421 switch (type) {
422 case AF_INET:
423 str2ent = str2hostent;
424 nss_initf = _initf_hosts;
425 nss_db_root = &db_root_hosts;
426 dbop = NSS_DBOP_HOSTS_BYADDR;
427 break;
428 case AF_INET6:
429 str2ent = str2hostent6;
430 nss_initf = _initf_ipnodes;
431 nss_db_root = &db_root_ipnodes;
432 dbop = NSS_DBOP_IPNODES_BYADDR;
433 default:
434 return (NULL);
435 }
436
437 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent);
438
439 arg.key.hostaddr.addr = addr;
440 arg.key.hostaddr.len = len;
441 arg.key.hostaddr.type = type;
442 arg.stayopen = 0;
443 arg.h_errno = NETDB_SUCCESS;
444
445 res = nss_search(nss_db_root, nss_initf, dbop, &arg);
446 arg.status = res;
447 *h_errnop = arg.h_errno;
448 return (struct hostent *)NSS_XbyY_FINI(&arg);
449 }
450
451 /*
452 * This routine is an analog of gethostbyaddr_r().
453 * But in addition __s_api_hostname2ip() performs the "LDAP SKIPDB" activity
454 * prior to querying the name services.
455 * If the buffer is not big enough to accommodate a returning data,
456 * NULL is returned and h_errnop is set to TRY_AGAIN.
457 */
458 struct hostent *
__s_api_hostname2ip(const char * name,struct hostent * result,char * buffer,int buflen,int * h_errnop)459 __s_api_hostname2ip(const char *name,
460 struct hostent *result, char *buffer, int buflen,
461 int *h_errnop)
462 {
463 DEFINE_NSS_DB_ROOT(db_root_ipnodes);
464 DEFINE_NSS_DB_ROOT(db_root_hosts);
465 nss_XbyY_args_t arg;
466 nss_status_t res;
467 struct in_addr addr;
468 struct in6_addr addr6;
469
470 if (inet_pton(AF_INET, name, &addr) > 0) {
471 if (buflen < strlen(name) + 1 +
472 sizeof (char *) * 2 + /* The h_aliases member */
473 sizeof (struct in_addr) +
474 sizeof (struct in_addr *) * 2) {
475 *h_errnop = TRY_AGAIN;
476 return (NULL);
477 }
478
479 result->h_addrtype = AF_INET;
480 result->h_length = sizeof (struct in_addr);
481 (void) strncpy(buffer, name, buflen);
482
483 result->h_addr_list = (char **)ROUND_UP(
484 buffer + strlen(name) + 1,
485 sizeof (char *));
486 result->h_aliases = (char **)ROUND_UP(result->h_addr_list,
487 sizeof (char *));
488 result->h_aliases[0] = buffer;
489 result->h_aliases[1] = NULL;
490 bcopy(&addr,
491 buffer + buflen - sizeof (struct in_addr),
492 sizeof (struct in_addr));
493 result->h_addr_list[0] = buffer + buflen -
494 sizeof (struct in_addr);
495 result->h_addr_list[1] = NULL;
496 result->h_aliases = result->h_addr_list;
497 result->h_name = buffer;
498
499 *h_errnop = NETDB_SUCCESS;
500 return (result);
501 }
502 if (inet_pton(AF_INET6, name, &addr6) > 0) {
503 if (buflen < strlen(name) + 1 +
504 sizeof (char *) * 2 + /* The h_aliases member */
505 sizeof (struct in6_addr) +
506 sizeof (struct in6_addr *) * 2) {
507 *h_errnop = TRY_AGAIN;
508 return (NULL);
509 }
510
511 result->h_addrtype = AF_INET6;
512 result->h_length = sizeof (struct in6_addr);
513 (void) strncpy(buffer, name, buflen);
514
515 result->h_addr_list = (char **)ROUND_UP(
516 buffer + strlen(name) + 1,
517 sizeof (char *));
518 result->h_aliases = (char **)ROUND_UP(result->h_addr_list,
519 sizeof (char *));
520 result->h_aliases[0] = buffer;
521 result->h_aliases[1] = NULL;
522 bcopy(&addr6,
523 buffer + buflen - sizeof (struct in6_addr),
524 sizeof (struct in6_addr));
525 result->h_addr_list[0] = buffer + buflen -
526 sizeof (struct in6_addr);
527 result->h_addr_list[1] = NULL;
528 result->h_aliases = result->h_addr_list;
529 result->h_name = buffer;
530
531 *h_errnop = NETDB_SUCCESS;
532 return (result);
533 }
534
535 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
536
537 arg.key.name = name;
538 arg.stayopen = 0;
539 arg.h_errno = NETDB_SUCCESS;
540
541 res = nss_search(&db_root_ipnodes, _initf_ipnodes,
542 NSS_DBOP_IPNODES_BYNAME, &arg);
543 if (res == NSS_NOTFOUND || res == NSS_UNAVAIL) {
544 arg.h_errno = NETDB_SUCCESS;
545 res = nss_search(&db_root_hosts, _initf_hosts,
546 NSS_DBOP_HOSTS_BYNAME, &arg);
547 }
548 arg.status = res;
549 *h_errnop = arg.h_errno;
550 return ((struct hostent *)NSS_XbyY_FINI(&arg));
551 }
552
553 /*
554 * Convert an IP to a host name.
555 */
556 ns_ldap_return_code
__s_api_ip2hostname(char * ipaddr,char ** hostname)557 __s_api_ip2hostname(char *ipaddr, char **hostname) {
558 struct in_addr in;
559 struct in6_addr in6;
560 struct hostent *hp = NULL, hostEnt;
561 char buffer[NSS_BUFLEN_HOSTS];
562 int buflen = NSS_BUFLEN_HOSTS;
563 char *start = NULL,
564 *end = NULL,
565 delim = '\0';
566 char *port = NULL,
567 *addr = NULL;
568 int errorNum = 0,
569 len = 0;
570
571 if (ipaddr == NULL || hostname == NULL)
572 return (NS_LDAP_INVALID_PARAM);
573 *hostname = NULL;
574 if ((addr = strdup(ipaddr)) == NULL)
575 return (NS_LDAP_MEMORY);
576
577 if (addr[0] == '[') {
578 /*
579 * Assume it's [ipv6]:port
580 * Extract ipv6 IP
581 */
582 start = &addr[1];
583 if ((end = strchr(addr, ']')) != NULL) {
584 *end = '\0';
585 delim = ']';
586 if (*(end + 1) == ':')
587 /* extract port */
588 port = end + 2;
589 } else {
590 free(addr);
591 return (NS_LDAP_INVALID_PARAM);
592 }
593 } else if ((end = strchr(addr, ':')) != NULL) {
594 /* assume it's ipv4:port */
595 *end = '\0';
596 delim = ':';
597 start = addr;
598 port = end + 1;
599 } else
600 /* No port */
601 start = addr;
602
603
604 if (inet_pton(AF_INET, start, &in) == 1) {
605 /* IPv4 */
606 hp = _filter_gethostbyaddr_r((char *)&in,
607 sizeof (in.s_addr),
608 AF_INET,
609 &hostEnt,
610 buffer,
611 buflen,
612 &errorNum);
613 if (hp && hp->h_name) {
614 /* hostname + '\0' */
615 len = strlen(hp->h_name) + 1;
616 if (port)
617 /* ':' + port */
618 len += strlen(port) + 1;
619 if ((*hostname = malloc(len)) == NULL) {
620 free(addr);
621 return (NS_LDAP_MEMORY);
622 }
623
624 if (port)
625 (void) snprintf(*hostname, len, "%s:%s",
626 hp->h_name, port);
627 else
628 (void) strlcpy(*hostname, hp->h_name, len);
629
630 free(addr);
631 return (NS_LDAP_SUCCESS);
632 } else {
633 free(addr);
634 return (NS_LDAP_NOTFOUND);
635 }
636 } else if (inet_pton(AF_INET6, start, &in6) == 1) {
637 /* IPv6 */
638 hp = _filter_gethostbyaddr_r((char *)&in6,
639 sizeof (in6.s6_addr),
640 AF_INET6,
641 &hostEnt,
642 buffer,
643 buflen,
644 &errorNum);
645 if (hp && hp->h_name) {
646 /* hostname + '\0' */
647 len = strlen(hp->h_name) + 1;
648 if (port)
649 /* ':' + port */
650 len += strlen(port) + 1;
651 if ((*hostname = malloc(len)) == NULL) {
652 free(addr);
653 return (NS_LDAP_MEMORY);
654 }
655
656 if (port)
657 (void) snprintf(*hostname, len, "%s:%s",
658 hp->h_name, port);
659 else
660 (void) strlcpy(*hostname, hp->h_name, len);
661
662 free(addr);
663 return (NS_LDAP_SUCCESS);
664 } else {
665 free(addr);
666 return (NS_LDAP_NOTFOUND);
667 }
668 } else {
669 /*
670 * A hostname
671 * Return it as is
672 */
673 if (end)
674 *end = delim;
675 *hostname = addr;
676 return (NS_LDAP_SUCCESS);
677 }
678 }
679
680 /*
681 * This function obtains data returned by an LDAP search request and puts it
682 * in a string in the ldap_cachmgr(1) door call format.
683 *
684 * INPUT:
685 * ld - a pointer to an LDAP structure used for a search operation,
686 * result_msg - a pointer to an LDAPMessage returned by the search,
687 * include_names - if set to INCLUDE_ATTR_NAMES, the output buffer will
688 * contain attribute names.
689 * Otherwise, only values will be return.
690 *
691 * OUTPUT:
692 * a buffer containing server info in the following format:
693 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
694 * Should be free'ed by the caller.
695 */
696 static
697 ns_ldap_return_code
convert_to_door_line(LDAP * ld,LDAPMessage * result_msg,int include_names,int is_profile,char ** door_line)698 convert_to_door_line(LDAP* ld,
699 LDAPMessage *result_msg,
700 int include_names,
701 int is_profile,
702 char **door_line)
703 {
704 uint32_t total_length = 0, attr_len = 0, i;
705 LDAPMessage *e;
706 char *a, **vals;
707 BerElement *ber;
708 int seen_objectclass = 0, rewind = 0;
709
710 if (!door_line) {
711 return (NS_LDAP_INVALID_PARAM);
712 }
713 *door_line = NULL;
714
715 if ((e = ldap_first_entry(ld, result_msg)) == NULL) {
716 return (NS_LDAP_NOTFOUND);
717 }
718
719 /* calculate length of received data */
720 for (a = ldap_first_attribute(ld, e, &ber);
721 a != NULL;
722 a = ldap_next_attribute(ld, e, ber)) {
723
724 if ((vals = ldap_get_values(ld, e, a)) != NULL) {
725 for (i = 0; vals[i] != NULL; i++) {
726 total_length += (include_names ?
727 strlen(a) : 0) +
728 strlen(vals[i]) +
729 strlen(DOORLINESEP) +1;
730 }
731 ldap_value_free(vals);
732 }
733 ldap_memfree(a);
734 }
735 if (ber != NULL) {
736 ber_free(ber, 0);
737 }
738
739 if (total_length == 0) {
740 return (NS_LDAP_NOTFOUND);
741 }
742
743 /* copy the data */
744 /* add 1 for the last '\0' */
745 *door_line = (char *)malloc(total_length + 1);
746 if (*door_line == NULL) {
747 return (NS_LDAP_MEMORY);
748 }
749
750 /* make it an empty string first */
751 **door_line = '\0';
752 a = ldap_first_attribute(ld, e, &ber);
753 while (a != NULL) {
754 if (is_profile) {
755 /*
756 * If we're processing DUAConfigProfile, we need to make
757 * sure we put objectclass attribute first.
758 * __s_api_create_config_door_str depends on that.
759 */
760 if (seen_objectclass) {
761 if (strcasecmp(a, "objectclass") == 0) {
762 /* Skip objectclass now. */
763 a = ldap_next_attribute(ld, e, ber);
764 continue;
765 }
766 } else {
767 if (strcasecmp(a, "objectclass") == 0) {
768 seen_objectclass = 1;
769 rewind = 1;
770 } else {
771 /* Skip all but objectclass first. */
772 a = ldap_next_attribute(ld, e, ber);
773 continue;
774 }
775 }
776 }
777
778 if ((vals = ldap_get_values(ld, e, a)) != NULL) {
779 for (i = 0; vals[i] != NULL; i++) {
780 if (include_names) {
781 attr_len += strlen(a);
782 }
783 attr_len += strlen(vals[i]) +
784 strlen(DOORLINESEP) + 2;
785 if (include_names) {
786 (void) snprintf(*door_line +
787 strlen(*door_line),
788 attr_len,
789 "%s=%s%s",
790 a, vals[i],
791 DOORLINESEP);
792 } else {
793 (void) snprintf(*door_line +
794 strlen(*door_line),
795 attr_len,
796 "%s%s",
797 vals[i],
798 DOORLINESEP);
799 }
800 }
801 ldap_value_free(vals);
802 }
803 ldap_memfree(a);
804
805 /* Rewind */
806 if (rewind) {
807 if (ber != NULL) {
808 ber_free(ber, 0);
809 }
810 a = ldap_first_attribute(ld, e, &ber);
811 rewind = 0;
812 } else {
813 a = ldap_next_attribute(ld, e, ber);
814 }
815 }
816 if (ber != NULL) {
817 ber_free(ber, 0);
818 }
819
820 if (e != result_msg) {
821 (void) ldap_msgfree(e);
822 }
823
824 return (NS_LDAP_SUCCESS);
825 }
826
827 /*
828 * This function looks up the base DN of a directory serving
829 * a specified domain name.
830 *
831 * INPUT:
832 * ld - a pointer to an LDAP structure used for the search operation,
833 * domain_name - the name of a domain.
834 *
835 * OUTPUT:
836 * a buffer containing a directory's base DN found.
837 * Should be free'ed by the caller.
838 */
839 static
840 ns_ldap_return_code
getDirBaseDN(LDAP * ld,const char * domain_name,char ** dir_base_dn)841 getDirBaseDN(LDAP *ld, const char *domain_name, char **dir_base_dn)
842 {
843 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
844 char *attrs[2], *DNlist, *rest, *ptr;
845 char filter[BUFSIZ], *a = NULL;
846 int ldap_rc;
847 LDAPMessage *resultMsg = NULL;
848 ns_ldap_return_code ret_code;
849
850 /* Get the whole list of naming contexts residing on the server */
851 attrs[0] = "namingcontexts";
852 attrs[1] = NULL;
853 ldap_rc = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
854 attrs, 0, NULL, NULL, &tv, 0, &resultMsg);
855 switch (ldap_rc) {
856 /* If successful, the root DSE was found. */
857 case LDAP_SUCCESS:
858 break;
859 /*
860 * If the root DSE was not found, the server does
861 * not comply with the LDAP v3 protocol.
862 */
863 default:
864 if (resultMsg) {
865 (void) ldap_msgfree(resultMsg);
866 resultMsg = NULL;
867 }
868
869 return (NS_LDAP_OP_FAILED);
870 }
871
872 if ((ret_code = convert_to_door_line(ld,
873 resultMsg,
874 DONT_INCLUDE_ATTR_NAMES,
875 NOT_PROFILE,
876 &DNlist)) != NS_LDAP_SUCCESS) {
877 if (resultMsg) {
878 (void) ldap_msgfree(resultMsg);
879 resultMsg = NULL;
880 }
881 return (ret_code);
882 }
883
884 if (resultMsg) {
885 (void) ldap_msgfree(resultMsg);
886 resultMsg = NULL;
887 }
888
889 if (DNlist == NULL ||
890 (ptr = strtok_r(DNlist, DOORLINESEP, &rest)) == NULL) {
891 return (NS_LDAP_NOTFOUND);
892 }
893 attrs[0] = "dn";
894 do {
895 /*
896 * For each context try to find a NIS domain object
897 * which 'nisdomain' attribute's value matches the domain name
898 */
899 (void) snprintf(filter,
900 BUFSIZ,
901 "(&(objectclass=nisDomainObject)"
902 "(nisdomain=%s))",
903 domain_name);
904 ldap_rc = ldap_search_ext_s(ld,
905 ptr,
906 LDAP_SCOPE_SUBTREE,
907 filter,
908 attrs,
909 0,
910 NULL,
911 NULL,
912 &tv,
913 0,
914 &resultMsg);
915 if (ldap_rc != LDAP_SUCCESS) {
916 if (resultMsg) {
917 (void) ldap_msgfree(resultMsg);
918 resultMsg = NULL;
919 }
920 continue;
921 }
922 if ((a = ldap_get_dn(ld, resultMsg)) != NULL) {
923 *dir_base_dn = strdup(a);
924 ldap_memfree(a);
925
926 if (resultMsg) {
927 (void) ldap_msgfree(resultMsg);
928 resultMsg = NULL;
929 }
930
931 if (!*dir_base_dn) {
932 free(DNlist);
933 return (NS_LDAP_MEMORY);
934 }
935 break;
936 }
937
938 if (resultMsg) {
939 (void) ldap_msgfree(resultMsg);
940 resultMsg = NULL;
941 }
942 } while (ptr = strtok_r(NULL, DOORLINESEP, &rest));
943
944 free(DNlist);
945
946 if (!*dir_base_dn) {
947 return (NS_LDAP_NOTFOUND);
948 }
949
950 return (NS_LDAP_SUCCESS);
951 }
952
953 /*
954 * This function parses the results of a search operation
955 * requesting a DUAProfile.
956 *
957 * INPUT:
958 * ld - a pointer to an LDAP structure used for the search operation,
959 * dir_base_dn - the name of a directory's base DN,
960 * profile_name - the name of a DUAProfile to be obtained.
961 *
962 * OUTPUT:
963 * a buffer containing the DUAProfile in the following format:
964 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
965 * Should be free'ed by the caller.
966 */
967 static
968 ns_ldap_return_code
getDUAProfile(LDAP * ld,const char * dir_base_dn,const char * profile_name,char ** profile)969 getDUAProfile(LDAP *ld,
970 const char *dir_base_dn,
971 const char *profile_name,
972 char **profile)
973 {
974 char searchBaseDN[BUFSIZ], filter[BUFSIZ];
975 LDAPMessage *resultMsg = NULL;
976 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
977 int ldap_rc;
978 ns_ldap_return_code ret_code;
979
980 (void) snprintf(searchBaseDN, BUFSIZ, "ou=profile,%s", dir_base_dn);
981 (void) snprintf(filter,
982 BUFSIZ,
983 _PROFILE_FILTER,
984 _PROFILE1_OBJECTCLASS,
985 _PROFILE2_OBJECTCLASS,
986 profile_name);
987 ldap_rc = ldap_search_ext_s(ld,
988 searchBaseDN,
989 LDAP_SCOPE_SUBTREE,
990 filter,
991 NULL,
992 0,
993 NULL,
994 NULL,
995 &tv,
996 0,
997 &resultMsg);
998
999 switch (ldap_rc) {
1000 /* If successful, the DUA profile was found. */
1001 case LDAP_SUCCESS:
1002 break;
1003 /*
1004 * If the root DSE was not found, the server does
1005 * not comply with the LDAP v3 protocol.
1006 */
1007 default:
1008 if (resultMsg) {
1009 (void) ldap_msgfree(resultMsg);
1010 resultMsg = NULL;
1011 }
1012
1013 return (NS_LDAP_OP_FAILED);
1014 }
1015
1016 ret_code = convert_to_door_line(ld,
1017 resultMsg,
1018 INCLUDE_ATTR_NAMES,
1019 IS_PROFILE,
1020 profile);
1021 if (resultMsg) {
1022 (void) ldap_msgfree(resultMsg);
1023 resultMsg = NULL;
1024 }
1025 return (ret_code);
1026 }
1027
1028 /*
1029 * This function derives the directory's base DN from a provided domain name.
1030 *
1031 * INPUT:
1032 * domain_name - the name of a domain to be converted into a base DN,
1033 * buffer - contains the derived base DN,
1034 * buf_len - the length of the buffer.
1035 *
1036 * OUTPUT:
1037 * The function returns the address of the buffer or NULL.
1038 */
1039 static
1040 char *
domainname2baseDN(char * domain_name,char * buffer,uint16_t buf_len)1041 domainname2baseDN(char *domain_name, char *buffer, uint16_t buf_len)
1042 {
1043 char *nextDC, *chr;
1044 uint16_t i, length;
1045
1046 if (!domain_name || !buffer || buf_len == 0) {
1047 return (NULL);
1048 }
1049
1050 buffer[0] = '\0';
1051 nextDC = chr = domain_name;
1052 length = strlen(domain_name);
1053 for (i = 0; i < length + 1; ++i, ++chr) {
1054 /* Simply replace dots with "dc=" */
1055 if (*chr != '.' && *chr != '\0') {
1056 continue;
1057 }
1058 *chr = '\0';
1059 if (strlcat(buffer, "dc=", buf_len) >= buf_len)
1060 return (NULL);
1061 if (strlcat(buffer, nextDC, buf_len) >= buf_len)
1062 return (NULL);
1063 if (i < length) {
1064 /*
1065 * The end of the domain name
1066 * has not been reached yet
1067 */
1068 if (strlcat(buffer, ",", buf_len) >= buf_len)
1069 return (NULL);
1070 nextDC = chr + 1;
1071 *chr = '.';
1072 }
1073 }
1074
1075 return (buffer);
1076 }
1077
1078 /*
1079 * This function obtains the directory's base DN and a DUAProfile
1080 * from a specified server.
1081 *
1082 * INPUT:
1083 * server - a structure describing a server to connect to and
1084 * a DUAProfile to be obtained from the server,
1085 * cred - credentials to be used during establishing connections to
1086 * the server.
1087 *
1088 * OUTPUT:
1089 * dua_profile - a buffer containing the DUAProfile in the following format:
1090 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
1091 * dir_base_dn - a buffer containing the base DN,
1092 * errorp - an error object describing an error, if any.
1093 *
1094 * All the output data structures should be free'ed by the caller.
1095 */
1096 ns_ldap_return_code
__ns_ldap_getConnectionInfoFromDUA(const ns_dir_server_t * server,const ns_cred_t * cred,char ** dua_profile,char ** dir_base_dn,ns_ldap_error_t ** errorp)1097 __ns_ldap_getConnectionInfoFromDUA(const ns_dir_server_t *server,
1098 const ns_cred_t *cred,
1099 char **dua_profile,
1100 char **dir_base_dn,
1101 ns_ldap_error_t **errorp)
1102 {
1103 char serverAddr[MAX_HOSTADDR_LEN];
1104 char *dirBaseDN = NULL, *duaProfile = NULL;
1105 ns_cred_t default_cred;
1106 ns_ldap_return_code ret_code;
1107
1108 ns_config_t *config_struct = __s_api_create_config();
1109 ConnectionID sessionId = 0;
1110 Connection *session = NULL;
1111 char errmsg[MAXERROR];
1112 char buffer[NSS_BUFLEN_HOSTS];
1113 ns_conn_user_t *cu = NULL;
1114
1115 if (errorp == NULL) {
1116 __s_api_destroy_config(config_struct);
1117 return (NS_LDAP_INVALID_PARAM);
1118 }
1119
1120 *errorp = NULL;
1121
1122 if (server == NULL) {
1123 __s_api_destroy_config(config_struct);
1124 return (NS_LDAP_INVALID_PARAM);
1125 }
1126
1127 if (config_struct == NULL) {
1128 return (NS_LDAP_MEMORY);
1129 }
1130
1131 /*
1132 * If no credentials are specified, try to establish a connection
1133 * as anonymous.
1134 */
1135 if (!cred) {
1136 default_cred.cred.unix_cred.passwd = NULL;
1137 default_cred.cred.unix_cred.userID = NULL;
1138 default_cred.auth.type = NS_LDAP_AUTH_NONE;
1139 }
1140
1141 /* Now create a default LDAP configuration */
1142
1143 (void) strncpy(buffer, server->server, sizeof (buffer));
1144 if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SERVERS_P, buffer,
1145 errorp) != NS_LDAP_SUCCESS) {
1146 __s_api_destroy_config(config_struct);
1147 return (NS_LDAP_CONFIG);
1148 }
1149
1150 /* Put together the address and the port specified by the user app. */
1151 if (server->port > 0) {
1152 (void) snprintf(serverAddr,
1153 sizeof (serverAddr),
1154 "%s:%hu",
1155 buffer,
1156 server->port);
1157 } else {
1158 (void) strncpy(serverAddr, buffer, sizeof (serverAddr));
1159 }
1160
1161 /*
1162 * There is no default value for the 'Default Search Base DN' attribute.
1163 * Derive one from the domain name to make __s_api_crosscheck() happy.
1164 */
1165 if (domainname2baseDN(server->domainName ?
1166 server->domainName : config_struct->domainName,
1167 buffer, NSS_BUFLEN_HOSTS) == NULL) {
1168 (void) snprintf(errmsg,
1169 sizeof (errmsg),
1170 gettext("Can not convert %s into a base DN name"),
1171 server->domainName ?
1172 server->domainName : config_struct->domainName);
1173 MKERROR(LOG_ERR,
1174 *errorp,
1175 NS_LDAP_INTERNAL,
1176 strdup(errmsg),
1177 NS_LDAP_MEMORY);
1178 __s_api_destroy_config(config_struct);
1179 return (NS_LDAP_INTERNAL);
1180 }
1181 if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SEARCH_BASEDN_P,
1182 buffer, errorp) != NS_LDAP_SUCCESS) {
1183 __s_api_destroy_config(config_struct);
1184 return (NS_LDAP_CONFIG);
1185 }
1186
1187 if (__s_api_crosscheck(config_struct, errmsg, B_FALSE) != NS_SUCCESS) {
1188 __s_api_destroy_config(config_struct);
1189 return (NS_LDAP_CONFIG);
1190 }
1191
1192 __s_api_init_config(config_struct);
1193
1194 __s_api_setInitMode();
1195
1196 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE);
1197 if (cu == NULL) {
1198 return (NS_LDAP_INTERNAL);
1199 }
1200
1201 if ((ret_code = __s_api_getConnection(serverAddr,
1202 NS_LDAP_NEW_CONN,
1203 cred ? cred : &default_cred,
1204 &sessionId,
1205 &session,
1206 errorp,
1207 0,
1208 0,
1209 cu)) != NS_LDAP_SUCCESS) {
1210 __s_api_conn_user_free(cu);
1211 __s_api_unsetInitMode();
1212 return (ret_code);
1213 }
1214
1215 __s_api_unsetInitMode();
1216
1217 if ((ret_code = getDirBaseDN(session->ld,
1218 server->domainName ?
1219 server->domainName :
1220 config_struct->domainName,
1221 &dirBaseDN)) != NS_LDAP_SUCCESS) {
1222 (void) snprintf(errmsg,
1223 sizeof (errmsg),
1224 gettext("Can not find the "
1225 "nisDomainObject for domain %s\n"),
1226 server->domainName ?
1227 server->domainName : config_struct->domainName);
1228 MKERROR(LOG_ERR,
1229 *errorp,
1230 ret_code,
1231 strdup(errmsg),
1232 NS_LDAP_MEMORY);
1233 __s_api_conn_user_free(cu);
1234 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1235 return (ret_code);
1236 }
1237
1238 /*
1239 * And here obtain a DUAProfile which will be used
1240 * as a real configuration.
1241 */
1242 if ((ret_code = getDUAProfile(session->ld,
1243 dirBaseDN,
1244 server->profileName ?
1245 server->profileName : "default",
1246 &duaProfile)) != NS_LDAP_SUCCESS) {
1247 (void) snprintf(errmsg,
1248 sizeof (errmsg),
1249 gettext("Can not find the "
1250 "%s DUAProfile\n"),
1251 server->profileName ?
1252 server->profileName : "default");
1253 MKERROR(LOG_ERR,
1254 *errorp,
1255 ret_code,
1256 strdup(errmsg),
1257 NS_LDAP_MEMORY);
1258 __s_api_conn_user_free(cu);
1259 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1260 return (ret_code);
1261 }
1262
1263 if (dir_base_dn) {
1264 *dir_base_dn = dirBaseDN;
1265 } else {
1266 free(dirBaseDN);
1267 }
1268
1269 if (dua_profile) {
1270 *dua_profile = duaProfile;
1271 } else {
1272 free(duaProfile);
1273 }
1274
1275 __s_api_conn_user_free(cu);
1276 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1277
1278 return (NS_LDAP_SUCCESS);
1279 }
1280
1281 /*
1282 * This function obtains the root DSE from a specified server.
1283 *
1284 * INPUT:
1285 * server_addr - an adress of a server to be connected to.
1286 *
1287 * OUTPUT:
1288 * root_dse - a buffer containing the root DSE in the following format:
1289 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
1290 * For example: ( here | used as DOORLINESEP for visual purposes)
1291 * supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL
1292 * Should be free'ed by the caller.
1293 */
1294 ns_ldap_return_code
__ns_ldap_getRootDSE(const char * server_addr,char ** root_dse,ns_ldap_error_t ** errorp,int anon_fallback)1295 __ns_ldap_getRootDSE(const char *server_addr,
1296 char **root_dse,
1297 ns_ldap_error_t **errorp,
1298 int anon_fallback)
1299 {
1300 char errmsg[MAXERROR];
1301 ns_ldap_return_code ret_code;
1302
1303 ConnectionID sessionId = 0;
1304 Connection *session = NULL;
1305
1306 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
1307 char *attrs[3];
1308 int ldap_rc, ldaperrno = 0;
1309 LDAPMessage *resultMsg = NULL;
1310 void **paramVal = NULL;
1311
1312 ns_cred_t anon;
1313 ns_conn_user_t *cu = NULL;
1314
1315 if (errorp == NULL) {
1316 return (NS_LDAP_INVALID_PARAM);
1317 }
1318
1319 *errorp = NULL;
1320
1321 if (!root_dse) {
1322 return (NS_LDAP_INVALID_PARAM);
1323 }
1324
1325 if (!server_addr) {
1326 return (NS_LDAP_INVALID_PARAM);
1327 }
1328
1329 __s_api_setInitMode();
1330
1331 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE);
1332 if (cu == NULL) {
1333 return (NS_LDAP_INTERNAL);
1334 }
1335
1336 /*
1337 * All the credentials will be taken from the current
1338 * libsldap configuration.
1339 */
1340 if ((ret_code = __s_api_getConnection(server_addr,
1341 NS_LDAP_NEW_CONN,
1342 NULL,
1343 &sessionId,
1344 &session,
1345 errorp,
1346 0,
1347 0,
1348 cu)) != NS_LDAP_SUCCESS) {
1349 /* Fallback to anonymous mode is disabled. Stop. */
1350 if (anon_fallback == 0) {
1351 syslog(LOG_WARNING,
1352 gettext("libsldap: can not get the root DSE from "
1353 " the %s server: %s. "
1354 "Falling back to anonymous disabled.\n"),
1355 server_addr,
1356 errorp && *errorp && (*errorp)->message ?
1357 (*errorp)->message : "");
1358 if (errorp != NULL && *errorp != NULL) {
1359 (void) __ns_ldap_freeError(errorp);
1360 }
1361 __s_api_unsetInitMode();
1362 return (ret_code);
1363 }
1364
1365 /*
1366 * Fallback to anonymous, non-SSL mode for backward
1367 * compatibility reasons. This mode should only be used when
1368 * this function (__ns_ldap_getRootDSE) is called from
1369 * ldap_cachemgr(1M).
1370 */
1371 syslog(LOG_WARNING,
1372 gettext("libsldap: Falling back to anonymous, non-SSL"
1373 " mode for __ns_ldap_getRootDSE. %s\n"),
1374 errorp && *errorp && (*errorp)->message ?
1375 (*errorp)->message : "");
1376
1377 /* Setup the anon credential for anonymous connection. */
1378 (void) memset(&anon, 0, sizeof (ns_cred_t));
1379 anon.auth.type = NS_LDAP_AUTH_NONE;
1380
1381 if (*errorp != NULL) {
1382 (void) __ns_ldap_freeError(errorp);
1383 }
1384 *errorp = NULL;
1385
1386 ret_code = __s_api_getConnection(server_addr,
1387 NS_LDAP_NEW_CONN,
1388 &anon,
1389 &sessionId,
1390 &session,
1391 errorp,
1392 0,
1393 0,
1394 cu);
1395
1396 if (ret_code != NS_LDAP_SUCCESS) {
1397 __s_api_conn_user_free(cu);
1398 __s_api_unsetInitMode();
1399 return (ret_code);
1400 }
1401 }
1402
1403 __s_api_unsetInitMode();
1404
1405 /* get search timeout value */
1406 (void) __ns_ldap_getParam(NS_LDAP_SEARCH_TIME_P, ¶mVal, errorp);
1407 if (paramVal != NULL && *paramVal != NULL) {
1408 tv.tv_sec = **((int **)paramVal);
1409 (void) __ns_ldap_freeParam(¶mVal);
1410 }
1411 if (*errorp != NULL) {
1412 (void) __ns_ldap_freeError(errorp);
1413 }
1414
1415 /* Get root DSE from the server specified by the caller. */
1416 attrs[0] = "supportedControl";
1417 attrs[1] = "supportedsaslmechanisms";
1418 attrs[2] = NULL;
1419 ldap_rc = ldap_search_ext_s(session->ld,
1420 "",
1421 LDAP_SCOPE_BASE,
1422 "(objectclass=*)",
1423 attrs,
1424 0,
1425 NULL,
1426 NULL,
1427 &tv,
1428 0,
1429 &resultMsg);
1430
1431 if (ldap_rc != LDAP_SUCCESS) {
1432 /*
1433 * If the root DSE was not found, the server does
1434 * not comply with the LDAP v3 protocol.
1435 */
1436 (void) ldap_get_option(session->ld,
1437 LDAP_OPT_ERROR_NUMBER,
1438 &ldaperrno);
1439 (void) snprintf(errmsg,
1440 sizeof (errmsg),
1441 gettext(ldap_err2string(ldaperrno)));
1442 MKERROR(LOG_ERR,
1443 *errorp,
1444 NS_LDAP_OP_FAILED,
1445 strdup(errmsg),
1446 NS_LDAP_MEMORY);
1447
1448 if (resultMsg) {
1449 (void) ldap_msgfree(resultMsg);
1450 resultMsg = NULL;
1451 }
1452
1453 __s_api_conn_user_free(cu);
1454 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1455 return (NS_LDAP_OP_FAILED);
1456 }
1457 __s_api_conn_user_free(cu);
1458
1459 ret_code = convert_to_door_line(session->ld,
1460 resultMsg,
1461 INCLUDE_ATTR_NAMES,
1462 NOT_PROFILE,
1463 root_dse);
1464 if (ret_code == NS_LDAP_NOTFOUND) {
1465 (void) snprintf(errmsg,
1466 sizeof (errmsg),
1467 gettext("No root DSE data "
1468 "for server %s returned."),
1469 server_addr);
1470 MKERROR(LOG_ERR,
1471 *errorp,
1472 NS_LDAP_NOTFOUND,
1473 strdup(errmsg),
1474 NS_LDAP_MEMORY);
1475 }
1476
1477 if (resultMsg) {
1478 (void) ldap_msgfree(resultMsg);
1479 resultMsg = NULL;
1480 }
1481
1482 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1483
1484 return (ret_code);
1485 }
1486
1487 /*
1488 * This function destroys the local list of root DSEs. The input parameter is
1489 * a pointer to the list to be erased.
1490 * The type of the pointer passed to this function should be
1491 * (dir_server_list_t *).
1492 */
1493 static
1494 void *
disposeOfOldList(void * param)1495 disposeOfOldList(void *param)
1496 {
1497 dir_server_list_t *old_list = (dir_server_list_t *)param;
1498 long i = 0, j;
1499
1500 (void) rw_wrlock(&old_list->listDestroyLock);
1501 /* Destroy the old list */
1502 while (old_list->nsServers[i]) {
1503 free(old_list->nsServers[i]->ip);
1504 j = 0;
1505 while (old_list->nsServers[i]->controls &&
1506 old_list->nsServers[i]->controls[j]) {
1507 free(old_list->nsServers[i]->controls[j]);
1508 ++j;
1509 }
1510 free(old_list->nsServers[i]->controls);
1511 j = 0;
1512 while (old_list->nsServers[i]->saslMech &&
1513 old_list->nsServers[i]->saslMech[j]) {
1514 free(old_list->nsServers[i]->saslMech[j]);
1515 ++j;
1516 }
1517 free(old_list->nsServers[i]->saslMech);
1518 ++i;
1519 }
1520 /*
1521 * All the structures pointed by old_list->nsServers were allocated
1522 * in one chunck. The nsServers[0] pointer points to the beginning
1523 * of that chunck.
1524 */
1525 free(old_list->nsServers[0]);
1526 free(old_list->nsServers);
1527 (void) rw_unlock(&old_list->listDestroyLock);
1528 (void) rwlock_destroy(&old_list->listDestroyLock);
1529 free(old_list);
1530
1531 return (NULL);
1532 }
1533
1534 /*
1535 * This function cancels the Standalone mode and destroys the list of root DSEs.
1536 */
1537 void
__ns_ldap_cancelStandalone(void)1538 __ns_ldap_cancelStandalone(void)
1539 {
1540 dir_server_list_t *old_list;
1541
1542 (void) mutex_lock(&dir_servers.listReplaceLock);
1543 dir_servers.standalone = 0;
1544 if (!dir_servers.list) {
1545 (void) mutex_unlock(&dir_servers.listReplaceLock);
1546 return;
1547 }
1548 old_list = dir_servers.list;
1549 dir_servers.list = NULL;
1550 (void) mutex_unlock(&dir_servers.listReplaceLock);
1551
1552 (void) disposeOfOldList(old_list);
1553 }
1554
1555
1556 static
1557 void*
create_ns_servers_entry(void * param)1558 create_ns_servers_entry(void *param)
1559 {
1560 #define CHUNK_SIZE 16
1561
1562 dir_server_t *server = (dir_server_t *)param;
1563 ns_ldap_return_code *retCode = calloc(1,
1564 sizeof (ns_ldap_return_code));
1565 uint32_t sc_counter = 0, sm_counter = 0;
1566 uint32_t sc_mem_blocks = 1, sm_mem_blocks = 1;
1567 char *rootDSE = NULL, *attr, *val, *rest, **ptr;
1568 ns_ldap_error_t *error = NULL;
1569
1570 if (retCode == NULL) {
1571 return (NULL);
1572 }
1573
1574 /*
1575 * We call this function in non anon-fallback mode because we
1576 * want the whole procedure to fail as soon as possible to
1577 * indicate there are problems with connecting to the server.
1578 */
1579 *retCode = __ns_ldap_getRootDSE(server->ip,
1580 &rootDSE,
1581 &error,
1582 SA_ALLOW_FALLBACK);
1583
1584 if (*retCode == NS_LDAP_MEMORY) {
1585 free(retCode);
1586 return (NULL);
1587 }
1588
1589 /*
1590 * If the root DSE can not be obtained, log an error and keep the
1591 * server.
1592 */
1593 if (*retCode != NS_LDAP_SUCCESS) {
1594 server->status = INFO_SERVER_ERROR;
1595 syslog(LOG_WARNING,
1596 gettext("libsldap (\"standalone\" mode): "
1597 "can not obtain the root DSE from %s. %s"),
1598 server->ip,
1599 error && error->message ? error->message : "");
1600 if (error) {
1601 (void) __ns_ldap_freeError(&error);
1602 }
1603 return (retCode);
1604 }
1605
1606 /* Get the first attribute of the root DSE. */
1607 attr = strtok_r(rootDSE, DOORLINESEP, &rest);
1608 if (attr == NULL) {
1609 free(rootDSE);
1610 server->status = INFO_SERVER_ERROR;
1611 syslog(LOG_WARNING,
1612 gettext("libsldap (\"standalone\" mode): "
1613 "the root DSE from %s is empty or corrupted."),
1614 server->ip);
1615 *retCode = NS_LDAP_INTERNAL;
1616 return (retCode);
1617 }
1618
1619 server->controls = (char **)calloc(CHUNK_SIZE, sizeof (char *));
1620 server->saslMech = (char **)calloc(CHUNK_SIZE, sizeof (char *));
1621 if (server->controls == NULL || server->saslMech == NULL) {
1622 free(rootDSE);
1623 free(retCode);
1624 return (NULL);
1625 }
1626
1627 do {
1628 if ((val = strchr(attr, '=')) == NULL) {
1629 continue;
1630 }
1631 ++val;
1632
1633 if (strncasecmp(attr,
1634 _SASLMECHANISM,
1635 _SASLMECHANISM_LEN) == 0) {
1636 if (sm_counter == CHUNK_SIZE * sm_mem_blocks - 1) {
1637 ptr = (char **)realloc(server->saslMech,
1638 CHUNK_SIZE *
1639 ++sm_mem_blocks *
1640 sizeof (char *));
1641 if (ptr == NULL) {
1642 *retCode = NS_LDAP_MEMORY;
1643 break;
1644 }
1645 bzero((char *)ptr +
1646 (sm_counter + 1) *
1647 sizeof (char *),
1648 CHUNK_SIZE *
1649 sm_mem_blocks *
1650 sizeof (char *) -
1651 (sm_counter + 1) *
1652 sizeof (char *));
1653 server->saslMech = ptr;
1654 }
1655 server->saslMech[sm_counter] = strdup(val);
1656 if (server->saslMech[sm_counter] == NULL) {
1657 *retCode = NS_LDAP_MEMORY;
1658 break;
1659 }
1660 ++sm_counter;
1661 continue;
1662 }
1663 if (strncasecmp(attr,
1664 _SUPPORTEDCONTROL,
1665 _SUPPORTEDCONTROL_LEN) == 0) {
1666 if (sc_counter == CHUNK_SIZE * sc_mem_blocks - 1) {
1667 ptr = (char **)realloc(server->controls,
1668 CHUNK_SIZE *
1669 ++sc_mem_blocks *
1670 sizeof (char *));
1671 if (ptr == NULL) {
1672 *retCode = NS_LDAP_MEMORY;
1673 break;
1674 }
1675 bzero((char *)ptr +
1676 (sc_counter + 1) *
1677 sizeof (char *),
1678 CHUNK_SIZE *
1679 sc_mem_blocks *
1680 sizeof (char *) -
1681 (sc_counter + 1) *
1682 sizeof (char *));
1683 server->controls = ptr;
1684 }
1685
1686 server->controls[sc_counter] = strdup(val);
1687 if (server->controls[sc_counter] == NULL) {
1688 *retCode = NS_LDAP_MEMORY;
1689 break;
1690 }
1691 ++sc_counter;
1692 continue;
1693 }
1694
1695 } while (attr = strtok_r(NULL, DOORLINESEP, &rest));
1696
1697 free(rootDSE);
1698
1699 if (*retCode == NS_LDAP_MEMORY) {
1700 free(retCode);
1701 return (NULL);
1702 }
1703
1704 server->controls[sc_counter] = NULL;
1705 server->saslMech[sm_counter] = NULL;
1706
1707 server->status = INFO_SERVER_UP;
1708
1709 return (retCode);
1710 #undef CHUNK_SIZE
1711 }
1712
1713
1714 /*
1715 * This function creates a new local list of root DSEs from all the servers
1716 * mentioned in the DUAProfile (or local NS BEC) and returns
1717 * a pointer to the list.
1718 */
1719 static
1720 ns_ldap_return_code
createDirServerList(dir_server_list_t ** new_list,ns_ldap_error_t ** errorp)1721 createDirServerList(dir_server_list_t **new_list,
1722 ns_ldap_error_t **errorp)
1723 {
1724 char **serverList;
1725 ns_ldap_return_code retCode = NS_LDAP_SUCCESS;
1726 dir_server_t *tmpSrvArray;
1727 long srvListLength, i;
1728 thread_t *thrPool, thrID;
1729 void *status = NULL;
1730
1731 if (errorp == NULL) {
1732 return (NS_LDAP_INVALID_PARAM);
1733 }
1734
1735 *errorp = NULL;
1736
1737 if (new_list == NULL) {
1738 return (NS_LDAP_INVALID_PARAM);
1739 }
1740
1741 retCode = __s_api_getServers(&serverList, errorp);
1742 if (retCode != NS_LDAP_SUCCESS || serverList == NULL) {
1743 return (retCode);
1744 }
1745
1746 for (i = 0; serverList[i]; ++i) {
1747 ;
1748 }
1749 srvListLength = i;
1750
1751 thrPool = calloc(srvListLength, sizeof (thread_t));
1752 if (thrPool == NULL) {
1753 __s_api_free2dArray(serverList);
1754 return (NS_LDAP_MEMORY);
1755 }
1756
1757 *new_list = (dir_server_list_t *)calloc(1,
1758 sizeof (dir_server_list_t));
1759 if (*new_list == NULL) {
1760 __s_api_free2dArray(serverList);
1761 free(thrPool);
1762 return (NS_LDAP_MEMORY);
1763 }
1764 (void) rwlock_init(&(*new_list)->listDestroyLock, USYNC_THREAD, NULL);
1765
1766 (*new_list)->nsServers = (dir_server_t **)calloc(srvListLength + 1,
1767 sizeof (dir_server_t *));
1768 if ((*new_list)->nsServers == NULL) {
1769 free(*new_list);
1770 *new_list = NULL;
1771 __s_api_free2dArray(serverList);
1772 free(thrPool);
1773 return (NS_LDAP_MEMORY);
1774 }
1775
1776 /*
1777 * Allocate a set of dir_server_t structures as an array,
1778 * with one alloc call and then initialize the nsServers pointers
1779 * with the addresses of the array's members.
1780 */
1781 tmpSrvArray = (dir_server_t *)calloc(srvListLength,
1782 sizeof (dir_server_t));
1783 for (i = 0; i < srvListLength; ++i) {
1784 (*new_list)->nsServers[i] = &tmpSrvArray[i];
1785
1786 (*new_list)->nsServers[i]->info = INFO_STATUS_NEW;
1787 (void) mutex_init(&(*new_list)->nsServers[i]->updateStatus,
1788 USYNC_THREAD,
1789 NULL);
1790
1791 (*new_list)->nsServers[i]->ip = strdup(serverList[i]);
1792 if ((*new_list)->nsServers[i]->ip == NULL) {
1793 retCode = NS_LDAP_MEMORY;
1794 break;
1795 }
1796
1797 (*new_list)->nsServers[i]->status = INFO_SERVER_CONNECTING;
1798
1799 switch (thr_create(NULL,
1800 0,
1801 create_ns_servers_entry,
1802 (*new_list)->nsServers[i],
1803 0,
1804 &thrID)) {
1805 case EAGAIN:
1806 (*new_list)->nsServers[i]->status =
1807 INFO_SERVER_ERROR;
1808 continue;
1809 case ENOMEM:
1810 (*new_list)->nsServers[i]->status =
1811 INFO_SERVER_ERROR;
1812 continue;
1813 default:
1814 thrPool[i] = thrID;
1815 continue;
1816 }
1817 }
1818
1819 for (i = 0; i < srvListLength; ++i) {
1820 if (thrPool[i] != 0 &&
1821 thr_join(thrPool[i], NULL, &status) == 0) {
1822 if (status == NULL) {
1823 /*
1824 * Some memory allocation problems occured. Just
1825 * ignore the server and hope there will be some
1826 * other good ones.
1827 */
1828 (*new_list)->nsServers[i]->status =
1829 INFO_SERVER_ERROR;
1830 }
1831 free(status);
1832 }
1833 }
1834
1835 __s_api_free2dArray(serverList);
1836 free(thrPool);
1837
1838 if (retCode == NS_LDAP_MEMORY) {
1839 (void) disposeOfOldList(*new_list);
1840 return (NS_LDAP_MEMORY);
1841 }
1842
1843 return (NS_LDAP_SUCCESS);
1844 }
1845
1846 /*
1847 * This functions replaces the local list of root DSEs with a new one and starts
1848 * a thread destroying the old list. There is no need for other threads to wait
1849 * until the old list will be destroyed.
1850 * Since it is possible that more than one thread can start creating the list,
1851 * this function should be protected by mutexes to be sure that only one thread
1852 * performs the initialization.
1853 */
1854 static
1855 ns_ldap_return_code
initGlobalList(ns_ldap_error_t ** error)1856 initGlobalList(ns_ldap_error_t **error)
1857 {
1858 dir_server_list_t *new_list, *old_list;
1859 ns_ldap_return_code ret_code;
1860 thread_t tid;
1861
1862 ret_code = createDirServerList(&new_list, error);
1863 if (ret_code != NS_LDAP_SUCCESS) {
1864 return (ret_code);
1865 }
1866
1867 old_list = dir_servers.list;
1868 dir_servers.list = new_list;
1869
1870 if (old_list) {
1871 (void) thr_create(NULL,
1872 0,
1873 disposeOfOldList,
1874 old_list,
1875 THR_DETACHED,
1876 &tid);
1877 }
1878
1879 return (NS_LDAP_SUCCESS);
1880 }
1881
1882 static
1883 struct {
1884 char *authMech;
1885 ns_auth_t auth;
1886 } authArray[] = {{"none", {NS_LDAP_AUTH_NONE,
1887 NS_LDAP_TLS_NONE,
1888 NS_LDAP_SASL_NONE,
1889 NS_LDAP_SASLOPT_NONE}},
1890 {"simple", {NS_LDAP_AUTH_SIMPLE,
1891 NS_LDAP_TLS_NONE,
1892 NS_LDAP_SASL_NONE,
1893 NS_LDAP_SASLOPT_NONE}},
1894 {"tls:simple", {NS_LDAP_AUTH_TLS,
1895 NS_LDAP_TLS_SIMPLE,
1896 NS_LDAP_SASL_NONE,
1897 NS_LDAP_SASLOPT_NONE}},
1898 {"tls:sasl/CRAM-MD5", {NS_LDAP_AUTH_TLS,
1899 NS_LDAP_TLS_SASL,
1900 NS_LDAP_SASL_CRAM_MD5,
1901 NS_LDAP_SASLOPT_NONE}},
1902 {"tls:sasl/DIGEST-MD5", {NS_LDAP_AUTH_TLS,
1903 NS_LDAP_TLS_SASL,
1904 NS_LDAP_SASL_DIGEST_MD5,
1905 NS_LDAP_SASLOPT_NONE}},
1906 {"sasl/CRAM-MD5", {NS_LDAP_AUTH_SASL,
1907 NS_LDAP_TLS_SASL,
1908 NS_LDAP_SASL_CRAM_MD5,
1909 NS_LDAP_SASLOPT_NONE}},
1910 {"sasl/DIGEST-MD5", {NS_LDAP_AUTH_SASL,
1911 NS_LDAP_TLS_SASL,
1912 NS_LDAP_SASL_DIGEST_MD5,
1913 NS_LDAP_SASLOPT_NONE}},
1914 {"sasl/GSSAPI", {NS_LDAP_AUTH_SASL,
1915 NS_LDAP_TLS_SASL,
1916 NS_LDAP_SASL_GSSAPI,
1917 NS_LDAP_SASLOPT_PRIV | NS_LDAP_SASLOPT_INT}},
1918 {NULL, {NS_LDAP_AUTH_NONE,
1919 NS_LDAP_TLS_NONE,
1920 NS_LDAP_SASL_NONE,
1921 NS_LDAP_SASLOPT_NONE}}};
1922
1923 ns_ldap_return_code
__ns_ldap_initAuth(const char * auth_mech,ns_auth_t * auth,ns_ldap_error_t ** errorp)1924 __ns_ldap_initAuth(const char *auth_mech,
1925 ns_auth_t *auth,
1926 ns_ldap_error_t **errorp)
1927 {
1928 uint32_t i;
1929 char errmsg[MAXERROR];
1930
1931 if (auth_mech == NULL) {
1932 (void) snprintf(errmsg,
1933 sizeof (errmsg),
1934 gettext("Invalid authentication method specified\n"));
1935 MKERROR(LOG_WARNING,
1936 *errorp,
1937 NS_LDAP_INTERNAL,
1938 strdup(errmsg),
1939 NS_LDAP_MEMORY);
1940 return (NS_LDAP_INTERNAL);
1941 }
1942
1943 for (i = 0; authArray[i].authMech != NULL; ++i) {
1944 if (strcasecmp(auth_mech, authArray[i].authMech) == 0) {
1945 *auth = authArray[i].auth;
1946 return (NS_LDAP_SUCCESS);
1947 }
1948 }
1949
1950 (void) snprintf(errmsg,
1951 sizeof (errmsg),
1952 gettext("Invalid authentication method specified\n"));
1953 MKERROR(LOG_WARNING,
1954 *errorp,
1955 NS_LDAP_INTERNAL,
1956 strdup(errmsg),
1957 NS_LDAP_MEMORY);
1958 return (NS_LDAP_INTERNAL);
1959 }
1960
1961 /*
1962 * This function "informs" libsldap that a client application has specified
1963 * a directory to use. The function obtains a DUAProfile, credentials,
1964 * and naming context. During all further operations on behalf
1965 * of the application requested a standalone schema libsldap will use
1966 * the information obtained by __ns_ldap_initStandalone() instead of
1967 * door_call(3C)ing ldap_cachemgr(1M).
1968 *
1969 * INPUT:
1970 * sa_conf - a structure describing where and in which way to obtain all
1971 * the configuration describing how to communicate to
1972 * a choosen LDAP directory,
1973 * errorp - an error object describing an error occured.
1974 */
1975 ns_ldap_return_code
__ns_ldap_initStandalone(const ns_standalone_conf_t * sa_conf,ns_ldap_error_t ** errorp)1976 __ns_ldap_initStandalone(const ns_standalone_conf_t *sa_conf,
1977 ns_ldap_error_t **errorp) {
1978
1979 ns_cred_t user_cred = {{NS_LDAP_AUTH_NONE,
1980 NS_LDAP_TLS_NONE,
1981 NS_LDAP_SASL_NONE,
1982 NS_LDAP_SASLOPT_NONE},
1983 NULL,
1984 {NULL, NULL}};
1985 char *dua_profile = NULL;
1986 char errmsg[MAXERROR];
1987 ns_config_t *cfg;
1988 int ret_code;
1989
1990 if (sa_conf->SA_BIND_DN == NULL && sa_conf->SA_BIND_PWD != NULL ||
1991 sa_conf->SA_BIND_DN != NULL && sa_conf->SA_BIND_PWD == NULL) {
1992 (void) snprintf(errmsg,
1993 sizeof (errmsg),
1994 gettext("Bind DN and bind password"
1995 " must both be provided\n"));
1996 MKERROR(LOG_ERR,
1997 *errorp,
1998 NS_CONFIG_NOTLOADED,
1999 strdup(errmsg),
2000 NS_LDAP_MEMORY);
2001 return (NS_LDAP_INTERNAL);
2002 }
2003
2004 switch (sa_conf->type) {
2005 case NS_LDAP_SERVER:
2006 if (sa_conf->SA_BIND_DN != NULL) {
2007 user_cred.cred.unix_cred.userID = sa_conf->SA_BIND_DN;
2008 user_cred.auth.type = NS_LDAP_AUTH_SIMPLE;
2009 }
2010
2011 if (sa_conf->SA_BIND_PWD != NULL) {
2012 user_cred.cred.unix_cred.passwd = sa_conf->SA_BIND_PWD;
2013 }
2014
2015 if (sa_conf->SA_AUTH != NULL) {
2016 user_cred.auth.type = sa_conf->SA_AUTH->type;
2017 user_cred.auth.tlstype = sa_conf->SA_AUTH->tlstype;
2018 user_cred.auth.saslmech = sa_conf->SA_AUTH->saslmech;
2019 user_cred.auth.saslopt = sa_conf->SA_AUTH->saslopt;
2020 }
2021
2022 if (sa_conf->SA_CERT_PATH != NULL) {
2023 user_cred.hostcertpath = sa_conf->SA_CERT_PATH;
2024 }
2025
2026 ret_code = __ns_ldap_getConnectionInfoFromDUA(
2027 &sa_conf->ds_profile.server,
2028 &user_cred,
2029 &dua_profile,
2030 NULL,
2031 errorp);
2032 if (ret_code != NS_LDAP_SUCCESS) {
2033 return (ret_code);
2034 }
2035
2036 cfg = __s_api_create_config_door_str(dua_profile, errorp);
2037 if (cfg == NULL) {
2038 free(dua_profile);
2039 return (NS_LDAP_CONFIG);
2040 }
2041
2042 if (sa_conf->SA_CERT_PATH != NULL) {
2043 char *certPathAttr;
2044 ParamIndexType type;
2045
2046 switch (cfg->version) {
2047 case NS_LDAP_V1:
2048 certPathAttr = "NS_LDAP_CERT_PATH";
2049 break;
2050 default: /* Version 2 */
2051 certPathAttr = "NS_LDAP_HOST_CERTPATH";
2052 break;
2053 }
2054
2055 if (__s_api_get_versiontype(cfg,
2056 certPathAttr,
2057 &type) == 0 &&
2058 (ret_code = __ns_ldap_setParamValue(cfg,
2059 type,
2060 sa_conf->SA_CERT_PATH,
2061 errorp)) != NS_LDAP_SUCCESS) {
2062 __s_api_destroy_config(cfg);
2063 return (ret_code);
2064 }
2065 }
2066
2067 if (sa_conf->SA_BIND_DN != NULL &&
2068 sa_conf->SA_BIND_PWD != NULL) {
2069 char *authMethods;
2070
2071 authMethods = __s_api_strValue(cfg, NS_LDAP_AUTH_P,
2072 NS_FILE_FMT);
2073 if (authMethods != NULL &&
2074 strstr(authMethods, "sasl/GSSAPI") != NULL) {
2075 /*
2076 * The received DUAProfile specifies
2077 * sasl/GSSAPI as an auth. mechanism.
2078 * The bind DN and password will be
2079 * ignored.
2080 */
2081 syslog(LOG_INFO, gettext("sasl/GSSAPI will be "
2082 "used as an authentication method. "
2083 "The bind DN and password will "
2084 "be ignored.\n"));
2085 free(authMethods);
2086 break;
2087 }
2088
2089 if (authMethods != NULL)
2090 free(authMethods);
2091
2092 if (__ns_ldap_setParamValue(cfg,
2093 NS_LDAP_BINDDN_P,
2094 sa_conf->SA_BIND_DN,
2095 errorp) != NS_LDAP_SUCCESS) {
2096 __s_api_destroy_config(cfg);
2097 return (NS_LDAP_CONFIG);
2098 }
2099
2100 if (__ns_ldap_setParamValue(cfg,
2101 NS_LDAP_BINDPASSWD_P,
2102 sa_conf->SA_BIND_PWD,
2103 errorp) != NS_LDAP_SUCCESS) {
2104 __s_api_destroy_config(cfg);
2105 return (NS_LDAP_CONFIG);
2106 }
2107 }
2108
2109 break;
2110 default: /* NS_CACHEMGR */
2111 return (NS_LDAP_SUCCESS);
2112 }
2113
2114 __s_api_init_config(cfg);
2115 /* Connection management should use the new config now. */
2116 __s_api_reinit_conn_mgmt_new_config(cfg);
2117 __ns_ldap_setServer(TRUE);
2118
2119 (void) mutex_lock(&dir_servers.listReplaceLock);
2120 if ((ret_code = initGlobalList(errorp)) != NS_SUCCESS) {
2121 (void) mutex_unlock(&dir_servers.listReplaceLock);
2122 return (ret_code);
2123 }
2124 dir_servers.standalone = 1;
2125 (void) mutex_unlock(&dir_servers.listReplaceLock);
2126
2127 return (NS_LDAP_SUCCESS);
2128 }
2129
2130 /*
2131 * INPUT:
2132 * serverAddr is the address of a server and
2133 * request is one of the following:
2134 * NS_CACHE_NEW: get a new server address, addr is ignored.
2135 * NS_CACHE_NORESP: get the next one, remove addr from list.
2136 * NS_CACHE_NEXT: get the next one, keep addr on list.
2137 * NS_CACHE_WRITE: get a non-replica server, if possible, if not, same
2138 * as NS_CACHE_NEXT.
2139 * addrType:
2140 * NS_CACHE_ADDR_IP: return server address as is, this is default.
2141 * NS_CACHE_ADDR_HOSTNAME: return server addess as FQDN format, only
2142 * self credential case requires such format.
2143 * OUTPUT:
2144 * ret
2145 *
2146 * a structure of type ns_server_info_t containing the server address
2147 * or name, server controls and supported SASL mechanisms.
2148 * NOTE: Caller should allocate space for the structure and free
2149 * all the space allocated by the function for the information contained
2150 * in the structure.
2151 *
2152 * error - an error object describing an error, if any.
2153 */
2154 ns_ldap_return_code
__s_api_findRootDSE(const char * request,const char * serverAddr,const char * addrType,ns_server_info_t * ret,ns_ldap_error_t ** error)2155 __s_api_findRootDSE(const char *request,
2156 const char *serverAddr,
2157 const char *addrType,
2158 ns_server_info_t *ret,
2159 ns_ldap_error_t **error)
2160 {
2161 dir_server_list_t *current_list = NULL;
2162 ns_ldap_return_code ret_code;
2163 long i = 0;
2164 int matched = FALSE;
2165 dir_server_t *server = NULL;
2166 char errmsg[MAXERROR];
2167
2168 (void) mutex_lock(&dir_servers.listReplaceLock);
2169 if (dir_servers.list == NULL) {
2170 (void) mutex_unlock(&dir_servers.listReplaceLock);
2171 (void) snprintf(errmsg,
2172 sizeof (errmsg),
2173 gettext("The list of root DSEs is empty: "
2174 "the Standalone mode was not properly initialized"));
2175 MKERROR(LOG_ERR,
2176 *error,
2177 NS_CONFIG_NOTLOADED,
2178 strdup(errmsg),
2179 NS_LDAP_MEMORY);
2180 return (NS_LDAP_INTERNAL);
2181 }
2182
2183 current_list = dir_servers.list;
2184 (void) rw_rdlock(¤t_list->listDestroyLock);
2185 (void) mutex_unlock(&dir_servers.listReplaceLock);
2186
2187 /*
2188 * The code below is mostly the clone of the
2189 * ldap_cachemgr::cachemgr_getldap.c::getldap_get_serverInfo() function.
2190 * Currently we have two different server lists: one is maintained
2191 * by libsldap ('standalone' mode), the other is in ldap_cachemgr
2192 * (a part of its standard functionality).
2193 */
2194
2195 /*
2196 * If NS_CACHE_NEW, or the server info is new,
2197 * starts from the beginning of the list.
2198 */
2199 (void) mutex_lock(¤t_list->nsServers[0]->updateStatus);
2200 if (strcmp(request, NS_CACHE_NEW) == 0 ||
2201 current_list->nsServers[0]->info == INFO_STATUS_NEW) {
2202 matched = TRUE;
2203 }
2204 (void) mutex_unlock(¤t_list->nsServers[i]->updateStatus);
2205
2206 for (i = 0; current_list->nsServers[i]; ++i) {
2207 /*
2208 * Lock the updateStatus mutex to
2209 * make sure the server status stays the same
2210 * while the data is being processed.
2211 */
2212 if (matched == FALSE &&
2213 strcmp(current_list->nsServers[i]->ip,
2214 serverAddr) == 0) {
2215 matched = TRUE;
2216 if (strcmp(request, NS_CACHE_NORESP) == 0) {
2217
2218 /*
2219 * if the server has already been removed,
2220 * don't bother.
2221 */
2222 (void) mutex_lock(¤t_list->
2223 nsServers[i]->updateStatus);
2224 if (current_list->nsServers[i]->status ==
2225 INFO_SERVER_REMOVED) {
2226 (void) mutex_unlock(¤t_list->
2227 nsServers[i]->
2228 updateStatus);
2229 continue;
2230 }
2231 (void) mutex_unlock(¤t_list->
2232 nsServers[i]->
2233 updateStatus);
2234
2235 /*
2236 * if the information is new,
2237 * give this server one more chance.
2238 */
2239 (void) mutex_lock(¤t_list->
2240 nsServers[i]->
2241 updateStatus);
2242 if (current_list->nsServers[i]->info ==
2243 INFO_STATUS_NEW &&
2244 current_list->nsServers[i]->status ==
2245 INFO_SERVER_UP) {
2246 server = current_list->nsServers[i];
2247 (void) mutex_unlock(¤t_list->
2248 nsServers[i]->
2249 updateStatus);
2250 break;
2251 } else {
2252 /*
2253 * it is recommended that
2254 * before removing the
2255 * server from the list,
2256 * the server should be
2257 * contacted one more time
2258 * to make sure that it is
2259 * really unavailable.
2260 * For now, just trust the client
2261 * (i.e., the sldap library)
2262 * that it knows what it is
2263 * doing and would not try
2264 * to mess up the server
2265 * list.
2266 */
2267 current_list->nsServers[i]->status =
2268 INFO_SERVER_REMOVED;
2269 (void) mutex_unlock(¤t_list->
2270 nsServers[i]->
2271 updateStatus);
2272 continue;
2273 }
2274 } else {
2275 /*
2276 * req == NS_CACHE_NEXT or NS_CACHE_WRITE
2277 */
2278 continue;
2279 }
2280 }
2281
2282 if (matched) {
2283 if (strcmp(request, NS_CACHE_WRITE) == 0) {
2284 /*
2285 * ldap_cachemgr checks here if the server
2286 * is not a non-replica server (a server
2287 * of type INFO_RW_WRITEABLE). But currently
2288 * it considers all the servers in its list
2289 * as those.
2290 */
2291 (void) mutex_lock(¤t_list->
2292 nsServers[i]->
2293 updateStatus);
2294 if (current_list->nsServers[i]->status ==
2295 INFO_SERVER_UP) {
2296 (void) mutex_unlock(¤t_list->
2297 nsServers[i]->
2298 updateStatus);
2299 server = current_list->nsServers[i];
2300 break;
2301 }
2302 } else {
2303 (void) mutex_lock(¤t_list->
2304 nsServers[i]->
2305 updateStatus);
2306 if (current_list->nsServers[i]->status ==
2307 INFO_SERVER_UP) {
2308 (void) mutex_unlock(¤t_list->
2309 nsServers[i]->
2310 updateStatus);
2311 server = current_list->nsServers[i];
2312 break;
2313 }
2314 }
2315
2316 (void) mutex_unlock(¤t_list->
2317 nsServers[i]->
2318 updateStatus);
2319 }
2320 }
2321
2322 if (server == NULL) {
2323 (void) rw_unlock(¤t_list->listDestroyLock);
2324 (void) snprintf(errmsg,
2325 sizeof (errmsg),
2326 gettext("No servers are available"));
2327 MKERROR(LOG_ERR,
2328 *error,
2329 NS_CONFIG_NOTLOADED,
2330 strdup(errmsg),
2331 NS_LDAP_MEMORY);
2332 return (NS_LDAP_NOTFOUND);
2333 }
2334
2335 (void) mutex_lock(&server->updateStatus);
2336 server->info = INFO_STATUS_OLD;
2337 (void) mutex_unlock(&server->updateStatus);
2338
2339 if (ret == NULL) {
2340 (void) rw_unlock(¤t_list->listDestroyLock);
2341 return (NS_LDAP_SUCCESS);
2342 }
2343
2344 if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
2345 ret_code = __s_api_ip2hostname(server->ip, &ret->serverFQDN);
2346 if (ret_code != NS_LDAP_SUCCESS) {
2347 (void) snprintf(errmsg,
2348 sizeof (errmsg),
2349 gettext("The %s address "
2350 "can not be resolved into "
2351 "a host name. Returning "
2352 "the address as it is."),
2353 server->ip);
2354 MKERROR(LOG_ERR,
2355 *error,
2356 NS_CONFIG_NOTLOADED,
2357 strdup(errmsg),
2358 NS_LDAP_MEMORY);
2359 return (NS_LDAP_INTERNAL);
2360 }
2361 }
2362
2363 ret->server = strdup(server->ip);
2364
2365 ret->controls = __s_api_cp2dArray(server->controls);
2366 ret->saslMechanisms = __s_api_cp2dArray(server->saslMech);
2367
2368 (void) rw_unlock(¤t_list->listDestroyLock);
2369
2370 return (NS_LDAP_SUCCESS);
2371 }
2372
2373 /*
2374 * This function iterates through the list of the configured LDAP servers
2375 * and "pings" those which are marked as removed or if any error occurred
2376 * during the previous receiving of the server's root DSE. If the
2377 * function is able to reach such a server and get its root DSE, it
2378 * marks the server as on-line. Otherwise, the server's status is set
2379 * to "Error".
2380 * For each server the function tries to connect to, it fires up
2381 * a separate thread and then waits until all the treads finish.
2382 * The function returns NS_LDAP_INTERNAL if the Standalone mode was not
2383 * initialized or was canceled prior to an invocation of
2384 * __ns_ldap_pingOfflineServers().
2385 */
2386 ns_ldap_return_code
__ns_ldap_pingOfflineServers(void)2387 __ns_ldap_pingOfflineServers(void)
2388 {
2389 dir_server_list_t *current_list = NULL;
2390 ns_ldap_return_code retCode = NS_LDAP_SUCCESS;
2391 long srvListLength, i = 0;
2392 thread_t *thrPool, thrID;
2393 void *status = NULL;
2394
2395 (void) mutex_lock(&dir_servers.listReplaceLock);
2396 if (dir_servers.list == NULL) {
2397 (void) mutex_unlock(&dir_servers.listReplaceLock);
2398 return (NS_LDAP_INTERNAL);
2399 }
2400
2401 current_list = dir_servers.list;
2402 (void) rw_wrlock(¤t_list->listDestroyLock);
2403 (void) mutex_unlock(&dir_servers.listReplaceLock);
2404
2405 while (current_list->nsServers[i] != NULL) {
2406 ++i;
2407 }
2408 srvListLength = i;
2409
2410 thrPool = calloc(srvListLength, sizeof (thread_t));
2411 if (thrPool == NULL) {
2412 (void) rw_unlock(¤t_list->listDestroyLock);
2413 return (NS_LDAP_MEMORY);
2414 }
2415
2416 for (i = 0; i < srvListLength; ++i) {
2417 if (current_list->nsServers[i]->status != INFO_SERVER_REMOVED &&
2418 current_list->nsServers[i]->status != INFO_SERVER_ERROR) {
2419 continue;
2420 }
2421 current_list->nsServers[i]->status = INFO_SERVER_CONNECTING;
2422 current_list->nsServers[i]->info = INFO_STATUS_NEW;
2423
2424 __s_api_free2dArray(current_list->nsServers[i]->controls);
2425 current_list->nsServers[i]->controls = NULL;
2426 __s_api_free2dArray(current_list->nsServers[i]->saslMech);
2427 current_list->nsServers[i]->saslMech = NULL;
2428
2429 switch (thr_create(NULL,
2430 0,
2431 create_ns_servers_entry,
2432 current_list->nsServers[i],
2433 0,
2434 &thrID)) {
2435 case EAGAIN:
2436 current_list->nsServers[i]->status = INFO_SERVER_ERROR;
2437 continue;
2438 case ENOMEM:
2439 current_list->nsServers[i]->status = INFO_SERVER_ERROR;
2440 retCode = NS_LDAP_MEMORY;
2441 break;
2442 default:
2443 thrPool[i] = thrID;
2444 continue;
2445 }
2446 /* A memory allocation error has occured */
2447 break;
2448
2449 }
2450
2451 for (i = 0; i < srvListLength; ++i) {
2452 if (thrPool[i] != 0 &&
2453 thr_join(thrPool[i], NULL, &status) == 0) {
2454 if (status == NULL) {
2455 current_list->nsServers[i]->status =
2456 INFO_SERVER_ERROR;
2457 retCode = NS_LDAP_MEMORY;
2458 }
2459 free(status);
2460 }
2461 }
2462
2463 (void) rw_unlock(¤t_list->listDestroyLock);
2464
2465 free(thrPool);
2466
2467 return (retCode);
2468 }
2469