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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2023 RackTop Systems, Inc.
24 */
25
26 #include <alloca.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <lber.h>
30 #include <sasl/sasl.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <synch.h>
34 #include <atomic.h>
35 #include <errno.h>
36 #include <assert.h>
37 #include <limits.h>
38 #include <syslog.h>
39 #include <sys/u8_textprep.h>
40 #include <sys/varargs.h>
41 #include "libadutils.h"
42 #include "adutils_impl.h"
43
44 /* List of DSs, needed by the idle connection reaper thread */
45 static pthread_mutex_t adhostlock = PTHREAD_MUTEX_INITIALIZER;
46 static adutils_host_t *host_head = NULL;
47
48 /*
49 * List of query state structs -- needed so we can "route" LDAP results
50 * to the right context if multiple threads should be using the same
51 * connection concurrently
52 */
53 static pthread_mutex_t qstatelock = PTHREAD_MUTEX_INITIALIZER;
54 static adutils_query_state_t *qstatehead = NULL;
55
56 static char *adutils_sid_ber2str(BerValue *bvalues);
57 static void adutils_lookup_batch_unlock(adutils_query_state_t **state);
58 static void delete_ds(adutils_ad_t *ad, const char *host, int port);
59
60 int ad_debug[AD_DEBUG_MAX+1] = {0};
61
62 typedef struct binary_attrs {
63 const char *name;
64 char *(*ber2str)(BerValue *bvalues);
65 } binary_attrs_t;
66
67 static binary_attrs_t binattrs[] = {
68 {"objectSID", adutils_sid_ber2str},
69 {NULL, NULL}
70 };
71
72
73 adutils_logger logger = syslog;
74
75
76 void
adutils_set_logger(adutils_logger funct)77 adutils_set_logger(adutils_logger funct)
78 {
79 logger = funct;
80 }
81
82
83 /*
84 * Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com"
85 */
86 static
87 char *
adutils_dns2dn(const char * dns)88 adutils_dns2dn(const char *dns)
89 {
90 int num_parts;
91
92 return (ldap_dns_to_dn((char *)dns, &num_parts));
93 }
94
95
96 /*
97 * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other
98 * attributes (CN, etc...).
99 */
100 char *
adutils_dn2dns(const char * dn)101 adutils_dn2dns(const char *dn)
102 {
103 return (DN_to_DNS(dn));
104 }
105
106
107 /*
108 * Convert a binary SID in a BerValue to a adutils_sid_t
109 */
110 int
adutils_getsid(BerValue * bval,adutils_sid_t * sidp)111 adutils_getsid(BerValue *bval, adutils_sid_t *sidp)
112 {
113 int i, j;
114 uchar_t *v;
115 uint32_t a;
116
117 /*
118 * The binary format of a SID is as follows:
119 *
120 * byte #0: version, always 0x01
121 * byte #1: RID count, always <= 0x0f
122 * bytes #2-#7: SID authority, big-endian 48-bit unsigned int
123 *
124 * followed by RID count RIDs, each a little-endian, unsigned
125 * 32-bit int.
126 */
127 /*
128 * Sanity checks: must have at least one RID, version must be
129 * 0x01, and the length must be 8 + rid count * 4
130 */
131 if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 &&
132 bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) {
133 v = (uchar_t *)bval->bv_val;
134 sidp->version = v[0];
135 sidp->sub_authority_count = v[1];
136 sidp->authority =
137 /* big endian -- so start from the left */
138 ((u_longlong_t)v[2] << 40) |
139 ((u_longlong_t)v[3] << 32) |
140 ((u_longlong_t)v[4] << 24) |
141 ((u_longlong_t)v[5] << 16) |
142 ((u_longlong_t)v[6] << 8) |
143 (u_longlong_t)v[7];
144 for (i = 0; i < sidp->sub_authority_count; i++) {
145 j = 8 + (i * 4);
146 /* little endian -- so start from the right */
147 a = (v[j + 3] << 24) | (v[j + 2] << 16) |
148 (v[j + 1] << 8) | (v[j]);
149 sidp->sub_authorities[i] = a;
150 }
151 return (0);
152 }
153 return (-1);
154 }
155
156 /*
157 * Convert a adutils_sid_t to S-1-...
158 */
159 char *
adutils_sid2txt(adutils_sid_t * sidp)160 adutils_sid2txt(adutils_sid_t *sidp)
161 {
162 int rlen, i, len;
163 char *str, *cp;
164
165 if (sidp->version != 1)
166 return (NULL);
167
168 len = sizeof ("S-1-") - 1;
169
170 /*
171 * We could optimize like so, but, why?
172 * if (sidp->authority < 10)
173 * len += 2;
174 * else if (sidp->authority < 100)
175 * len += 3;
176 * else
177 * len += snprintf(NULL, 0"%llu", sidp->authority);
178 */
179 len += snprintf(NULL, 0, "%llu", sidp->authority);
180
181 /* Max length of a uint32_t printed out in ASCII is 10 bytes */
182 len += 1 + (sidp->sub_authority_count + 1) * 10;
183
184 if ((cp = str = malloc(len)) == NULL)
185 return (NULL);
186
187 rlen = snprintf(str, len, "S-1-%llu", sidp->authority);
188
189 cp += rlen;
190 len -= rlen;
191
192 for (i = 0; i < sidp->sub_authority_count; i++) {
193 assert(len > 0);
194 rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]);
195 cp += rlen;
196 len -= rlen;
197 assert(len >= 0);
198 }
199
200 return (str);
201 }
202
203 /*
204 * Convert a adutils_sid_t to on-the-wire encoding
205 */
206 static
207 int
sid2binsid(adutils_sid_t * sid,uchar_t * binsid,int binsidlen)208 sid2binsid(adutils_sid_t *sid, uchar_t *binsid, int binsidlen)
209 {
210 uchar_t *p;
211 int i;
212 uint64_t a;
213 uint32_t r;
214
215 if (sid->version != 1 ||
216 binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4))
217 return (-1);
218
219 p = binsid;
220 *p++ = 0x01; /* version */
221 /* sub authority count */
222 *p++ = sid->sub_authority_count;
223 /* Authority */
224 a = sid->authority;
225 /* big-endian -- start from left */
226 *p++ = (a >> 40) & 0xFF;
227 *p++ = (a >> 32) & 0xFF;
228 *p++ = (a >> 24) & 0xFF;
229 *p++ = (a >> 16) & 0xFF;
230 *p++ = (a >> 8) & 0xFF;
231 *p++ = a & 0xFF;
232
233 /* sub-authorities */
234 for (i = 0; i < sid->sub_authority_count; i++) {
235 r = sid->sub_authorities[i];
236 /* little-endian -- start from right */
237 *p++ = (r & 0x000000FF);
238 *p++ = (r & 0x0000FF00) >> 8;
239 *p++ = (r & 0x00FF0000) >> 16;
240 *p++ = (r & 0xFF000000) >> 24;
241 }
242
243 return (0);
244 }
245
246 /*
247 * Convert a stringified SID (S-1-...) into a hex-encoded version of the
248 * on-the-wire encoding, but with each pair of hex digits pre-pended
249 * with a '\', so we can pass this to libldap.
250 */
251 int
adutils_txtsid2hexbinsid(const char * txt,const uint32_t * rid,char * hexbinsid,int hexbinsidlen)252 adutils_txtsid2hexbinsid(const char *txt, const uint32_t *rid,
253 char *hexbinsid, int hexbinsidlen)
254 {
255 adutils_sid_t sid = { 0 };
256 int i, j;
257 const char *cp;
258 char *ecp;
259 u_longlong_t a;
260 unsigned long r;
261 uchar_t *binsid, b, hb;
262
263 /* Only version 1 SIDs please */
264 if (strncmp(txt, "S-1-", strlen("S-1-")) != 0)
265 return (-1);
266
267 if (strlen(txt) < (strlen("S-1-") + 1))
268 return (-1);
269
270 /* count '-'s */
271 for (j = 0, cp = strchr(txt, '-');
272 cp != NULL && *cp != '\0';
273 j++, cp = strchr(cp + 1, '-')) {
274 /* can't end on a '-' */
275 if (*(cp + 1) == '\0')
276 return (-1);
277 }
278
279 /* Adjust count for version and authority */
280 j -= 2;
281
282 /* we know the version number and RID count */
283 sid.version = 1;
284 sid.sub_authority_count = (rid != NULL) ? j + 1 : j;
285
286 /* must have at least one RID, but not too many */
287 if (sid.sub_authority_count < 1 ||
288 sid.sub_authority_count > ADUTILS_SID_MAX_SUB_AUTHORITIES)
289 return (-1);
290
291 /* check that we only have digits and '-' */
292 if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1))
293 return (-1);
294
295 cp = txt + strlen("S-1-");
296
297 /* 64-bit safe parsing of unsigned 48-bit authority value */
298 errno = 0;
299 a = strtoull(cp, &ecp, 10);
300
301 /* errors parsing the authority or too many bits */
302 if (cp == ecp || (a == 0 && errno == EINVAL) ||
303 (a == ULLONG_MAX && errno == ERANGE) ||
304 (a & 0x0000ffffffffffffULL) != a)
305 return (-1);
306
307 cp = ecp;
308
309 sid.authority = (uint64_t)a;
310
311 for (i = 0; i < j; i++) {
312 if (*cp++ != '-')
313 return (-1);
314 /* 64-bit safe parsing of unsigned 32-bit RID */
315 errno = 0;
316 r = strtoul(cp, &ecp, 10);
317 /* errors parsing the RID or too many bits */
318 if (cp == ecp || (r == 0 && errno == EINVAL) ||
319 (r == ULONG_MAX && errno == ERANGE) ||
320 (r & 0xffffffffUL) != r)
321 return (-1);
322 sid.sub_authorities[i] = (uint32_t)r;
323 cp = ecp;
324 }
325
326 /* check that all of the string SID has been consumed */
327 if (*cp != '\0')
328 return (-1);
329
330 if (rid != NULL)
331 sid.sub_authorities[j] = *rid;
332
333 j = 1 + 1 + 6 + sid.sub_authority_count * 4;
334
335 if (hexbinsidlen < (j * 3))
336 return (-2);
337
338 /* binary encode the SID */
339 binsid = (uchar_t *)alloca(j);
340 (void) sid2binsid(&sid, binsid, j);
341
342 /* hex encode, with a backslash before each byte */
343 for (ecp = hexbinsid, i = 0; i < j; i++) {
344 b = binsid[i];
345 *ecp++ = '\\';
346 hb = (b >> 4) & 0xF;
347 *ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
348 hb = b & 0xF;
349 *ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
350 }
351 *ecp = '\0';
352
353 return (0);
354 }
355
356 static
357 char *
convert_bval2sid(BerValue * bval,uint32_t * rid)358 convert_bval2sid(BerValue *bval, uint32_t *rid)
359 {
360 adutils_sid_t sid;
361
362 if (adutils_getsid(bval, &sid) < 0)
363 return (NULL);
364
365 /*
366 * If desired and if the SID is what should be a domain/computer
367 * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then
368 * save the last RID and truncate the SID
369 */
370 if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5)
371 *rid = sid.sub_authorities[--sid.sub_authority_count];
372 return (adutils_sid2txt(&sid));
373 }
374
375
376 /*
377 * Return a NUL-terminated stringified SID from the value of an
378 * objectSid attribute and put the last RID in *rid.
379 */
380 char *
adutils_bv_objsid2sidstr(BerValue * bval,uint32_t * rid)381 adutils_bv_objsid2sidstr(BerValue *bval, uint32_t *rid)
382 {
383 char *sid;
384
385 if (bval == NULL)
386 return (NULL);
387 /* objectSid is single valued */
388 if ((sid = convert_bval2sid(bval, rid)) == NULL)
389 return (NULL);
390 return (sid);
391 }
392
393 static
394 char *
adutils_sid_ber2str(BerValue * bval)395 adutils_sid_ber2str(BerValue *bval)
396 {
397 return (adutils_bv_objsid2sidstr(bval, NULL));
398 }
399
400
401 /*
402 * Extract an int from the Ber value
403 * Return B_TRUE if a valid integer was found, B_FALSE if not.
404 */
405 boolean_t
adutils_bv_uint(BerValue * bval,unsigned int * result)406 adutils_bv_uint(BerValue *bval, unsigned int *result)
407 {
408 char buf[40]; /* big enough for any int */
409 unsigned int tmp;
410 char *p;
411
412 *result = 0; /* for error cases */
413
414 if (bval == NULL || bval->bv_val == NULL)
415 return (B_FALSE);
416 if (bval->bv_len >= sizeof (buf))
417 return (B_FALSE);
418
419 (void) memcpy(buf, bval->bv_val, bval->bv_len);
420 buf[bval->bv_len] = '\0';
421
422 tmp = strtoul(buf, &p, 10);
423
424 /* Junk after the number? */
425 if (*p != '\0')
426 return (B_FALSE);
427
428 *result = tmp;
429
430 return (B_TRUE);
431 }
432
433 /* Return a NUL-terminated string from the Ber value */
434 char *
adutils_bv_str(BerValue * bval)435 adutils_bv_str(BerValue *bval)
436 {
437 char *s;
438
439 if (bval == NULL || bval->bv_val == NULL)
440 return (NULL);
441 if ((s = malloc(bval->bv_len + 1)) == NULL)
442 return (NULL);
443 (void) snprintf(s, bval->bv_len + 1, "%.*s", bval->bv_len,
444 bval->bv_val);
445 return (s);
446 }
447
448 /*ARGSUSED*/
449 int
saslcallback(LDAP * ld,unsigned flags,void * defaults,void * prompts)450 saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts)
451 {
452 sasl_interact_t *interact;
453
454 if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
455 return (LDAP_PARAM_ERROR);
456
457 /* There should be no extra arguemnts for SASL/GSSAPI authentication */
458 for (interact = prompts; interact->id != SASL_CB_LIST_END;
459 interact++) {
460 interact->result = NULL;
461 interact->len = 0;
462 }
463 return (LDAP_SUCCESS);
464 }
465
466
467 #define ADCONN_TIME 300
468
469 /*
470 * Idle connection reaping side of connection management
471 */
472 void
adutils_reap_idle_connections()473 adutils_reap_idle_connections()
474 {
475 adutils_host_t *adh;
476 time_t now;
477
478 (void) pthread_mutex_lock(&adhostlock);
479 now = time(NULL);
480 for (adh = host_head; adh != NULL; adh = adh->next) {
481 (void) pthread_mutex_lock(&adh->lock);
482 if (adh->ref == 0 && adh->idletime != 0 &&
483 adh->idletime + ADCONN_TIME < now) {
484 if (adh->ld) {
485 (void) ldap_unbind(adh->ld);
486 adh->ld = NULL;
487 adh->idletime = 0;
488 adh->ref = 0;
489 }
490 }
491 (void) pthread_mutex_unlock(&adh->lock);
492 }
493 (void) pthread_mutex_unlock(&adhostlock);
494 }
495
496
497 adutils_rc
adutils_ad_alloc(adutils_ad_t ** new_ad,const char * domain_name,adutils_ad_partition_t part)498 adutils_ad_alloc(adutils_ad_t **new_ad, const char *domain_name,
499 adutils_ad_partition_t part)
500 {
501 adutils_ad_t *ad;
502
503 *new_ad = NULL;
504
505 if ((ad = calloc(1, sizeof (*ad))) == NULL)
506 return (ADUTILS_ERR_MEMORY);
507 ad->ref = 1;
508 ad->partition = part;
509
510 /* domain_name is required iff we are talking directly to a DC */
511 if (part == ADUTILS_AD_DATA) {
512 assert(domain_name != NULL);
513 assert(*domain_name != '\0');
514
515 ad->basedn = adutils_dns2dn(domain_name);
516 } else {
517 assert(domain_name == NULL);
518 ad->basedn = strdup("");
519 }
520 if (ad->basedn == NULL)
521 goto err;
522
523 if (pthread_mutex_init(&ad->lock, NULL) != 0)
524 goto err;
525 *new_ad = ad;
526 return (ADUTILS_SUCCESS);
527
528 err:
529 free(ad->basedn);
530 free(ad);
531 return (ADUTILS_ERR_MEMORY);
532 }
533
534 void
adutils_ad_free(adutils_ad_t ** ad)535 adutils_ad_free(adutils_ad_t **ad)
536 {
537 adutils_host_t *p;
538 adutils_host_t *prev;
539
540 if (ad == NULL || *ad == NULL)
541 return;
542
543 (void) pthread_mutex_lock(&(*ad)->lock);
544
545 if (atomic_dec_32_nv(&(*ad)->ref) > 0) {
546 (void) pthread_mutex_unlock(&(*ad)->lock);
547 *ad = NULL;
548 return;
549 }
550
551 (void) pthread_mutex_lock(&adhostlock);
552 prev = NULL;
553 p = host_head;
554 while (p != NULL) {
555 if (p->owner != (*ad)) {
556 prev = p;
557 p = p->next;
558 continue;
559 } else {
560 delete_ds((*ad), p->host, p->port);
561 if (prev == NULL)
562 p = host_head;
563 else
564 p = prev->next;
565 }
566 }
567 (void) pthread_mutex_unlock(&adhostlock);
568
569 (void) pthread_mutex_unlock(&(*ad)->lock);
570 (void) pthread_mutex_destroy(&(*ad)->lock);
571
572 if ((*ad)->known_domains)
573 free((*ad)->known_domains);
574 free((*ad)->basedn);
575 free(*ad);
576
577 *ad = NULL;
578 }
579
580 static
581 int
open_conn(adutils_host_t * adh,int timeoutsecs)582 open_conn(adutils_host_t *adh, int timeoutsecs)
583 {
584 int zero = 0;
585 int ldversion, rc;
586 int timeoutms = timeoutsecs * 1000;
587
588 if (adh == NULL)
589 return (0);
590
591 (void) pthread_mutex_lock(&adh->lock);
592
593 if (!adh->dead && adh->ld != NULL)
594 /* done! */
595 goto out;
596
597 if (adh->ld != NULL) {
598 (void) ldap_unbind(adh->ld);
599 adh->ld = NULL;
600 }
601 adh->num_requests = 0;
602
603 atomic_inc_64(&adh->generation);
604
605 /* Open and bind an LDAP connection */
606 adh->ld = ldap_init(adh->host, adh->port);
607 if (adh->ld == NULL) {
608 logger(LOG_INFO, "ldap_init() to server "
609 "%s port %d failed. (%s)", adh->host,
610 adh->port, strerror(errno));
611 goto out;
612 }
613 ldversion = LDAP_VERSION3;
614 (void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
615 (void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
616 (void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero);
617 (void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero);
618 (void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
619 (void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
620
621 rc = adutils_set_thread_functions(adh->ld);
622 if (rc != LDAP_SUCCESS) {
623 /* Error has already been logged */
624 (void) ldap_unbind(adh->ld);
625 adh->ld = NULL;
626 goto out;
627 }
628
629 rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */,
630 adh->saslmech, NULL, NULL, adh->saslflags, &saslcallback,
631 NULL);
632
633 if (rc != LDAP_SUCCESS) {
634 logger(LOG_INFO, "ldap_sasl_interactive_bind_s() to server "
635 "%s port %d failed. (%s)", adh->host, adh->port,
636 ldap_err2string(rc));
637 ldap_perror(adh->ld, adh->host);
638 (void) ldap_unbind(adh->ld);
639 adh->ld = NULL;
640 goto out;
641 }
642
643 logger(LOG_DEBUG, "Using server %s:%d",
644 adh->host, adh->port);
645
646 out:
647 if (adh->ld != NULL) {
648 atomic_inc_32(&adh->ref);
649 adh->idletime = time(NULL);
650 adh->dead = 0;
651 (void) pthread_mutex_unlock(&adh->lock);
652 return (1);
653 }
654
655 (void) pthread_mutex_unlock(&adh->lock);
656 return (0);
657 }
658
659
660 /*
661 * Connection management: find an open connection or open one
662 */
663 static
664 adutils_host_t *
get_conn(adutils_ad_t * ad)665 get_conn(adutils_ad_t *ad)
666 {
667 adutils_host_t *adh = NULL;
668 int tries;
669 int dscount = 0;
670 int timeoutsecs = ADUTILS_LDAP_OPEN_TIMEOUT;
671
672 retry:
673 (void) pthread_mutex_lock(&adhostlock);
674
675 if (host_head == NULL) {
676 (void) pthread_mutex_unlock(&adhostlock);
677 goto out;
678 }
679
680 if (dscount == 0) {
681 /*
682 * First try: count the number of DSes.
683 *
684 * Integer overflow is not an issue -- we can't have so many
685 * DSes because they won't fit even DNS over TCP, and SMF
686 * shouldn't let you set so many.
687 */
688 for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) {
689 if (adh->owner == ad)
690 dscount++;
691 }
692
693 if (dscount == 0) {
694 (void) pthread_mutex_unlock(&adhostlock);
695 goto out;
696 }
697
698 tries = dscount * 3; /* three tries per-ds */
699
700 /*
701 * Begin round-robin at the next DS in the list after the last
702 * one that we had a connection to, else start with the first
703 * DS in the list.
704 */
705 adh = ad->last_adh;
706 }
707
708 /*
709 * Round-robin -- pick the next one on the list; if the list
710 * changes on us, no big deal, we'll just potentially go
711 * around the wrong number of times.
712 */
713 for (;;) {
714 if (adh != NULL && adh->owner == ad && adh->ld != NULL &&
715 !adh->dead)
716 break;
717 if (adh == NULL || (adh = adh->next) == NULL)
718 adh = host_head;
719 if (adh->owner == ad)
720 break;
721 }
722
723 ad->last_adh = adh;
724 (void) pthread_mutex_unlock(&adhostlock);
725
726 /* Found suitable DS, open it if not already opened */
727 if (open_conn(adh, timeoutsecs))
728 return (adh);
729
730 tries--;
731 if ((tries % dscount) == 0)
732 timeoutsecs *= 2;
733 if (tries > 0)
734 goto retry;
735
736 out:
737 logger(LOG_NOTICE, "Couldn't open an LDAP connection to any global "
738 "catalog server!");
739 return (NULL);
740 }
741
742 static
743 void
release_conn(adutils_host_t * adh)744 release_conn(adutils_host_t *adh)
745 {
746 int delete = 0;
747
748 (void) pthread_mutex_lock(&adh->lock);
749 if (atomic_dec_32_nv(&adh->ref) == 0) {
750 if (adh->owner == NULL)
751 delete = 1;
752 adh->idletime = time(NULL);
753 }
754 (void) pthread_mutex_unlock(&adh->lock);
755
756 /* Free this host if its owner no longer exists. */
757 if (delete) {
758 (void) pthread_mutex_lock(&adhostlock);
759 delete_ds(NULL, adh->host, adh->port);
760 (void) pthread_mutex_unlock(&adhostlock);
761 }
762 }
763
764 /*
765 * Create a adutils_host_t, populate it and add it to the list of hosts.
766 */
767 adutils_rc
adutils_add_ds(adutils_ad_t * ad,const char * host,int port)768 adutils_add_ds(adutils_ad_t *ad, const char *host, int port)
769 {
770 adutils_host_t *p;
771 adutils_host_t *new = NULL;
772 int ret;
773 adutils_rc rc;
774
775 (void) pthread_mutex_lock(&adhostlock);
776 for (p = host_head; p != NULL; p = p->next) {
777 if (p->owner != ad)
778 continue;
779
780 if (strcmp(host, p->host) == 0 && p->port == port) {
781 /* already added */
782 rc = ADUTILS_SUCCESS;
783 goto err;
784 }
785 }
786
787 rc = ADUTILS_ERR_MEMORY;
788
789 /* add new entry */
790 new = (adutils_host_t *)calloc(1, sizeof (*new));
791 if (new == NULL)
792 goto err;
793 new->owner = ad;
794 new->port = port;
795 new->dead = 0;
796 new->max_requests = 80;
797 new->num_requests = 0;
798 if ((new->host = strdup(host)) == NULL)
799 goto err;
800 new->saslflags = LDAP_SASL_INTERACTIVE;
801 new->saslmech = "GSSAPI";
802
803 if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) {
804 free(new->host);
805 new->host = NULL;
806 errno = ret;
807 rc = ADUTILS_ERR_INTERNAL;
808 goto err;
809 }
810
811 /* link in */
812 rc = ADUTILS_SUCCESS;
813 new->next = host_head;
814 host_head = new;
815
816 err:
817 (void) pthread_mutex_unlock(&adhostlock);
818
819 if (rc != 0 && new != NULL) {
820 if (new->host != NULL) {
821 (void) pthread_mutex_destroy(&new->lock);
822 free(new->host);
823 }
824 free(new);
825 }
826
827 return (rc);
828 }
829
830 /*
831 * Free a DS configuration.
832 * Caller must lock the adhostlock mutex
833 */
834 static
835 void
delete_ds(adutils_ad_t * ad,const char * host,int port)836 delete_ds(adutils_ad_t *ad, const char *host, int port)
837 {
838 adutils_host_t **p, *q;
839
840 for (p = &host_head; *p != NULL; p = &((*p)->next)) {
841 if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 ||
842 (*p)->port != port)
843 continue;
844 /* found */
845
846 (void) pthread_mutex_lock(&((*p)->lock));
847 if ((*p)->ref > 0) {
848 /*
849 * Still in use. Set its owner to NULL so
850 * that it can be freed when its ref count
851 * becomes 0.
852 */
853 (*p)->owner = NULL;
854 (void) pthread_mutex_unlock(&((*p)->lock));
855 break;
856 }
857 (void) pthread_mutex_unlock(&((*p)->lock));
858
859 q = *p;
860 *p = (*p)->next;
861
862 (void) pthread_mutex_destroy(&q->lock);
863
864 if (q->ld)
865 (void) ldap_unbind(q->ld);
866 if (q->host)
867 free(q->host);
868 free(q);
869 break;
870 }
871
872 }
873 /*
874 * Add known domain name and domain SID to AD configuration.
875 */
876
877 adutils_rc
adutils_add_domain(adutils_ad_t * ad,const char * domain,const char * sid)878 adutils_add_domain(adutils_ad_t *ad, const char *domain, const char *sid)
879 {
880 struct known_domain *new;
881 int num = ad->num_known_domains;
882
883 ad->num_known_domains++;
884 new = realloc(ad->known_domains,
885 sizeof (struct known_domain) * ad->num_known_domains);
886 if (new != NULL) {
887 ad->known_domains = new;
888 (void) strlcpy(ad->known_domains[num].name, domain,
889 sizeof (ad->known_domains[num].name));
890 (void) strlcpy(ad->known_domains[num].sid, sid,
891 sizeof (ad->known_domains[num].sid));
892 return (ADUTILS_SUCCESS);
893 } else {
894 if (ad->known_domains != NULL) {
895 free(ad->known_domains);
896 ad->known_domains = NULL;
897 }
898 ad->num_known_domains = 0;
899 return (ADUTILS_ERR_MEMORY);
900 }
901 }
902
903
904 /*
905 * Check that this AD supports this domain.
906 * If there are no known domains assume that the
907 * domain is supported by this AD.
908 *
909 * Returns 1 if this domain is supported by this AD
910 * else returns 0;
911 */
912
913 int
adutils_lookup_check_domain(adutils_query_state_t * qs,const char * domain)914 adutils_lookup_check_domain(adutils_query_state_t *qs, const char *domain)
915 {
916 adutils_ad_t *ad = qs->qadh->owner;
917 int i;
918
919 for (i = 0; i < ad->num_known_domains; i++) {
920 if (domain_eq(domain, ad->known_domains[i].name))
921 return (1);
922 }
923
924 return ((i == 0) ? 1 : 0);
925 }
926
927
928 /*
929 * Check that this AD supports the SID prefix.
930 * The SID prefix should match the domain SID.
931 * If there are no known domains assume that the
932 * SID prefix is supported by this AD.
933 *
934 * Returns 1 if this sid prefix is supported by this AD
935 * else returns 0;
936 */
937
938 int
adutils_lookup_check_sid_prefix(adutils_query_state_t * qs,const char * sid)939 adutils_lookup_check_sid_prefix(adutils_query_state_t *qs, const char *sid)
940 {
941 adutils_ad_t *ad = qs->qadh->owner;
942 int i;
943
944
945 for (i = 0; i < ad->num_known_domains; i++) {
946 if (strcmp(sid, ad->known_domains[i].sid) == 0)
947 return (1);
948 }
949
950 return ((i == 0) ? 1 : 0);
951 }
952
953
954 adutils_rc
adutils_lookup_batch_start(adutils_ad_t * ad,int nqueries,adutils_ldap_res_search_cb ldap_res_search_cb,void * ldap_res_search_argp,adutils_query_state_t ** state)955 adutils_lookup_batch_start(adutils_ad_t *ad, int nqueries,
956 adutils_ldap_res_search_cb ldap_res_search_cb,
957 void *ldap_res_search_argp,
958 adutils_query_state_t **state)
959 {
960 adutils_query_state_t *new_state;
961 adutils_host_t *adh = NULL;
962
963 if (ad == NULL)
964 return (ADUTILS_ERR_INTERNAL);
965
966 *state = NULL;
967 adh = get_conn(ad);
968 if (adh == NULL)
969 return (ADUTILS_ERR_RETRIABLE_NET_ERR);
970
971 new_state = calloc(1, sizeof (adutils_query_state_t) +
972 (nqueries - 1) * sizeof (adutils_q_t));
973 if (new_state == NULL)
974 return (ADUTILS_ERR_MEMORY);
975
976 new_state->ref_cnt = 1;
977 new_state->qadh = adh;
978 new_state->qsize = nqueries;
979 new_state->qadh_gen = adh->generation;
980 new_state->qcount = 0;
981 new_state->ldap_res_search_cb = ldap_res_search_cb;
982 new_state->ldap_res_search_argp = ldap_res_search_argp;
983 (void) pthread_cond_init(&new_state->cv, NULL);
984
985 (void) pthread_mutex_lock(&qstatelock);
986 new_state->next = qstatehead;
987 qstatehead = new_state;
988 (void) pthread_mutex_unlock(&qstatelock);
989 *state = new_state;
990
991 return (ADUTILS_SUCCESS);
992 }
993
994 /*
995 * Find the adutils_query_state_t to which a given LDAP result msgid on a
996 * given connection belongs. This routine increaments the reference count
997 * so that the object can not be freed. adutils_lookup_batch_unlock()
998 * must be called to decreament the reference count.
999 */
1000 static
1001 int
msgid2query(adutils_host_t * adh,int msgid,adutils_query_state_t ** state,int * qid)1002 msgid2query(adutils_host_t *adh, int msgid,
1003 adutils_query_state_t **state, int *qid)
1004 {
1005 adutils_query_state_t *p;
1006 int i;
1007 int ret;
1008
1009 (void) pthread_mutex_lock(&qstatelock);
1010 for (p = qstatehead; p != NULL; p = p->next) {
1011 if (p->qadh != adh || adh->generation != p->qadh_gen)
1012 continue;
1013 for (i = 0; i < p->qcount; i++) {
1014 if ((p->queries[i]).msgid == msgid) {
1015 if (!p->qdead) {
1016 p->ref_cnt++;
1017 *state = p;
1018 *qid = i;
1019 ret = 1;
1020 } else
1021 ret = 0;
1022 (void) pthread_mutex_unlock(&qstatelock);
1023 return (ret);
1024 }
1025 }
1026 }
1027 (void) pthread_mutex_unlock(&qstatelock);
1028 return (0);
1029 }
1030
1031 static
1032 int
check_for_binary_attrs(const char * attr)1033 check_for_binary_attrs(const char *attr)
1034 {
1035 int i;
1036 for (i = 0; binattrs[i].name != NULL; i++) {
1037 if (strcasecmp(binattrs[i].name, attr) == 0)
1038 return (i);
1039 }
1040 return (-1);
1041 }
1042
1043 static
1044 void
free_entry(adutils_entry_t * entry)1045 free_entry(adutils_entry_t *entry)
1046 {
1047 int i, j;
1048 adutils_attr_t *ap;
1049
1050 if (entry == NULL)
1051 return;
1052 if (entry->attr_nvpairs == NULL) {
1053 free(entry);
1054 return;
1055 }
1056 for (i = 0; i < entry->num_nvpairs; i++) {
1057 ap = &entry->attr_nvpairs[i];
1058 if (ap->attr_name == NULL) {
1059 ldap_value_free(ap->attr_values);
1060 continue;
1061 }
1062 if (check_for_binary_attrs(ap->attr_name) >= 0) {
1063 free(ap->attr_name);
1064 if (ap->attr_values == NULL)
1065 continue;
1066 for (j = 0; j < ap->num_values; j++)
1067 free(ap->attr_values[j]);
1068 free(ap->attr_values);
1069 } else if (strcasecmp(ap->attr_name, "dn") == 0) {
1070 free(ap->attr_name);
1071 ldap_memfree(ap->attr_values[0]);
1072 free(ap->attr_values);
1073 } else {
1074 free(ap->attr_name);
1075 ldap_value_free(ap->attr_values);
1076 }
1077 }
1078 free(entry->attr_nvpairs);
1079 free(entry);
1080 }
1081
1082 void
adutils_freeresult(adutils_result_t ** result)1083 adutils_freeresult(adutils_result_t **result)
1084 {
1085 adutils_entry_t *e, *next;
1086
1087 if (result == NULL || *result == NULL)
1088 return;
1089 if ((*result)->entries == NULL) {
1090 free(*result);
1091 *result = NULL;
1092 return;
1093 }
1094 for (e = (*result)->entries; e != NULL; e = next) {
1095 next = e->next;
1096 free_entry(e);
1097 }
1098 free(*result);
1099 *result = NULL;
1100 }
1101
1102 const adutils_entry_t *
adutils_getfirstentry(adutils_result_t * result)1103 adutils_getfirstentry(adutils_result_t *result)
1104 {
1105 if (result != NULL)
1106 return (result->entries);
1107 return (NULL);
1108 }
1109
1110
1111 char **
adutils_getattr(const adutils_entry_t * entry,const char * attrname)1112 adutils_getattr(const adutils_entry_t *entry, const char *attrname)
1113 {
1114 int i;
1115 adutils_attr_t *ap;
1116
1117 if (entry == NULL || entry->attr_nvpairs == NULL)
1118 return (NULL);
1119 for (i = 0; i < entry->num_nvpairs; i++) {
1120 ap = &entry->attr_nvpairs[i];
1121 if (ap->attr_name != NULL &&
1122 strcasecmp(ap->attr_name, attrname) == 0)
1123 return (ap->attr_values);
1124 }
1125 return (NULL);
1126 }
1127
1128
1129 /*
1130 * Queue LDAP result for the given query.
1131 *
1132 * Return values:
1133 * 0 success
1134 * -1 ignore result
1135 * -2 error
1136 */
1137 static
1138 int
make_entry(adutils_q_t * q,adutils_host_t * adh,LDAPMessage * search_res,adutils_entry_t ** entry)1139 make_entry(adutils_q_t *q, adutils_host_t *adh, LDAPMessage *search_res,
1140 adutils_entry_t **entry)
1141 {
1142 BerElement *ber = NULL;
1143 BerValue **bvalues = NULL;
1144 char **strvalues;
1145 char *attr = NULL, *dn = NULL, *domain = NULL;
1146 adutils_entry_t *ep;
1147 adutils_attr_t *ap;
1148 int i, j, b, ret = -2;
1149
1150 *entry = NULL;
1151
1152 /* Check that this is the domain that we were looking for */
1153 if ((dn = ldap_get_dn(adh->ld, search_res)) == NULL)
1154 return (-2);
1155 if ((domain = adutils_dn2dns(dn)) == NULL) {
1156 ldap_memfree(dn);
1157 return (-2);
1158 }
1159 if (q->edomain != NULL) {
1160 if (!domain_eq(q->edomain, domain)) {
1161 ldap_memfree(dn);
1162 free(domain);
1163 return (-1);
1164 }
1165 }
1166 free(domain);
1167
1168 /* Allocate memory for the entry */
1169 if ((ep = calloc(1, sizeof (*ep))) == NULL)
1170 goto out;
1171
1172 /* For 'dn' */
1173 ep->num_nvpairs = 1;
1174
1175 /* Count the number of name-value pairs for this entry */
1176 for (attr = ldap_first_attribute(adh->ld, search_res, &ber);
1177 attr != NULL;
1178 attr = ldap_next_attribute(adh->ld, search_res, ber)) {
1179 ep->num_nvpairs++;
1180 ldap_memfree(attr);
1181 }
1182 ber_free(ber, 0);
1183 ber = NULL;
1184
1185 /* Allocate array for the attribute name-value pairs */
1186 ep->attr_nvpairs = calloc(ep->num_nvpairs, sizeof (*ep->attr_nvpairs));
1187 if (ep->attr_nvpairs == NULL) {
1188 ep->num_nvpairs = 0;
1189 goto out;
1190 }
1191
1192 /* For dn */
1193 ap = &ep->attr_nvpairs[0];
1194 if ((ap->attr_name = strdup("dn")) == NULL)
1195 goto out;
1196 ap->num_values = 1;
1197 ap->attr_values = calloc(ap->num_values, sizeof (*ap->attr_values));
1198 if (ap->attr_values == NULL) {
1199 ap->num_values = 0;
1200 goto out;
1201 }
1202 ap->attr_values[0] = dn;
1203 dn = NULL;
1204
1205 for (attr = ldap_first_attribute(adh->ld, search_res, &ber), i = 1;
1206 attr != NULL;
1207 ldap_memfree(attr), i++,
1208 attr = ldap_next_attribute(adh->ld, search_res, ber)) {
1209 ap = &ep->attr_nvpairs[i];
1210 if ((ap->attr_name = strdup(attr)) == NULL)
1211 goto out;
1212
1213 if ((b = check_for_binary_attrs(attr)) >= 0) {
1214 bvalues =
1215 ldap_get_values_len(adh->ld, search_res, attr);
1216 if (bvalues == NULL)
1217 continue;
1218 ap->num_values = ldap_count_values_len(bvalues);
1219 if (ap->num_values == 0) {
1220 ldap_value_free_len(bvalues);
1221 bvalues = NULL;
1222 continue;
1223 }
1224 ap->attr_values = calloc(ap->num_values,
1225 sizeof (*ap->attr_values));
1226 if (ap->attr_values == NULL) {
1227 ap->num_values = 0;
1228 goto out;
1229 }
1230 for (j = 0; j < ap->num_values; j++) {
1231 ap->attr_values[j] =
1232 binattrs[b].ber2str(bvalues[j]);
1233 if (ap->attr_values[j] == NULL)
1234 goto out;
1235 }
1236 ldap_value_free_len(bvalues);
1237 bvalues = NULL;
1238 continue;
1239 }
1240
1241 strvalues = ldap_get_values(adh->ld, search_res, attr);
1242 if (strvalues == NULL)
1243 continue;
1244 ap->num_values = ldap_count_values(strvalues);
1245 if (ap->num_values == 0) {
1246 ldap_value_free(strvalues);
1247 continue;
1248 }
1249 ap->attr_values = strvalues;
1250 }
1251
1252 ret = 0;
1253 out:
1254 ldap_memfree(attr);
1255 ldap_memfree(dn);
1256 ber_free(ber, 0);
1257 ldap_value_free_len(bvalues);
1258 if (ret < 0)
1259 free_entry(ep);
1260 else
1261 *entry = ep;
1262 return (ret);
1263 }
1264
1265 /*
1266 * Put the search result onto the given adutils_q_t.
1267 * Returns: 0 success
1268 * < 0 error
1269 */
1270 static
1271 int
add_entry(adutils_host_t * adh,adutils_q_t * q,LDAPMessage * search_res)1272 add_entry(adutils_host_t *adh, adutils_q_t *q, LDAPMessage *search_res)
1273 {
1274 int ret = -1;
1275 adutils_entry_t *entry = NULL;
1276 adutils_result_t *res;
1277
1278 ret = make_entry(q, adh, search_res, &entry);
1279 if (ret < -1) {
1280 *q->rc = ADUTILS_ERR_MEMORY;
1281 goto out;
1282 } else if (ret == -1) {
1283 /* ignore result */
1284 goto out;
1285 }
1286 if (*q->result == NULL) {
1287 res = calloc(1, sizeof (*res));
1288 if (res == NULL) {
1289 *q->rc = ADUTILS_ERR_MEMORY;
1290 goto out;
1291 }
1292 res->num_entries = 1;
1293 res->entries = entry;
1294 *q->result = res;
1295 } else {
1296 res = *q->result;
1297 entry->next = res->entries;
1298 res->entries = entry;
1299 res->num_entries++;
1300 }
1301 *q->rc = ADUTILS_SUCCESS;
1302 entry = NULL;
1303 ret = 0;
1304
1305 out:
1306 free_entry(entry);
1307 return (ret);
1308 }
1309
1310 /*
1311 * Try to get a result; if there is one, find the corresponding
1312 * adutils_q_t and process the result.
1313 *
1314 * Returns: 0 success
1315 * -1 error
1316 */
1317 static
1318 int
get_adobject_batch(adutils_host_t * adh,struct timeval * timeout)1319 get_adobject_batch(adutils_host_t *adh, struct timeval *timeout)
1320 {
1321 adutils_query_state_t *query_state;
1322 LDAPMessage *res = NULL;
1323 int rc, ret, msgid, qid;
1324 adutils_q_t *que;
1325 int num;
1326
1327 (void) pthread_mutex_lock(&adh->lock);
1328 if (adh->dead || adh->num_requests == 0) {
1329 ret = (adh->dead) ? -1 : -2;
1330 (void) pthread_mutex_unlock(&adh->lock);
1331 return (ret);
1332 }
1333
1334 /* Get one result */
1335 rc = ldap_result(adh->ld, LDAP_RES_ANY, 0, timeout, &res);
1336 if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) ||
1337 rc < 0)
1338 adh->dead = 1;
1339
1340 if (rc == LDAP_RES_SEARCH_RESULT && adh->num_requests > 0)
1341 adh->num_requests--;
1342 if (adh->dead) {
1343 num = adh->num_requests;
1344 (void) pthread_mutex_unlock(&adh->lock);
1345 logger(LOG_DEBUG,
1346 "AD ldap_result error - %d queued requests", num);
1347 return (-1);
1348 }
1349
1350 switch (rc) {
1351 case LDAP_RES_SEARCH_RESULT:
1352 msgid = ldap_msgid(res);
1353 if (msgid2query(adh, msgid, &query_state, &qid)) {
1354 if (query_state->ldap_res_search_cb != NULL) {
1355 /*
1356 * We use the caller-provided callback
1357 * to process the result.
1358 */
1359 query_state->ldap_res_search_cb(
1360 adh->ld, &res, rc, qid,
1361 query_state->ldap_res_search_argp);
1362 (void) pthread_mutex_unlock(&adh->lock);
1363 } else {
1364 /*
1365 * No callback. We fallback to our
1366 * default behaviour. All the entries
1367 * gotten from this search have been
1368 * added to the result list during
1369 * LDAP_RES_SEARCH_ENTRY (see below).
1370 * Here we set the return status to
1371 * notfound if the result is still empty.
1372 */
1373 (void) pthread_mutex_unlock(&adh->lock);
1374 que = &(query_state->queries[qid]);
1375 if (*que->result == NULL)
1376 *que->rc = ADUTILS_ERR_NOTFOUND;
1377 }
1378 atomic_dec_32(&query_state->qinflight);
1379 adutils_lookup_batch_unlock(&query_state);
1380 } else {
1381 num = adh->num_requests;
1382 (void) pthread_mutex_unlock(&adh->lock);
1383 logger(LOG_DEBUG,
1384 "AD cannot find message ID (%d) "
1385 "- %d queued requests",
1386 msgid, num);
1387 }
1388 (void) ldap_msgfree(res);
1389 ret = 0;
1390 break;
1391
1392 case LDAP_RES_SEARCH_ENTRY:
1393 msgid = ldap_msgid(res);
1394 if (msgid2query(adh, msgid, &query_state, &qid)) {
1395 if (query_state->ldap_res_search_cb != NULL) {
1396 /*
1397 * We use the caller-provided callback
1398 * to process the entry.
1399 */
1400 query_state->ldap_res_search_cb(
1401 adh->ld, &res, rc, qid,
1402 query_state->ldap_res_search_argp);
1403 (void) pthread_mutex_unlock(&adh->lock);
1404 } else {
1405 /*
1406 * No callback. We fallback to our
1407 * default behaviour. This entry
1408 * will be added to the result list.
1409 */
1410 que = &(query_state->queries[qid]);
1411 rc = add_entry(adh, que, res);
1412 (void) pthread_mutex_unlock(&adh->lock);
1413 if (rc < 0) {
1414 logger(LOG_DEBUG,
1415 "Failed to queue entry by "
1416 "message ID (%d) "
1417 "- %d queued requests",
1418 msgid, num);
1419 }
1420 }
1421 adutils_lookup_batch_unlock(&query_state);
1422 } else {
1423 num = adh->num_requests;
1424 (void) pthread_mutex_unlock(&adh->lock);
1425 logger(LOG_DEBUG,
1426 "AD cannot find message ID (%d) "
1427 "- %d queued requests",
1428 msgid, num);
1429 }
1430 (void) ldap_msgfree(res);
1431 ret = 0;
1432 break;
1433
1434 case LDAP_RES_SEARCH_REFERENCE:
1435 /*
1436 * We have no need for these at the moment. Eventually,
1437 * when we query things that we can't expect to find in
1438 * the Global Catalog then we'll need to learn to follow
1439 * references.
1440 */
1441 (void) pthread_mutex_unlock(&adh->lock);
1442 (void) ldap_msgfree(res);
1443 ret = 0;
1444 break;
1445
1446 default:
1447 /* timeout or error; treat the same */
1448 (void) pthread_mutex_unlock(&adh->lock);
1449 ret = -1;
1450 break;
1451 }
1452
1453 return (ret);
1454 }
1455
1456 /*
1457 * This routine decreament the reference count of the
1458 * adutils_query_state_t
1459 */
1460 static void
adutils_lookup_batch_unlock(adutils_query_state_t ** state)1461 adutils_lookup_batch_unlock(adutils_query_state_t **state)
1462 {
1463 /*
1464 * Decrement reference count with qstatelock locked
1465 */
1466 (void) pthread_mutex_lock(&qstatelock);
1467 (*state)->ref_cnt--;
1468 /*
1469 * If there are no references wakup the allocating thread
1470 */
1471 if ((*state)->ref_cnt <= 1)
1472 (void) pthread_cond_signal(&(*state)->cv);
1473 (void) pthread_mutex_unlock(&qstatelock);
1474 *state = NULL;
1475 }
1476
1477 /*
1478 * This routine frees the adutils_query_state_t structure
1479 * If the reference count is greater than 1 it waits
1480 * for the other threads to finish using it.
1481 */
1482 void
adutils_lookup_batch_release(adutils_query_state_t ** state)1483 adutils_lookup_batch_release(adutils_query_state_t **state)
1484 {
1485 adutils_query_state_t **p;
1486 int i;
1487
1488 if (state == NULL || *state == NULL)
1489 return;
1490
1491 /*
1492 * Set state to dead to stop further operations.
1493 * Wait for reference count with qstatelock locked
1494 * to get to one.
1495 */
1496 (void) pthread_mutex_lock(&qstatelock);
1497 (*state)->qdead = 1;
1498 while ((*state)->ref_cnt > 1) {
1499 (void) pthread_cond_wait(&(*state)->cv, &qstatelock);
1500 }
1501
1502 /* Remove this state struct from the list of state structs */
1503 for (p = &qstatehead; *p != NULL; p = &(*p)->next) {
1504 if (*p == (*state)) {
1505 *p = (*state)->next;
1506 break;
1507 }
1508 }
1509 (void) pthread_mutex_unlock(&qstatelock);
1510 (void) pthread_cond_destroy(&(*state)->cv);
1511 release_conn((*state)->qadh);
1512
1513 /* Clear results for queries that failed */
1514 for (i = 0; i < (*state)->qcount; i++) {
1515 if (*(*state)->queries[i].rc != ADUTILS_SUCCESS) {
1516 adutils_freeresult((*state)->queries[i].result);
1517 }
1518 }
1519 free(*state);
1520 *state = NULL;
1521 }
1522
1523
1524 /*
1525 * This routine waits for other threads using the
1526 * adutils_query_state_t structure to finish.
1527 * If the reference count is greater than 1 it waits
1528 * for the other threads to finish using it.
1529 */
1530 static
1531 void
adutils_lookup_batch_wait(adutils_query_state_t * state)1532 adutils_lookup_batch_wait(adutils_query_state_t *state)
1533 {
1534 /*
1535 * Set state to dead to stop further operation.
1536 * stating.
1537 * Wait for reference count to get to one
1538 * with qstatelock locked.
1539 */
1540 (void) pthread_mutex_lock(&qstatelock);
1541 state->qdead = 1;
1542 while (state->ref_cnt > 1) {
1543 (void) pthread_cond_wait(&state->cv, &qstatelock);
1544 }
1545 (void) pthread_mutex_unlock(&qstatelock);
1546 }
1547
1548 /*
1549 * Process active queries in the AD lookup batch and then finalize the
1550 * result.
1551 */
1552 adutils_rc
adutils_lookup_batch_end(adutils_query_state_t ** state)1553 adutils_lookup_batch_end(adutils_query_state_t **state)
1554 {
1555 int rc = LDAP_SUCCESS;
1556 adutils_rc ad_rc = ADUTILS_SUCCESS;
1557 struct timeval tv;
1558
1559 tv.tv_sec = ADUTILS_SEARCH_TIMEOUT;
1560 tv.tv_usec = 0;
1561
1562 /* Process results until done or until timeout, if given */
1563 while ((*state)->qinflight > 0) {
1564 if ((rc = get_adobject_batch((*state)->qadh,
1565 &tv)) != 0)
1566 break;
1567 }
1568 (*state)->qdead = 1;
1569 /* Wait for other threads processing search result to finish */
1570 adutils_lookup_batch_wait(*state);
1571 if (rc == -1 || (*state)->qinflight != 0)
1572 ad_rc = ADUTILS_ERR_RETRIABLE_NET_ERR;
1573 adutils_lookup_batch_release(state);
1574 return (ad_rc);
1575 }
1576
1577 /*
1578 * Send one prepared search, queue up msgid, process what results are
1579 * available
1580 */
1581 adutils_rc
adutils_lookup_batch_add(adutils_query_state_t * state,const char * filter,const char * const * attrs,const char * edomain,adutils_result_t ** result,adutils_rc * rc)1582 adutils_lookup_batch_add(adutils_query_state_t *state,
1583 const char *filter, const char * const *attrs, const char *edomain,
1584 adutils_result_t **result, adutils_rc *rc)
1585 {
1586 adutils_rc retcode = ADUTILS_SUCCESS;
1587 int lrc, qid;
1588 int num;
1589 int dead;
1590 struct timeval tv;
1591 adutils_q_t *q;
1592
1593 qid = atomic_inc_32_nv(&state->qcount) - 1;
1594 q = &(state->queries[qid]);
1595
1596 assert(qid < state->qsize);
1597
1598 /*
1599 * Remember the expected domain so we can check the results
1600 * against it
1601 */
1602 q->edomain = edomain;
1603
1604 /* Remember where to put the results */
1605 q->result = result;
1606 q->rc = rc;
1607
1608 /*
1609 * Provide sane defaults for the results in case we never hear
1610 * back from the DS before closing the connection.
1611 */
1612 *rc = ADUTILS_ERR_RETRIABLE_NET_ERR;
1613 if (result != NULL)
1614 *result = NULL;
1615
1616 /* Check the number of queued requests first */
1617 tv.tv_sec = ADUTILS_SEARCH_TIMEOUT;
1618 tv.tv_usec = 0;
1619 while (!state->qadh->dead &&
1620 state->qadh->num_requests > state->qadh->max_requests) {
1621 if (get_adobject_batch(state->qadh, &tv) != 0)
1622 break;
1623 }
1624
1625 /* Send this lookup, don't wait for a result here */
1626 lrc = LDAP_SUCCESS;
1627 (void) pthread_mutex_lock(&state->qadh->lock);
1628
1629 if (!state->qadh->dead) {
1630 state->qadh->idletime = time(NULL);
1631
1632 lrc = ldap_search_ext(state->qadh->ld,
1633 state->qadh->owner->basedn,
1634 LDAP_SCOPE_SUBTREE, filter, (char **)attrs,
1635 0, NULL, NULL, NULL, -1, &q->msgid);
1636
1637 if (lrc == LDAP_SUCCESS) {
1638 state->qadh->num_requests++;
1639 } else if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE ||
1640 lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN ||
1641 lrc == LDAP_UNWILLING_TO_PERFORM) {
1642 retcode = ADUTILS_ERR_RETRIABLE_NET_ERR;
1643 state->qadh->dead = 1;
1644 } else {
1645 retcode = ADUTILS_ERR_OTHER;
1646 state->qadh->dead = 1;
1647 }
1648 }
1649 dead = state->qadh->dead;
1650 num = state->qadh->num_requests;
1651 (void) pthread_mutex_unlock(&state->qadh->lock);
1652
1653 if (dead) {
1654 if (lrc != LDAP_SUCCESS)
1655 logger(LOG_DEBUG,
1656 "AD ldap_search_ext error (%s) "
1657 "- %d queued requests",
1658 ldap_err2string(lrc), num);
1659 return (retcode);
1660 }
1661
1662 atomic_inc_32(&state->qinflight);
1663
1664 /*
1665 * Reap as many requests as we can _without_ waiting to prevent
1666 * any possible TCP socket buffer starvation deadlocks.
1667 */
1668 (void) memset(&tv, 0, sizeof (tv));
1669 while (get_adobject_batch(state->qadh, &tv) == 0)
1670 ;
1671
1672 return (ADUTILS_SUCCESS);
1673 }
1674
1675 /*
1676 * Single AD lookup request implemented on top of the batch API.
1677 */
1678 adutils_rc
adutils_lookup(adutils_ad_t * ad,const char * filter,const char ** attrs,const char * domain,adutils_result_t ** result)1679 adutils_lookup(adutils_ad_t *ad, const char *filter, const char **attrs,
1680 const char *domain, adutils_result_t **result)
1681 {
1682 adutils_rc rc, brc;
1683 adutils_query_state_t *qs;
1684
1685 rc = adutils_lookup_batch_start(ad, 1, NULL, NULL, &qs);
1686 if (rc != ADUTILS_SUCCESS)
1687 return (rc);
1688
1689 rc = adutils_lookup_batch_add(qs, filter, attrs, domain, result, &brc);
1690 if (rc != ADUTILS_SUCCESS) {
1691 adutils_lookup_batch_release(&qs);
1692 return (rc);
1693 }
1694
1695 rc = adutils_lookup_batch_end(&qs);
1696 if (rc != ADUTILS_SUCCESS)
1697 return (rc);
1698 return (brc);
1699 }
1700
1701 boolean_t
domain_eq(const char * a,const char * b)1702 domain_eq(const char *a, const char *b)
1703 {
1704 int err;
1705
1706 return (u8_strcmp(a, b, 0, U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err)
1707 == 0 && err == 0);
1708 }
1709
1710 void
adutils_set_debug(enum ad_debug item,int value)1711 adutils_set_debug(enum ad_debug item, int value)
1712 {
1713 if (item >= 0 && item <= AD_DEBUG_MAX)
1714 ad_debug[item] = value;
1715 }
1716