8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libsldap / common / ns_reads.c
blob61a59e16db6981373b07dbb0127d44a2730ee8e2
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <stdlib.h>
28 #include <libintl.h>
29 #include <ctype.h>
30 #include <syslog.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <priv.h>
38 #include "ns_sldap.h"
39 #include "ns_internal.h"
40 #include "ns_cache_door.h"
41 #include "ns_connmgmt.h"
43 #define _NIS_FILTER "nisdomain=*"
44 #define _NIS_DOMAIN "nisdomain"
45 static const char *nis_domain_attrs[] = {
46 _NIS_DOMAIN,
47 (char *)NULL
50 static int validate_filter(ns_ldap_cookie_t *cookie);
52 void
53 __ns_ldap_freeEntry(ns_ldap_entry_t *ep)
55 int j, k = 0;
57 if (ep == NULL)
58 return;
60 if (ep->attr_pair == NULL) {
61 free(ep);
62 return;
64 for (j = 0; j < ep->attr_count; j++) {
65 if (ep->attr_pair[j] == NULL)
66 continue;
67 if (ep->attr_pair[j]->attrname)
68 free(ep->attr_pair[j]->attrname);
69 if (ep->attr_pair[j]->attrvalue) {
70 for (k = 0; (k < ep->attr_pair[j]->value_count) &&
71 (ep->attr_pair[j]->attrvalue[k]); k++) {
72 free(ep->attr_pair[j]->attrvalue[k]);
74 free(ep->attr_pair[j]->attrvalue);
76 free(ep->attr_pair[j]);
78 free(ep->attr_pair);
79 free(ep);
82 static void
83 _freeControlList(LDAPControl ***ctrls)
85 LDAPControl **ctrl;
87 if (ctrls == NULL || *ctrls == NULL)
88 return;
90 for (ctrl = *ctrls; *ctrl != NULL; ctrl++)
91 ldap_control_free(*ctrl);
92 free(*ctrls);
93 *ctrls = NULL;
96 * Convert attribute type in a RDN that has an attribute mapping to the
97 * original mappped type.
98 * e.g.
99 * cn<->cn-st and iphostnumber<->iphostnumber-st
100 * cn-st=aaa+iphostnumber-st=10.10.01.01
101 * is mapped to
102 * cn=aaa+iphostnumber=10.10.01.01
104 * Input - service: e.g. hosts, passwd etc.
105 * rdn: RDN
106 * Return: NULL - No attribute mapping in the RDN
107 * Non-NULL - The attribute type(s) in the RDN are mapped and
108 * the memory is allocated for the new rdn.
111 static char *
112 _cvtRDN(const char *service, const char *rdn) {
113 char **attrs, **mapped_attrs, **mapp, *type, *value, *attr;
114 char *new_rdn = NULL;
115 int nAttr = 0, i, attr_mapped, len = 0;
117 /* Break down "type=value\0" pairs. Assume RDN is normalized */
118 if ((attrs = ldap_explode_rdn(rdn, 0)) == NULL)
119 return (NULL);
121 for (nAttr = 0; attrs[nAttr] != NULL; nAttr++);
123 if ((mapped_attrs = (char **)calloc(nAttr, sizeof (char *))) == NULL) {
124 ldap_value_free(attrs);
125 return (NULL);
128 attr_mapped = 0;
129 for (i = 0; i < nAttr; i++) {
130 /* Parse type=value pair */
131 if ((type = strtok_r(attrs[i], "=", &value)) == NULL ||
132 value == NULL)
133 goto cleanup;
134 /* Reverse map: e.g. cn-sm -> cn */
135 mapp = __ns_ldap_getOrigAttribute(service, type);
136 if (mapp != NULL && mapp[0] != NULL) {
137 /* The attribute mapping is found */
138 type = mapp[0];
139 attr_mapped = 1;
141 /* "type=value\0" */
142 len = strlen(type) + strlen(value) + 2;
144 /* Reconstruct type=value pair. A string is allocated */
145 if ((attr = (char *)calloc(1, len)) == NULL) {
146 __s_api_free2dArray(mapp);
147 goto cleanup;
149 (void) snprintf(attr, len, "%s=%s",
150 type, value);
151 mapped_attrs[i] = attr;
152 } else {
154 * No attribute mapping. attrs[i] is going to be copied
155 * later. Restore "type\0value\0" back to
156 * "type=value\0".
158 type[strlen(type)] = '=';
160 __s_api_free2dArray(mapp);
162 if (attr_mapped == 0)
163 /* No attribute mapping. Don't bother to reconstruct RDN */
164 goto cleanup;
166 len = 0;
167 /* Reconstruct RDN from type=value pairs */
168 for (i = 0; i < nAttr; i++) {
169 if (mapped_attrs[i])
170 len += strlen(mapped_attrs[i]);
171 else
172 len += strlen(attrs[i]);
173 /* Add 1 for "+" */
174 len++;
176 if ((new_rdn = (char *)calloc(1, ++len)) == NULL)
177 goto cleanup;
178 for (i = 0; i < nAttr; i++) {
179 if (i > 0)
180 /* Add seperator */
181 (void) strlcat(new_rdn, "+", len);
183 if (mapped_attrs[i])
184 (void) strlcat(new_rdn, mapped_attrs[i], len);
185 else
186 (void) strlcat(new_rdn, attrs[i], len);
189 cleanup:
190 ldap_value_free(attrs);
191 if (mapped_attrs) {
192 if (attr_mapped) {
193 for (i = 0; i < nAttr; i++) {
194 if (mapped_attrs[i])
195 free(mapped_attrs[i]);
198 free(mapped_attrs);
201 return (new_rdn);
204 * Convert attribute type in a DN that has an attribute mapping to the
205 * original mappped type.
206 * e.g
207 * The mappings are cn<->cn-sm, iphostnumber<->iphostnumber-sm
209 * dn: cn-sm=aaa+iphostnumber-sm=9.9.9.9,dc=central,dc=sun,dc=com
210 * is converted to
211 * dn: cn=aaa+iphostnumber=9.9.9.9,dc=central,dc=sun,dc=com
213 * Input - service: e.g. hosts, passwd etc.
214 * dn: the value of a distinguished name
215 * Return - NULL: error
216 * non-NULL: A converted DN and the memory is allocated
218 static char *
219 _cvtDN(const char *service, const char *dn) {
220 char **mapped_rdns;
221 char **rdns, *new_rdn, *new_dn = NULL;
222 int nRdn = 0, i, len = 0, rdn_mapped;
224 if (service == NULL || dn == NULL)
225 return (NULL);
227 if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
228 return (NULL);
230 for (nRdn = 0; rdns[nRdn] != NULL; nRdn++);
232 if ((mapped_rdns = (char **)calloc(nRdn, sizeof (char *))) == NULL) {
233 ldap_value_free(rdns);
234 return (NULL);
237 rdn_mapped = 0;
238 /* Break down RDNs in a DN */
239 for (i = 0; i < nRdn; i++) {
240 if ((new_rdn = _cvtRDN(service, rdns[i])) != NULL) {
241 mapped_rdns[i] = new_rdn;
242 rdn_mapped = 1;
245 if (rdn_mapped == 0) {
247 * No RDN contains any attribute mapping.
248 * Don't bother to reconstruct DN from RDN. Copy DN directly.
250 new_dn = strdup(dn);
251 goto cleanup;
254 * Reconstruct dn from RDNs.
255 * Calculate the length first.
257 for (i = 0; i < nRdn; i++) {
258 if (mapped_rdns[i])
259 len += strlen(mapped_rdns[i]);
260 else
261 len += strlen(rdns[i]);
263 /* add 1 for ',' */
264 len ++;
266 if ((new_dn = (char *)calloc(1, ++len)) == NULL)
267 goto cleanup;
268 for (i = 0; i < nRdn; i++) {
269 if (i > 0)
270 /* Add seperator */
271 (void) strlcat(new_dn, ",", len);
273 if (mapped_rdns[i])
274 (void) strlcat(new_dn, mapped_rdns[i], len);
275 else
276 (void) strlcat(new_dn, rdns[i], len);
280 cleanup:
281 ldap_value_free(rdns);
282 if (mapped_rdns) {
283 if (rdn_mapped) {
284 for (i = 0; i < nRdn; i++) {
285 if (mapped_rdns[i])
286 free(mapped_rdns[i]);
289 free(mapped_rdns);
292 return (new_dn);
295 * Convert a single ldap entry from a LDAPMessage
296 * into an ns_ldap_entry structure.
297 * Schema map the entry if specified in flags
300 static int
301 __s_api_cvtEntry(LDAP *ld,
302 const char *service,
303 LDAPMessage *e,
304 int flags,
305 ns_ldap_entry_t **ret,
306 ns_ldap_error_t **error)
309 ns_ldap_entry_t *ep = NULL;
310 ns_ldap_attr_t **ap = NULL;
311 BerElement *ber;
312 char *attr = NULL;
313 char **vals = NULL;
314 char **mapping;
315 char *dn;
316 int nAttrs = 0;
317 int i, j, k = 0;
318 char **gecos_mapping = NULL;
319 int gecos_val_index[3] = { -1, -1, -1};
320 char errstr[MAXERROR];
321 int schema_mapping_existed = FALSE;
322 int gecos_mapping_existed = FALSE;
323 int gecos_attr_matched;
324 int auto_service = FALSE;
325 int rc = NS_LDAP_SUCCESS;
327 if (e == NULL || ret == NULL || error == NULL)
328 return (NS_LDAP_INVALID_PARAM);
330 *error = NULL;
332 ep = (ns_ldap_entry_t *)calloc(1, sizeof (ns_ldap_entry_t));
333 if (ep == NULL)
334 return (NS_LDAP_MEMORY);
336 if (service != NULL &&
337 (strncasecmp(service, "auto_", 5) == 0 ||
338 strcasecmp(service, "automount") == 0))
339 auto_service = TRUE;
341 * see if schema mapping existed for the given service
343 mapping = __ns_ldap_getOrigAttribute(service,
344 NS_HASH_SCHEMA_MAPPING_EXISTED);
345 if (mapping) {
346 schema_mapping_existed = TRUE;
347 __s_api_free2dArray(mapping);
348 mapping = NULL;
349 } else if (auto_service) {
351 * If service == auto_* and no
352 * schema mapping found
353 * then try automount
354 * There is certain case that schema mapping exist
355 * but __ns_ldap_getOrigAttribute(service,
356 * NS_HASH_SCHEMA_MAPPING_EXISTED);
357 * returns NULL.
358 * e.g.
359 * NS_LDAP_ATTRIBUTEMAP = automount:automountMapName=AAA
360 * NS_LDAP_OBJECTCLASSMAP = automount:automountMap=MynisMap
361 * NS_LDAP_OBJECTCLASSMAP = automount:automount=MynisObject
363 * Make a check for schema_mapping_existed here
364 * so later on __s_api_convert_automountmapname won't be called
365 * unnecessarily. It is also used for attribute mapping
366 * and objectclass mapping.
368 mapping = __ns_ldap_getOrigAttribute("automount",
369 NS_HASH_SCHEMA_MAPPING_EXISTED);
370 if (mapping) {
371 schema_mapping_existed = TRUE;
372 __s_api_free2dArray(mapping);
373 mapping = NULL;
377 nAttrs = 1; /* start with 1 for the DN attr */
378 for (attr = ldap_first_attribute(ld, e, &ber); attr != NULL;
379 attr = ldap_next_attribute(ld, e, ber)) {
380 nAttrs++;
381 ldap_memfree(attr);
382 attr = NULL;
384 ber_free(ber, 0);
385 ber = NULL;
387 ep->attr_count = nAttrs;
390 * add 1 for "gecos" 1 to N attribute mapping,
391 * just in case it is needed.
392 * ep->attr_count will be updated later if that is true.
394 ap = (ns_ldap_attr_t **)calloc(ep->attr_count + 1,
395 sizeof (ns_ldap_attr_t *));
396 if (ap == NULL) {
397 __ns_ldap_freeEntry(ep);
398 ep = NULL;
399 return (NS_LDAP_MEMORY);
401 ep->attr_pair = ap;
403 /* DN attribute */
404 dn = ldap_get_dn(ld, e);
405 ap[0] = (ns_ldap_attr_t *)calloc(1, sizeof (ns_ldap_attr_t));
406 if (ap[0] == NULL) {
407 ldap_memfree(dn);
408 dn = NULL;
409 __ns_ldap_freeEntry(ep);
410 ep = NULL;
411 return (NS_LDAP_MEMORY);
414 if ((ap[0]->attrname = strdup("dn")) == NULL) {
415 ldap_memfree(dn);
416 dn = NULL;
417 __ns_ldap_freeEntry(ep);
418 ep = NULL;
419 return (NS_LDAP_INVALID_PARAM);
421 ap[0]->value_count = 1;
422 if ((ap[0]->attrvalue = (char **)
423 calloc(2, sizeof (char *))) == NULL) {
424 ldap_memfree(dn);
425 dn = NULL;
426 __ns_ldap_freeEntry(ep);
427 ep = NULL;
428 return (NS_LDAP_MEMORY);
431 if (schema_mapping_existed && ((flags & NS_LDAP_NOT_CVT_DN) == 0))
432 ap[0]->attrvalue[0] = _cvtDN(service, dn);
433 else
434 ap[0]->attrvalue[0] = strdup(dn);
436 if (ap[0]->attrvalue[0] == NULL) {
437 ldap_memfree(dn);
438 dn = NULL;
439 __ns_ldap_freeEntry(ep);
440 ep = NULL;
441 return (NS_LDAP_MEMORY);
443 ldap_memfree(dn);
444 dn = NULL;
446 if ((flags & NS_LDAP_NOMAP) == 0 && auto_service &&
447 schema_mapping_existed) {
448 rc = __s_api_convert_automountmapname(service,
449 &ap[0]->attrvalue[0],
450 error);
451 if (rc != NS_LDAP_SUCCESS) {
452 __ns_ldap_freeEntry(ep);
453 ep = NULL;
454 return (rc);
458 /* other attributes */
459 for (attr = ldap_first_attribute(ld, e, &ber), j = 1;
460 attr != NULL && j != nAttrs;
461 attr = ldap_next_attribute(ld, e, ber), j++) {
462 /* allocate new attr name */
464 if ((ap[j] = (ns_ldap_attr_t *)
465 calloc(1, sizeof (ns_ldap_attr_t))) == NULL) {
466 ber_free(ber, 0);
467 ber = NULL;
468 __ns_ldap_freeEntry(ep);
469 ep = NULL;
470 if (gecos_mapping)
471 __s_api_free2dArray(gecos_mapping);
472 gecos_mapping = NULL;
473 return (NS_LDAP_MEMORY);
476 if ((flags & NS_LDAP_NOMAP) || schema_mapping_existed == FALSE)
477 mapping = NULL;
478 else
479 mapping = __ns_ldap_getOrigAttribute(service, attr);
481 if (mapping == NULL && auto_service &&
482 schema_mapping_existed && (flags & NS_LDAP_NOMAP) == 0)
484 * if service == auto_* and no schema mapping found
485 * and schema_mapping_existed is TRUE and NS_LDAP_NOMAP
486 * is not set then try automount e.g.
487 * NS_LDAP_ATTRIBUTEMAP = automount:automountMapName=AAA
489 mapping = __ns_ldap_getOrigAttribute("automount",
490 attr);
492 if (mapping == NULL) {
493 if ((ap[j]->attrname = strdup(attr)) == NULL) {
494 ber_free(ber, 0);
495 ber = NULL;
496 __ns_ldap_freeEntry(ep);
497 ep = NULL;
498 if (gecos_mapping)
499 __s_api_free2dArray(gecos_mapping);
500 gecos_mapping = NULL;
501 return (NS_LDAP_MEMORY);
503 } else {
505 * for "gecos" 1 to N mapping,
506 * do not remove the mapped attribute,
507 * just create a new gecos attribute
508 * and append it to the end of the attribute list
510 if (strcasecmp(mapping[0], "gecos") == 0) {
511 ap[j]->attrname = strdup(attr);
512 gecos_mapping_existed = TRUE;
513 } else
514 ap[j]->attrname = strdup(mapping[0]);
516 if (ap[j]->attrname == NULL) {
517 ber_free(ber, 0);
518 ber = NULL;
519 __ns_ldap_freeEntry(ep);
520 ep = NULL;
521 if (gecos_mapping)
522 __s_api_free2dArray(gecos_mapping);
523 gecos_mapping = NULL;
524 return (NS_LDAP_MEMORY);
527 * 1 to N attribute mapping processing
528 * is only done for "gecos"
531 if (strcasecmp(mapping[0], "gecos") == 0) {
533 * get attribute mapping for "gecos",
534 * need to know the number and order of the
535 * mapped attributes
537 if (gecos_mapping == NULL) {
538 gecos_mapping =
539 __ns_ldap_getMappedAttributes(
540 service, mapping[0]);
541 if (gecos_mapping == NULL ||
542 gecos_mapping[0] == NULL) {
544 * this should never happens,
545 * syslog the error
547 (void) sprintf(errstr,
548 gettext(
549 "Attribute mapping "
550 "inconsistency "
551 "found for attributes "
552 "'%s' and '%s'."),
553 mapping[0], attr);
554 syslog(LOG_ERR, "libsldap: %s",
555 errstr);
557 ber_free(ber, 0);
558 ber = NULL;
559 __ns_ldap_freeEntry(ep);
560 ep = NULL;
561 __s_api_free2dArray(mapping);
562 mapping = NULL;
563 if (gecos_mapping)
564 __s_api_free2dArray(
565 gecos_mapping);
566 gecos_mapping = NULL;
567 return (NS_LDAP_INTERNAL);
572 * is this attribute the 1st, 2nd, or
573 * 3rd attr in the mapping list?
575 gecos_attr_matched = FALSE;
576 for (i = 0; i < 3 && gecos_mapping[i]; i++) {
577 if (gecos_mapping[i] &&
578 strcasecmp(gecos_mapping[i],
579 attr) == 0) {
580 gecos_val_index[i] = j;
581 gecos_attr_matched = TRUE;
582 break;
585 if (gecos_attr_matched == FALSE) {
587 * Not match found.
588 * This should never happens,
589 * syslog the error
591 (void) sprintf(errstr,
592 gettext(
593 "Attribute mapping "
594 "inconsistency "
595 "found for attributes "
596 "'%s' and '%s'."),
597 mapping[0], attr);
598 syslog(LOG_ERR, "libsldap: %s", errstr);
600 ber_free(ber, 0);
601 ber = NULL;
602 __ns_ldap_freeEntry(ep);
603 ep = NULL;
604 __s_api_free2dArray(mapping);
605 mapping = NULL;
606 __s_api_free2dArray(gecos_mapping);
607 gecos_mapping = NULL;
608 return (NS_LDAP_INTERNAL);
611 __s_api_free2dArray(mapping);
612 mapping = NULL;
615 if ((vals = ldap_get_values(ld, e, attr)) != NULL) {
617 if ((ap[j]->value_count =
618 ldap_count_values(vals)) == 0) {
619 ldap_value_free(vals);
620 vals = NULL;
621 continue;
622 } else {
623 ap[j]->attrvalue = (char **)
624 calloc(ap[j]->value_count+1,
625 sizeof (char *));
626 if (ap[j]->attrvalue == NULL) {
627 ber_free(ber, 0);
628 ber = NULL;
629 __ns_ldap_freeEntry(ep);
630 ep = NULL;
631 if (gecos_mapping)
632 __s_api_free2dArray(
633 gecos_mapping);
634 gecos_mapping = NULL;
635 return (NS_LDAP_MEMORY);
639 /* map object classes if necessary */
640 if ((flags & NS_LDAP_NOMAP) == 0 &&
641 schema_mapping_existed && ap[j]->attrname &&
642 strcasecmp(ap[j]->attrname, "objectclass") == 0) {
643 for (k = 0; k < ap[j]->value_count; k++) {
644 mapping =
645 __ns_ldap_getOrigObjectClass(
646 service, vals[k]);
648 if (mapping == NULL && auto_service)
650 * if service == auto_* and no
651 * schema mapping found
652 * then try automount
654 mapping =
655 __ns_ldap_getOrigObjectClass(
656 "automount", vals[k]);
658 if (mapping == NULL) {
659 ap[j]->attrvalue[k] =
660 strdup(vals[k]);
661 } else {
662 ap[j]->attrvalue[k] =
663 strdup(mapping[0]);
664 __s_api_free2dArray(mapping);
665 mapping = NULL;
667 if (ap[j]->attrvalue[k] == NULL) {
668 ber_free(ber, 0);
669 ber = NULL;
670 __ns_ldap_freeEntry(ep);
671 ep = NULL;
672 if (gecos_mapping)
673 __s_api_free2dArray(
674 gecos_mapping);
675 gecos_mapping = NULL;
676 return (NS_LDAP_MEMORY);
679 } else {
680 for (k = 0; k < ap[j]->value_count; k++) {
681 if ((ap[j]->attrvalue[k] =
682 strdup(vals[k])) == NULL) {
683 ber_free(ber, 0);
684 ber = NULL;
685 __ns_ldap_freeEntry(ep);
686 ep = NULL;
687 if (gecos_mapping)
688 __s_api_free2dArray(
689 gecos_mapping);
690 gecos_mapping = NULL;
691 return (NS_LDAP_MEMORY);
696 ap[j]->attrvalue[k] = NULL;
697 ldap_value_free(vals);
698 vals = NULL;
701 ldap_memfree(attr);
702 attr = NULL;
705 ber_free(ber, 0);
706 ber = NULL;
708 if (gecos_mapping) {
709 __s_api_free2dArray(gecos_mapping);
710 gecos_mapping = NULL;
713 /* special processing for gecos 1 to up to 3 attribute mapping */
714 if (schema_mapping_existed && gecos_mapping_existed) {
716 int f = -1;
718 for (i = 0; i < 3; i++) {
719 k = gecos_val_index[i];
722 * f is the index of the first returned
723 * attribute which "gecos" attribute mapped to
725 if (k != -1 && f == -1)
726 f = k;
728 if (k != -1 && ap[k]->value_count > 0 &&
729 ap[k]->attrvalue[0] &&
730 strlen(ap[k]->attrvalue[0]) > 0) {
732 if (k == f) {
734 * Create and fill in the last reserved
735 * ap with the data from the "gecos"
736 * mapping attributes
738 ap[nAttrs] = (ns_ldap_attr_t *)
739 calloc(1,
740 sizeof (ns_ldap_attr_t));
741 if (ap[nAttrs] == NULL) {
742 __ns_ldap_freeEntry(ep);
743 ep = NULL;
744 return (NS_LDAP_MEMORY);
746 ap[nAttrs]->attrvalue = (char **)calloc(
747 2, sizeof (char *));
748 if (ap[nAttrs]->attrvalue == NULL) {
749 __ns_ldap_freeEntry(ep);
750 ep = NULL;
751 return (NS_LDAP_MEMORY);
753 /* add 1 more for a possible "," */
754 ap[nAttrs]->attrvalue[0] =
755 (char *)calloc(
756 strlen(ap[f]->attrvalue[0]) +
757 2, 1);
758 if (ap[nAttrs]->attrvalue[0] == NULL) {
759 __ns_ldap_freeEntry(ep);
760 ep = NULL;
761 return (NS_LDAP_MEMORY);
763 (void) strcpy(ap[nAttrs]->attrvalue[0],
764 ap[f]->attrvalue[0]);
766 ap[nAttrs]->attrname = strdup("gecos");
767 if (ap[nAttrs]->attrname == NULL) {
768 __ns_ldap_freeEntry(ep);
769 ep = NULL;
770 return (NS_LDAP_MEMORY);
773 ap[nAttrs]->value_count = 1;
774 ep->attr_count = nAttrs + 1;
776 } else {
777 char *tmp = NULL;
780 * realloc to add "," and
781 * ap[k]->attrvalue[0]
783 tmp = (char *)realloc(
784 ap[nAttrs]->attrvalue[0],
785 strlen(ap[nAttrs]->
786 attrvalue[0]) +
787 strlen(ap[k]->
788 attrvalue[0]) + 2);
789 if (tmp == NULL) {
790 __ns_ldap_freeEntry(ep);
791 ep = NULL;
792 return (NS_LDAP_MEMORY);
794 ap[nAttrs]->attrvalue[0] = tmp;
795 (void) strcat(ap[nAttrs]->attrvalue[0],
796 ",");
797 (void) strcat(ap[nAttrs]->attrvalue[0],
798 ap[k]->attrvalue[0]);
804 *ret = ep;
805 return (NS_LDAP_SUCCESS);
808 static int
809 __s_api_getEntry(ns_ldap_cookie_t *cookie)
811 ns_ldap_entry_t *curEntry = NULL;
812 int ret;
814 #ifdef DEBUG
815 (void) fprintf(stderr, "__s_api_getEntry START\n");
816 #endif
818 if (cookie->resultMsg == NULL) {
819 return (NS_LDAP_INVALID_PARAM);
821 ret = __s_api_cvtEntry(cookie->conn->ld, cookie->service,
822 cookie->resultMsg, cookie->i_flags,
823 &curEntry, &cookie->errorp);
824 if (ret != NS_LDAP_SUCCESS) {
825 return (ret);
828 if (cookie->result == NULL) {
829 cookie->result = (ns_ldap_result_t *)
830 calloc(1, sizeof (ns_ldap_result_t));
831 if (cookie->result == NULL) {
832 __ns_ldap_freeEntry(curEntry);
833 curEntry = NULL;
834 return (NS_LDAP_MEMORY);
836 cookie->result->entry = curEntry;
837 cookie->nextEntry = curEntry;
838 } else {
839 cookie->nextEntry->next = curEntry;
840 cookie->nextEntry = curEntry;
842 cookie->result->entries_count++;
844 return (NS_LDAP_SUCCESS);
847 static int
848 __s_api_get_cachemgr_data(const char *type,
849 const char *from, char **to)
851 union {
852 ldap_data_t s_d;
853 char s_b[DOORBUFFERSIZE];
854 } space;
855 ldap_data_t *sptr;
856 int ndata;
857 int adata;
858 int rc;
860 #ifdef DEBUG
861 (void) fprintf(stderr, "__s_api_get_cachemgr_data START\n");
862 #endif
864 * We are not going to perform DN to domain mapping
865 * in the Standalone mode
867 if (__s_api_isStandalone()) {
868 return (-1);
871 if (from == NULL || from[0] == '\0' || to == NULL)
872 return (-1);
874 *to = NULL;
875 (void) memset(space.s_b, 0, DOORBUFFERSIZE);
877 space.s_d.ldap_call.ldap_callnumber = GETCACHE;
878 (void) snprintf(space.s_d.ldap_call.ldap_u.domainname,
879 DOORBUFFERSIZE - sizeof (space.s_d.ldap_call.ldap_callnumber),
880 "%s%s%s",
881 type,
882 DOORLINESEP,
883 from);
884 ndata = sizeof (space);
885 adata = sizeof (ldap_call_t) +
886 strlen(space.s_d.ldap_call.ldap_u.domainname) + 1;
887 sptr = &space.s_d;
889 rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
890 if (rc != NS_CACHE_SUCCESS)
891 return (-1);
892 else
893 *to = strdup(sptr->ldap_ret.ldap_u.buff);
894 return (NS_LDAP_SUCCESS);
897 static int
898 __s_api_set_cachemgr_data(const char *type,
899 const char *from, const char *to)
901 union {
902 ldap_data_t s_d;
903 char s_b[DOORBUFFERSIZE];
904 } space;
905 ldap_data_t *sptr;
906 int ndata;
907 int adata;
908 int rc;
910 #ifdef DEBUG
911 (void) fprintf(stderr, "__s_api_set_cachemgr_data START\n");
912 #endif
914 * We are not going to perform DN to domain mapping
915 * in the Standalone mode
917 if (__s_api_isStandalone()) {
918 return (-1);
921 if ((from == NULL) || (from[0] == '\0') ||
922 (to == NULL) || (to[0] == '\0'))
923 return (-1);
925 (void) memset(space.s_b, 0, DOORBUFFERSIZE);
927 space.s_d.ldap_call.ldap_callnumber = SETCACHE;
928 (void) snprintf(space.s_d.ldap_call.ldap_u.domainname,
929 DOORBUFFERSIZE - sizeof (space.s_d.ldap_call.ldap_callnumber),
930 "%s%s%s%s%s",
931 type,
932 DOORLINESEP,
933 from,
934 DOORLINESEP,
935 to);
937 ndata = sizeof (space);
938 adata = sizeof (ldap_call_t) +
939 strlen(space.s_d.ldap_call.ldap_u.domainname) + 1;
940 sptr = &space.s_d;
942 rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
943 if (rc != NS_CACHE_SUCCESS)
944 return (-1);
946 return (NS_LDAP_SUCCESS);
950 static char *
951 __s_api_remove_rdn_space(char *rdn)
953 char *tf, *tl, *vf, *vl, *eqsign;
955 /* if no space(s) to remove, return */
956 if (strchr(rdn, SPACETOK) == NULL)
957 return (rdn);
959 /* if no '=' separator, return */
960 eqsign = strchr(rdn, '=');
961 if (eqsign == NULL)
962 return (rdn);
964 tf = rdn;
965 tl = eqsign - 1;
966 vf = eqsign + 1;
967 vl = rdn + strlen(rdn) - 1;
969 /* now two strings, type and value */
970 *eqsign = '\0';
972 /* remove type's leading spaces */
973 while (tf < tl && *tf == SPACETOK)
974 tf++;
975 /* remove type's trailing spaces */
976 while (tf < tl && *tl == SPACETOK)
977 tl--;
978 /* add '=' separator back */
979 *(++tl) = '=';
980 /* remove value's leading spaces */
981 while (vf < vl && *vf == SPACETOK)
982 vf++;
983 /* remove value's trailing spaces */
984 while (vf < vl && *vl == SPACETOK)
985 *vl-- = '\0';
987 /* move value up if necessary */
988 if (vf != tl + 1)
989 (void) strcpy(tl + 1, vf);
991 return (tf);
994 static
995 ns_ldap_cookie_t *
996 init_search_state_machine()
998 ns_ldap_cookie_t *cookie;
999 ns_config_t *cfg;
1001 cookie = (ns_ldap_cookie_t *)calloc(1, sizeof (ns_ldap_cookie_t));
1002 if (cookie == NULL)
1003 return (NULL);
1004 cookie->state = INIT;
1005 /* assign other state variables */
1006 cfg = __s_api_loadrefresh_config();
1007 cookie->connectionId = -1;
1008 if (cfg == NULL ||
1009 cfg->paramList[NS_LDAP_SEARCH_TIME_P].ns_ptype == NS_UNKNOWN) {
1010 cookie->search_timeout.tv_sec = NS_DEFAULT_SEARCH_TIMEOUT;
1011 } else {
1012 cookie->search_timeout.tv_sec =
1013 cfg->paramList[NS_LDAP_SEARCH_TIME_P].ns_i;
1015 if (cfg != NULL)
1016 __s_api_release_config(cfg);
1017 cookie->search_timeout.tv_usec = 0;
1019 return (cookie);
1022 static void
1023 delete_search_cookie(ns_ldap_cookie_t *cookie)
1025 if (cookie == NULL)
1026 return;
1027 if (cookie->connectionId > -1)
1028 DropConnection(cookie->connectionId, cookie->i_flags);
1029 if (cookie->filter)
1030 free(cookie->filter);
1031 if (cookie->i_filter)
1032 free(cookie->i_filter);
1033 if (cookie->service)
1034 free(cookie->service);
1035 if (cookie->sdlist)
1036 (void) __ns_ldap_freeSearchDescriptors(&(cookie->sdlist));
1037 if (cookie->result)
1038 (void) __ns_ldap_freeResult(&cookie->result);
1039 if (cookie->attribute)
1040 __s_api_free2dArray(cookie->attribute);
1041 if (cookie->errorp)
1042 (void) __ns_ldap_freeError(&cookie->errorp);
1043 if (cookie->reflist)
1044 __s_api_deleteRefInfo(cookie->reflist);
1045 if (cookie->basedn)
1046 free(cookie->basedn);
1047 if (cookie->ctrlCookie)
1048 ber_bvfree(cookie->ctrlCookie);
1049 _freeControlList(&cookie->p_serverctrls);
1050 if (cookie->resultctrl)
1051 ldap_controls_free(cookie->resultctrl);
1052 free(cookie);
1055 static int
1056 get_mapped_filter(ns_ldap_cookie_t *cookie, char **new_filter)
1059 typedef struct filter_mapping_info {
1060 char oc_or_attr;
1061 char *name_start;
1062 char *name_end;
1063 char *veq_pos;
1064 char *from_name;
1065 char *to_name;
1066 char **mapping;
1067 } filter_mapping_info_t;
1069 char *c, *last_copied;
1070 char *filter_c, *filter_c_next;
1071 char *key, *tail, *head;
1072 char errstr[MAXERROR];
1073 int num_eq = 0, num_veq = 0;
1074 int in_quote = FALSE;
1075 int is_value = FALSE;
1076 int i, j, oc_len, len;
1077 int at_least_one = FALSE;
1078 filter_mapping_info_t **info, *info1;
1079 char **mapping;
1080 char *service, *filter, *err;
1081 int auto_service = FALSE;
1083 if (cookie == NULL || new_filter == NULL)
1084 return (NS_LDAP_INVALID_PARAM);
1086 *new_filter = NULL;
1087 service = cookie->service;
1088 filter = cookie->filter;
1091 * count the number of '=' char
1093 for (c = filter; *c; c++) {
1094 if (*c == TOKENSEPARATOR)
1095 num_eq++;
1098 if (service != NULL && strncasecmp(service, "auto_", 5) == 0)
1099 auto_service = TRUE;
1102 * See if schema mapping existed for the given service.
1103 * If not, just return success.
1105 mapping = __ns_ldap_getOrigAttribute(service,
1106 NS_HASH_SCHEMA_MAPPING_EXISTED);
1108 if (mapping == NULL && auto_service)
1110 * if service == auto_* and no
1111 * schema mapping found
1112 * then try automount
1114 mapping = __ns_ldap_getOrigAttribute(
1115 "automount", NS_HASH_SCHEMA_MAPPING_EXISTED);
1117 if (mapping)
1118 __s_api_free2dArray(mapping);
1119 else
1120 return (NS_LDAP_SUCCESS);
1123 * no '=' sign, just say OK and return nothing
1125 if (num_eq == 0)
1126 return (NS_LDAP_SUCCESS);
1129 * Make a copy of the filter string
1130 * for saving the name of the objectclasses or
1131 * attributes that need to be passed to the
1132 * objectclass or attribute mapping functions.
1133 * pointer "info->from_name" points to the locations
1134 * within this string.
1136 * The input filter string, filter, will be used
1137 * to indicate where these names start and end.
1138 * pointers "info->name_start" and "info->name_end"
1139 * point to locations within the input filter string,
1140 * and are used at the end of this function to
1141 * merge the original filter data with the
1142 * mapped objectclass or attribute names.
1144 filter_c = strdup(filter);
1145 if (filter_c == NULL)
1146 return (NS_LDAP_MEMORY);
1147 filter_c_next = filter_c;
1150 * get memory for info arrays
1152 info = (filter_mapping_info_t **)calloc(num_eq + 1,
1153 sizeof (filter_mapping_info_t *));
1155 if (info == NULL) {
1156 free(filter_c);
1157 return (NS_LDAP_MEMORY);
1161 * find valid '=' for further processing,
1162 * ignore the "escaped =" (.i.e. "\="), or
1163 * "=" in quoted string
1165 for (c = filter_c; *c; c++) {
1167 switch (*c) {
1168 case TOKENSEPARATOR:
1169 if (!in_quote && !is_value) {
1170 info1 = (filter_mapping_info_t *)calloc(1,
1171 sizeof (filter_mapping_info_t));
1172 if (!info1) {
1173 free(filter_c);
1174 for (i = 0; i < num_veq; i++)
1175 free(info[i]);
1176 free(info);
1177 return (NS_LDAP_MEMORY);
1179 info[num_veq] = info1;
1182 * remember the location of this "="
1184 info[num_veq++]->veq_pos = c;
1187 * skip until the end of the attribute value
1189 is_value = TRUE;
1191 break;
1192 case CPARATOK:
1194 * mark the end of the attribute value
1196 if (!in_quote)
1197 is_value = FALSE;
1198 break;
1199 case QUOTETOK:
1201 * switch on/off the in_quote mode
1203 in_quote = (in_quote == FALSE);
1204 break;
1205 case '\\':
1207 * ignore escape characters
1208 * don't skip if next char is '\0'
1210 if (!in_quote)
1211 if (*(++c) == '\0')
1212 c--;
1213 break;
1219 * for each valid "=" found, get the name to
1220 * be mapped
1222 oc_len = strlen("objectclass");
1223 for (i = 0; i < num_veq; i++) {
1226 * look at the left side of "=" to see
1227 * if assertion is "objectclass=<ocname>"
1228 * or "<attribute name>=<attribute value>"
1230 * first skip spaces before "=".
1231 * Note that filter_c_next may not point to the
1232 * start of the filter string. For i > 0,
1233 * it points to the end of the last name processed + 2
1235 for (tail = info[i]->veq_pos; (tail > filter_c_next) &&
1236 (*(tail - 1) == SPACETOK); tail--)
1240 * mark the end of the left side string (the key)
1242 *tail = '\0';
1243 info[i]->name_end = tail - filter_c - 1 + filter;
1246 * find the start of the key
1248 key = filter_c_next;
1249 for (c = tail; filter_c_next <= c; c--) {
1250 /* OPARATOK is '(' */
1251 if (*c == OPARATOK ||
1252 *c == SPACETOK) {
1253 key = c + 1;
1254 break;
1257 info[i]->name_start = key - filter_c + filter;
1259 if ((key + oc_len) <= tail) {
1260 if (strncasecmp(key, "objectclass",
1261 oc_len) == 0) {
1263 * assertion is "objectclass=ocname",
1264 * ocname is the one needs to be mapped
1266 * skip spaces after "=" to find start
1267 * of the ocname
1269 head = info[i]->veq_pos;
1270 for (head = info[i]->veq_pos + 1;
1271 *head && *head == SPACETOK; head++)
1274 /* ignore empty ocname */
1275 if (!(*head))
1276 continue;
1278 info[i]->name_start = head - filter_c +
1279 filter;
1282 * now find the end of the ocname
1284 for (c = head; ; c++) {
1285 /* CPARATOK is ')' */
1286 if (*c == CPARATOK ||
1287 *c == '\0' ||
1288 *c == SPACETOK) {
1289 *c = '\0';
1290 info[i]->name_end =
1291 c - filter_c - 1 +
1292 filter;
1293 filter_c_next = c + 1;
1294 info[i]->oc_or_attr = 'o';
1295 info[i]->from_name = head;
1296 break;
1303 * assertion is not "objectclass=ocname",
1304 * assume assertion is "<key> = <value>",
1305 * <key> is the one needs to be mapped
1307 if (info[i]->from_name == NULL && strlen(key) > 0) {
1308 info[i]->oc_or_attr = 'a';
1309 info[i]->from_name = key;
1313 /* perform schema mapping */
1314 for (i = 0; i < num_veq; i++) {
1315 if (info[i]->from_name == NULL)
1316 continue;
1318 if (info[i]->oc_or_attr == 'a')
1319 info[i]->mapping =
1320 __ns_ldap_getMappedAttributes(service,
1321 info[i]->from_name);
1322 else
1323 info[i]->mapping =
1324 __ns_ldap_getMappedObjectClass(service,
1325 info[i]->from_name);
1327 if (info[i]->mapping == NULL && auto_service) {
1329 * If no mapped attribute/objectclass is found
1330 * and service == auto*
1331 * try to find automount's
1332 * mapped attribute/objectclass
1334 if (info[i]->oc_or_attr == 'a')
1335 info[i]->mapping =
1336 __ns_ldap_getMappedAttributes("automount",
1337 info[i]->from_name);
1338 else
1339 info[i]->mapping =
1340 __ns_ldap_getMappedObjectClass("automount",
1341 info[i]->from_name);
1344 if (info[i]->mapping == NULL ||
1345 info[i]->mapping[0] == NULL) {
1346 info[i]->to_name = NULL;
1347 } else if (info[i]->mapping[1] == NULL) {
1348 info[i]->to_name = info[i]->mapping[0];
1349 at_least_one = TRUE;
1350 } else {
1351 __s_api_free2dArray(info[i]->mapping);
1353 * multiple mapping
1354 * not allowed
1356 (void) sprintf(errstr,
1357 gettext(
1358 "Multiple attribute or objectclass "
1359 "mapping for '%s' in filter "
1360 "'%s' not allowed."),
1361 info[i]->from_name, filter);
1362 err = strdup(errstr);
1363 if (err)
1364 MKERROR(LOG_WARNING, cookie->errorp,
1365 NS_CONFIG_SYNTAX,
1366 err, NULL);
1368 free(filter_c);
1369 for (j = 0; j < num_veq; j++) {
1370 if (info[j]->mapping)
1371 __s_api_free2dArray(
1372 info[j]->mapping);
1373 free(info[j]);
1375 free(info);
1376 return (NS_LDAP_CONFIG);
1381 if (at_least_one) {
1383 len = strlen(filter);
1384 last_copied = filter - 1;
1386 for (i = 0; i < num_veq; i++) {
1387 if (info[i]->to_name)
1388 len += strlen(info[i]->to_name);
1391 *new_filter = (char *)calloc(1, len);
1392 if (*new_filter == NULL) {
1393 free(filter_c);
1394 for (j = 0; j < num_veq; j++) {
1395 if (info[j]->mapping)
1396 __s_api_free2dArray(
1397 info[j]->mapping);
1398 free(info[j]);
1400 free(info);
1401 return (NS_LDAP_MEMORY);
1404 for (i = 0; i < num_veq; i++) {
1405 if (info[i]->to_name != NULL &&
1406 info[i]->to_name != NULL) {
1409 * copy the original filter data
1410 * between the last name and current
1411 * name
1413 if ((last_copied + 1) != info[i]->name_start)
1414 (void) strncat(*new_filter,
1415 last_copied + 1,
1416 info[i]->name_start -
1417 last_copied - 1);
1419 /* the data is copied */
1420 last_copied = info[i]->name_end;
1423 * replace the name with
1424 * the mapped name
1426 (void) strcat(*new_filter, info[i]->to_name);
1429 /* copy the filter data after the last name */
1430 if (i == (num_veq -1) &&
1431 info[i]->name_end <
1432 (filter + strlen(filter)))
1433 (void) strncat(*new_filter, last_copied + 1,
1434 filter + strlen(filter) -
1435 last_copied - 1);
1440 /* free memory */
1441 free(filter_c);
1442 for (j = 0; j < num_veq; j++) {
1443 if (info[j]->mapping)
1444 __s_api_free2dArray(info[j]->mapping);
1445 free(info[j]);
1447 free(info);
1449 return (NS_LDAP_SUCCESS);
1452 static int
1453 setup_next_search(ns_ldap_cookie_t *cookie)
1455 ns_ldap_search_desc_t *dptr;
1456 int scope;
1457 char *filter, *str;
1458 int baselen;
1459 int rc;
1460 void **param;
1462 dptr = *cookie->sdpos;
1463 scope = cookie->i_flags & (NS_LDAP_SCOPE_BASE |
1464 NS_LDAP_SCOPE_ONELEVEL |
1465 NS_LDAP_SCOPE_SUBTREE);
1466 if (scope)
1467 cookie->scope = scope;
1468 else
1469 cookie->scope = dptr->scope;
1470 switch (cookie->scope) {
1471 case NS_LDAP_SCOPE_BASE:
1472 cookie->scope = LDAP_SCOPE_BASE;
1473 break;
1474 case NS_LDAP_SCOPE_ONELEVEL:
1475 cookie->scope = LDAP_SCOPE_ONELEVEL;
1476 break;
1477 case NS_LDAP_SCOPE_SUBTREE:
1478 cookie->scope = LDAP_SCOPE_SUBTREE;
1479 break;
1482 filter = NULL;
1483 if (cookie->use_filtercb && cookie->init_filter_cb &&
1484 dptr->filter && strlen(dptr->filter) > 0) {
1485 (*cookie->init_filter_cb)(dptr, &filter,
1486 cookie->userdata);
1488 if (filter == NULL) {
1489 if (cookie->i_filter == NULL) {
1490 cookie->err_rc = NS_LDAP_INVALID_PARAM;
1491 return (-1);
1492 } else {
1493 if (cookie->filter)
1494 free(cookie->filter);
1495 cookie->filter = strdup(cookie->i_filter);
1496 if (cookie->filter == NULL) {
1497 cookie->err_rc = NS_LDAP_MEMORY;
1498 return (-1);
1501 } else {
1502 if (cookie->filter)
1503 free(cookie->filter);
1504 cookie->filter = strdup(filter);
1505 free(filter);
1506 if (cookie->filter == NULL) {
1507 cookie->err_rc = NS_LDAP_MEMORY;
1508 return (-1);
1513 * perform attribute/objectclass mapping on filter
1515 filter = NULL;
1517 if (cookie->service) {
1518 rc = get_mapped_filter(cookie, &filter);
1519 if (rc != NS_LDAP_SUCCESS) {
1520 cookie->err_rc = rc;
1521 return (-1);
1522 } else {
1524 * get_mapped_filter returns
1525 * NULL filter pointer, if
1526 * no mapping was done
1528 if (filter) {
1529 free(cookie->filter);
1530 cookie->filter = filter;
1536 * validate filter to make sure it's legal
1537 * [remove redundant ()'s]
1539 rc = validate_filter(cookie);
1540 if (rc != NS_LDAP_SUCCESS) {
1541 cookie->err_rc = rc;
1542 return (-1);
1545 baselen = strlen(dptr->basedn);
1546 if (baselen > 0 && dptr->basedn[baselen-1] == COMMATOK) {
1547 rc = __ns_ldap_getParam(NS_LDAP_SEARCH_BASEDN_P,
1548 (void ***)&param, &cookie->errorp);
1549 if (rc != NS_LDAP_SUCCESS) {
1550 cookie->err_rc = rc;
1551 return (-1);
1553 str = ((char **)param)[0];
1554 baselen += strlen(str)+1;
1555 if (cookie->basedn)
1556 free(cookie->basedn);
1557 cookie->basedn = (char *)malloc(baselen);
1558 if (cookie->basedn == NULL) {
1559 cookie->err_rc = NS_LDAP_MEMORY;
1560 return (-1);
1562 (void) strcpy(cookie->basedn, dptr->basedn);
1563 (void) strcat(cookie->basedn, str);
1564 (void) __ns_ldap_freeParam(&param);
1565 } else {
1566 if (cookie->basedn)
1567 free(cookie->basedn);
1568 cookie->basedn = strdup(dptr->basedn);
1570 return (0);
1573 static int
1574 setup_referral_search(ns_ldap_cookie_t *cookie)
1576 ns_referral_info_t *ref;
1578 ref = cookie->refpos;
1579 cookie->scope = ref->refScope;
1580 if (cookie->filter) {
1581 free(cookie->filter);
1583 cookie->filter = strdup(ref->refFilter);
1584 if (cookie->basedn) {
1585 free(cookie->basedn);
1587 cookie->basedn = strdup(ref->refDN);
1588 if (cookie->filter == NULL || cookie->basedn == NULL) {
1589 cookie->err_rc = NS_LDAP_MEMORY;
1590 return (-1);
1592 return (0);
1595 static int
1596 get_current_session(ns_ldap_cookie_t *cookie)
1598 ConnectionID connectionId = -1;
1599 Connection *conp = NULL;
1600 int rc;
1601 int fail_if_new_pwd_reqd = 1;
1603 rc = __s_api_getConnection(NULL, cookie->i_flags,
1604 cookie->i_auth, &connectionId, &conp,
1605 &cookie->errorp, fail_if_new_pwd_reqd,
1606 cookie->nopasswd_acct_mgmt, cookie->conn_user);
1609 * If password control attached in *cookie->errorp,
1610 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1611 * free the error structure (we do not need
1612 * the sec_to_expired info).
1613 * Reset rc to NS_LDAP_SUCCESS.
1615 if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1616 (void) __ns_ldap_freeError(
1617 &cookie->errorp);
1618 cookie->errorp = NULL;
1619 rc = NS_LDAP_SUCCESS;
1622 if (rc != NS_LDAP_SUCCESS) {
1623 cookie->err_rc = rc;
1624 return (-1);
1626 cookie->conn = conp;
1627 cookie->connectionId = connectionId;
1629 return (0);
1632 static int
1633 get_next_session(ns_ldap_cookie_t *cookie)
1635 ConnectionID connectionId = -1;
1636 Connection *conp = NULL;
1637 int rc;
1638 int fail_if_new_pwd_reqd = 1;
1640 if (cookie->connectionId > -1) {
1641 DropConnection(cookie->connectionId, cookie->i_flags);
1642 cookie->connectionId = -1;
1645 /* If using a MT connection, return it. */
1646 if (cookie->conn_user != NULL &&
1647 cookie->conn_user->conn_mt != NULL)
1648 __s_api_conn_mt_return(cookie->conn_user);
1650 rc = __s_api_getConnection(NULL, cookie->i_flags,
1651 cookie->i_auth, &connectionId, &conp,
1652 &cookie->errorp, fail_if_new_pwd_reqd,
1653 cookie->nopasswd_acct_mgmt, cookie->conn_user);
1656 * If password control attached in *cookie->errorp,
1657 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1658 * free the error structure (we do not need
1659 * the sec_to_expired info).
1660 * Reset rc to NS_LDAP_SUCCESS.
1662 if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1663 (void) __ns_ldap_freeError(
1664 &cookie->errorp);
1665 cookie->errorp = NULL;
1666 rc = NS_LDAP_SUCCESS;
1669 if (rc != NS_LDAP_SUCCESS) {
1670 cookie->err_rc = rc;
1671 return (-1);
1673 cookie->conn = conp;
1674 cookie->connectionId = connectionId;
1675 return (0);
1678 static int
1679 get_referral_session(ns_ldap_cookie_t *cookie)
1681 ConnectionID connectionId = -1;
1682 Connection *conp = NULL;
1683 int rc;
1684 int fail_if_new_pwd_reqd = 1;
1686 if (cookie->connectionId > -1) {
1687 DropConnection(cookie->connectionId, cookie->i_flags);
1688 cookie->connectionId = -1;
1691 /* set it up to use a connection opened for referral */
1692 if (cookie->conn_user != NULL) {
1693 /* If using a MT connection, return it. */
1694 if (cookie->conn_user->conn_mt != NULL)
1695 __s_api_conn_mt_return(cookie->conn_user);
1696 cookie->conn_user->referral = B_TRUE;
1699 rc = __s_api_getConnection(cookie->refpos->refHost, 0,
1700 cookie->i_auth, &connectionId, &conp,
1701 &cookie->errorp, fail_if_new_pwd_reqd,
1702 cookie->nopasswd_acct_mgmt, cookie->conn_user);
1705 * If password control attached in *cookie->errorp,
1706 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1707 * free the error structure (we do not need
1708 * the sec_to_expired info).
1709 * Reset rc to NS_LDAP_SUCCESS.
1711 if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1712 (void) __ns_ldap_freeError(
1713 &cookie->errorp);
1714 cookie->errorp = NULL;
1715 rc = NS_LDAP_SUCCESS;
1718 if (rc != NS_LDAP_SUCCESS) {
1719 cookie->err_rc = rc;
1720 return (-1);
1722 cookie->conn = conp;
1723 cookie->connectionId = connectionId;
1724 return (0);
1727 static int
1728 paging_supported(ns_ldap_cookie_t *cookie)
1730 int rc;
1732 cookie->listType = 0;
1733 rc = __s_api_isCtrlSupported(cookie->conn,
1734 LDAP_CONTROL_VLVREQUEST);
1735 if (rc == NS_LDAP_SUCCESS) {
1736 cookie->listType = VLVCTRLFLAG;
1737 return (1);
1739 rc = __s_api_isCtrlSupported(cookie->conn,
1740 LDAP_CONTROL_SIMPLE_PAGE);
1741 if (rc == NS_LDAP_SUCCESS) {
1742 cookie->listType = SIMPLEPAGECTRLFLAG;
1743 return (1);
1745 return (0);
1748 typedef struct servicesorttype {
1749 char *service;
1750 ns_srvsidesort_t type;
1751 } servicesorttype_t;
1753 static servicesorttype_t *sort_type = NULL;
1754 static int sort_type_size = 0;
1755 static int sort_type_hwm = 0;
1756 static mutex_t sort_type_mutex = DEFAULTMUTEX;
1759 static ns_srvsidesort_t
1760 get_srvsidesort_type(char *service)
1762 int i;
1763 ns_srvsidesort_t type = SSS_UNKNOWN;
1765 if (service == NULL)
1766 return (type);
1768 (void) mutex_lock(&sort_type_mutex);
1769 if (sort_type != NULL) {
1770 for (i = 0; i < sort_type_hwm; i++) {
1771 if (strcmp(sort_type[i].service, service) == 0) {
1772 type = sort_type[i].type;
1773 break;
1777 (void) mutex_unlock(&sort_type_mutex);
1778 return (type);
1781 static void
1782 update_srvsidesort_type(char *service, ns_srvsidesort_t type)
1784 int i, size;
1785 servicesorttype_t *tmp;
1787 if (service == NULL)
1788 return;
1790 (void) mutex_lock(&sort_type_mutex);
1792 for (i = 0; i < sort_type_hwm; i++) {
1793 if (strcmp(sort_type[i].service, service) == 0) {
1794 sort_type[i].type = type;
1795 (void) mutex_unlock(&sort_type_mutex);
1796 return;
1799 if (sort_type == NULL) {
1800 size = 10;
1801 tmp = malloc(size * sizeof (servicesorttype_t));
1802 if (tmp == NULL) {
1803 (void) mutex_unlock(&sort_type_mutex);
1804 return;
1806 sort_type = tmp;
1807 sort_type_size = size;
1808 } else if (sort_type_hwm >= sort_type_size) {
1809 size = sort_type_size + 10;
1810 tmp = realloc(sort_type, size * sizeof (servicesorttype_t));
1811 if (tmp == NULL) {
1812 (void) mutex_unlock(&sort_type_mutex);
1813 return;
1815 sort_type = tmp;
1816 sort_type_size = size;
1818 sort_type[sort_type_hwm].service = strdup(service);
1819 if (sort_type[sort_type_hwm].service == NULL) {
1820 (void) mutex_unlock(&sort_type_mutex);
1821 return;
1823 sort_type[sort_type_hwm].type = type;
1824 sort_type_hwm++;
1826 (void) mutex_unlock(&sort_type_mutex);
1829 static int
1830 setup_vlv_params(ns_ldap_cookie_t *cookie)
1832 LDAPControl **ctrls;
1833 LDAPsortkey **sortkeylist;
1834 LDAPControl *sortctrl = NULL;
1835 LDAPControl *vlvctrl = NULL;
1836 LDAPVirtualList vlist;
1837 char *sortattr;
1838 int rc;
1839 int free_sort = FALSE;
1841 _freeControlList(&cookie->p_serverctrls);
1843 if (cookie->sortTypeTry == SSS_UNKNOWN)
1844 cookie->sortTypeTry = get_srvsidesort_type(cookie->service);
1845 if (cookie->sortTypeTry == SSS_UNKNOWN)
1846 cookie->sortTypeTry = SSS_SINGLE_ATTR;
1848 if (cookie->sortTypeTry == SSS_SINGLE_ATTR) {
1849 if ((cookie->i_flags & NS_LDAP_NOMAP) == 0 &&
1850 cookie->i_sortattr) {
1851 sortattr = __ns_ldap_mapAttribute(cookie->service,
1852 cookie->i_sortattr);
1853 free_sort = TRUE;
1854 } else if (cookie->i_sortattr) {
1855 sortattr = (char *)cookie->i_sortattr;
1856 } else {
1857 sortattr = "cn";
1859 } else {
1860 sortattr = "cn uid";
1863 rc = ldap_create_sort_keylist(&sortkeylist, sortattr);
1864 if (free_sort)
1865 free(sortattr);
1866 if (rc != LDAP_SUCCESS) {
1867 (void) ldap_get_option(cookie->conn->ld,
1868 LDAP_OPT_ERROR_NUMBER, &rc);
1869 return (rc);
1871 rc = ldap_create_sort_control(cookie->conn->ld,
1872 sortkeylist, 1, &sortctrl);
1873 ldap_free_sort_keylist(sortkeylist);
1874 if (rc != LDAP_SUCCESS) {
1875 (void) ldap_get_option(cookie->conn->ld,
1876 LDAP_OPT_ERROR_NUMBER, &rc);
1877 return (rc);
1880 vlist.ldvlist_index = cookie->index;
1881 vlist.ldvlist_size = 0;
1883 vlist.ldvlist_before_count = 0;
1884 vlist.ldvlist_after_count = LISTPAGESIZE-1;
1885 vlist.ldvlist_attrvalue = NULL;
1886 vlist.ldvlist_extradata = NULL;
1888 rc = ldap_create_virtuallist_control(cookie->conn->ld,
1889 &vlist, &vlvctrl);
1890 if (rc != LDAP_SUCCESS) {
1891 ldap_control_free(sortctrl);
1892 (void) ldap_get_option(cookie->conn->ld, LDAP_OPT_ERROR_NUMBER,
1893 &rc);
1894 return (rc);
1897 ctrls = (LDAPControl **)calloc(3, sizeof (LDAPControl *));
1898 if (ctrls == NULL) {
1899 ldap_control_free(sortctrl);
1900 ldap_control_free(vlvctrl);
1901 return (LDAP_NO_MEMORY);
1904 ctrls[0] = sortctrl;
1905 ctrls[1] = vlvctrl;
1907 cookie->p_serverctrls = ctrls;
1908 return (LDAP_SUCCESS);
1911 static int
1912 setup_simplepg_params(ns_ldap_cookie_t *cookie)
1914 LDAPControl **ctrls;
1915 LDAPControl *pgctrl = NULL;
1916 int rc;
1918 _freeControlList(&cookie->p_serverctrls);
1920 rc = ldap_create_page_control(cookie->conn->ld, LISTPAGESIZE,
1921 cookie->ctrlCookie, (char)0, &pgctrl);
1922 if (rc != LDAP_SUCCESS) {
1923 (void) ldap_get_option(cookie->conn->ld, LDAP_OPT_ERROR_NUMBER,
1924 &rc);
1925 return (rc);
1928 ctrls = (LDAPControl **)calloc(2, sizeof (LDAPControl *));
1929 if (ctrls == NULL) {
1930 ldap_control_free(pgctrl);
1931 return (LDAP_NO_MEMORY);
1933 ctrls[0] = pgctrl;
1934 cookie->p_serverctrls = ctrls;
1935 return (LDAP_SUCCESS);
1938 static void
1939 proc_result_referrals(ns_ldap_cookie_t *cookie)
1941 int errCode, i, rc;
1942 char **referrals = NULL;
1945 * Only follow one level of referrals, i.e.
1946 * if already in referral mode, do nothing
1948 if (cookie->refpos == NULL) {
1949 cookie->new_state = END_RESULT;
1950 rc = ldap_parse_result(cookie->conn->ld,
1951 cookie->resultMsg,
1952 &errCode, NULL,
1953 NULL, &referrals,
1954 NULL, 0);
1955 if (rc != NS_LDAP_SUCCESS) {
1956 (void) ldap_get_option(cookie->conn->ld,
1957 LDAP_OPT_ERROR_NUMBER,
1958 &cookie->err_rc);
1959 cookie->new_state = LDAP_ERROR;
1960 return;
1962 if (errCode == LDAP_REFERRAL) {
1963 for (i = 0; referrals[i] != NULL;
1964 i++) {
1965 /* add to referral list */
1966 rc = __s_api_addRefInfo(
1967 &cookie->reflist,
1968 referrals[i],
1969 cookie->basedn,
1970 &cookie->scope,
1971 cookie->filter,
1972 cookie->conn->ld);
1973 if (rc != NS_LDAP_SUCCESS) {
1974 cookie->new_state =
1975 ERROR;
1976 break;
1979 ldap_value_free(referrals);
1984 static void
1985 proc_search_references(ns_ldap_cookie_t *cookie)
1987 char **refurls = NULL;
1988 int i, rc;
1991 * Only follow one level of referrals, i.e.
1992 * if already in referral mode, do nothing
1994 if (cookie->refpos == NULL) {
1995 refurls = ldap_get_reference_urls(
1996 cookie->conn->ld,
1997 cookie->resultMsg);
1998 if (refurls == NULL) {
1999 (void) ldap_get_option(cookie->conn->ld,
2000 LDAP_OPT_ERROR_NUMBER,
2001 &cookie->err_rc);
2002 cookie->new_state = LDAP_ERROR;
2003 return;
2005 for (i = 0; refurls[i] != NULL; i++) {
2006 /* add to referral list */
2007 rc = __s_api_addRefInfo(
2008 &cookie->reflist,
2009 refurls[i],
2010 cookie->basedn,
2011 &cookie->scope,
2012 cookie->filter,
2013 cookie->conn->ld);
2014 if (rc != NS_LDAP_SUCCESS) {
2015 cookie->new_state =
2016 ERROR;
2017 break;
2020 /* free allocated storage */
2021 for (i = 0; refurls[i] != NULL; i++)
2022 free(refurls[i]);
2026 static ns_state_t
2027 multi_result(ns_ldap_cookie_t *cookie)
2029 char errstr[MAXERROR];
2030 char *err;
2031 ns_ldap_error_t **errorp = NULL;
2032 LDAPControl **retCtrls = NULL;
2033 int i, rc;
2034 int errCode;
2035 int finished = 0;
2036 unsigned long target_posp = 0;
2037 unsigned long list_size = 0;
2038 unsigned int count = 0;
2039 char **referrals = NULL;
2041 if (cookie->listType == VLVCTRLFLAG) {
2042 rc = ldap_parse_result(cookie->conn->ld, cookie->resultMsg,
2043 &errCode, NULL, NULL, &referrals, &retCtrls, 0);
2044 if (rc != LDAP_SUCCESS) {
2045 (void) ldap_get_option(cookie->conn->ld,
2046 LDAP_OPT_ERROR_NUMBER,
2047 &cookie->err_rc);
2048 (void) sprintf(errstr,
2049 gettext("LDAP ERROR (%d): %s.\n"),
2050 cookie->err_rc,
2051 gettext(ldap_err2string(cookie->err_rc)));
2052 err = strdup(errstr);
2053 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2054 NULL);
2055 cookie->err_rc = NS_LDAP_INTERNAL;
2056 cookie->errorp = *errorp;
2057 return (LDAP_ERROR);
2059 if (errCode == LDAP_REFERRAL) {
2060 for (i = 0; referrals[i] != NULL;
2061 i++) {
2062 /* add to referral list */
2063 rc = __s_api_addRefInfo(
2064 &cookie->reflist,
2065 referrals[i],
2066 cookie->basedn,
2067 &cookie->scope,
2068 cookie->filter,
2069 cookie->conn->ld);
2070 if (rc != NS_LDAP_SUCCESS) {
2071 ldap_value_free(
2072 referrals);
2073 if (retCtrls)
2074 ldap_controls_free(
2075 retCtrls);
2076 return (ERROR);
2079 ldap_value_free(referrals);
2080 if (retCtrls)
2081 ldap_controls_free(retCtrls);
2082 return (END_RESULT);
2084 if (retCtrls) {
2085 rc = ldap_parse_virtuallist_control(
2086 cookie->conn->ld, retCtrls,
2087 &target_posp, &list_size, &errCode);
2088 if (rc == LDAP_SUCCESS) {
2090 * AD does not return valid target_posp
2091 * and list_size
2093 if (target_posp != 0 && list_size != 0) {
2094 cookie->index =
2095 target_posp + LISTPAGESIZE;
2096 if (cookie->index > list_size)
2097 finished = 1;
2098 } else {
2099 if (cookie->entryCount < LISTPAGESIZE)
2100 finished = 1;
2101 else
2102 cookie->index +=
2103 cookie->entryCount;
2106 ldap_controls_free(retCtrls);
2107 retCtrls = NULL;
2109 else
2110 finished = 1;
2111 } else if (cookie->listType == SIMPLEPAGECTRLFLAG) {
2112 rc = ldap_parse_result(cookie->conn->ld, cookie->resultMsg,
2113 &errCode, NULL, NULL, &referrals, &retCtrls, 0);
2114 if (rc != LDAP_SUCCESS) {
2115 (void) ldap_get_option(cookie->conn->ld,
2116 LDAP_OPT_ERROR_NUMBER,
2117 &cookie->err_rc);
2118 (void) sprintf(errstr,
2119 gettext("LDAP ERROR (%d): %s.\n"),
2120 cookie->err_rc,
2121 gettext(ldap_err2string(cookie->err_rc)));
2122 err = strdup(errstr);
2123 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2124 NULL);
2125 cookie->err_rc = NS_LDAP_INTERNAL;
2126 cookie->errorp = *errorp;
2127 return (LDAP_ERROR);
2129 if (errCode == LDAP_REFERRAL) {
2130 for (i = 0; referrals[i] != NULL;
2131 i++) {
2132 /* add to referral list */
2133 rc = __s_api_addRefInfo(
2134 &cookie->reflist,
2135 referrals[i],
2136 cookie->basedn,
2137 &cookie->scope,
2138 cookie->filter,
2139 cookie->conn->ld);
2140 if (rc != NS_LDAP_SUCCESS) {
2141 ldap_value_free(
2142 referrals);
2143 if (retCtrls)
2144 ldap_controls_free(
2145 retCtrls);
2146 return (ERROR);
2149 ldap_value_free(referrals);
2150 if (retCtrls)
2151 ldap_controls_free(retCtrls);
2152 return (END_RESULT);
2154 if (retCtrls) {
2155 if (cookie->ctrlCookie)
2156 ber_bvfree(cookie->ctrlCookie);
2157 cookie->ctrlCookie = NULL;
2158 rc = ldap_parse_page_control(
2159 cookie->conn->ld, retCtrls,
2160 &count, &cookie->ctrlCookie);
2161 if (rc == LDAP_SUCCESS) {
2162 if ((cookie->ctrlCookie == NULL) ||
2163 (cookie->ctrlCookie->bv_val == NULL) ||
2164 (cookie->ctrlCookie->bv_len == 0))
2165 finished = 1;
2167 ldap_controls_free(retCtrls);
2168 retCtrls = NULL;
2170 else
2171 finished = 1;
2173 if (!finished && cookie->listType == VLVCTRLFLAG)
2174 return (NEXT_VLV);
2175 if (!finished && cookie->listType == SIMPLEPAGECTRLFLAG)
2176 return (NEXT_PAGE);
2177 if (finished)
2178 return (END_RESULT);
2179 return (ERROR);
2183 * clear_results(ns_ldap_cookie_t):
2185 * Attempt to obtain remnants of ldap responses and free them. If remnants are
2186 * not obtained within a certain time period tell the server we wish to abandon
2187 * the request.
2189 * Note that we do not initially tell the server to abandon the request as that
2190 * can be an expensive operation for the server, while it is cheap for us to
2191 * just flush the input.
2193 * If something was to remain in libldap queue as a result of some error then
2194 * it would be freed later during drop connection call or when no other
2195 * requests share the connection.
2197 static void
2198 clear_results(ns_ldap_cookie_t *cookie)
2200 int rc;
2201 if (cookie->conn != NULL && cookie->conn->ld != NULL &&
2202 (cookie->connectionId != -1 ||
2203 (cookie->conn_user != NULL &&
2204 cookie->conn_user->conn_mt != NULL)) &&
2205 cookie->msgId != 0) {
2207 * We need to cleanup the rest of response (if there is such)
2208 * and LDAP abandon is too heavy for LDAP servers, so we will
2209 * wait for the rest of response till timeout and "process" it.
2211 rc = ldap_result(cookie->conn->ld, cookie->msgId, LDAP_MSG_ALL,
2212 (struct timeval *)&cookie->search_timeout,
2213 &cookie->resultMsg);
2214 if (rc != -1 && rc != 0 && cookie->resultMsg != NULL) {
2215 (void) ldap_msgfree(cookie->resultMsg);
2216 cookie->resultMsg = NULL;
2220 * If there was timeout then we will send ABANDON request to
2221 * LDAP server to decrease load.
2223 if (rc == 0)
2224 (void) ldap_abandon_ext(cookie->conn->ld, cookie->msgId,
2225 NULL, NULL);
2226 /* Disassociate cookie with msgId */
2227 cookie->msgId = 0;
2232 * This state machine performs one or more LDAP searches to a given
2233 * directory server using service search descriptors and schema
2234 * mapping as appropriate. The approximate pseudocode for
2235 * this routine is the following:
2236 * Given the current configuration [set/reset connection etc.]
2237 * and the current service search descriptor list
2238 * or default search filter parameters
2239 * foreach (service search filter) {
2240 * initialize the filter [via filter_init if appropriate]
2241 * get a valid session/connection (preferably the current one)
2242 * Recover if the connection is lost
2243 * perform the search
2244 * foreach (result entry) {
2245 * process result [via callback if appropriate]
2246 * save result for caller if accepted.
2247 * exit and return all collected if allResults found;
2250 * return collected results and exit
2253 static
2254 ns_state_t
2255 search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
2257 char errstr[MAXERROR];
2258 char *err;
2259 int rc, ret;
2260 int rc_save;
2261 ns_ldap_entry_t *nextEntry;
2262 ns_ldap_error_t *error = NULL;
2263 ns_ldap_error_t **errorp;
2264 struct timeval tv;
2266 errorp = &error;
2267 cookie->state = state;
2268 errstr[0] = '\0';
2270 for (;;) {
2271 switch (cookie->state) {
2272 case CLEAR_RESULTS:
2273 clear_results(cookie);
2274 cookie->new_state = EXIT;
2275 break;
2276 case GET_ACCT_MGMT_INFO:
2278 * Set the flag to get ldap account management controls.
2280 cookie->nopasswd_acct_mgmt = 1;
2281 cookie->new_state = INIT;
2282 break;
2283 case EXIT:
2284 /* state engine/connection cleaned up in delete */
2285 if (cookie->attribute) {
2286 __s_api_free2dArray(cookie->attribute);
2287 cookie->attribute = NULL;
2289 if (cookie->reflist) {
2290 __s_api_deleteRefInfo(cookie->reflist);
2291 cookie->reflist = NULL;
2293 return (EXIT);
2294 case INIT:
2295 cookie->sdpos = NULL;
2296 cookie->new_state = NEXT_SEARCH_DESCRIPTOR;
2297 if (cookie->attribute) {
2298 __s_api_free2dArray(cookie->attribute);
2299 cookie->attribute = NULL;
2301 if ((cookie->i_flags & NS_LDAP_NOMAP) == 0 &&
2302 cookie->i_attr) {
2303 cookie->attribute =
2304 __ns_ldap_mapAttributeList(
2305 cookie->service,
2306 cookie->i_attr);
2308 break;
2309 case REINIT:
2310 /* Check if we've reached MAX retries. */
2311 cookie->retries++;
2312 if (cookie->retries > NS_LIST_TRY_MAX - 1) {
2313 cookie->new_state = LDAP_ERROR;
2314 break;
2318 * Even if we still have retries left, check
2319 * if retry is possible.
2321 if (cookie->conn_user != NULL) {
2322 int retry;
2323 ns_conn_mgmt_t *cmg;
2324 cmg = cookie->conn_user->conn_mgmt;
2325 retry = cookie->conn_user->retry;
2326 if (cmg != NULL && cmg->cfg_reloaded == 1)
2327 retry = 1;
2328 if (retry == 0) {
2329 cookie->new_state = LDAP_ERROR;
2330 break;
2334 * Free results if any, reset to the first
2335 * search descriptor and start a new session.
2337 if (cookie->resultMsg != NULL) {
2338 (void) ldap_msgfree(cookie->resultMsg);
2339 cookie->resultMsg = NULL;
2341 (void) __ns_ldap_freeError(&cookie->errorp);
2342 (void) __ns_ldap_freeResult(&cookie->result);
2343 cookie->sdpos = cookie->sdlist;
2344 cookie->err_from_result = 0;
2345 cookie->err_rc = 0;
2346 cookie->new_state = NEXT_SESSION;
2347 break;
2348 case NEXT_SEARCH_DESCRIPTOR:
2349 /* get next search descriptor */
2350 if (cookie->sdpos == NULL) {
2351 cookie->sdpos = cookie->sdlist;
2352 cookie->new_state = GET_SESSION;
2353 } else {
2354 cookie->sdpos++;
2355 cookie->new_state = NEXT_SEARCH;
2357 if (*cookie->sdpos == NULL)
2358 cookie->new_state = EXIT;
2359 break;
2360 case GET_SESSION:
2361 if (get_current_session(cookie) < 0)
2362 cookie->new_state = NEXT_SESSION;
2363 else
2364 cookie->new_state = NEXT_SEARCH;
2365 break;
2366 case NEXT_SESSION:
2367 if (get_next_session(cookie) < 0)
2368 cookie->new_state = RESTART_SESSION;
2369 else
2370 cookie->new_state = NEXT_SEARCH;
2371 break;
2372 case RESTART_SESSION:
2373 if (cookie->i_flags & NS_LDAP_HARD) {
2374 cookie->new_state = NEXT_SESSION;
2375 break;
2377 (void) sprintf(errstr,
2378 gettext("Session error no available conn.\n"),
2379 state);
2380 err = strdup(errstr);
2381 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2382 NULL);
2383 cookie->err_rc = NS_LDAP_INTERNAL;
2384 cookie->errorp = *errorp;
2385 cookie->new_state = EXIT;
2386 break;
2387 case NEXT_SEARCH:
2388 /* setup referrals search if necessary */
2389 if (cookie->refpos) {
2390 if (setup_referral_search(cookie) < 0) {
2391 cookie->new_state = EXIT;
2392 break;
2394 } else if (setup_next_search(cookie) < 0) {
2395 cookie->new_state = EXIT;
2396 break;
2398 /* only do VLV/PAGE on scopes onelevel/subtree */
2399 if (paging_supported(cookie)) {
2400 if (cookie->use_paging &&
2401 (cookie->scope != LDAP_SCOPE_BASE)) {
2402 cookie->index = 1;
2403 if (cookie->listType == VLVCTRLFLAG)
2404 cookie->new_state = NEXT_VLV;
2405 else
2406 cookie->new_state = NEXT_PAGE;
2407 break;
2410 cookie->new_state = ONE_SEARCH;
2411 break;
2412 case NEXT_VLV:
2413 rc = setup_vlv_params(cookie);
2414 if (rc != LDAP_SUCCESS) {
2415 cookie->err_rc = rc;
2416 cookie->new_state = LDAP_ERROR;
2417 break;
2419 cookie->next_state = MULTI_RESULT;
2420 cookie->new_state = DO_SEARCH;
2421 break;
2422 case NEXT_PAGE:
2423 rc = setup_simplepg_params(cookie);
2424 if (rc != LDAP_SUCCESS) {
2425 cookie->err_rc = rc;
2426 cookie->new_state = LDAP_ERROR;
2427 break;
2429 cookie->next_state = MULTI_RESULT;
2430 cookie->new_state = DO_SEARCH;
2431 break;
2432 case ONE_SEARCH:
2433 cookie->next_state = NEXT_RESULT;
2434 cookie->new_state = DO_SEARCH;
2435 break;
2436 case DO_SEARCH:
2437 cookie->entryCount = 0;
2438 rc = ldap_search_ext(cookie->conn->ld,
2439 cookie->basedn,
2440 cookie->scope,
2441 cookie->filter,
2442 cookie->attribute,
2444 cookie->p_serverctrls,
2445 NULL,
2446 &cookie->search_timeout, 0,
2447 &cookie->msgId);
2448 if (rc != LDAP_SUCCESS) {
2449 if (rc == LDAP_BUSY ||
2450 rc == LDAP_UNAVAILABLE ||
2451 rc == LDAP_UNWILLING_TO_PERFORM ||
2452 rc == LDAP_CONNECT_ERROR ||
2453 rc == LDAP_SERVER_DOWN) {
2455 if (cookie->reinit_on_retriable_err) {
2456 cookie->err_rc = rc;
2457 cookie->new_state = REINIT;
2458 } else
2459 cookie->new_state =
2460 NEXT_SESSION;
2463 * If not able to reach the
2464 * server, inform the ldap
2465 * cache manager that the
2466 * server should be removed
2467 * from it's server list.
2468 * Thus, the manager will not
2469 * return this server on the next
2470 * get-server request and will
2471 * also reduce the server list
2472 * refresh TTL, so that it will
2473 * find out sooner when the server
2474 * is up again.
2476 if ((rc == LDAP_CONNECT_ERROR ||
2477 rc == LDAP_SERVER_DOWN) &&
2478 (cookie->conn_user == NULL ||
2479 cookie->conn_user->conn_mt ==
2480 NULL)) {
2481 ret = __s_api_removeServer(
2482 cookie->conn->serverAddr);
2483 if (ret == NS_CACHE_NOSERVER &&
2484 cookie->conn_auth_type
2485 == NS_LDAP_AUTH_NONE) {
2487 * Couldn't remove
2488 * server from server
2489 * list.
2490 * Exit to avoid
2491 * potential infinite
2492 * loop.
2494 cookie->err_rc = rc;
2495 cookie->new_state =
2496 LDAP_ERROR;
2498 if (cookie->connectionId > -1) {
2500 * NS_LDAP_NEW_CONN
2501 * indicates that the
2502 * connection should
2503 * be deleted, not
2504 * kept alive
2506 DropConnection(
2507 cookie->
2508 connectionId,
2509 NS_LDAP_NEW_CONN);
2510 cookie->connectionId =
2513 } else if ((rc == LDAP_CONNECT_ERROR ||
2514 rc == LDAP_SERVER_DOWN) &&
2515 cookie->conn_user != NULL) {
2516 if (cookie->
2517 reinit_on_retriable_err) {
2519 * MT connection not
2520 * usable, close it
2521 * before REINIT.
2522 * rc has already
2523 * been saved in
2524 * cookie->err_rc above.
2526 __s_api_conn_mt_close(
2527 cookie->conn_user,
2529 &cookie->errorp);
2530 } else {
2532 * MT connection not
2533 * usable, close it in
2534 * the LDAP_ERROR state.
2535 * A retry will be done
2536 * next if allowed.
2538 cookie->err_rc = rc;
2539 cookie->new_state =
2540 LDAP_ERROR;
2543 break;
2545 cookie->err_rc = rc;
2546 cookie->new_state = LDAP_ERROR;
2547 break;
2549 cookie->new_state = cookie->next_state;
2550 break;
2551 case NEXT_RESULT:
2553 * Caller (e.g. __ns_ldap_list_batch_add)
2554 * does not want to block on ldap_result().
2555 * Therefore we execute ldap_result() with
2556 * a zeroed timeval.
2558 if (cookie->no_wait == B_TRUE)
2559 (void) memset(&tv, 0, sizeof (tv));
2560 else
2561 tv = cookie->search_timeout;
2562 rc = ldap_result(cookie->conn->ld, cookie->msgId,
2563 LDAP_MSG_ONE,
2564 &tv,
2565 &cookie->resultMsg);
2566 if (rc == LDAP_RES_SEARCH_RESULT) {
2567 cookie->new_state = END_RESULT;
2568 /* check and process referrals info */
2569 if (cookie->followRef)
2570 proc_result_referrals(
2571 cookie);
2572 (void) ldap_msgfree(cookie->resultMsg);
2573 cookie->resultMsg = NULL;
2574 break;
2576 /* handle referrals if necessary */
2577 if (rc == LDAP_RES_SEARCH_REFERENCE) {
2578 if (cookie->followRef)
2579 proc_search_references(cookie);
2580 (void) ldap_msgfree(cookie->resultMsg);
2581 cookie->resultMsg = NULL;
2582 break;
2584 if (rc != LDAP_RES_SEARCH_ENTRY) {
2585 switch (rc) {
2586 case 0:
2587 if (cookie->no_wait == B_TRUE) {
2588 (void) ldap_msgfree(
2589 cookie->resultMsg);
2590 cookie->resultMsg = NULL;
2591 return (cookie->new_state);
2593 rc = LDAP_TIMEOUT;
2594 break;
2595 case -1:
2596 rc = ldap_get_lderrno(cookie->conn->ld,
2597 NULL, NULL);
2598 break;
2599 default:
2600 rc = ldap_result2error(cookie->conn->ld,
2601 cookie->resultMsg, 1);
2602 break;
2604 if ((rc == LDAP_TIMEOUT ||
2605 rc == LDAP_SERVER_DOWN) &&
2606 (cookie->conn_user == NULL ||
2607 cookie->conn_user->conn_mt == NULL)) {
2608 if (rc == LDAP_TIMEOUT)
2609 (void) __s_api_removeServer(
2610 cookie->conn->serverAddr);
2611 if (cookie->connectionId > -1) {
2612 DropConnection(
2613 cookie->connectionId,
2614 NS_LDAP_NEW_CONN);
2615 cookie->connectionId = -1;
2617 cookie->err_from_result = 1;
2619 (void) ldap_msgfree(cookie->resultMsg);
2620 cookie->resultMsg = NULL;
2621 if (rc == LDAP_BUSY ||
2622 rc == LDAP_UNAVAILABLE ||
2623 rc == LDAP_UNWILLING_TO_PERFORM) {
2624 if (cookie->reinit_on_retriable_err) {
2625 cookie->err_rc = rc;
2626 cookie->err_from_result = 1;
2627 cookie->new_state = REINIT;
2628 } else
2629 cookie->new_state =
2630 NEXT_SESSION;
2631 break;
2633 if ((rc == LDAP_CONNECT_ERROR ||
2634 rc == LDAP_SERVER_DOWN) &&
2635 cookie->reinit_on_retriable_err) {
2636 ns_ldap_error_t *errorp = NULL;
2637 cookie->err_rc = rc;
2638 cookie->err_from_result = 1;
2639 cookie->new_state = REINIT;
2640 if (cookie->conn_user != NULL)
2641 __s_api_conn_mt_close(
2642 cookie->conn_user,
2643 rc, &errorp);
2644 if (errorp != NULL) {
2645 (void) __ns_ldap_freeError(
2646 &cookie->errorp);
2647 cookie->errorp = errorp;
2649 break;
2651 cookie->err_rc = rc;
2652 cookie->new_state = LDAP_ERROR;
2653 break;
2655 /* else LDAP_RES_SEARCH_ENTRY */
2656 /* get account management response control */
2657 if (cookie->nopasswd_acct_mgmt == 1) {
2658 rc = ldap_get_entry_controls(cookie->conn->ld,
2659 cookie->resultMsg,
2660 &(cookie->resultctrl));
2661 if (rc != LDAP_SUCCESS) {
2662 cookie->new_state = LDAP_ERROR;
2663 cookie->err_rc = rc;
2664 break;
2667 rc = __s_api_getEntry(cookie);
2668 (void) ldap_msgfree(cookie->resultMsg);
2669 cookie->resultMsg = NULL;
2670 if (rc != NS_LDAP_SUCCESS) {
2671 cookie->new_state = LDAP_ERROR;
2672 break;
2674 cookie->new_state = PROCESS_RESULT;
2675 cookie->next_state = NEXT_RESULT;
2676 break;
2677 case MULTI_RESULT:
2678 if (cookie->no_wait == B_TRUE)
2679 (void) memset(&tv, 0, sizeof (tv));
2680 else
2681 tv = cookie->search_timeout;
2682 rc = ldap_result(cookie->conn->ld, cookie->msgId,
2683 LDAP_MSG_ONE,
2684 &tv,
2685 &cookie->resultMsg);
2686 if (rc == LDAP_RES_SEARCH_RESULT) {
2687 rc = ldap_result2error(cookie->conn->ld,
2688 cookie->resultMsg, 0);
2689 if (rc == LDAP_ADMINLIMIT_EXCEEDED &&
2690 cookie->listType == VLVCTRLFLAG &&
2691 cookie->sortTypeTry == SSS_SINGLE_ATTR) {
2692 /* Try old "cn uid" server side sort */
2693 cookie->sortTypeTry = SSS_CN_UID_ATTRS;
2694 cookie->new_state = NEXT_VLV;
2695 (void) ldap_msgfree(cookie->resultMsg);
2696 cookie->resultMsg = NULL;
2697 break;
2699 if (rc != LDAP_SUCCESS) {
2700 cookie->err_rc = rc;
2701 cookie->new_state = LDAP_ERROR;
2702 (void) ldap_msgfree(cookie->resultMsg);
2703 cookie->resultMsg = NULL;
2704 break;
2706 cookie->new_state = multi_result(cookie);
2707 (void) ldap_msgfree(cookie->resultMsg);
2708 cookie->resultMsg = NULL;
2709 break;
2711 /* handle referrals if necessary */
2712 if (rc == LDAP_RES_SEARCH_REFERENCE &&
2713 cookie->followRef) {
2714 proc_search_references(cookie);
2715 (void) ldap_msgfree(cookie->resultMsg);
2716 cookie->resultMsg = NULL;
2717 break;
2719 if (rc != LDAP_RES_SEARCH_ENTRY) {
2720 switch (rc) {
2721 case 0:
2722 if (cookie->no_wait == B_TRUE) {
2723 (void) ldap_msgfree(
2724 cookie->resultMsg);
2725 cookie->resultMsg = NULL;
2726 return (cookie->new_state);
2728 rc = LDAP_TIMEOUT;
2729 break;
2730 case -1:
2731 rc = ldap_get_lderrno(cookie->conn->ld,
2732 NULL, NULL);
2733 break;
2734 default:
2735 rc = ldap_result2error(cookie->conn->ld,
2736 cookie->resultMsg, 1);
2737 break;
2739 if ((rc == LDAP_TIMEOUT ||
2740 rc == LDAP_SERVER_DOWN) &&
2741 (cookie->conn_user == NULL ||
2742 cookie->conn_user->conn_mt == NULL)) {
2743 if (rc == LDAP_TIMEOUT)
2744 (void) __s_api_removeServer(
2745 cookie->conn->serverAddr);
2746 if (cookie->connectionId > -1) {
2747 DropConnection(
2748 cookie->connectionId,
2749 NS_LDAP_NEW_CONN);
2750 cookie->connectionId = -1;
2752 cookie->err_from_result = 1;
2754 (void) ldap_msgfree(cookie->resultMsg);
2755 cookie->resultMsg = NULL;
2756 if (rc == LDAP_BUSY ||
2757 rc == LDAP_UNAVAILABLE ||
2758 rc == LDAP_UNWILLING_TO_PERFORM) {
2759 if (cookie->reinit_on_retriable_err) {
2760 cookie->err_rc = rc;
2761 cookie->err_from_result = 1;
2762 cookie->new_state = REINIT;
2763 } else
2764 cookie->new_state =
2765 NEXT_SESSION;
2766 break;
2769 if ((rc == LDAP_CONNECT_ERROR ||
2770 rc == LDAP_SERVER_DOWN) &&
2771 cookie->reinit_on_retriable_err) {
2772 ns_ldap_error_t *errorp = NULL;
2773 cookie->err_rc = rc;
2774 cookie->err_from_result = 1;
2775 cookie->new_state = REINIT;
2776 if (cookie->conn_user != NULL)
2777 __s_api_conn_mt_close(
2778 cookie->conn_user,
2779 rc, &errorp);
2780 if (errorp != NULL) {
2781 (void) __ns_ldap_freeError(
2782 &cookie->errorp);
2783 cookie->errorp = errorp;
2785 break;
2787 cookie->err_rc = rc;
2788 cookie->new_state = LDAP_ERROR;
2789 break;
2791 /* else LDAP_RES_SEARCH_ENTRY */
2792 cookie->entryCount++;
2793 rc = __s_api_getEntry(cookie);
2794 (void) ldap_msgfree(cookie->resultMsg);
2795 cookie->resultMsg = NULL;
2796 if (rc != NS_LDAP_SUCCESS) {
2797 cookie->new_state = LDAP_ERROR;
2798 break;
2801 * If VLV search was successfull save the server
2802 * side sort type tried.
2804 if (cookie->listType == VLVCTRLFLAG)
2805 update_srvsidesort_type(cookie->service,
2806 cookie->sortTypeTry);
2808 cookie->new_state = PROCESS_RESULT;
2809 cookie->next_state = MULTI_RESULT;
2810 break;
2811 case PROCESS_RESULT:
2812 /* NOTE THIS STATE MAY BE PROCESSED BY CALLER */
2813 if (cookie->use_usercb && cookie->callback) {
2814 rc = 0;
2815 for (nextEntry = cookie->result->entry;
2816 nextEntry != NULL;
2817 nextEntry = nextEntry->next) {
2818 rc = (*cookie->callback)(nextEntry,
2819 cookie->userdata);
2821 if (rc == NS_LDAP_CB_DONE) {
2822 /* cb doesn't want any more data */
2823 rc = NS_LDAP_PARTIAL;
2824 cookie->err_rc = rc;
2825 break;
2826 } else if (rc != NS_LDAP_CB_NEXT) {
2827 /* invalid return code */
2828 rc = NS_LDAP_OP_FAILED;
2829 cookie->err_rc = rc;
2830 break;
2833 (void) __ns_ldap_freeResult(&cookie->result);
2834 cookie->result = NULL;
2836 if (rc != 0) {
2837 cookie->new_state = EXIT;
2838 break;
2840 /* NOTE PREVIOUS STATE SPECIFIES NEXT STATE */
2841 cookie->new_state = cookie->next_state;
2842 break;
2843 case END_PROCESS_RESULT:
2844 cookie->new_state = cookie->next_state;
2845 break;
2846 case END_RESULT:
2848 * XXX DO WE NEED THIS CASE?
2849 * if (search is complete) {
2850 * cookie->new_state = EXIT;
2851 * } else
2854 * entering referral mode if necessary
2856 if (cookie->followRef && cookie->reflist)
2857 cookie->new_state =
2858 NEXT_REFERRAL;
2859 else
2860 cookie->new_state =
2861 NEXT_SEARCH_DESCRIPTOR;
2862 break;
2863 case NEXT_REFERRAL:
2864 /* get next referral info */
2865 if (cookie->refpos == NULL)
2866 cookie->refpos =
2867 cookie->reflist;
2868 else
2869 cookie->refpos =
2870 cookie->refpos->next;
2871 /* check see if done with all referrals */
2872 if (cookie->refpos != NULL)
2873 cookie->new_state =
2874 GET_REFERRAL_SESSION;
2875 else {
2876 __s_api_deleteRefInfo(cookie->reflist);
2877 cookie->reflist = NULL;
2878 cookie->new_state =
2879 NEXT_SEARCH_DESCRIPTOR;
2880 if (cookie->conn_user != NULL)
2881 cookie->conn_user->referral = B_FALSE;
2883 break;
2884 case GET_REFERRAL_SESSION:
2885 if (get_referral_session(cookie) < 0)
2886 cookie->new_state = EXIT;
2887 else {
2888 cookie->new_state = NEXT_SEARCH;
2890 break;
2891 case LDAP_ERROR:
2892 rc_save = cookie->err_rc;
2893 if (cookie->err_from_result) {
2894 if (cookie->err_rc == LDAP_SERVER_DOWN) {
2895 (void) sprintf(errstr,
2896 gettext("LDAP ERROR (%d): "
2897 "Error occurred during"
2898 " receiving results. "
2899 "Connection to server lost."),
2900 cookie->err_rc);
2901 } else if (cookie->err_rc == LDAP_TIMEOUT) {
2902 (void) sprintf(errstr,
2903 gettext("LDAP ERROR (%d): "
2904 "Error occurred during"
2905 " receiving results. %s"
2906 "."), cookie->err_rc,
2907 ldap_err2string(
2908 cookie->err_rc));
2910 } else
2911 (void) sprintf(errstr,
2912 gettext("LDAP ERROR (%d): %s."),
2913 cookie->err_rc,
2914 ldap_err2string(cookie->err_rc));
2915 err = strdup(errstr);
2916 if (cookie->err_from_result) {
2917 if (cookie->err_rc == LDAP_SERVER_DOWN) {
2918 MKERROR(LOG_INFO, *errorp,
2919 cookie->err_rc, err, NULL);
2920 } else {
2921 MKERROR(LOG_WARNING, *errorp,
2922 cookie->err_rc, err, NULL);
2924 } else {
2925 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2926 err, NULL);
2928 cookie->err_rc = NS_LDAP_INTERNAL;
2929 cookie->errorp = *errorp;
2930 if (cookie->conn_user != NULL) {
2931 if (rc_save == LDAP_SERVER_DOWN ||
2932 rc_save == LDAP_CONNECT_ERROR) {
2934 * MT connection is not usable,
2935 * close it.
2937 __s_api_conn_mt_close(cookie->conn_user,
2938 rc_save, &cookie->errorp);
2939 return (ERROR);
2942 return (ERROR);
2943 default:
2944 case ERROR:
2945 (void) sprintf(errstr,
2946 gettext("Internal State machine exit (%d).\n"),
2947 cookie->state);
2948 err = strdup(errstr);
2949 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2950 NULL);
2951 cookie->err_rc = NS_LDAP_INTERNAL;
2952 cookie->errorp = *errorp;
2953 return (ERROR);
2956 if (cookie->conn_user != NULL &&
2957 cookie->conn_user->bad_mt_conn == B_TRUE) {
2958 __s_api_conn_mt_close(cookie->conn_user, 0, NULL);
2959 cookie->err_rc = cookie->conn_user->ns_rc;
2960 cookie->errorp = cookie->conn_user->ns_error;
2961 cookie->conn_user->ns_error = NULL;
2962 return (ERROR);
2965 if (cycle == ONE_STEP) {
2966 return (cookie->new_state);
2968 cookie->state = cookie->new_state;
2970 /*NOTREACHED*/
2971 #if 0
2972 (void) sprintf(errstr,
2973 gettext("Unexpected State machine error.\n"));
2974 err = strdup(errstr);
2975 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err, NULL);
2976 cookie->err_rc = NS_LDAP_INTERNAL;
2977 cookie->errorp = *errorp;
2978 return (ERROR);
2979 #endif
2983 * For a lookup of shadow data, if shadow update is enabled,
2984 * check the calling process' privilege to ensure it's
2985 * allowed to perform such operation.
2987 static int
2988 check_shadow(ns_ldap_cookie_t *cookie, const char *service)
2990 char errstr[MAXERROR];
2991 char *err;
2992 boolean_t priv;
2993 /* caller */
2994 priv_set_t *ps;
2995 /* zone */
2996 priv_set_t *zs;
2999 * If service is "shadow", we may need
3000 * to use privilege credentials.
3002 if ((strcmp(service, "shadow") == 0) &&
3003 __ns_ldap_is_shadow_update_enabled()) {
3005 * Since we release admin credentials after
3006 * connection is closed and we do not cache
3007 * them, we allow any root or all zone
3008 * privilege process to read shadow data.
3010 priv = (geteuid() == 0);
3011 if (!priv) {
3012 /* caller */
3013 ps = priv_allocset();
3015 (void) getppriv(PRIV_EFFECTIVE, ps);
3016 zs = priv_str_to_set("zone", ",", NULL);
3017 priv = priv_isequalset(ps, zs);
3018 priv_freeset(ps);
3019 priv_freeset(zs);
3021 if (!priv) {
3022 (void) sprintf(errstr,
3023 gettext("Permission denied"));
3024 err = strdup(errstr);
3025 if (err == NULL)
3026 return (NS_LDAP_MEMORY);
3027 MKERROR(LOG_INFO, cookie->errorp, NS_LDAP_INTERNAL, err,
3028 NULL);
3029 return (NS_LDAP_INTERNAL);
3031 cookie->i_flags |= NS_LDAP_READ_SHADOW;
3033 * We do not want to reuse connection (hence
3034 * keep it open) with admin credentials.
3035 * If NS_LDAP_KEEP_CONN is set, reject the
3036 * request.
3038 if (cookie->i_flags & NS_LDAP_KEEP_CONN)
3039 return (NS_LDAP_INVALID_PARAM);
3040 cookie->i_flags |= NS_LDAP_NEW_CONN;
3043 return (NS_LDAP_SUCCESS);
3047 * internal function for __ns_ldap_list
3049 static int
3050 ldap_list(
3051 ns_ldap_list_batch_t *batch,
3052 const char *service,
3053 const char *filter,
3054 const char *sortattr,
3055 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3056 char **realfilter, const void *userdata),
3057 const char * const *attribute,
3058 const ns_cred_t *auth,
3059 const int flags,
3060 ns_ldap_result_t **rResult, /* return result entries */
3061 ns_ldap_error_t **errorp,
3062 int *rcp,
3063 int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3064 const void *userdata, ns_conn_user_t *conn_user)
3066 ns_ldap_cookie_t *cookie;
3067 ns_ldap_search_desc_t **sdlist = NULL;
3068 ns_ldap_search_desc_t *dptr;
3069 ns_ldap_error_t *error = NULL;
3070 char **dns = NULL;
3071 int scope;
3072 int rc;
3073 int from_result;
3075 *errorp = NULL;
3076 *rResult = NULL;
3077 *rcp = NS_LDAP_SUCCESS;
3080 * Sanity check - NS_LDAP_READ_SHADOW is for our
3081 * own internal use.
3083 if (flags & NS_LDAP_READ_SHADOW)
3084 return (NS_LDAP_INVALID_PARAM);
3086 /* Initialize State machine cookie */
3087 cookie = init_search_state_machine();
3088 if (cookie == NULL) {
3089 *rcp = NS_LDAP_MEMORY;
3090 return (NS_LDAP_MEMORY);
3092 cookie->conn_user = conn_user;
3094 /* see if need to follow referrals */
3095 rc = __s_api_toFollowReferrals(flags,
3096 &cookie->followRef, errorp);
3097 if (rc != NS_LDAP_SUCCESS) {
3098 delete_search_cookie(cookie);
3099 *rcp = rc;
3100 return (rc);
3103 /* get the service descriptor - or create a default one */
3104 rc = __s_api_get_SSD_from_SSDtoUse_service(service,
3105 &sdlist, &error);
3106 if (rc != NS_LDAP_SUCCESS) {
3107 delete_search_cookie(cookie);
3108 *errorp = error;
3109 *rcp = rc;
3110 return (rc);
3113 if (sdlist == NULL) {
3114 /* Create default service Desc */
3115 sdlist = (ns_ldap_search_desc_t **)calloc(2,
3116 sizeof (ns_ldap_search_desc_t *));
3117 if (sdlist == NULL) {
3118 delete_search_cookie(cookie);
3119 cookie = NULL;
3120 *rcp = NS_LDAP_MEMORY;
3121 return (NS_LDAP_MEMORY);
3123 dptr = (ns_ldap_search_desc_t *)
3124 calloc(1, sizeof (ns_ldap_search_desc_t));
3125 if (dptr == NULL) {
3126 free(sdlist);
3127 delete_search_cookie(cookie);
3128 cookie = NULL;
3129 *rcp = NS_LDAP_MEMORY;
3130 return (NS_LDAP_MEMORY);
3132 sdlist[0] = dptr;
3134 /* default base */
3135 rc = __s_api_getDNs(&dns, service, &cookie->errorp);
3136 if (rc != NS_LDAP_SUCCESS) {
3137 if (dns) {
3138 __s_api_free2dArray(dns);
3139 dns = NULL;
3141 *errorp = cookie->errorp;
3142 cookie->errorp = NULL;
3143 delete_search_cookie(cookie);
3144 cookie = NULL;
3145 *rcp = rc;
3146 return (rc);
3148 dptr->basedn = strdup(dns[0]);
3149 __s_api_free2dArray(dns);
3150 dns = NULL;
3152 /* default scope */
3153 scope = 0;
3154 rc = __s_api_getSearchScope(&scope, &cookie->errorp);
3155 dptr->scope = scope;
3158 cookie->sdlist = sdlist;
3161 * use VLV/PAGE control only if NS_LDAP_PAGE_CTRL is set
3163 if (flags & NS_LDAP_PAGE_CTRL)
3164 cookie->use_paging = TRUE;
3165 else
3166 cookie->use_paging = FALSE;
3168 /* Set up other arguments */
3169 cookie->userdata = userdata;
3170 if (init_filter_cb != NULL) {
3171 cookie->init_filter_cb = init_filter_cb;
3172 cookie->use_filtercb = 1;
3174 if (callback != NULL) {
3175 cookie->callback = callback;
3176 cookie->use_usercb = 1;
3179 /* check_shadow() may add extra value to cookie->i_flags */
3180 cookie->i_flags = flags;
3181 if (service) {
3182 cookie->service = strdup(service);
3183 if (cookie->service == NULL) {
3184 delete_search_cookie(cookie);
3185 cookie = NULL;
3186 *rcp = NS_LDAP_MEMORY;
3187 return (NS_LDAP_MEMORY);
3191 * If given, use the credential given by the caller, and
3192 * skip the credential check required for shadow update.
3194 if (auth == NULL) {
3195 rc = check_shadow(cookie, service);
3196 if (rc != NS_LDAP_SUCCESS) {
3197 *errorp = cookie->errorp;
3198 cookie->errorp = NULL;
3199 delete_search_cookie(cookie);
3200 cookie = NULL;
3201 *rcp = rc;
3202 return (rc);
3207 cookie->i_filter = strdup(filter);
3208 cookie->i_attr = attribute;
3209 cookie->i_auth = auth;
3210 cookie->i_sortattr = sortattr;
3212 if (batch != NULL) {
3213 cookie->batch = batch;
3214 cookie->reinit_on_retriable_err = B_TRUE;
3215 cookie->no_wait = B_TRUE;
3216 (void) search_state_machine(cookie, INIT, 0);
3217 cookie->no_wait = B_FALSE;
3218 rc = cookie->err_rc;
3220 if (rc == NS_LDAP_SUCCESS) {
3222 * Here rc == NS_LDAP_SUCCESS means that the state
3223 * machine init'ed successfully. The actual status
3224 * of the search will be determined by
3225 * __ns_ldap_list_batch_end(). Add the cookie to our
3226 * batch.
3228 cookie->caller_result = rResult;
3229 cookie->caller_errorp = errorp;
3230 cookie->caller_rc = rcp;
3231 cookie->next_cookie_in_batch = batch->cookie_list;
3232 batch->cookie_list = cookie;
3233 batch->nactive++;
3234 return (rc);
3237 * If state machine init failed then copy error to the caller
3238 * and delete the cookie.
3240 } else {
3241 (void) search_state_machine(cookie, INIT, 0);
3244 /* Copy results back to user */
3245 rc = cookie->err_rc;
3246 if (rc != NS_LDAP_SUCCESS) {
3247 if (conn_user != NULL && conn_user->ns_error != NULL) {
3248 *errorp = conn_user->ns_error;
3249 conn_user->ns_error = NULL;
3250 } else
3251 *errorp = cookie->errorp;
3253 *rResult = cookie->result;
3254 from_result = cookie->err_from_result;
3256 cookie->errorp = NULL;
3257 cookie->result = NULL;
3258 delete_search_cookie(cookie);
3259 cookie = NULL;
3261 if (from_result == 0 && *rResult == NULL)
3262 rc = NS_LDAP_NOTFOUND;
3263 *rcp = rc;
3264 return (rc);
3269 * __ns_ldap_list performs one or more LDAP searches to a given
3270 * directory server using service search descriptors and schema
3271 * mapping as appropriate. The operation may be retried a
3272 * couple of times in error situations.
3275 __ns_ldap_list(
3276 const char *service,
3277 const char *filter,
3278 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3279 char **realfilter, const void *userdata),
3280 const char * const *attribute,
3281 const ns_cred_t *auth,
3282 const int flags,
3283 ns_ldap_result_t **rResult, /* return result entries */
3284 ns_ldap_error_t **errorp,
3285 int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3286 const void *userdata)
3288 int mod_flags;
3290 * Strip the NS_LDAP_PAGE_CTRL option as this interface does not
3291 * support this. If you want to use this option call the API
3292 * __ns_ldap_list_sort() with has the sort attribute.
3294 mod_flags = flags & (~NS_LDAP_PAGE_CTRL);
3296 return (__ns_ldap_list_sort(service, filter, NULL, init_filter_cb,
3297 attribute, auth, mod_flags, rResult, errorp,
3298 callback, userdata));
3302 * __ns_ldap_list_sort performs one or more LDAP searches to a given
3303 * directory server using service search descriptors and schema
3304 * mapping as appropriate. The operation may be retried a
3305 * couple of times in error situations.
3308 __ns_ldap_list_sort(
3309 const char *service,
3310 const char *filter,
3311 const char *sortattr,
3312 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3313 char **realfilter, const void *userdata),
3314 const char * const *attribute,
3315 const ns_cred_t *auth,
3316 const int flags,
3317 ns_ldap_result_t **rResult, /* return result entries */
3318 ns_ldap_error_t **errorp,
3319 int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3320 const void *userdata)
3322 ns_conn_user_t *cu = NULL;
3323 int try_cnt = 0;
3324 int rc = NS_LDAP_SUCCESS, trc;
3326 for (;;) {
3327 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
3328 &try_cnt, &rc, errorp) == 0)
3329 break;
3330 rc = ldap_list(NULL, service, filter, sortattr, init_filter_cb,
3331 attribute, auth, flags, rResult, errorp, &trc, callback,
3332 userdata, cu);
3335 return (rc);
3339 * Create and initialize batch for native LDAP lookups
3342 __ns_ldap_list_batch_start(ns_ldap_list_batch_t **batch)
3344 *batch = calloc(1, sizeof (ns_ldap_list_batch_t));
3345 if (*batch == NULL)
3346 return (NS_LDAP_MEMORY);
3347 return (NS_LDAP_SUCCESS);
3352 * Add a LDAP search request to the batch.
3355 __ns_ldap_list_batch_add(
3356 ns_ldap_list_batch_t *batch,
3357 const char *service,
3358 const char *filter,
3359 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3360 char **realfilter, const void *userdata),
3361 const char * const *attribute,
3362 const ns_cred_t *auth,
3363 const int flags,
3364 ns_ldap_result_t **rResult, /* return result entries */
3365 ns_ldap_error_t **errorp,
3366 int *rcp,
3367 int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3368 const void *userdata)
3370 ns_conn_user_t *cu;
3371 int rc;
3372 int mod_flags;
3374 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, 0);
3375 if (cu == NULL) {
3376 if (rcp != NULL)
3377 *rcp = NS_LDAP_MEMORY;
3378 return (NS_LDAP_MEMORY);
3382 * Strip the NS_LDAP_PAGE_CTRL option as the batch interface does not
3383 * support this.
3385 mod_flags = flags & (~NS_LDAP_PAGE_CTRL);
3387 rc = ldap_list(batch, service, filter, NULL, init_filter_cb, attribute,
3388 auth, mod_flags, rResult, errorp, rcp, callback, userdata, cu);
3391 * Free the conn_user if the cookie was not batched. If the cookie
3392 * was batched then __ns_ldap_list_batch_end or release will free the
3393 * conn_user. The batch API instructs the search_state_machine
3394 * to reinit and retry (max 3 times) on retriable LDAP errors.
3396 if (rc != NS_LDAP_SUCCESS && cu != NULL) {
3397 if (cu->conn_mt != NULL)
3398 __s_api_conn_mt_return(cu);
3399 __s_api_conn_user_free(cu);
3401 return (rc);
3406 * Free batch.
3408 void
3409 __ns_ldap_list_batch_release(ns_ldap_list_batch_t *batch)
3411 ns_ldap_cookie_t *c, *next;
3413 for (c = batch->cookie_list; c != NULL; c = next) {
3414 next = c->next_cookie_in_batch;
3415 if (c->conn_user != NULL) {
3416 if (c->conn_user->conn_mt != NULL)
3417 __s_api_conn_mt_return(c->conn_user);
3418 __s_api_conn_user_free(c->conn_user);
3419 c->conn_user = NULL;
3421 delete_search_cookie(c);
3423 free(batch);
3426 #define LD_USING_STATE(st) \
3427 ((st == DO_SEARCH) || (st == MULTI_RESULT) || (st == NEXT_RESULT))
3430 * Process batch. Everytime this function is called it selects an
3431 * active cookie from the batch and single steps through the
3432 * search_state_machine for the selected cookie. If lookup associated
3433 * with the cookie is complete (success or error) then the cookie is
3434 * removed from the batch and its memory freed.
3436 * Returns 1 (if batch still has active cookies)
3437 * 0 (if batch has no more active cookies)
3438 * -1 (on errors, *rcp will contain the error code)
3440 * The caller should call this function in a loop as long as it returns 1
3441 * to process all the requests added to the batch. The results (and errors)
3442 * will be available in the locations provided by the caller at the time of
3443 * __ns_ldap_list_batch_add().
3445 static
3447 __ns_ldap_list_batch_process(ns_ldap_list_batch_t *batch, int *rcp)
3449 ns_ldap_cookie_t *c, *ptr, **prev;
3450 ns_state_t state;
3451 ns_ldap_error_t *errorp = NULL;
3452 int rc;
3454 /* Check if are already done */
3455 if (batch->nactive == 0)
3456 return (0);
3458 /* Get the next cookie from the batch */
3459 c = (batch->next_cookie == NULL) ?
3460 batch->cookie_list : batch->next_cookie;
3462 batch->next_cookie = c->next_cookie_in_batch;
3465 * Checks the status of the cookie's connection if it needs
3466 * to use that connection for ldap_search_ext or ldap_result.
3467 * If the connection is no longer good but worth retrying
3468 * then reinit the search_state_machine for this cookie
3469 * starting from the first search descriptor. REINIT will
3470 * clear any leftover results if max retries have not been
3471 * reached and redo the search (which may also involve
3472 * following referrals again).
3474 * Note that each cookie in the batch will make this
3475 * determination when it reaches one of the LD_USING_STATES.
3477 if (LD_USING_STATE(c->new_state) && c->conn_user != NULL) {
3478 rc = __s_api_setup_getnext(c->conn_user, &c->err_rc, &errorp);
3479 if (rc == LDAP_BUSY || rc == LDAP_UNAVAILABLE ||
3480 rc == LDAP_UNWILLING_TO_PERFORM) {
3481 if (errorp != NULL) {
3482 (void) __ns_ldap_freeError(&c->errorp);
3483 c->errorp = errorp;
3485 c->new_state = REINIT;
3486 } else if (rc == LDAP_CONNECT_ERROR ||
3487 rc == LDAP_SERVER_DOWN) {
3488 if (errorp != NULL) {
3489 (void) __ns_ldap_freeError(&c->errorp);
3490 c->errorp = errorp;
3492 c->new_state = REINIT;
3494 * MT connection is not usable,
3495 * close it before REINIT.
3497 __s_api_conn_mt_close(
3498 c->conn_user, rc, NULL);
3499 } else if (rc != NS_LDAP_SUCCESS) {
3500 if (rcp != NULL)
3501 *rcp = rc;
3502 *c->caller_result = NULL;
3503 *c->caller_errorp = errorp;
3504 *c->caller_rc = rc;
3505 return (-1);
3509 for (;;) {
3510 /* Single step through the search_state_machine */
3511 state = search_state_machine(c, c->new_state, ONE_STEP);
3512 switch (state) {
3513 case LDAP_ERROR:
3514 (void) search_state_machine(c, state, ONE_STEP);
3515 (void) search_state_machine(c, CLEAR_RESULTS, ONE_STEP);
3516 /* FALLTHROUGH */
3517 case ERROR:
3518 case EXIT:
3519 *c->caller_result = c->result;
3520 *c->caller_errorp = c->errorp;
3521 *c->caller_rc =
3522 (c->result == NULL && c->err_from_result == 0)
3523 ? NS_LDAP_NOTFOUND : c->err_rc;
3524 c->result = NULL;
3525 c->errorp = NULL;
3526 /* Remove the cookie from the batch */
3527 ptr = batch->cookie_list;
3528 prev = &batch->cookie_list;
3529 while (ptr != NULL) {
3530 if (ptr == c) {
3531 *prev = ptr->next_cookie_in_batch;
3532 break;
3534 prev = &ptr->next_cookie_in_batch;
3535 ptr = ptr->next_cookie_in_batch;
3537 /* Delete cookie and decrement active cookie count */
3538 if (c->conn_user != NULL) {
3539 if (c->conn_user->conn_mt != NULL)
3540 __s_api_conn_mt_return(c->conn_user);
3541 __s_api_conn_user_free(c->conn_user);
3542 c->conn_user = NULL;
3544 delete_search_cookie(c);
3545 batch->nactive--;
3546 break;
3547 case NEXT_RESULT:
3548 case MULTI_RESULT:
3550 * This means that search_state_machine needs to do
3551 * another ldap_result() for the cookie in question.
3552 * We only do at most one ldap_result() per call in
3553 * this function and therefore we return. This allows
3554 * the caller to process results from other cookies
3555 * in the batch without getting tied up on just one
3556 * cookie.
3558 break;
3559 default:
3561 * This includes states that follow NEXT_RESULT or
3562 * MULTI_RESULT such as PROCESS_RESULT and
3563 * END_PROCESS_RESULT. We continue processing
3564 * this cookie till we reach either the error, exit
3565 * or the result states.
3567 continue;
3569 break;
3572 /* Return 0 if no more cookies left otherwise 1 */
3573 return ((batch->nactive > 0) ? 1 : 0);
3578 * Process all the active cookies in the batch and when none
3579 * remains finalize the batch.
3582 __ns_ldap_list_batch_end(ns_ldap_list_batch_t *batch)
3584 int rc = NS_LDAP_SUCCESS;
3585 while (__ns_ldap_list_batch_process(batch, &rc) > 0)
3587 __ns_ldap_list_batch_release(batch);
3588 return (rc);
3592 * find_domainname performs one or more LDAP searches to
3593 * find the value of the nisdomain attribute associated with
3594 * the input DN (with no retry).
3597 static int
3598 find_domainname(const char *dn, char **domainname, const ns_cred_t *cred,
3599 ns_ldap_error_t **errorp, ns_conn_user_t *conn_user)
3602 ns_ldap_cookie_t *cookie;
3603 ns_ldap_search_desc_t **sdlist;
3604 ns_ldap_search_desc_t *dptr;
3605 int rc;
3606 char **value;
3607 int flags = 0;
3609 *domainname = NULL;
3610 *errorp = NULL;
3612 /* Initialize State machine cookie */
3613 cookie = init_search_state_machine();
3614 if (cookie == NULL) {
3615 return (NS_LDAP_MEMORY);
3617 cookie->conn_user = conn_user;
3619 /* see if need to follow referrals */
3620 rc = __s_api_toFollowReferrals(flags,
3621 &cookie->followRef, errorp);
3622 if (rc != NS_LDAP_SUCCESS) {
3623 delete_search_cookie(cookie);
3624 return (rc);
3627 /* Create default service Desc */
3628 sdlist = (ns_ldap_search_desc_t **)calloc(2,
3629 sizeof (ns_ldap_search_desc_t *));
3630 if (sdlist == NULL) {
3631 delete_search_cookie(cookie);
3632 cookie = NULL;
3633 return (NS_LDAP_MEMORY);
3635 dptr = (ns_ldap_search_desc_t *)
3636 calloc(1, sizeof (ns_ldap_search_desc_t));
3637 if (dptr == NULL) {
3638 free(sdlist);
3639 delete_search_cookie(cookie);
3640 cookie = NULL;
3641 return (NS_LDAP_MEMORY);
3643 sdlist[0] = dptr;
3645 /* search base is dn */
3646 dptr->basedn = strdup(dn);
3648 /* search scope is base */
3649 dptr->scope = NS_LDAP_SCOPE_BASE;
3651 /* search filter is "nisdomain=*" */
3652 dptr->filter = strdup(_NIS_FILTER);
3654 cookie->sdlist = sdlist;
3655 cookie->i_filter = strdup(dptr->filter);
3656 cookie->i_attr = nis_domain_attrs;
3657 cookie->i_auth = cred;
3658 cookie->i_flags = 0;
3660 /* Process search */
3661 rc = search_state_machine(cookie, INIT, 0);
3663 /* Copy domain name if found */
3664 rc = cookie->err_rc;
3665 if (rc != NS_LDAP_SUCCESS) {
3666 if (conn_user != NULL && conn_user->ns_error != NULL) {
3667 *errorp = conn_user->ns_error;
3668 conn_user->ns_error = NULL;
3669 } else
3670 *errorp = cookie->errorp;
3672 if (cookie->result == NULL)
3673 rc = NS_LDAP_NOTFOUND;
3674 if (rc == NS_LDAP_SUCCESS) {
3675 value = __ns_ldap_getAttr(cookie->result->entry,
3676 _NIS_DOMAIN);
3677 if (value[0])
3678 *domainname = strdup(value[0]);
3679 else
3680 rc = NS_LDAP_NOTFOUND;
3682 if (cookie->result != NULL)
3683 (void) __ns_ldap_freeResult(&cookie->result);
3684 cookie->errorp = NULL;
3685 delete_search_cookie(cookie);
3686 cookie = NULL;
3687 return (rc);
3691 * __s_api_find_domainname performs one or more LDAP searches to
3692 * find the value of the nisdomain attribute associated with
3693 * the input DN (with retry).
3696 static int
3697 __s_api_find_domainname(const char *dn, char **domainname,
3698 const ns_cred_t *cred, ns_ldap_error_t **errorp)
3700 ns_conn_user_t *cu = NULL;
3701 int try_cnt = 0;
3702 int rc = NS_LDAP_SUCCESS;
3704 for (;;) {
3705 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
3706 &try_cnt, &rc, errorp) == 0)
3707 break;
3708 rc = find_domainname(dn, domainname, cred, errorp, cu);
3711 return (rc);
3714 static int
3715 firstEntry(
3716 const char *service,
3717 const char *filter,
3718 const char *sortattr,
3719 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3720 char **realfilter, const void *userdata),
3721 const char * const *attribute,
3722 const ns_cred_t *auth,
3723 const int flags,
3724 void **vcookie,
3725 ns_ldap_result_t **result,
3726 ns_ldap_error_t ** errorp,
3727 const void *userdata,
3728 ns_conn_user_t *conn_user)
3730 ns_ldap_cookie_t *cookie = NULL;
3731 ns_ldap_error_t *error = NULL;
3732 ns_state_t state;
3733 ns_ldap_search_desc_t **sdlist;
3734 ns_ldap_search_desc_t *dptr;
3735 char **dns = NULL;
3736 int scope;
3737 int rc;
3739 *errorp = NULL;
3740 *result = NULL;
3743 * Sanity check - NS_LDAP_READ_SHADOW is for our
3744 * own internal use.
3746 if (flags & NS_LDAP_READ_SHADOW)
3747 return (NS_LDAP_INVALID_PARAM);
3749 /* get the service descriptor - or create a default one */
3750 rc = __s_api_get_SSD_from_SSDtoUse_service(service,
3751 &sdlist, &error);
3752 if (rc != NS_LDAP_SUCCESS) {
3753 *errorp = error;
3754 return (rc);
3756 if (sdlist == NULL) {
3757 /* Create default service Desc */
3758 sdlist = (ns_ldap_search_desc_t **)calloc(2,
3759 sizeof (ns_ldap_search_desc_t *));
3760 if (sdlist == NULL) {
3761 return (NS_LDAP_MEMORY);
3763 dptr = (ns_ldap_search_desc_t *)
3764 calloc(1, sizeof (ns_ldap_search_desc_t));
3765 if (dptr == NULL) {
3766 free(sdlist);
3767 return (NS_LDAP_MEMORY);
3769 sdlist[0] = dptr;
3771 /* default base */
3772 rc = __s_api_getDNs(&dns, service, &error);
3773 if (rc != NS_LDAP_SUCCESS) {
3774 if (dns) {
3775 __s_api_free2dArray(dns);
3776 dns = NULL;
3778 if (sdlist) {
3779 (void) __ns_ldap_freeSearchDescriptors(
3780 &sdlist);
3782 sdlist = NULL;
3784 *errorp = error;
3785 return (rc);
3787 dptr->basedn = strdup(dns[0]);
3788 __s_api_free2dArray(dns);
3789 dns = NULL;
3791 /* default scope */
3792 scope = 0;
3793 cookie = init_search_state_machine();
3794 if (cookie == NULL) {
3795 if (sdlist) {
3796 (void) __ns_ldap_freeSearchDescriptors(&sdlist);
3797 sdlist = NULL;
3799 return (NS_LDAP_MEMORY);
3801 rc = __s_api_getSearchScope(&scope, &cookie->errorp);
3802 dptr->scope = scope;
3805 /* Initialize State machine cookie */
3806 if (cookie == NULL)
3807 cookie = init_search_state_machine();
3808 if (cookie == NULL) {
3809 if (sdlist) {
3810 (void) __ns_ldap_freeSearchDescriptors(&sdlist);
3811 sdlist = NULL;
3813 return (NS_LDAP_MEMORY);
3816 /* identify self as a getent user */
3817 cookie->conn_user = conn_user;
3819 cookie->sdlist = sdlist;
3821 /* see if need to follow referrals */
3822 rc = __s_api_toFollowReferrals(flags,
3823 &cookie->followRef, errorp);
3824 if (rc != NS_LDAP_SUCCESS) {
3825 delete_search_cookie(cookie);
3826 return (rc);
3830 * use VLV/PAGE control only if NS_LDAP_NO_PAGE_CTRL is not set
3832 if (flags & NS_LDAP_NO_PAGE_CTRL)
3833 cookie->use_paging = FALSE;
3834 else
3835 cookie->use_paging = TRUE;
3837 /* Set up other arguments */
3838 cookie->userdata = userdata;
3839 if (init_filter_cb != NULL) {
3840 cookie->init_filter_cb = init_filter_cb;
3841 cookie->use_filtercb = 1;
3843 cookie->use_usercb = 0;
3844 /* check_shadow() may add extra value to cookie->i_flags */
3845 cookie->i_flags = flags;
3846 if (service) {
3847 cookie->service = strdup(service);
3848 if (cookie->service == NULL) {
3849 delete_search_cookie(cookie);
3850 return (NS_LDAP_MEMORY);
3854 * If given, use the credential given by the caller, and
3855 * skip the credential check required for shadow update.
3857 if (auth == NULL) {
3858 rc = check_shadow(cookie, service);
3859 if (rc != NS_LDAP_SUCCESS) {
3860 *errorp = cookie->errorp;
3861 cookie->errorp = NULL;
3862 delete_search_cookie(cookie);
3863 cookie = NULL;
3864 return (rc);
3869 cookie->i_filter = strdup(filter);
3870 cookie->i_attr = attribute;
3871 cookie->i_sortattr = sortattr;
3872 cookie->i_auth = auth;
3874 state = INIT;
3875 for (;;) {
3876 state = search_state_machine(cookie, state, ONE_STEP);
3877 switch (state) {
3878 case PROCESS_RESULT:
3879 *result = cookie->result;
3880 cookie->result = NULL;
3881 *vcookie = (void *)cookie;
3882 return (NS_LDAP_SUCCESS);
3883 case LDAP_ERROR:
3884 state = search_state_machine(cookie, state, ONE_STEP);
3885 state = search_state_machine(cookie, CLEAR_RESULTS,
3886 ONE_STEP);
3887 /* FALLTHROUGH */
3888 case ERROR:
3889 rc = cookie->err_rc;
3890 if (conn_user != NULL && conn_user->ns_error != NULL) {
3891 *errorp = conn_user->ns_error;
3892 conn_user->ns_error = NULL;
3893 } else {
3894 *errorp = cookie->errorp;
3895 cookie->errorp = NULL;
3897 delete_search_cookie(cookie);
3898 return (rc);
3899 case EXIT:
3900 rc = cookie->err_rc;
3901 if (rc != NS_LDAP_SUCCESS) {
3902 *errorp = cookie->errorp;
3903 cookie->errorp = NULL;
3904 } else {
3905 rc = NS_LDAP_NOTFOUND;
3908 delete_search_cookie(cookie);
3909 return (rc);
3911 default:
3912 break;
3918 __ns_ldap_firstEntry(
3919 const char *service,
3920 const char *filter,
3921 const char *vlv_sort,
3922 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3923 char **realfilter, const void *userdata),
3924 const char * const *attribute,
3925 const ns_cred_t *auth,
3926 const int flags,
3927 void **vcookie,
3928 ns_ldap_result_t **result,
3929 ns_ldap_error_t ** errorp,
3930 const void *userdata)
3932 ns_conn_user_t *cu = NULL;
3933 int try_cnt = 0;
3934 int rc = NS_LDAP_SUCCESS;
3936 for (;;) {
3937 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_GETENT,
3938 &try_cnt, &rc, errorp) == 0)
3939 break;
3940 rc = firstEntry(service, filter, vlv_sort, init_filter_cb,
3941 attribute, auth, flags, vcookie, result, errorp, userdata,
3942 cu);
3944 return (rc);
3947 /*ARGSUSED2*/
3949 __ns_ldap_nextEntry(void *vcookie, ns_ldap_result_t **result,
3950 ns_ldap_error_t ** errorp)
3952 ns_ldap_cookie_t *cookie;
3953 ns_state_t state;
3954 int rc;
3956 cookie = (ns_ldap_cookie_t *)vcookie;
3957 cookie->result = NULL;
3958 *result = NULL;
3960 if (cookie->conn_user != NULL) {
3961 rc = __s_api_setup_getnext(cookie->conn_user,
3962 &cookie->err_rc, errorp);
3963 if (rc != NS_LDAP_SUCCESS)
3964 return (rc);
3967 state = END_PROCESS_RESULT;
3968 for (;;) {
3969 state = search_state_machine(cookie, state, ONE_STEP);
3970 switch (state) {
3971 case PROCESS_RESULT:
3972 *result = cookie->result;
3973 cookie->result = NULL;
3974 return (NS_LDAP_SUCCESS);
3975 case LDAP_ERROR:
3976 state = search_state_machine(cookie, state, ONE_STEP);
3977 state = search_state_machine(cookie, CLEAR_RESULTS,
3978 ONE_STEP);
3979 /* FALLTHROUGH */
3980 case ERROR:
3981 rc = cookie->err_rc;
3982 *errorp = cookie->errorp;
3983 cookie->errorp = NULL;
3984 return (rc);
3985 case EXIT:
3986 return (NS_LDAP_SUCCESS);
3992 __ns_ldap_endEntry(
3993 void **vcookie,
3994 ns_ldap_error_t ** errorp)
3996 ns_ldap_cookie_t *cookie;
3997 int rc;
3999 if (*vcookie == NULL)
4000 return (NS_LDAP_INVALID_PARAM);
4002 cookie = (ns_ldap_cookie_t *)(*vcookie);
4003 cookie->result = NULL;
4005 /* Complete search */
4006 rc = search_state_machine(cookie, CLEAR_RESULTS, 0);
4008 /* Copy results back to user */
4009 rc = cookie->err_rc;
4010 if (rc != NS_LDAP_SUCCESS)
4011 *errorp = cookie->errorp;
4013 cookie->errorp = NULL;
4014 if (cookie->conn_user != NULL) {
4015 if (cookie->conn_user->conn_mt != NULL)
4016 __s_api_conn_mt_return(cookie->conn_user);
4017 __s_api_conn_user_free(cookie->conn_user);
4019 delete_search_cookie(cookie);
4020 cookie = NULL;
4021 *vcookie = NULL;
4023 return (rc);
4028 __ns_ldap_freeResult(ns_ldap_result_t **result)
4031 ns_ldap_entry_t *curEntry = NULL;
4032 ns_ldap_entry_t *delEntry = NULL;
4033 int i;
4034 ns_ldap_result_t *res = *result;
4036 #ifdef DEBUG
4037 (void) fprintf(stderr, "__ns_ldap_freeResult START\n");
4038 #endif
4039 if (res == NULL)
4040 return (NS_LDAP_INVALID_PARAM);
4042 if (res->entry != NULL)
4043 curEntry = res->entry;
4045 for (i = 0; i < res->entries_count; i++) {
4046 if (curEntry != NULL) {
4047 delEntry = curEntry;
4048 curEntry = curEntry->next;
4049 __ns_ldap_freeEntry(delEntry);
4053 free(res);
4054 *result = NULL;
4055 return (NS_LDAP_SUCCESS);
4058 /*ARGSUSED*/
4060 __ns_ldap_auth(const ns_cred_t *auth,
4061 const int flags,
4062 ns_ldap_error_t **errorp,
4063 LDAPControl **serverctrls,
4064 LDAPControl **clientctrls)
4067 ConnectionID connectionId = -1;
4068 Connection *conp;
4069 int rc = 0;
4070 int do_not_fail_if_new_pwd_reqd = 0;
4071 int nopasswd_acct_mgmt = 0;
4072 ns_conn_user_t *conn_user;
4075 #ifdef DEBUG
4076 (void) fprintf(stderr, "__ns_ldap_auth START\n");
4077 #endif
4079 *errorp = NULL;
4080 if (!auth)
4081 return (NS_LDAP_INVALID_PARAM);
4083 conn_user = __s_api_conn_user_init(NS_CONN_USER_AUTH,
4084 NULL, B_FALSE);
4086 rc = __s_api_getConnection(NULL, flags | NS_LDAP_NEW_CONN,
4087 auth, &connectionId, &conp, errorp,
4088 do_not_fail_if_new_pwd_reqd, nopasswd_acct_mgmt,
4089 conn_user);
4091 if (conn_user != NULL)
4092 __s_api_conn_user_free(conn_user);
4094 if (rc == NS_LDAP_OP_FAILED && *errorp)
4095 (void) __ns_ldap_freeError(errorp);
4097 if (connectionId > -1)
4098 DropConnection(connectionId, flags);
4099 return (rc);
4102 char **
4103 __ns_ldap_getAttr(const ns_ldap_entry_t *entry, const char *attrname)
4105 int i;
4107 if (entry == NULL)
4108 return (NULL);
4109 for (i = 0; i < entry->attr_count; i++) {
4110 if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == NULL)
4111 return (entry->attr_pair[i]->attrvalue);
4113 return (NULL);
4116 ns_ldap_attr_t *
4117 __ns_ldap_getAttrStruct(const ns_ldap_entry_t *entry, const char *attrname)
4119 int i;
4121 if (entry == NULL)
4122 return (NULL);
4123 for (i = 0; i < entry->attr_count; i++) {
4124 if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == NULL)
4125 return (entry->attr_pair[i]);
4127 return (NULL);
4131 /*ARGSUSED*/
4133 __ns_ldap_uid2dn(const char *uid,
4134 char **userDN,
4135 const ns_cred_t *cred, /* cred is ignored */
4136 ns_ldap_error_t **errorp)
4138 ns_ldap_result_t *result = NULL;
4139 char *filter, *userdata;
4140 char errstr[MAXERROR];
4141 char **value;
4142 int rc = 0;
4143 int i = 0;
4144 size_t len;
4146 *errorp = NULL;
4147 *userDN = NULL;
4148 if ((uid == NULL) || (uid[0] == '\0'))
4149 return (NS_LDAP_INVALID_PARAM);
4151 while (uid[i] != '\0') {
4152 if (uid[i] == '=') {
4153 *userDN = strdup(uid);
4154 return (NS_LDAP_SUCCESS);
4156 i++;
4158 i = 0;
4159 while ((uid[i] != '\0') && (isdigit(uid[i])))
4160 i++;
4161 if (uid[i] == '\0') {
4162 len = strlen(UIDNUMFILTER) + strlen(uid) + 1;
4163 filter = (char *)malloc(len);
4164 if (filter == NULL) {
4165 *userDN = NULL;
4166 return (NS_LDAP_MEMORY);
4168 (void) snprintf(filter, len, UIDNUMFILTER, uid);
4170 len = strlen(UIDNUMFILTER_SSD) + strlen(uid) + 1;
4171 userdata = (char *)malloc(len);
4172 if (userdata == NULL) {
4173 *userDN = NULL;
4174 return (NS_LDAP_MEMORY);
4176 (void) snprintf(userdata, len, UIDNUMFILTER_SSD, uid);
4177 } else {
4178 len = strlen(UIDFILTER) + strlen(uid) + 1;
4179 filter = (char *)malloc(len);
4180 if (filter == NULL) {
4181 *userDN = NULL;
4182 return (NS_LDAP_MEMORY);
4184 (void) snprintf(filter, len, UIDFILTER, uid);
4186 len = strlen(UIDFILTER_SSD) + strlen(uid) + 1;
4187 userdata = (char *)malloc(len);
4188 if (userdata == NULL) {
4189 *userDN = NULL;
4190 return (NS_LDAP_MEMORY);
4192 (void) snprintf(userdata, len, UIDFILTER_SSD, uid);
4196 * we want to retrieve the DN as it appears in LDAP
4197 * hence the use of NS_LDAP_NOT_CVT_DN in flags
4199 rc = __ns_ldap_list("passwd", filter,
4200 __s_api_merge_SSD_filter,
4201 NULL, cred, NS_LDAP_NOT_CVT_DN,
4202 &result, errorp, NULL,
4203 userdata);
4204 free(filter);
4205 filter = NULL;
4206 free(userdata);
4207 userdata = NULL;
4208 if (rc != NS_LDAP_SUCCESS) {
4209 if (result) {
4210 (void) __ns_ldap_freeResult(&result);
4211 result = NULL;
4213 return (rc);
4215 if (result->entries_count > 1) {
4216 (void) __ns_ldap_freeResult(&result);
4217 result = NULL;
4218 *userDN = NULL;
4219 (void) sprintf(errstr,
4220 gettext("Too many entries are returned for %s"), uid);
4221 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
4222 NULL);
4223 return (NS_LDAP_INTERNAL);
4226 value = __ns_ldap_getAttr(result->entry, "dn");
4227 *userDN = strdup(value[0]);
4228 (void) __ns_ldap_freeResult(&result);
4229 result = NULL;
4230 return (NS_LDAP_SUCCESS);
4234 /*ARGSUSED*/
4236 __ns_ldap_host2dn(const char *host,
4237 const char *domain,
4238 char **hostDN,
4239 const ns_cred_t *cred, /* cred is ignored */
4240 ns_ldap_error_t **errorp)
4242 ns_ldap_result_t *result = NULL;
4243 char *filter, *userdata;
4244 char errstr[MAXERROR];
4245 char **value;
4246 int rc;
4247 size_t len;
4250 * XXX
4251 * the domain parameter needs to be used in case domain is not local, if
4252 * this routine is to support multi domain setups, it needs lots of work...
4254 *errorp = NULL;
4255 *hostDN = NULL;
4256 if ((host == NULL) || (host[0] == '\0'))
4257 return (NS_LDAP_INVALID_PARAM);
4259 len = strlen(HOSTFILTER) + strlen(host) + 1;
4260 filter = (char *)malloc(len);
4261 if (filter == NULL) {
4262 return (NS_LDAP_MEMORY);
4264 (void) snprintf(filter, len, HOSTFILTER, host);
4266 len = strlen(HOSTFILTER_SSD) + strlen(host) + 1;
4267 userdata = (char *)malloc(len);
4268 if (userdata == NULL) {
4269 return (NS_LDAP_MEMORY);
4271 (void) snprintf(userdata, len, HOSTFILTER_SSD, host);
4274 * we want to retrieve the DN as it appears in LDAP
4275 * hence the use of NS_LDAP_NOT_CVT_DN in flags
4277 rc = __ns_ldap_list("hosts", filter,
4278 __s_api_merge_SSD_filter,
4279 NULL, cred, NS_LDAP_NOT_CVT_DN, &result,
4280 errorp, NULL,
4281 userdata);
4282 free(filter);
4283 filter = NULL;
4284 free(userdata);
4285 userdata = NULL;
4286 if (rc != NS_LDAP_SUCCESS) {
4287 if (result) {
4288 (void) __ns_ldap_freeResult(&result);
4289 result = NULL;
4291 return (rc);
4294 if (result->entries_count > 1) {
4295 (void) __ns_ldap_freeResult(&result);
4296 result = NULL;
4297 *hostDN = NULL;
4298 (void) sprintf(errstr,
4299 gettext("Too many entries are returned for %s"), host);
4300 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
4301 NULL);
4302 return (NS_LDAP_INTERNAL);
4305 value = __ns_ldap_getAttr(result->entry, "dn");
4306 *hostDN = strdup(value[0]);
4307 (void) __ns_ldap_freeResult(&result);
4308 result = NULL;
4309 return (NS_LDAP_SUCCESS);
4312 /*ARGSUSED*/
4314 __ns_ldap_dn2domain(const char *dn,
4315 char **domain,
4316 const ns_cred_t *cred,
4317 ns_ldap_error_t **errorp)
4319 int rc, pnum, i, j, len = 0;
4320 char *newdn, **rdns = NULL;
4321 char **dns, *dn1;
4323 *errorp = NULL;
4325 if (domain == NULL)
4326 return (NS_LDAP_INVALID_PARAM);
4327 else
4328 *domain = NULL;
4330 if ((dn == NULL) || (dn[0] == '\0'))
4331 return (NS_LDAP_INVALID_PARAM);
4334 * break dn into rdns
4336 dn1 = strdup(dn);
4337 if (dn1 == NULL)
4338 return (NS_LDAP_MEMORY);
4339 rdns = ldap_explode_dn(dn1, 0);
4340 free(dn1);
4341 if (rdns == NULL || *rdns == NULL)
4342 return (NS_LDAP_INVALID_PARAM);
4344 for (i = 0; rdns[i]; i++)
4345 len += strlen(rdns[i]) + 1;
4346 pnum = i;
4348 newdn = (char *)malloc(len + 1);
4349 dns = (char **)calloc(pnum, sizeof (char *));
4350 if (newdn == NULL || dns == NULL) {
4351 if (newdn)
4352 free(newdn);
4353 ldap_value_free(rdns);
4354 return (NS_LDAP_MEMORY);
4357 /* construct a semi-normalized dn, newdn */
4358 *newdn = '\0';
4359 for (i = 0; rdns[i]; i++) {
4360 dns[i] = newdn + strlen(newdn);
4361 (void) strcat(newdn,
4362 __s_api_remove_rdn_space(rdns[i]));
4363 (void) strcat(newdn, ",");
4365 /* remove the last ',' */
4366 newdn[strlen(newdn) - 1] = '\0';
4367 ldap_value_free(rdns);
4370 * loop and find the domain name associated with newdn,
4371 * removing rdn one by one from left to right
4373 for (i = 0; i < pnum; i++) {
4375 if (*errorp)
4376 (void) __ns_ldap_freeError(errorp);
4379 * try cache manager first
4381 rc = __s_api_get_cachemgr_data(NS_CACHE_DN2DOMAIN,
4382 dns[i], domain);
4383 if (rc != NS_LDAP_SUCCESS) {
4385 * try ldap server second
4387 rc = __s_api_find_domainname(dns[i], domain,
4388 cred, errorp);
4389 } else {
4391 * skip the last one,
4392 * since it is already cached by ldap_cachemgr
4394 i--;
4396 if (rc == NS_LDAP_SUCCESS) {
4397 if (__s_api_nscd_proc()) {
4399 * If it's nscd, ask cache manager to save the
4400 * dn to domain mapping(s)
4402 for (j = 0; j <= i; j++) {
4403 (void) __s_api_set_cachemgr_data(
4404 NS_CACHE_DN2DOMAIN,
4405 dns[j],
4406 *domain);
4409 break;
4413 free(dns);
4414 free(newdn);
4415 if (rc != NS_LDAP_SUCCESS)
4416 rc = NS_LDAP_NOTFOUND;
4417 return (rc);
4420 /*ARGSUSED*/
4422 __ns_ldap_getServiceAuthMethods(const char *service,
4423 ns_auth_t ***auth,
4424 ns_ldap_error_t **errorp)
4426 char errstr[MAXERROR];
4427 int rc, i, done = 0;
4428 int slen;
4429 void **param;
4430 char **sam, *srv, *send;
4431 ns_auth_t **authpp = NULL, *ap;
4432 int cnt, max;
4433 ns_config_t *cfg;
4434 ns_ldap_error_t *error = NULL;
4436 if (errorp == NULL)
4437 return (NS_LDAP_INVALID_PARAM);
4438 *errorp = NULL;
4440 if ((service == NULL) || (service[0] == '\0') ||
4441 (auth == NULL))
4442 return (NS_LDAP_INVALID_PARAM);
4444 *auth = NULL;
4445 rc = __ns_ldap_getParam(NS_LDAP_SERVICE_AUTH_METHOD_P, &param, &error);
4446 if (rc != NS_LDAP_SUCCESS || param == NULL) {
4447 *errorp = error;
4448 return (rc);
4450 sam = (char **)param;
4452 cfg = __s_api_get_default_config();
4453 cnt = 0;
4455 slen = strlen(service);
4457 for (; *sam; sam++) {
4458 srv = *sam;
4459 if (strncasecmp(service, srv, slen) != 0)
4460 continue;
4461 srv += slen;
4462 if (*srv != COLONTOK)
4463 continue;
4464 send = srv;
4465 srv++;
4466 for (max = 1; (send = strchr(++send, SEMITOK)) != NULL;
4467 max++) {}
4468 authpp = (ns_auth_t **)calloc(++max, sizeof (ns_auth_t *));
4469 if (authpp == NULL) {
4470 (void) __ns_ldap_freeParam(&param);
4471 __s_api_release_config(cfg);
4472 return (NS_LDAP_MEMORY);
4474 while (!done) {
4475 send = strchr(srv, SEMITOK);
4476 if (send != NULL) {
4477 *send = '\0';
4478 send++;
4480 i = __s_get_enum_value(cfg, srv, NS_LDAP_AUTH_P);
4481 if (i == -1) {
4482 (void) __ns_ldap_freeParam(&param);
4483 (void) sprintf(errstr,
4484 gettext("Unsupported "
4485 "serviceAuthenticationMethod: %s.\n"), srv);
4486 MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX,
4487 strdup(errstr), NULL);
4488 __s_api_release_config(cfg);
4489 return (NS_LDAP_CONFIG);
4491 ap = __s_api_AuthEnumtoStruct((EnumAuthType_t)i);
4492 if (ap == NULL) {
4493 (void) __ns_ldap_freeParam(&param);
4494 __s_api_release_config(cfg);
4495 return (NS_LDAP_MEMORY);
4497 authpp[cnt++] = ap;
4498 if (send == NULL)
4499 done = TRUE;
4500 else
4501 srv = send;
4505 *auth = authpp;
4506 (void) __ns_ldap_freeParam(&param);
4507 __s_api_release_config(cfg);
4508 return (NS_LDAP_SUCCESS);
4512 * This routine is called when certain scenario occurs
4513 * e.g.
4514 * service == auto_home
4515 * SSD = automount: ou = mytest,
4516 * NS_LDAP_MAPATTRIBUTE= auto_home: automountMapName=AAA
4517 * NS_LDAP_OBJECTCLASSMAP= auto_home:automountMap=MynisMap
4518 * NS_LDAP_OBJECTCLASSMAP= auto_home:automount=MynisObject
4520 * The automountMapName is prepended implicitely but is mapped
4521 * to AAA. So dn could appers as
4522 * dn: AAA=auto_home,ou=bar,dc=foo,dc=com
4523 * dn: automountKey=user_01,AAA=auto_home,ou=bar,dc=foo,dc=com
4524 * dn: automountKey=user_02,AAA=auto_home,ou=bar,dc=foo,dc=com
4525 * in the directory.
4526 * This function is called to covert the mapped attr back to
4527 * orig attr when the entries are searched and returned
4531 __s_api_convert_automountmapname(const char *service, char **dn,
4532 ns_ldap_error_t **errp) {
4534 char **mapping = NULL;
4535 char *mapped_attr = NULL;
4536 char *automountmapname = "automountMapName";
4537 char *buffer = NULL;
4538 int rc = NS_LDAP_SUCCESS;
4539 char errstr[MAXERROR];
4542 * dn is an input/out parameter, check it first
4545 if (service == NULL || dn == NULL || *dn == NULL)
4546 return (NS_LDAP_INVALID_PARAM);
4549 * Check to see if there is a mapped attribute for auto_xxx
4552 mapping = __ns_ldap_getMappedAttributes(service, automountmapname);
4555 * if no mapped attribute for auto_xxx, try automount
4558 if (mapping == NULL)
4559 mapping = __ns_ldap_getMappedAttributes(
4560 "automount", automountmapname);
4563 * if no mapped attribute is found, return SUCCESS (no op)
4566 if (mapping == NULL)
4567 return (NS_LDAP_SUCCESS);
4570 * if the mapped attribute is found and attr is not empty,
4571 * copy it
4574 if (mapping[0] != NULL) {
4575 mapped_attr = strdup(mapping[0]);
4576 __s_api_free2dArray(mapping);
4577 if (mapped_attr == NULL) {
4578 return (NS_LDAP_MEMORY);
4580 } else {
4581 __s_api_free2dArray(mapping);
4583 (void) snprintf(errstr, (2 * MAXERROR),
4584 gettext(
4585 "Attribute nisMapName is mapped to an "
4586 "empty string.\n"));
4588 MKERROR(LOG_ERR, *errp, NS_CONFIG_SYNTAX,
4589 strdup(errstr), NULL);
4591 return (NS_LDAP_CONFIG);
4595 * Locate the mapped attribute in the dn
4596 * and replace it if it exists
4599 rc = __s_api_replace_mapped_attr_in_dn(
4600 (const char *) automountmapname, (const char *) mapped_attr,
4601 (const char *) *dn, &buffer);
4603 /* clean up */
4605 free(mapped_attr);
4608 * If mapped attr is found(buffer != NULL)
4609 * a new dn is returned
4610 * If no mapped attribute is in dn,
4611 * return NS_LDAP_SUCCESS (no op)
4612 * If no memory,
4613 * return NS_LDAP_MEMORY (no op)
4616 if (buffer != NULL) {
4617 free(*dn);
4618 *dn = buffer;
4621 return (rc);
4625 * If the mapped attr is found in the dn,
4626 * return NS_LDAP_SUCCESS and a new_dn.
4627 * If no mapped attr is found,
4628 * return NS_LDAP_SUCCESS and *new_dn == NULL
4629 * If there is not enough memory,
4630 * return NS_LDAP_MEMORY and *new_dn == NULL
4634 __s_api_replace_mapped_attr_in_dn(
4635 const char *orig_attr, const char *mapped_attr,
4636 const char *dn, char **new_dn) {
4638 char **dnArray = NULL;
4639 char *cur = NULL, *start = NULL;
4640 int i = 0, found = 0;
4641 int len = 0, orig_len = 0, mapped_len = 0;
4642 int dn_len = 0, tmp_len = 0;
4644 *new_dn = NULL;
4647 * seperate dn into individual componets
4648 * e.g.
4649 * "automountKey=user_01" , "automountMapName_test=auto_home", ...
4651 dnArray = ldap_explode_dn(dn, 0);
4654 * This will find "mapped attr=value" in dn.
4655 * It won't find match if mapped attr appears
4656 * in the value.
4658 for (i = 0; dnArray[i] != NULL; i++) {
4660 * This function is called when reading from
4661 * the directory so assume each component has "=".
4662 * Any ill formatted dn should be rejected
4663 * before adding to the directory
4665 cur = strchr(dnArray[i], '=');
4666 *cur = '\0';
4667 if (strcasecmp(mapped_attr, dnArray[i]) == 0)
4668 found = 1;
4669 *cur = '=';
4670 if (found) break;
4673 if (!found) {
4674 __s_api_free2dArray(dnArray);
4675 *new_dn = NULL;
4676 return (NS_LDAP_SUCCESS);
4679 * The new length is *dn length + (difference between
4680 * orig attr and mapped attr) + 1 ;
4681 * e.g.
4682 * automountKey=aa,automountMapName_test=auto_home,dc=foo,dc=com
4683 * ==>
4684 * automountKey=aa,automountMapName=auto_home,dc=foo,dc=com
4686 mapped_len = strlen(mapped_attr);
4687 orig_len = strlen(orig_attr);
4688 dn_len = strlen(dn);
4689 len = dn_len + orig_len - mapped_len + 1;
4690 *new_dn = (char *)calloc(1, len);
4691 if (*new_dn == NULL) {
4692 __s_api_free2dArray(dnArray);
4693 return (NS_LDAP_MEMORY);
4697 * Locate the mapped attr in the dn.
4698 * Use dnArray[i] instead of mapped_attr
4699 * because mapped_attr could appear in
4700 * the value
4703 cur = strstr(dn, dnArray[i]);
4704 __s_api_free2dArray(dnArray);
4705 /* copy the portion before mapped attr in dn */
4706 start = *new_dn;
4707 tmp_len = cur - dn;
4708 (void) memcpy((void *) start, (const void*) dn, tmp_len);
4711 * Copy the orig_attr. e.g. automountMapName
4712 * This replaces mapped attr with orig attr
4714 start = start + (cur - dn); /* move cursor in buffer */
4715 (void) memcpy((void *) start, (const void*) orig_attr, orig_len);
4718 * Copy the portion after mapped attr in dn
4720 cur = cur + mapped_len; /* move cursor in dn */
4721 start = start + orig_len; /* move cursor in buffer */
4722 (void) strcpy(start, cur);
4724 return (NS_LDAP_SUCCESS);
4728 * Validate Filter functions
4731 /* ***** Start of modified libldap.so.5 filter parser ***** */
4733 /* filter parsing routine forward references */
4734 static int adj_filter_list(char *str);
4735 static int adj_simple_filter(char *str);
4736 static int unescape_filterval(char *val);
4737 static int hexchar2int(char c);
4738 static int adj_substring_filter(char *val);
4742 * assumes string manipulation is in-line
4743 * and all strings are sufficient in size
4744 * return value is the position after 'c'
4747 static char *
4748 resync_str(char *str, char *next, char c)
4750 char *ret;
4752 ret = str + strlen(str);
4753 *next = c;
4754 if (ret == next)
4755 return (ret);
4756 (void) strcat(str, next);
4757 return (ret);
4760 static char *
4761 find_right_paren(char *s)
4763 int balance, escape;
4765 balance = 1;
4766 escape = 0;
4767 while (*s && balance) {
4768 if (escape == 0) {
4769 if (*s == '(')
4770 balance++;
4771 else if (*s == ')')
4772 balance--;
4774 if (*s == '\\' && ! escape)
4775 escape = 1;
4776 else
4777 escape = 0;
4778 if (balance)
4779 s++;
4782 return (*s ? s : NULL);
4785 static char *
4786 adj_complex_filter(char *str)
4788 char *next;
4791 * We have (x(filter)...) with str sitting on
4792 * the x. We have to find the paren matching
4793 * the one before the x and put the intervening
4794 * filters by calling adj_filter_list().
4797 str++;
4798 if ((next = find_right_paren(str)) == NULL)
4799 return (NULL);
4801 *next = '\0';
4802 if (adj_filter_list(str) == -1)
4803 return (NULL);
4804 next = resync_str(str, next, ')');
4805 next++;
4807 return (next);
4810 static int
4811 adj_filter(char *str)
4813 char *next;
4814 int parens, balance, escape;
4815 char *np, *cp, *dp;
4817 parens = 0;
4818 while (*str) {
4819 switch (*str) {
4820 case '(':
4821 str++;
4822 parens++;
4823 switch (*str) {
4824 case '&':
4825 if ((str = adj_complex_filter(str)) == NULL)
4826 return (-1);
4828 parens--;
4829 break;
4831 case '|':
4832 if ((str = adj_complex_filter(str)) == NULL)
4833 return (-1);
4835 parens--;
4836 break;
4838 case '!':
4839 if ((str = adj_complex_filter(str)) == NULL)
4840 return (-1);
4842 parens--;
4843 break;
4845 case '(':
4846 /* illegal ((case - generated by conversion */
4848 /* find missing close) */
4849 np = find_right_paren(str+1);
4851 /* error if not found */
4852 if (np == NULL)
4853 return (-1);
4855 /* remove redundant (and) */
4856 for (dp = str, cp = str+1; cp < np; ) {
4857 *dp++ = *cp++;
4859 cp++;
4860 while (*cp)
4861 *dp++ = *cp++;
4862 *dp = '\0';
4864 /* re-start test at original ( */
4865 parens--;
4866 str--;
4867 break;
4869 default:
4870 balance = 1;
4871 escape = 0;
4872 next = str;
4873 while (*next && balance) {
4874 if (escape == 0) {
4875 if (*next == '(')
4876 balance++;
4877 else if (*next == ')')
4878 balance--;
4880 if (*next == '\\' && ! escape)
4881 escape = 1;
4882 else
4883 escape = 0;
4884 if (balance)
4885 next++;
4887 if (balance != 0)
4888 return (-1);
4890 *next = '\0';
4891 if (adj_simple_filter(str) == -1) {
4892 return (-1);
4894 next = resync_str(str, next, ')');
4895 next++;
4896 str = next;
4897 parens--;
4898 break;
4900 break;
4902 case ')':
4903 str++;
4904 parens--;
4905 break;
4907 case ' ':
4908 str++;
4909 break;
4911 default: /* assume it's a simple type=value filter */
4912 next = strchr(str, '\0');
4913 if (adj_simple_filter(str) == -1) {
4914 return (-1);
4916 str = next;
4917 break;
4921 return (parens ? -1 : 0);
4926 * Put a list of filters like this "(filter1)(filter2)..."
4929 static int
4930 adj_filter_list(char *str)
4932 char *next;
4933 char save;
4935 while (*str) {
4936 while (*str && isspace(*str))
4937 str++;
4938 if (*str == '\0')
4939 break;
4941 if ((next = find_right_paren(str + 1)) == NULL)
4942 return (-1);
4943 save = *++next;
4945 /* now we have "(filter)" with str pointing to it */
4946 *next = '\0';
4947 if (adj_filter(str) == -1)
4948 return (-1);
4949 next = resync_str(str, next, save);
4951 str = next;
4954 return (0);
4959 * is_valid_attr - returns 1 if a is a syntactically valid left-hand side
4960 * of a filter expression, 0 otherwise. A valid string may contain only
4961 * letters, numbers, hyphens, semi-colons, colons and periods. examples:
4962 * cn
4963 * cn;lang-fr
4964 * 1.2.3.4;binary;dynamic
4965 * mail;dynamic
4966 * cn:dn:1.2.3.4
4968 * For compatibility with older servers, we also allow underscores in
4969 * attribute types, even through they are not allowed by the LDAPv3 RFCs.
4971 static int
4972 is_valid_attr(char *a)
4974 for (; *a; a++) {
4975 if (!isascii(*a)) {
4976 return (0);
4977 } else if (!isalnum(*a)) {
4978 switch (*a) {
4979 case '-':
4980 case '.':
4981 case ';':
4982 case ':':
4983 case '_':
4984 break; /* valid */
4985 default:
4986 return (0);
4990 return (1);
4993 static char *
4994 find_star(char *s)
4996 for (; *s; ++s) {
4997 switch (*s) {
4998 case '*':
4999 return (s);
5000 case '\\':
5001 ++s;
5002 if (hexchar2int(s[0]) >= 0 && hexchar2int(s[1]) >= 0)
5003 ++s;
5004 default:
5005 break;
5008 return (NULL);
5011 static int
5012 adj_simple_filter(char *str)
5014 char *s, *s2, *s3, filterop;
5015 char *value;
5016 int ftype = 0;
5017 int rc;
5019 rc = -1; /* pessimistic */
5021 if ((str = strdup(str)) == NULL) {
5022 return (rc);
5025 if ((s = strchr(str, '=')) == NULL) {
5026 goto free_and_return;
5028 value = s + 1;
5029 *s-- = '\0';
5030 filterop = *s;
5031 if (filterop == '<' || filterop == '>' || filterop == '~' ||
5032 filterop == ':') {
5033 *s = '\0';
5036 if (! is_valid_attr(str)) {
5037 goto free_and_return;
5040 switch (filterop) {
5041 case '<': /* LDAP_FILTER_LE */
5042 case '>': /* LDAP_FILTER_GE */
5043 case '~': /* LDAP_FILTER_APPROX */
5044 break;
5045 case ':': /* extended filter - v3 only */
5047 * extended filter looks like this:
5049 * [type][':dn'][':'oid]':='value
5051 * where one of type or :oid is required.
5054 s2 = s3 = NULL;
5055 if ((s2 = strrchr(str, ':')) == NULL) {
5056 goto free_and_return;
5058 if (strcasecmp(s2, ":dn") == 0) {
5059 *s2 = '\0';
5060 } else {
5061 *s2 = '\0';
5062 if ((s3 = strrchr(str, ':')) != NULL) {
5063 if (strcasecmp(s3, ":dn") != 0) {
5064 goto free_and_return;
5066 *s3 = '\0';
5069 if (unescape_filterval(value) < 0) {
5070 goto free_and_return;
5072 rc = 0;
5073 goto free_and_return;
5074 /* break; */
5075 default:
5076 if (find_star(value) == NULL) {
5077 ftype = 0; /* LDAP_FILTER_EQUALITY */
5078 } else if (strcmp(value, "*") == 0) {
5079 ftype = 1; /* LDAP_FILTER_PRESENT */
5080 } else {
5081 rc = adj_substring_filter(value);
5082 goto free_and_return;
5084 break;
5087 if (ftype != 0) { /* == LDAP_FILTER_PRESENT */
5088 rc = 0;
5089 } else if (unescape_filterval(value) >= 0) {
5090 rc = 0;
5092 if (rc != -1) {
5093 rc = 0;
5096 free_and_return:
5097 free(str);
5098 return (rc);
5103 * Check in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape
5104 * sequences within the null-terminated string 'val'.
5106 * If 'val' contains invalid escape sequences we return -1.
5107 * Otherwise return 1
5109 static int
5110 unescape_filterval(char *val)
5112 int escape, firstdigit;
5113 char *s;
5115 firstdigit = 0;
5116 escape = 0;
5117 for (s = val; *s; s++) {
5118 if (escape) {
5120 * first try LDAPv3 escape (hexadecimal) sequence
5122 if (hexchar2int(*s) < 0) {
5123 if (firstdigit) {
5125 * LDAPv2 (RFC1960) escape sequence
5127 escape = 0;
5128 } else {
5129 return (-1);
5132 if (firstdigit) {
5133 firstdigit = 0;
5134 } else {
5135 escape = 0;
5138 } else if (*s != '\\') {
5139 escape = 0;
5141 } else {
5142 escape = 1;
5143 firstdigit = 1;
5147 return (1);
5152 * convert character 'c' that represents a hexadecimal digit to an integer.
5153 * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned.
5154 * otherwise the converted value is returned.
5156 static int
5157 hexchar2int(char c)
5159 if (c >= '0' && c <= '9') {
5160 return (c - '0');
5162 if (c >= 'A' && c <= 'F') {
5163 return (c - 'A' + 10);
5165 if (c >= 'a' && c <= 'f') {
5166 return (c - 'a' + 10);
5168 return (-1);
5171 static int
5172 adj_substring_filter(char *val)
5174 char *nextstar;
5176 for (; val != NULL; val = nextstar) {
5177 if ((nextstar = find_star(val)) != NULL) {
5178 *nextstar++ = '\0';
5181 if (*val != '\0') {
5182 if (unescape_filterval(val) < 0) {
5183 return (-1);
5188 return (0);
5191 /* ***** End of modified libldap.so.5 filter parser ***** */
5195 * Walk filter, remove redundant parentheses in-line
5196 * verify that the filter is reasonable
5198 static int
5199 validate_filter(ns_ldap_cookie_t *cookie)
5201 char *filter = cookie->filter;
5202 int rc;
5204 /* Parse filter looking for illegal values */
5206 rc = adj_filter(filter);
5207 if (rc != 0) {
5208 return (NS_LDAP_OP_FAILED);
5211 /* end of filter checking */
5213 return (NS_LDAP_SUCCESS);
5217 * Set the account management request control that needs to be sent to server.
5218 * This control is required to get the account management information of
5219 * a user to do local account checking.
5221 static int
5222 setup_acctmgmt_params(ns_ldap_cookie_t *cookie)
5224 LDAPControl *req = NULL, **requestctrls;
5226 req = (LDAPControl *)malloc(sizeof (LDAPControl));
5228 if (req == NULL)
5229 return (NS_LDAP_MEMORY);
5231 /* fill in the fields of this new control */
5232 req->ldctl_iscritical = 1;
5233 req->ldctl_oid = strdup(NS_LDAP_ACCOUNT_USABLE_CONTROL);
5234 if (req->ldctl_oid == NULL) {
5235 free(req);
5236 return (NS_LDAP_MEMORY);
5238 req->ldctl_value.bv_len = 0;
5239 req->ldctl_value.bv_val = NULL;
5241 requestctrls = (LDAPControl **)calloc(2, sizeof (LDAPControl *));
5242 if (requestctrls == NULL) {
5243 ldap_control_free(req);
5244 return (NS_LDAP_MEMORY);
5247 requestctrls[0] = req;
5249 cookie->p_serverctrls = requestctrls;
5251 return (NS_LDAP_SUCCESS);
5255 * int get_new_acct_more_info(BerElement *ber,
5256 * AcctUsableResponse_t *acctResp)
5258 * Decode the more_info data from an Account Management control response,
5259 * when the account is not usable and when code style is from recent LDAP
5260 * servers (see below comments for parse_acct_cont_resp_msg() to get more
5261 * details on coding styles and ASN1 description).
5263 * Expected BER encoding: {tbtbtbtiti}
5264 * +t: tag is 0
5265 * +b: TRUE if inactive due to account inactivation
5266 * +t: tag is 1
5267 * +b: TRUE if password has been reset
5268 * +t: tag is 2
5269 * +b: TRUE if password is expired
5270 * +t: tag is 3
5271 * +i: contains num of remaining grace, 0 means no grace
5272 * +t: tag is 4
5273 * +i: contains num of seconds before auto-unlock. -1 means acct is locked
5274 * forever (i.e. until reset)
5276 * Asumptions:
5277 * - ber is not null
5278 * - acctResp is not null and is initialized with default values for the
5279 * fields in its AcctUsableResp.more_info structure
5280 * - the ber stream is received in the correct order, per the ASN1 description.
5281 * We do not check this order and make the asumption that it is correct.
5282 * Note that the ber stream may not (and will not in most cases) contain
5283 * all fields.
5285 static int
5286 get_new_acct_more_info(BerElement *ber, AcctUsableResponse_t *acctResp)
5288 int rc = NS_LDAP_SUCCESS;
5289 char errstr[MAXERROR];
5290 ber_tag_t rTag = LBER_DEFAULT;
5291 ber_len_t rLen = 0;
5292 ber_int_t rValue;
5293 char *last;
5294 int berRC = 0;
5297 * Look at what more_info BER element is/are left to be decoded.
5298 * look at each of them 1 by 1, without checking on their order
5299 * and possible multi values.
5301 for (rTag = ber_first_element(ber, &rLen, &last);
5302 rTag != LBER_END_OF_SEQORSET;
5303 rTag = ber_next_element(ber, &rLen, last)) {
5305 berRC = 0;
5306 switch (rTag) {
5307 case 0 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5308 /* inactive */
5309 berRC = ber_scanf(ber, "b", &rValue);
5310 if (berRC != LBER_ERROR) {
5311 (acctResp->AcctUsableResp).more_info.
5312 inactive = (rValue != 0) ? 1 : 0;
5314 break;
5316 case 1 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5317 /* reset */
5318 berRC = ber_scanf(ber, "b", &rValue);
5319 if (berRC != LBER_ERROR) {
5320 (acctResp->AcctUsableResp).more_info.reset
5321 = (rValue != 0) ? 1 : 0;
5323 break;
5325 case 2 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5326 /* expired */
5327 berRC = ber_scanf(ber, "b", &rValue);
5328 if (berRC != LBER_ERROR) {
5329 (acctResp->AcctUsableResp).more_info.expired
5330 = (rValue != 0) ? 1 : 0;
5332 break;
5334 case 3 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5335 /* remaining grace */
5336 berRC = ber_scanf(ber, "i", &rValue);
5337 if (berRC != LBER_ERROR) {
5338 (acctResp->AcctUsableResp).more_info.rem_grace
5339 = rValue;
5341 break;
5343 case 4 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5344 /* seconds before unlock */
5345 berRC = ber_scanf(ber, "i", &rValue);
5346 if (berRC != LBER_ERROR) {
5347 (acctResp->AcctUsableResp).more_info.
5348 sec_b4_unlock = rValue;
5350 break;
5352 default :
5353 (void) sprintf(errstr,
5354 gettext("invalid reason tag 0x%x"), rTag);
5355 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5356 rc = NS_LDAP_INTERNAL;
5357 break;
5359 if (berRC == LBER_ERROR) {
5360 (void) sprintf(errstr,
5361 gettext("error 0x%x decoding value for "
5362 "tag 0x%x"), berRC, rTag);
5363 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5364 rc = NS_LDAP_INTERNAL;
5366 if (rc != NS_LDAP_SUCCESS) {
5367 /* exit the for loop */
5368 break;
5372 return (rc);
5376 * int get_old_acct_opt_more_info(BerElement *ber,
5377 * AcctUsableResponse_t *acctResp)
5379 * Decode the optional more_info data from an Account Management control
5380 * response, when the account is not usable and when code style is from LDAP
5381 * server 5.2p4 (see below comments for parse_acct_cont_resp_msg() to get more
5382 * details on coding styles and ASN1 description).
5384 * Expected BER encoding: titi}
5385 * +t: tag is 2
5386 * +i: contains num of remaining grace, 0 means no grace
5387 * +t: tag is 3
5388 * +i: contains num of seconds before auto-unlock. -1 means acct is locked
5389 * forever (i.e. until reset)
5391 * Asumptions:
5392 * - ber is a valid BER element
5393 * - acctResp is initialized for the fields in its AcctUsableResp.more_info
5394 * structure
5396 static int
5397 get_old_acct_opt_more_info(ber_tag_t tag, BerElement *ber,
5398 AcctUsableResponse_t *acctResp)
5400 int rc = NS_LDAP_SUCCESS;
5401 char errstr[MAXERROR];
5402 ber_len_t len;
5403 int rem_grace, sec_b4_unlock;
5405 switch (tag) {
5406 case 2:
5407 /* decode and maybe 3 is following */
5408 if ((tag = ber_scanf(ber, "i", &rem_grace)) == LBER_ERROR) {
5409 (void) sprintf(errstr, gettext("Can not get "
5410 "rem_grace"));
5411 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5412 rc = NS_LDAP_INTERNAL;
5413 break;
5415 (acctResp->AcctUsableResp).more_info.rem_grace = rem_grace;
5417 if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5418 /* this is a success case, break to exit */
5419 (void) sprintf(errstr, gettext("No more "
5420 "optional data"));
5421 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5422 break;
5425 if (tag == 3) {
5426 if (ber_scanf(ber, "i", &sec_b4_unlock) == LBER_ERROR) {
5427 (void) sprintf(errstr,
5428 gettext("Can not get sec_b4_unlock "
5429 "- 1st case"));
5430 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5431 rc = NS_LDAP_INTERNAL;
5432 break;
5434 (acctResp->AcctUsableResp).more_info.sec_b4_unlock =
5435 sec_b4_unlock;
5436 } else { /* unknown tag */
5437 (void) sprintf(errstr, gettext("Unknown tag "
5438 "- 1st case"));
5439 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5440 rc = NS_LDAP_INTERNAL;
5441 break;
5443 break;
5445 case 3:
5446 if (ber_scanf(ber, "i", &sec_b4_unlock) == LBER_ERROR) {
5447 (void) sprintf(errstr, gettext("Can not get "
5448 "sec_b4_unlock - 2nd case"));
5449 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5450 rc = NS_LDAP_INTERNAL;
5451 break;
5453 (acctResp->AcctUsableResp).more_info.sec_b4_unlock =
5454 sec_b4_unlock;
5455 break;
5457 default: /* unknown tag */
5458 (void) sprintf(errstr, gettext("Unknown tag - 2nd case"));
5459 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5460 rc = NS_LDAP_INTERNAL;
5461 break;
5464 return (rc);
5468 * **** This function needs to be moved to libldap library ****
5469 * parse_acct_cont_resp_msg() parses the message received by server according to
5470 * following format (ASN1 notation):
5472 * ACCOUNT_USABLE_RESPONSE::= CHOICE {
5473 * is_available [0] INTEGER,
5474 * ** seconds before expiration **
5475 * is_not_available [1] more_info
5477 * more_info::= SEQUENCE {
5478 * inactive [0] BOOLEAN DEFAULT FALSE,
5479 * reset [1] BOOLEAN DEFAULT FALSE,
5480 * expired [2] BOOLEAN DEFAULT FALSE,
5481 * remaining_grace [3] INTEGER OPTIONAL,
5482 * seconds_before_unlock [4] INTEGER OPTIONAL
5486 * #define used to make the difference between coding style as done
5487 * by LDAP server 5.2p4 and newer LDAP servers. There are 4 values:
5488 * - DS52p4_USABLE: 5.2p4 coding style, account is usable
5489 * - DS52p4_NOT_USABLE: 5.2p4 coding style, account is not usable
5490 * - NEW_USABLE: newer LDAP servers coding style, account is usable
5491 * - NEW_NOT_USABLE: newer LDAP servers coding style, account is not usable
5493 * An account would be considered not usable if for instance:
5494 * - it's been made inactive in the LDAP server
5495 * - or its password was reset in the LDAP server database
5496 * - or its password expired
5497 * - or the account has been locked, possibly forever
5499 #define DS52p4_USABLE 0x00
5500 #define DS52p4_NOT_USABLE 0x01
5501 #define NEW_USABLE 0x00 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE
5502 #define NEW_NOT_USABLE 0x01 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED
5503 static int
5504 parse_acct_cont_resp_msg(LDAPControl **ectrls, AcctUsableResponse_t *acctResp)
5506 int rc = NS_LDAP_SUCCESS;
5507 BerElement *ber;
5508 ber_tag_t tag;
5509 ber_len_t len;
5510 int i;
5511 char errstr[MAXERROR];
5512 /* used for any coding style when account is usable */
5513 int seconds_before_expiry;
5514 /* used for 5.2p4 coding style when account is not usable */
5515 int inactive, reset, expired;
5517 if (ectrls == NULL) {
5518 (void) sprintf(errstr, gettext("Invalid ectrls parameter"));
5519 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5520 return (NS_LDAP_INVALID_PARAM);
5523 for (i = 0; ectrls[i] != NULL; i++) {
5524 if (strcmp(ectrls[i]->ldctl_oid, NS_LDAP_ACCOUNT_USABLE_CONTROL)
5525 == 0) {
5526 break;
5530 if (ectrls[i] == NULL) {
5531 /* Ldap control is not found */
5532 (void) sprintf(errstr, gettext("Account Usable Control "
5533 "not found"));
5534 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5535 return (NS_LDAP_NOTFOUND);
5538 /* Allocate a BER element from the control value and parse it. */
5539 if ((ber = ber_init(&ectrls[i]->ldctl_value)) == NULL)
5540 return (NS_LDAP_MEMORY);
5542 if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5543 /* Ldap decoding error */
5544 (void) sprintf(errstr, gettext("Error decoding 1st tag"));
5545 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5546 ber_free(ber, 1);
5547 return (NS_LDAP_INTERNAL);
5550 switch (tag) {
5551 case DS52p4_USABLE:
5552 case NEW_USABLE:
5553 acctResp->choice = 0;
5554 if (ber_scanf(ber, "i", &seconds_before_expiry)
5555 == LBER_ERROR) {
5556 /* Ldap decoding error */
5557 (void) sprintf(errstr, gettext("Can not get "
5558 "seconds_before_expiry"));
5559 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5560 rc = NS_LDAP_INTERNAL;
5561 break;
5563 /* ber_scanf() succeeded */
5564 (acctResp->AcctUsableResp).seconds_before_expiry =
5565 seconds_before_expiry;
5566 break;
5568 case DS52p4_NOT_USABLE:
5569 acctResp->choice = 1;
5570 if (ber_scanf(ber, "{bbb", &inactive, &reset, &expired)
5571 == LBER_ERROR) {
5572 /* Ldap decoding error */
5573 (void) sprintf(errstr, gettext("Can not get "
5574 "inactive/reset/expired"));
5575 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5576 rc = NS_LDAP_INTERNAL;
5577 break;
5579 /* ber_scanf() succeeded */
5580 (acctResp->AcctUsableResp).more_info.inactive =
5581 ((inactive == 0) ? 0 : 1);
5582 (acctResp->AcctUsableResp).more_info.reset =
5583 ((reset == 0) ? 0 : 1);
5584 (acctResp->AcctUsableResp).more_info.expired =
5585 ((expired == 0) ? 0 : 1);
5586 (acctResp->AcctUsableResp).more_info.rem_grace = 0;
5587 (acctResp->AcctUsableResp).more_info.sec_b4_unlock = 0;
5589 if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5590 /* this is a success case, break to exit */
5591 (void) sprintf(errstr, gettext("No optional data"));
5592 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5593 break;
5597 * Look at what optional more_info BER element is/are
5598 * left to be decoded.
5600 rc = get_old_acct_opt_more_info(tag, ber, acctResp);
5601 break;
5603 case NEW_NOT_USABLE:
5604 acctResp->choice = 1;
5606 * Recent LDAP servers won't code more_info data for default
5607 * values (see above comments on ASN1 description for what
5608 * fields have default values & what fields are optional).
5610 (acctResp->AcctUsableResp).more_info.inactive = 0;
5611 (acctResp->AcctUsableResp).more_info.reset = 0;
5612 (acctResp->AcctUsableResp).more_info.expired = 0;
5613 (acctResp->AcctUsableResp).more_info.rem_grace = 0;
5614 (acctResp->AcctUsableResp).more_info.sec_b4_unlock = 0;
5616 if (len == 0) {
5618 * Nothing else to decode; this is valid and we
5619 * use default values set above.
5621 (void) sprintf(errstr, gettext("more_info is "
5622 "empty, using default values"));
5623 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5624 break;
5628 * Look at what more_info BER element is/are left to
5629 * be decoded.
5631 rc = get_new_acct_more_info(ber, acctResp);
5632 break;
5634 default:
5635 (void) sprintf(errstr, gettext("unknwon coding style "
5636 "(tag: 0x%x)"), tag);
5637 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5638 rc = NS_LDAP_INTERNAL;
5639 break;
5642 ber_free(ber, 1);
5643 return (rc);
5647 * internal function for __ns_ldap_getAcctMgmt()
5649 static int
5650 getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp,
5651 ns_conn_user_t *conn_user)
5653 int scope, rc;
5654 char ldapfilter[1024];
5655 ns_ldap_cookie_t *cookie;
5656 ns_ldap_search_desc_t **sdlist = NULL;
5657 ns_ldap_search_desc_t *dptr;
5658 ns_ldap_error_t *error = NULL;
5659 char **dns = NULL;
5660 char service[] = "shadow";
5662 if (user == NULL || acctResp == NULL)
5663 return (NS_LDAP_INVALID_PARAM);
5665 /* Initialize State machine cookie */
5666 cookie = init_search_state_machine();
5667 if (cookie == NULL)
5668 return (NS_LDAP_MEMORY);
5669 cookie->conn_user = conn_user;
5671 /* see if need to follow referrals */
5672 rc = __s_api_toFollowReferrals(0,
5673 &cookie->followRef, &error);
5674 if (rc != NS_LDAP_SUCCESS) {
5675 (void) __ns_ldap_freeError(&error);
5676 goto out;
5679 /* get the service descriptor - or create a default one */
5680 rc = __s_api_get_SSD_from_SSDtoUse_service(service,
5681 &sdlist, &error);
5682 if (rc != NS_LDAP_SUCCESS) {
5683 (void) __ns_ldap_freeError(&error);
5684 goto out;
5687 if (sdlist == NULL) {
5688 /* Create default service Desc */
5689 sdlist = (ns_ldap_search_desc_t **)calloc(2,
5690 sizeof (ns_ldap_search_desc_t *));
5691 if (sdlist == NULL) {
5692 rc = NS_LDAP_MEMORY;
5693 goto out;
5695 dptr = (ns_ldap_search_desc_t *)
5696 calloc(1, sizeof (ns_ldap_search_desc_t));
5697 if (dptr == NULL) {
5698 free(sdlist);
5699 rc = NS_LDAP_MEMORY;
5700 goto out;
5702 sdlist[0] = dptr;
5704 /* default base */
5705 rc = __s_api_getDNs(&dns, service, &cookie->errorp);
5706 if (rc != NS_LDAP_SUCCESS) {
5707 if (dns) {
5708 __s_api_free2dArray(dns);
5709 dns = NULL;
5711 (void) __ns_ldap_freeError(&(cookie->errorp));
5712 cookie->errorp = NULL;
5713 goto out;
5715 dptr->basedn = strdup(dns[0]);
5716 if (dptr->basedn == NULL) {
5717 free(sdlist);
5718 free(dptr);
5719 if (dns) {
5720 __s_api_free2dArray(dns);
5721 dns = NULL;
5723 rc = NS_LDAP_MEMORY;
5724 goto out;
5726 __s_api_free2dArray(dns);
5727 dns = NULL;
5729 /* default scope */
5730 scope = 0;
5731 rc = __s_api_getSearchScope(&scope, &cookie->errorp);
5732 dptr->scope = scope;
5735 cookie->sdlist = sdlist;
5737 cookie->service = strdup(service);
5738 if (cookie->service == NULL) {
5739 rc = NS_LDAP_MEMORY;
5740 goto out;
5743 /* search for entries for this particular uid */
5744 (void) snprintf(ldapfilter, sizeof (ldapfilter), "(uid=%s)", user);
5745 cookie->i_filter = strdup(ldapfilter);
5746 if (cookie->i_filter == NULL) {
5747 rc = NS_LDAP_MEMORY;
5748 goto out;
5751 /* create the control request */
5752 if ((rc = setup_acctmgmt_params(cookie)) != NS_LDAP_SUCCESS)
5753 goto out;
5755 /* Process search */
5756 rc = search_state_machine(cookie, GET_ACCT_MGMT_INFO, 0);
5758 /* Copy results back to user */
5759 rc = cookie->err_rc;
5760 if (rc != NS_LDAP_SUCCESS)
5761 (void) __ns_ldap_freeError(&(cookie->errorp));
5763 if (cookie->result == NULL)
5764 goto out;
5766 if ((rc = parse_acct_cont_resp_msg(cookie->resultctrl, acctResp))
5767 != NS_LDAP_SUCCESS)
5768 goto out;
5770 rc = NS_LDAP_SUCCESS;
5772 out:
5773 delete_search_cookie(cookie);
5775 return (rc);
5779 * __ns_ldap_getAcctMgmt() is called from pam account management stack
5780 * for retrieving accounting information of users with no user password -
5781 * eg. rlogin, rsh, etc. This function uses the account management control
5782 * request to do a search on the server for the user in question. The
5783 * response control returned from the server is got from the cookie.
5784 * Input params: username of whose account mgmt information is to be got
5785 * pointer to hold the parsed account management information
5786 * Return values: NS_LDAP_SUCCESS on success or appropriate error
5787 * code on failure
5790 __ns_ldap_getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp)
5792 ns_conn_user_t *cu = NULL;
5793 int try_cnt = 0;
5794 int rc = NS_LDAP_SUCCESS;
5795 ns_ldap_error_t *error = NULL;
5797 for (;;) {
5798 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
5799 &try_cnt, &rc, &error) == 0)
5800 break;
5801 rc = getAcctMgmt(user, acctResp, cu);
5803 return (rc);