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