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