Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / lib / libsldap / common / ns_reads.c
blob0104fd994514a8cbc17bbf501bfb8f3ca5b9e9ff
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 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 free(ep->attr_pair[j]->attrname);
68 if (ep->attr_pair[j]->attrvalue) {
69 for (k = 0; (k < ep->attr_pair[j]->value_count) &&
70 (ep->attr_pair[j]->attrvalue[k]); k++) {
71 free(ep->attr_pair[j]->attrvalue[k]);
73 free(ep->attr_pair[j]->attrvalue);
75 free(ep->attr_pair[j]);
77 free(ep->attr_pair);
78 free(ep);
81 static void
82 _freeControlList(LDAPControl ***ctrls)
84 LDAPControl **ctrl;
86 if (ctrls == NULL || *ctrls == NULL)
87 return;
89 for (ctrl = *ctrls; *ctrl != NULL; ctrl++)
90 ldap_control_free(*ctrl);
91 free(*ctrls);
92 *ctrls = NULL;
95 * Convert attribute type in a RDN that has an attribute mapping to the
96 * original mappped type.
97 * e.g.
98 * cn<->cn-st and iphostnumber<->iphostnumber-st
99 * cn-st=aaa+iphostnumber-st=10.10.01.01
100 * is mapped to
101 * cn=aaa+iphostnumber=10.10.01.01
103 * Input - service: e.g. hosts, passwd etc.
104 * rdn: RDN
105 * Return: NULL - No attribute mapping in the RDN
106 * Non-NULL - The attribute type(s) in the RDN are mapped and
107 * the memory is allocated for the new rdn.
110 static char *
111 _cvtRDN(const char *service, const char *rdn) {
112 char **attrs, **mapped_attrs, **mapp, *type, *value, *attr;
113 char *new_rdn = NULL;
114 int nAttr = 0, i, attr_mapped, len = 0;
116 /* Break down "type=value\0" pairs. Assume RDN is normalized */
117 if ((attrs = ldap_explode_rdn(rdn, 0)) == NULL)
118 return (NULL);
120 for (nAttr = 0; attrs[nAttr] != NULL; nAttr++);
122 if ((mapped_attrs = (char **)calloc(nAttr, sizeof (char *))) == NULL) {
123 ldap_value_free(attrs);
124 return (NULL);
127 attr_mapped = 0;
128 for (i = 0; i < nAttr; i++) {
129 /* Parse type=value pair */
130 if ((type = strtok_r(attrs[i], "=", &value)) == NULL ||
131 value == NULL)
132 goto cleanup;
133 /* Reverse map: e.g. cn-sm -> cn */
134 mapp = __ns_ldap_getOrigAttribute(service, type);
135 if (mapp != NULL && mapp[0] != NULL) {
136 /* The attribute mapping is found */
137 type = mapp[0];
138 attr_mapped = 1;
140 /* "type=value\0" */
141 len = strlen(type) + strlen(value) + 2;
143 /* Reconstruct type=value pair. A string is allocated */
144 if ((attr = (char *)calloc(1, len)) == NULL) {
145 __s_api_free2dArray(mapp);
146 goto cleanup;
148 (void) snprintf(attr, len, "%s=%s",
149 type, value);
150 mapped_attrs[i] = attr;
151 } else {
153 * No attribute mapping. attrs[i] is going to be copied
154 * later. Restore "type\0value\0" back to
155 * "type=value\0".
157 type[strlen(type)] = '=';
159 __s_api_free2dArray(mapp);
161 if (attr_mapped == 0)
162 /* No attribute mapping. Don't bother to reconstruct RDN */
163 goto cleanup;
165 len = 0;
166 /* Reconstruct RDN from type=value pairs */
167 for (i = 0; i < nAttr; i++) {
168 if (mapped_attrs[i])
169 len += strlen(mapped_attrs[i]);
170 else
171 len += strlen(attrs[i]);
172 /* Add 1 for "+" */
173 len++;
175 if ((new_rdn = (char *)calloc(1, ++len)) == NULL)
176 goto cleanup;
177 for (i = 0; i < nAttr; i++) {
178 if (i > 0)
179 /* Add seperator */
180 (void) strlcat(new_rdn, "+", len);
182 if (mapped_attrs[i])
183 (void) strlcat(new_rdn, mapped_attrs[i], len);
184 else
185 (void) strlcat(new_rdn, attrs[i], len);
188 cleanup:
189 ldap_value_free(attrs);
190 if (mapped_attrs) {
191 if (attr_mapped) {
192 for (i = 0; i < nAttr; i++) {
193 free(mapped_attrs[i]);
196 free(mapped_attrs);
199 return (new_rdn);
202 * Convert attribute type in a DN that has an attribute mapping to the
203 * original mappped type.
204 * e.g
205 * The mappings are cn<->cn-sm, iphostnumber<->iphostnumber-sm
207 * dn: cn-sm=aaa+iphostnumber-sm=9.9.9.9,dc=central,dc=sun,dc=com
208 * is converted to
209 * dn: cn=aaa+iphostnumber=9.9.9.9,dc=central,dc=sun,dc=com
211 * Input - service: e.g. hosts, passwd etc.
212 * dn: the value of a distinguished name
213 * Return - NULL: error
214 * non-NULL: A converted DN and the memory is allocated
216 static char *
217 _cvtDN(const char *service, const char *dn) {
218 char **mapped_rdns;
219 char **rdns, *new_rdn, *new_dn = NULL;
220 int nRdn = 0, i, len = 0, rdn_mapped;
222 if (service == NULL || dn == NULL)
223 return (NULL);
225 if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
226 return (NULL);
228 for (nRdn = 0; rdns[nRdn] != NULL; nRdn++);
230 if ((mapped_rdns = (char **)calloc(nRdn, sizeof (char *))) == NULL) {
231 ldap_value_free(rdns);
232 return (NULL);
235 rdn_mapped = 0;
236 /* Break down RDNs in a DN */
237 for (i = 0; i < nRdn; i++) {
238 if ((new_rdn = _cvtRDN(service, rdns[i])) != NULL) {
239 mapped_rdns[i] = new_rdn;
240 rdn_mapped = 1;
243 if (rdn_mapped == 0) {
245 * No RDN contains any attribute mapping.
246 * Don't bother to reconstruct DN from RDN. Copy DN directly.
248 new_dn = strdup(dn);
249 goto cleanup;
252 * Reconstruct dn from RDNs.
253 * Calculate the length first.
255 for (i = 0; i < nRdn; i++) {
256 if (mapped_rdns[i])
257 len += strlen(mapped_rdns[i]);
258 else
259 len += strlen(rdns[i]);
261 /* add 1 for ',' */
262 len ++;
264 if ((new_dn = (char *)calloc(1, ++len)) == NULL)
265 goto cleanup;
266 for (i = 0; i < nRdn; i++) {
267 if (i > 0)
268 /* Add seperator */
269 (void) strlcat(new_dn, ",", len);
271 if (mapped_rdns[i])
272 (void) strlcat(new_dn, mapped_rdns[i], len);
273 else
274 (void) strlcat(new_dn, rdns[i], len);
278 cleanup:
279 ldap_value_free(rdns);
280 if (mapped_rdns) {
281 if (rdn_mapped) {
282 for (i = 0; i < nRdn; i++) {
283 free(mapped_rdns[i]);
286 free(mapped_rdns);
289 return (new_dn);
292 * Convert a single ldap entry from a LDAPMessage
293 * into an ns_ldap_entry structure.
294 * Schema map the entry if specified in flags
297 static int
298 __s_api_cvtEntry(LDAP *ld,
299 const char *service,
300 LDAPMessage *e,
301 int flags,
302 ns_ldap_entry_t **ret,
303 ns_ldap_error_t **error)
306 ns_ldap_entry_t *ep = NULL;
307 ns_ldap_attr_t **ap = NULL;
308 BerElement *ber;
309 char *attr = NULL;
310 char **vals = NULL;
311 char **mapping;
312 char *dn;
313 int nAttrs = 0;
314 int i, j, k = 0;
315 char **gecos_mapping = NULL;
316 int gecos_val_index[3] = { -1, -1, -1};
317 char errstr[MAXERROR];
318 int schema_mapping_existed = FALSE;
319 int gecos_mapping_existed = FALSE;
320 int gecos_attr_matched;
321 int auto_service = FALSE;
322 int rc = NS_LDAP_SUCCESS;
324 if (e == NULL || ret == NULL || error == NULL)
325 return (NS_LDAP_INVALID_PARAM);
327 *error = NULL;
329 ep = (ns_ldap_entry_t *)calloc(1, sizeof (ns_ldap_entry_t));
330 if (ep == NULL)
331 return (NS_LDAP_MEMORY);
333 if (service != NULL &&
334 (strncasecmp(service, "auto_", 5) == 0 ||
335 strcasecmp(service, "automount") == 0))
336 auto_service = TRUE;
338 * see if schema mapping existed for the given service
340 mapping = __ns_ldap_getOrigAttribute(service,
341 NS_HASH_SCHEMA_MAPPING_EXISTED);
342 if (mapping) {
343 schema_mapping_existed = TRUE;
344 __s_api_free2dArray(mapping);
345 mapping = NULL;
346 } else if (auto_service) {
348 * If service == auto_* and no
349 * schema mapping found
350 * then try automount
351 * There is certain case that schema mapping exist
352 * but __ns_ldap_getOrigAttribute(service,
353 * NS_HASH_SCHEMA_MAPPING_EXISTED);
354 * returns NULL.
355 * e.g.
356 * NS_LDAP_ATTRIBUTEMAP = automount:automountMapName=AAA
357 * NS_LDAP_OBJECTCLASSMAP = automount:automountMap=MynisMap
358 * NS_LDAP_OBJECTCLASSMAP = automount:automount=MynisObject
360 * Make a check for schema_mapping_existed here
361 * so later on __s_api_convert_automountmapname won't be called
362 * unnecessarily. It is also used for attribute mapping
363 * and objectclass mapping.
365 mapping = __ns_ldap_getOrigAttribute("automount",
366 NS_HASH_SCHEMA_MAPPING_EXISTED);
367 if (mapping) {
368 schema_mapping_existed = TRUE;
369 __s_api_free2dArray(mapping);
370 mapping = NULL;
374 nAttrs = 1; /* start with 1 for the DN attr */
375 for (attr = ldap_first_attribute(ld, e, &ber); attr != NULL;
376 attr = ldap_next_attribute(ld, e, ber)) {
377 nAttrs++;
378 ldap_memfree(attr);
379 attr = NULL;
381 ber_free(ber, 0);
382 ber = NULL;
384 ep->attr_count = nAttrs;
387 * add 1 for "gecos" 1 to N attribute mapping,
388 * just in case it is needed.
389 * ep->attr_count will be updated later if that is true.
391 ap = (ns_ldap_attr_t **)calloc(ep->attr_count + 1,
392 sizeof (ns_ldap_attr_t *));
393 if (ap == NULL) {
394 __ns_ldap_freeEntry(ep);
395 ep = NULL;
396 return (NS_LDAP_MEMORY);
398 ep->attr_pair = ap;
400 /* DN attribute */
401 dn = ldap_get_dn(ld, e);
402 ap[0] = (ns_ldap_attr_t *)calloc(1, sizeof (ns_ldap_attr_t));
403 if (ap[0] == NULL) {
404 ldap_memfree(dn);
405 dn = NULL;
406 __ns_ldap_freeEntry(ep);
407 ep = NULL;
408 return (NS_LDAP_MEMORY);
411 if ((ap[0]->attrname = strdup("dn")) == NULL) {
412 ldap_memfree(dn);
413 dn = NULL;
414 __ns_ldap_freeEntry(ep);
415 ep = NULL;
416 return (NS_LDAP_INVALID_PARAM);
418 ap[0]->value_count = 1;
419 if ((ap[0]->attrvalue = (char **)
420 calloc(2, sizeof (char *))) == NULL) {
421 ldap_memfree(dn);
422 dn = NULL;
423 __ns_ldap_freeEntry(ep);
424 ep = NULL;
425 return (NS_LDAP_MEMORY);
428 if (schema_mapping_existed && ((flags & NS_LDAP_NOT_CVT_DN) == 0))
429 ap[0]->attrvalue[0] = _cvtDN(service, dn);
430 else
431 ap[0]->attrvalue[0] = strdup(dn);
433 if (ap[0]->attrvalue[0] == NULL) {
434 ldap_memfree(dn);
435 dn = NULL;
436 __ns_ldap_freeEntry(ep);
437 ep = NULL;
438 return (NS_LDAP_MEMORY);
440 ldap_memfree(dn);
441 dn = NULL;
443 if ((flags & NS_LDAP_NOMAP) == 0 && auto_service &&
444 schema_mapping_existed) {
445 rc = __s_api_convert_automountmapname(service,
446 &ap[0]->attrvalue[0],
447 error);
448 if (rc != NS_LDAP_SUCCESS) {
449 __ns_ldap_freeEntry(ep);
450 ep = NULL;
451 return (rc);
455 /* other attributes */
456 for (attr = ldap_first_attribute(ld, e, &ber), j = 1;
457 attr != NULL && j != nAttrs;
458 attr = ldap_next_attribute(ld, e, ber), j++) {
459 /* allocate new attr name */
461 if ((ap[j] = (ns_ldap_attr_t *)
462 calloc(1, sizeof (ns_ldap_attr_t))) == NULL) {
463 ber_free(ber, 0);
464 ber = NULL;
465 __ns_ldap_freeEntry(ep);
466 ep = NULL;
467 if (gecos_mapping)
468 __s_api_free2dArray(gecos_mapping);
469 gecos_mapping = NULL;
470 return (NS_LDAP_MEMORY);
473 if ((flags & NS_LDAP_NOMAP) || schema_mapping_existed == FALSE)
474 mapping = NULL;
475 else
476 mapping = __ns_ldap_getOrigAttribute(service, attr);
478 if (mapping == NULL && auto_service &&
479 schema_mapping_existed && (flags & NS_LDAP_NOMAP) == 0)
481 * if service == auto_* and no schema mapping found
482 * and schema_mapping_existed is TRUE and NS_LDAP_NOMAP
483 * is not set then try automount e.g.
484 * NS_LDAP_ATTRIBUTEMAP = automount:automountMapName=AAA
486 mapping = __ns_ldap_getOrigAttribute("automount",
487 attr);
489 if (mapping == NULL) {
490 if ((ap[j]->attrname = strdup(attr)) == NULL) {
491 ber_free(ber, 0);
492 ber = NULL;
493 __ns_ldap_freeEntry(ep);
494 ep = NULL;
495 if (gecos_mapping)
496 __s_api_free2dArray(gecos_mapping);
497 gecos_mapping = NULL;
498 return (NS_LDAP_MEMORY);
500 } else {
502 * for "gecos" 1 to N mapping,
503 * do not remove the mapped attribute,
504 * just create a new gecos attribute
505 * and append it to the end of the attribute list
507 if (strcasecmp(mapping[0], "gecos") == 0) {
508 ap[j]->attrname = strdup(attr);
509 gecos_mapping_existed = TRUE;
510 } else
511 ap[j]->attrname = strdup(mapping[0]);
513 if (ap[j]->attrname == NULL) {
514 ber_free(ber, 0);
515 ber = NULL;
516 __ns_ldap_freeEntry(ep);
517 ep = NULL;
518 if (gecos_mapping)
519 __s_api_free2dArray(gecos_mapping);
520 gecos_mapping = NULL;
521 return (NS_LDAP_MEMORY);
524 * 1 to N attribute mapping processing
525 * is only done for "gecos"
528 if (strcasecmp(mapping[0], "gecos") == 0) {
530 * get attribute mapping for "gecos",
531 * need to know the number and order of the
532 * mapped attributes
534 if (gecos_mapping == NULL) {
535 gecos_mapping =
536 __ns_ldap_getMappedAttributes(
537 service, mapping[0]);
538 if (gecos_mapping == NULL ||
539 gecos_mapping[0] == NULL) {
541 * this should never happens,
542 * syslog the error
544 (void) sprintf(errstr,
545 gettext(
546 "Attribute mapping "
547 "inconsistency "
548 "found for attributes "
549 "'%s' and '%s'."),
550 mapping[0], attr);
551 syslog(LOG_ERR, "libsldap: %s",
552 errstr);
554 ber_free(ber, 0);
555 ber = NULL;
556 __ns_ldap_freeEntry(ep);
557 ep = NULL;
558 __s_api_free2dArray(mapping);
559 mapping = NULL;
560 if (gecos_mapping)
561 __s_api_free2dArray(
562 gecos_mapping);
563 gecos_mapping = NULL;
564 return (NS_LDAP_INTERNAL);
569 * is this attribute the 1st, 2nd, or
570 * 3rd attr in the mapping list?
572 gecos_attr_matched = FALSE;
573 for (i = 0; i < 3 && gecos_mapping[i]; i++) {
574 if (gecos_mapping[i] &&
575 strcasecmp(gecos_mapping[i],
576 attr) == 0) {
577 gecos_val_index[i] = j;
578 gecos_attr_matched = TRUE;
579 break;
582 if (gecos_attr_matched == FALSE) {
584 * Not match found.
585 * This should never happens,
586 * syslog the error
588 (void) sprintf(errstr,
589 gettext(
590 "Attribute mapping "
591 "inconsistency "
592 "found for attributes "
593 "'%s' and '%s'."),
594 mapping[0], attr);
595 syslog(LOG_ERR, "libsldap: %s", errstr);
597 ber_free(ber, 0);
598 ber = NULL;
599 __ns_ldap_freeEntry(ep);
600 ep = NULL;
601 __s_api_free2dArray(mapping);
602 mapping = NULL;
603 __s_api_free2dArray(gecos_mapping);
604 gecos_mapping = NULL;
605 return (NS_LDAP_INTERNAL);
608 __s_api_free2dArray(mapping);
609 mapping = NULL;
612 if ((vals = ldap_get_values(ld, e, attr)) != NULL) {
614 if ((ap[j]->value_count =
615 ldap_count_values(vals)) == 0) {
616 ldap_value_free(vals);
617 vals = NULL;
618 continue;
619 } else {
620 ap[j]->attrvalue = (char **)
621 calloc(ap[j]->value_count+1,
622 sizeof (char *));
623 if (ap[j]->attrvalue == NULL) {
624 ber_free(ber, 0);
625 ber = NULL;
626 __ns_ldap_freeEntry(ep);
627 ep = NULL;
628 if (gecos_mapping)
629 __s_api_free2dArray(
630 gecos_mapping);
631 gecos_mapping = NULL;
632 return (NS_LDAP_MEMORY);
636 /* map object classes if necessary */
637 if ((flags & NS_LDAP_NOMAP) == 0 &&
638 schema_mapping_existed && ap[j]->attrname &&
639 strcasecmp(ap[j]->attrname, "objectclass") == 0) {
640 for (k = 0; k < ap[j]->value_count; k++) {
641 mapping =
642 __ns_ldap_getOrigObjectClass(
643 service, vals[k]);
645 if (mapping == NULL && auto_service)
647 * if service == auto_* and no
648 * schema mapping found
649 * then try automount
651 mapping =
652 __ns_ldap_getOrigObjectClass(
653 "automount", vals[k]);
655 if (mapping == NULL) {
656 ap[j]->attrvalue[k] =
657 strdup(vals[k]);
658 } else {
659 ap[j]->attrvalue[k] =
660 strdup(mapping[0]);
661 __s_api_free2dArray(mapping);
662 mapping = NULL;
664 if (ap[j]->attrvalue[k] == NULL) {
665 ber_free(ber, 0);
666 ber = NULL;
667 __ns_ldap_freeEntry(ep);
668 ep = NULL;
669 if (gecos_mapping)
670 __s_api_free2dArray(
671 gecos_mapping);
672 gecos_mapping = NULL;
673 return (NS_LDAP_MEMORY);
676 } else {
677 for (k = 0; k < ap[j]->value_count; k++) {
678 if ((ap[j]->attrvalue[k] =
679 strdup(vals[k])) == NULL) {
680 ber_free(ber, 0);
681 ber = NULL;
682 __ns_ldap_freeEntry(ep);
683 ep = NULL;
684 if (gecos_mapping)
685 __s_api_free2dArray(
686 gecos_mapping);
687 gecos_mapping = NULL;
688 return (NS_LDAP_MEMORY);
693 ap[j]->attrvalue[k] = NULL;
694 ldap_value_free(vals);
695 vals = NULL;
698 ldap_memfree(attr);
699 attr = NULL;
702 ber_free(ber, 0);
703 ber = NULL;
705 if (gecos_mapping) {
706 __s_api_free2dArray(gecos_mapping);
707 gecos_mapping = NULL;
710 /* special processing for gecos 1 to up to 3 attribute mapping */
711 if (schema_mapping_existed && gecos_mapping_existed) {
713 int f = -1;
715 for (i = 0; i < 3; i++) {
716 k = gecos_val_index[i];
719 * f is the index of the first returned
720 * attribute which "gecos" attribute mapped to
722 if (k != -1 && f == -1)
723 f = k;
725 if (k != -1 && ap[k]->value_count > 0 &&
726 ap[k]->attrvalue[0] &&
727 strlen(ap[k]->attrvalue[0]) > 0) {
729 if (k == f) {
731 * Create and fill in the last reserved
732 * ap with the data from the "gecos"
733 * mapping attributes
735 ap[nAttrs] = (ns_ldap_attr_t *)
736 calloc(1,
737 sizeof (ns_ldap_attr_t));
738 if (ap[nAttrs] == NULL) {
739 __ns_ldap_freeEntry(ep);
740 ep = NULL;
741 return (NS_LDAP_MEMORY);
743 ap[nAttrs]->attrvalue = (char **)calloc(
744 2, sizeof (char *));
745 if (ap[nAttrs]->attrvalue == NULL) {
746 __ns_ldap_freeEntry(ep);
747 ep = NULL;
748 return (NS_LDAP_MEMORY);
750 /* add 1 more for a possible "," */
751 ap[nAttrs]->attrvalue[0] =
752 (char *)calloc(
753 strlen(ap[f]->attrvalue[0]) +
754 2, 1);
755 if (ap[nAttrs]->attrvalue[0] == NULL) {
756 __ns_ldap_freeEntry(ep);
757 ep = NULL;
758 return (NS_LDAP_MEMORY);
760 (void) strcpy(ap[nAttrs]->attrvalue[0],
761 ap[f]->attrvalue[0]);
763 ap[nAttrs]->attrname = strdup("gecos");
764 if (ap[nAttrs]->attrname == NULL) {
765 __ns_ldap_freeEntry(ep);
766 ep = NULL;
767 return (NS_LDAP_MEMORY);
770 ap[nAttrs]->value_count = 1;
771 ep->attr_count = nAttrs + 1;
773 } else {
774 char *tmp = NULL;
777 * realloc to add "," and
778 * ap[k]->attrvalue[0]
780 tmp = (char *)realloc(
781 ap[nAttrs]->attrvalue[0],
782 strlen(ap[nAttrs]->
783 attrvalue[0]) +
784 strlen(ap[k]->
785 attrvalue[0]) + 2);
786 if (tmp == NULL) {
787 __ns_ldap_freeEntry(ep);
788 ep = NULL;
789 return (NS_LDAP_MEMORY);
791 ap[nAttrs]->attrvalue[0] = tmp;
792 (void) strcat(ap[nAttrs]->attrvalue[0],
793 ",");
794 (void) strcat(ap[nAttrs]->attrvalue[0],
795 ap[k]->attrvalue[0]);
801 *ret = ep;
802 return (NS_LDAP_SUCCESS);
805 static int
806 __s_api_getEntry(ns_ldap_cookie_t *cookie)
808 ns_ldap_entry_t *curEntry = NULL;
809 int ret;
811 #ifdef DEBUG
812 (void) fprintf(stderr, "__s_api_getEntry START\n");
813 #endif
815 if (cookie->resultMsg == NULL) {
816 return (NS_LDAP_INVALID_PARAM);
818 ret = __s_api_cvtEntry(cookie->conn->ld, cookie->service,
819 cookie->resultMsg, cookie->i_flags,
820 &curEntry, &cookie->errorp);
821 if (ret != NS_LDAP_SUCCESS) {
822 return (ret);
825 if (cookie->result == NULL) {
826 cookie->result = (ns_ldap_result_t *)
827 calloc(1, sizeof (ns_ldap_result_t));
828 if (cookie->result == NULL) {
829 __ns_ldap_freeEntry(curEntry);
830 curEntry = NULL;
831 return (NS_LDAP_MEMORY);
833 cookie->result->entry = curEntry;
834 cookie->nextEntry = curEntry;
835 } else {
836 cookie->nextEntry->next = curEntry;
837 cookie->nextEntry = curEntry;
839 cookie->result->entries_count++;
841 return (NS_LDAP_SUCCESS);
844 static int
845 __s_api_get_cachemgr_data(const char *type,
846 const char *from, char **to)
848 union {
849 ldap_data_t s_d;
850 char s_b[DOORBUFFERSIZE];
851 } space;
852 ldap_data_t *sptr;
853 int ndata;
854 int adata;
855 int rc;
857 #ifdef DEBUG
858 (void) fprintf(stderr, "__s_api_get_cachemgr_data START\n");
859 #endif
861 * We are not going to perform DN to domain mapping
862 * in the Standalone mode
864 if (__s_api_isStandalone()) {
865 return (-1);
868 if (from == NULL || from[0] == '\0' || to == NULL)
869 return (-1);
871 *to = NULL;
872 (void) memset(space.s_b, 0, DOORBUFFERSIZE);
874 space.s_d.ldap_call.ldap_callnumber = GETCACHE;
875 (void) snprintf(space.s_d.ldap_call.ldap_u.domainname,
876 DOORBUFFERSIZE - sizeof (space.s_d.ldap_call.ldap_callnumber),
877 "%s%s%s",
878 type,
879 DOORLINESEP,
880 from);
881 ndata = sizeof (space);
882 adata = sizeof (ldap_call_t) +
883 strlen(space.s_d.ldap_call.ldap_u.domainname) + 1;
884 sptr = &space.s_d;
886 rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
887 if (rc != NS_CACHE_SUCCESS)
888 return (-1);
889 else
890 *to = strdup(sptr->ldap_ret.ldap_u.buff);
891 return (NS_LDAP_SUCCESS);
894 static int
895 __s_api_set_cachemgr_data(const char *type,
896 const char *from, const char *to)
898 union {
899 ldap_data_t s_d;
900 char s_b[DOORBUFFERSIZE];
901 } space;
902 ldap_data_t *sptr;
903 int ndata;
904 int adata;
905 int rc;
907 #ifdef DEBUG
908 (void) fprintf(stderr, "__s_api_set_cachemgr_data START\n");
909 #endif
911 * We are not going to perform DN to domain mapping
912 * in the Standalone mode
914 if (__s_api_isStandalone()) {
915 return (-1);
918 if ((from == NULL) || (from[0] == '\0') ||
919 (to == NULL) || (to[0] == '\0'))
920 return (-1);
922 (void) memset(space.s_b, 0, DOORBUFFERSIZE);
924 space.s_d.ldap_call.ldap_callnumber = SETCACHE;
925 (void) snprintf(space.s_d.ldap_call.ldap_u.domainname,
926 DOORBUFFERSIZE - sizeof (space.s_d.ldap_call.ldap_callnumber),
927 "%s%s%s%s%s",
928 type,
929 DOORLINESEP,
930 from,
931 DOORLINESEP,
932 to);
934 ndata = sizeof (space);
935 adata = sizeof (ldap_call_t) +
936 strlen(space.s_d.ldap_call.ldap_u.domainname) + 1;
937 sptr = &space.s_d;
939 rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
940 if (rc != NS_CACHE_SUCCESS)
941 return (-1);
943 return (NS_LDAP_SUCCESS);
947 static char *
948 __s_api_remove_rdn_space(char *rdn)
950 char *tf, *tl, *vf, *vl, *eqsign;
952 /* if no space(s) to remove, return */
953 if (strchr(rdn, SPACETOK) == NULL)
954 return (rdn);
956 /* if no '=' separator, return */
957 eqsign = strchr(rdn, '=');
958 if (eqsign == NULL)
959 return (rdn);
961 tf = rdn;
962 tl = eqsign - 1;
963 vf = eqsign + 1;
964 vl = rdn + strlen(rdn) - 1;
966 /* now two strings, type and value */
967 *eqsign = '\0';
969 /* remove type's leading spaces */
970 while (tf < tl && *tf == SPACETOK)
971 tf++;
972 /* remove type's trailing spaces */
973 while (tf < tl && *tl == SPACETOK)
974 tl--;
975 /* add '=' separator back */
976 *(++tl) = '=';
977 /* remove value's leading spaces */
978 while (vf < vl && *vf == SPACETOK)
979 vf++;
980 /* remove value's trailing spaces */
981 while (vf < vl && *vl == SPACETOK)
982 *vl-- = '\0';
984 /* move value up if necessary */
985 if (vf != tl + 1)
986 (void) strcpy(tl + 1, vf);
988 return (tf);
991 static
992 ns_ldap_cookie_t *
993 init_search_state_machine()
995 ns_ldap_cookie_t *cookie;
996 ns_config_t *cfg;
998 cookie = (ns_ldap_cookie_t *)calloc(1, sizeof (ns_ldap_cookie_t));
999 if (cookie == NULL)
1000 return (NULL);
1001 cookie->state = INIT;
1002 /* assign other state variables */
1003 cfg = __s_api_loadrefresh_config();
1004 cookie->connectionId = -1;
1005 if (cfg == NULL ||
1006 cfg->paramList[NS_LDAP_SEARCH_TIME_P].ns_ptype == NS_UNKNOWN) {
1007 cookie->search_timeout.tv_sec = NS_DEFAULT_SEARCH_TIMEOUT;
1008 } else {
1009 cookie->search_timeout.tv_sec =
1010 cfg->paramList[NS_LDAP_SEARCH_TIME_P].ns_i;
1012 if (cfg != NULL)
1013 __s_api_release_config(cfg);
1014 cookie->search_timeout.tv_usec = 0;
1016 return (cookie);
1019 static void
1020 delete_search_cookie(ns_ldap_cookie_t *cookie)
1022 if (cookie == NULL)
1023 return;
1024 if (cookie->connectionId > -1)
1025 DropConnection(cookie->connectionId, cookie->i_flags);
1026 free(cookie->filter);
1027 free(cookie->i_filter);
1028 free(cookie->service);
1029 if (cookie->sdlist)
1030 (void) __ns_ldap_freeSearchDescriptors(&(cookie->sdlist));
1031 if (cookie->result)
1032 (void) __ns_ldap_freeResult(&cookie->result);
1033 if (cookie->attribute)
1034 __s_api_free2dArray(cookie->attribute);
1035 if (cookie->errorp)
1036 (void) __ns_ldap_freeError(&cookie->errorp);
1037 if (cookie->reflist)
1038 __s_api_deleteRefInfo(cookie->reflist);
1039 free(cookie->basedn);
1040 if (cookie->ctrlCookie)
1041 ber_bvfree(cookie->ctrlCookie);
1042 _freeControlList(&cookie->p_serverctrls);
1043 if (cookie->resultctrl)
1044 ldap_controls_free(cookie->resultctrl);
1045 free(cookie);
1048 static int
1049 get_mapped_filter(ns_ldap_cookie_t *cookie, char **new_filter)
1052 typedef struct filter_mapping_info {
1053 char oc_or_attr;
1054 char *name_start;
1055 char *name_end;
1056 char *veq_pos;
1057 char *from_name;
1058 char *to_name;
1059 char **mapping;
1060 } filter_mapping_info_t;
1062 char *c, *last_copied;
1063 char *filter_c, *filter_c_next;
1064 char *key, *tail, *head;
1065 char errstr[MAXERROR];
1066 int num_eq = 0, num_veq = 0;
1067 int in_quote = FALSE;
1068 int is_value = FALSE;
1069 int i, j, oc_len, len;
1070 int at_least_one = FALSE;
1071 filter_mapping_info_t **info, *info1;
1072 char **mapping;
1073 char *service, *filter, *err;
1074 int auto_service = FALSE;
1076 if (cookie == NULL || new_filter == NULL)
1077 return (NS_LDAP_INVALID_PARAM);
1079 *new_filter = NULL;
1080 service = cookie->service;
1081 filter = cookie->filter;
1084 * count the number of '=' char
1086 for (c = filter; *c; c++) {
1087 if (*c == TOKENSEPARATOR)
1088 num_eq++;
1091 if (service != NULL && strncasecmp(service, "auto_", 5) == 0)
1092 auto_service = TRUE;
1095 * See if schema mapping existed for the given service.
1096 * If not, just return success.
1098 mapping = __ns_ldap_getOrigAttribute(service,
1099 NS_HASH_SCHEMA_MAPPING_EXISTED);
1101 if (mapping == NULL && auto_service)
1103 * if service == auto_* and no
1104 * schema mapping found
1105 * then try automount
1107 mapping = __ns_ldap_getOrigAttribute(
1108 "automount", NS_HASH_SCHEMA_MAPPING_EXISTED);
1110 if (mapping)
1111 __s_api_free2dArray(mapping);
1112 else
1113 return (NS_LDAP_SUCCESS);
1116 * no '=' sign, just say OK and return nothing
1118 if (num_eq == 0)
1119 return (NS_LDAP_SUCCESS);
1122 * Make a copy of the filter string
1123 * for saving the name of the objectclasses or
1124 * attributes that need to be passed to the
1125 * objectclass or attribute mapping functions.
1126 * pointer "info->from_name" points to the locations
1127 * within this string.
1129 * The input filter string, filter, will be used
1130 * to indicate where these names start and end.
1131 * pointers "info->name_start" and "info->name_end"
1132 * point to locations within the input filter string,
1133 * and are used at the end of this function to
1134 * merge the original filter data with the
1135 * mapped objectclass or attribute names.
1137 filter_c = strdup(filter);
1138 if (filter_c == NULL)
1139 return (NS_LDAP_MEMORY);
1140 filter_c_next = filter_c;
1143 * get memory for info arrays
1145 info = (filter_mapping_info_t **)calloc(num_eq + 1,
1146 sizeof (filter_mapping_info_t *));
1148 if (info == NULL) {
1149 free(filter_c);
1150 return (NS_LDAP_MEMORY);
1154 * find valid '=' for further processing,
1155 * ignore the "escaped =" (.i.e. "\="), or
1156 * "=" in quoted string
1158 for (c = filter_c; *c; c++) {
1160 switch (*c) {
1161 case TOKENSEPARATOR:
1162 if (!in_quote && !is_value) {
1163 info1 = (filter_mapping_info_t *)calloc(1,
1164 sizeof (filter_mapping_info_t));
1165 if (!info1) {
1166 free(filter_c);
1167 for (i = 0; i < num_veq; i++)
1168 free(info[i]);
1169 free(info);
1170 return (NS_LDAP_MEMORY);
1172 info[num_veq] = info1;
1175 * remember the location of this "="
1177 info[num_veq++]->veq_pos = c;
1180 * skip until the end of the attribute value
1182 is_value = TRUE;
1184 break;
1185 case CPARATOK:
1187 * mark the end of the attribute value
1189 if (!in_quote)
1190 is_value = FALSE;
1191 break;
1192 case QUOTETOK:
1194 * switch on/off the in_quote mode
1196 in_quote = (in_quote == FALSE);
1197 break;
1198 case '\\':
1200 * ignore escape characters
1201 * don't skip if next char is '\0'
1203 if (!in_quote)
1204 if (*(++c) == '\0')
1205 c--;
1206 break;
1212 * for each valid "=" found, get the name to
1213 * be mapped
1215 oc_len = strlen("objectclass");
1216 for (i = 0; i < num_veq; i++) {
1219 * look at the left side of "=" to see
1220 * if assertion is "objectclass=<ocname>"
1221 * or "<attribute name>=<attribute value>"
1223 * first skip spaces before "=".
1224 * Note that filter_c_next may not point to the
1225 * start of the filter string. For i > 0,
1226 * it points to the end of the last name processed + 2
1228 for (tail = info[i]->veq_pos; (tail > filter_c_next) &&
1229 (*(tail - 1) == SPACETOK); tail--)
1233 * mark the end of the left side string (the key)
1235 *tail = '\0';
1236 info[i]->name_end = tail - filter_c - 1 + filter;
1239 * find the start of the key
1241 key = filter_c_next;
1242 for (c = tail; filter_c_next <= c; c--) {
1243 /* OPARATOK is '(' */
1244 if (*c == OPARATOK ||
1245 *c == SPACETOK) {
1246 key = c + 1;
1247 break;
1250 info[i]->name_start = key - filter_c + filter;
1252 if ((key + oc_len) <= tail) {
1253 if (strncasecmp(key, "objectclass",
1254 oc_len) == 0) {
1256 * assertion is "objectclass=ocname",
1257 * ocname is the one needs to be mapped
1259 * skip spaces after "=" to find start
1260 * of the ocname
1262 head = info[i]->veq_pos;
1263 for (head = info[i]->veq_pos + 1;
1264 *head && *head == SPACETOK; head++)
1267 /* ignore empty ocname */
1268 if (!(*head))
1269 continue;
1271 info[i]->name_start = head - filter_c +
1272 filter;
1275 * now find the end of the ocname
1277 for (c = head; ; c++) {
1278 /* CPARATOK is ')' */
1279 if (*c == CPARATOK ||
1280 *c == '\0' ||
1281 *c == SPACETOK) {
1282 *c = '\0';
1283 info[i]->name_end =
1284 c - filter_c - 1 +
1285 filter;
1286 filter_c_next = c + 1;
1287 info[i]->oc_or_attr = 'o';
1288 info[i]->from_name = head;
1289 break;
1296 * assertion is not "objectclass=ocname",
1297 * assume assertion is "<key> = <value>",
1298 * <key> is the one needs to be mapped
1300 if (info[i]->from_name == NULL && strlen(key) > 0) {
1301 info[i]->oc_or_attr = 'a';
1302 info[i]->from_name = key;
1306 /* perform schema mapping */
1307 for (i = 0; i < num_veq; i++) {
1308 if (info[i]->from_name == NULL)
1309 continue;
1311 if (info[i]->oc_or_attr == 'a')
1312 info[i]->mapping =
1313 __ns_ldap_getMappedAttributes(service,
1314 info[i]->from_name);
1315 else
1316 info[i]->mapping =
1317 __ns_ldap_getMappedObjectClass(service,
1318 info[i]->from_name);
1320 if (info[i]->mapping == NULL && auto_service) {
1322 * If no mapped attribute/objectclass is found
1323 * and service == auto*
1324 * try to find automount's
1325 * mapped attribute/objectclass
1327 if (info[i]->oc_or_attr == 'a')
1328 info[i]->mapping =
1329 __ns_ldap_getMappedAttributes("automount",
1330 info[i]->from_name);
1331 else
1332 info[i]->mapping =
1333 __ns_ldap_getMappedObjectClass("automount",
1334 info[i]->from_name);
1337 if (info[i]->mapping == NULL ||
1338 info[i]->mapping[0] == NULL) {
1339 info[i]->to_name = NULL;
1340 } else if (info[i]->mapping[1] == NULL) {
1341 info[i]->to_name = info[i]->mapping[0];
1342 at_least_one = TRUE;
1343 } else {
1344 __s_api_free2dArray(info[i]->mapping);
1346 * multiple mapping
1347 * not allowed
1349 (void) sprintf(errstr,
1350 gettext(
1351 "Multiple attribute or objectclass "
1352 "mapping for '%s' in filter "
1353 "'%s' not allowed."),
1354 info[i]->from_name, filter);
1355 err = strdup(errstr);
1356 if (err) {
1357 MKERROR(LOG_WARNING, cookie->errorp,
1358 NS_CONFIG_SYNTAX,
1359 err, 0);
1362 free(filter_c);
1363 for (j = 0; j < num_veq; j++) {
1364 if (info[j]->mapping)
1365 __s_api_free2dArray(
1366 info[j]->mapping);
1367 free(info[j]);
1369 free(info);
1370 return (NS_LDAP_CONFIG);
1375 if (at_least_one) {
1377 len = strlen(filter);
1378 last_copied = filter - 1;
1380 for (i = 0; i < num_veq; i++) {
1381 if (info[i]->to_name)
1382 len += strlen(info[i]->to_name);
1385 *new_filter = (char *)calloc(1, len);
1386 if (*new_filter == NULL) {
1387 free(filter_c);
1388 for (j = 0; j < num_veq; j++) {
1389 if (info[j]->mapping)
1390 __s_api_free2dArray(
1391 info[j]->mapping);
1392 free(info[j]);
1394 free(info);
1395 return (NS_LDAP_MEMORY);
1398 for (i = 0; i < num_veq; i++) {
1399 if (info[i]->to_name != NULL &&
1400 info[i]->to_name != NULL) {
1403 * copy the original filter data
1404 * between the last name and current
1405 * name
1407 if ((last_copied + 1) != info[i]->name_start)
1408 (void) strncat(*new_filter,
1409 last_copied + 1,
1410 info[i]->name_start -
1411 last_copied - 1);
1413 /* the data is copied */
1414 last_copied = info[i]->name_end;
1417 * replace the name with
1418 * the mapped name
1420 (void) strcat(*new_filter, info[i]->to_name);
1423 /* copy the filter data after the last name */
1424 if (i == (num_veq -1) &&
1425 info[i]->name_end <
1426 (filter + strlen(filter)))
1427 (void) strncat(*new_filter, last_copied + 1,
1428 filter + strlen(filter) -
1429 last_copied - 1);
1434 /* free memory */
1435 free(filter_c);
1436 for (j = 0; j < num_veq; j++) {
1437 if (info[j]->mapping)
1438 __s_api_free2dArray(info[j]->mapping);
1439 free(info[j]);
1441 free(info);
1443 return (NS_LDAP_SUCCESS);
1446 static int
1447 setup_next_search(ns_ldap_cookie_t *cookie)
1449 ns_ldap_search_desc_t *dptr;
1450 int scope;
1451 char *filter, *str;
1452 int baselen;
1453 int rc;
1454 void **param;
1456 dptr = *cookie->sdpos;
1457 scope = cookie->i_flags & (NS_LDAP_SCOPE_BASE |
1458 NS_LDAP_SCOPE_ONELEVEL |
1459 NS_LDAP_SCOPE_SUBTREE);
1460 if (scope)
1461 cookie->scope = scope;
1462 else
1463 cookie->scope = dptr->scope;
1464 switch (cookie->scope) {
1465 case NS_LDAP_SCOPE_BASE:
1466 cookie->scope = LDAP_SCOPE_BASE;
1467 break;
1468 case NS_LDAP_SCOPE_ONELEVEL:
1469 cookie->scope = LDAP_SCOPE_ONELEVEL;
1470 break;
1471 case NS_LDAP_SCOPE_SUBTREE:
1472 cookie->scope = LDAP_SCOPE_SUBTREE;
1473 break;
1476 filter = NULL;
1477 if (cookie->use_filtercb && cookie->init_filter_cb &&
1478 dptr->filter && strlen(dptr->filter) > 0) {
1479 (*cookie->init_filter_cb)(dptr, &filter,
1480 cookie->userdata);
1482 if (filter == NULL) {
1483 if (cookie->i_filter == NULL) {
1484 cookie->err_rc = NS_LDAP_INVALID_PARAM;
1485 return (-1);
1486 } else {
1487 free(cookie->filter);
1488 cookie->filter = strdup(cookie->i_filter);
1489 if (cookie->filter == NULL) {
1490 cookie->err_rc = NS_LDAP_MEMORY;
1491 return (-1);
1494 } else {
1495 free(cookie->filter);
1496 cookie->filter = strdup(filter);
1497 free(filter);
1498 if (cookie->filter == NULL) {
1499 cookie->err_rc = NS_LDAP_MEMORY;
1500 return (-1);
1505 * perform attribute/objectclass mapping on filter
1507 filter = NULL;
1509 if (cookie->service) {
1510 rc = get_mapped_filter(cookie, &filter);
1511 if (rc != NS_LDAP_SUCCESS) {
1512 cookie->err_rc = rc;
1513 return (-1);
1514 } else {
1516 * get_mapped_filter returns
1517 * NULL filter pointer, if
1518 * no mapping was done
1520 if (filter) {
1521 free(cookie->filter);
1522 cookie->filter = filter;
1528 * validate filter to make sure it's legal
1529 * [remove redundant ()'s]
1531 rc = validate_filter(cookie);
1532 if (rc != NS_LDAP_SUCCESS) {
1533 cookie->err_rc = rc;
1534 return (-1);
1537 baselen = strlen(dptr->basedn);
1538 if (baselen > 0 && dptr->basedn[baselen-1] == COMMATOK) {
1539 rc = __ns_ldap_getParam(NS_LDAP_SEARCH_BASEDN_P,
1540 (void ***)&param, &cookie->errorp);
1541 if (rc != NS_LDAP_SUCCESS) {
1542 cookie->err_rc = rc;
1543 return (-1);
1545 str = ((char **)param)[0];
1546 baselen += strlen(str)+1;
1547 free(cookie->basedn);
1548 cookie->basedn = (char *)malloc(baselen);
1549 if (cookie->basedn == NULL) {
1550 cookie->err_rc = NS_LDAP_MEMORY;
1551 return (-1);
1553 (void) strcpy(cookie->basedn, dptr->basedn);
1554 (void) strcat(cookie->basedn, str);
1555 (void) __ns_ldap_freeParam(&param);
1556 } else {
1557 free(cookie->basedn);
1558 cookie->basedn = strdup(dptr->basedn);
1560 return (0);
1563 static int
1564 setup_referral_search(ns_ldap_cookie_t *cookie)
1566 ns_referral_info_t *ref;
1568 ref = cookie->refpos;
1569 cookie->scope = ref->refScope;
1570 if (cookie->filter) {
1571 free(cookie->filter);
1573 cookie->filter = strdup(ref->refFilter);
1574 if (cookie->basedn) {
1575 free(cookie->basedn);
1577 cookie->basedn = strdup(ref->refDN);
1578 if (cookie->filter == NULL || cookie->basedn == NULL) {
1579 cookie->err_rc = NS_LDAP_MEMORY;
1580 return (-1);
1582 return (0);
1585 static int
1586 get_current_session(ns_ldap_cookie_t *cookie)
1588 ConnectionID connectionId = -1;
1589 Connection *conp = NULL;
1590 int rc;
1591 int fail_if_new_pwd_reqd = 1;
1593 rc = __s_api_getConnection(NULL, cookie->i_flags,
1594 cookie->i_auth, &connectionId, &conp,
1595 &cookie->errorp, fail_if_new_pwd_reqd,
1596 cookie->nopasswd_acct_mgmt, cookie->conn_user);
1599 * If password control attached in *cookie->errorp,
1600 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1601 * free the error structure (we do not need
1602 * the sec_to_expired info).
1603 * Reset rc to NS_LDAP_SUCCESS.
1605 if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1606 (void) __ns_ldap_freeError(
1607 &cookie->errorp);
1608 cookie->errorp = NULL;
1609 rc = NS_LDAP_SUCCESS;
1612 if (rc != NS_LDAP_SUCCESS) {
1613 cookie->err_rc = rc;
1614 return (-1);
1616 cookie->conn = conp;
1617 cookie->connectionId = connectionId;
1619 return (0);
1622 static int
1623 get_next_session(ns_ldap_cookie_t *cookie)
1625 ConnectionID connectionId = -1;
1626 Connection *conp = NULL;
1627 int rc;
1628 int fail_if_new_pwd_reqd = 1;
1630 if (cookie->connectionId > -1) {
1631 DropConnection(cookie->connectionId, cookie->i_flags);
1632 cookie->connectionId = -1;
1635 /* If using a MT connection, return it. */
1636 if (cookie->conn_user != NULL &&
1637 cookie->conn_user->conn_mt != NULL)
1638 __s_api_conn_mt_return(cookie->conn_user);
1640 rc = __s_api_getConnection(NULL, cookie->i_flags,
1641 cookie->i_auth, &connectionId, &conp,
1642 &cookie->errorp, fail_if_new_pwd_reqd,
1643 cookie->nopasswd_acct_mgmt, cookie->conn_user);
1646 * If password control attached in *cookie->errorp,
1647 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1648 * free the error structure (we do not need
1649 * the sec_to_expired info).
1650 * Reset rc to NS_LDAP_SUCCESS.
1652 if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1653 (void) __ns_ldap_freeError(
1654 &cookie->errorp);
1655 cookie->errorp = NULL;
1656 rc = NS_LDAP_SUCCESS;
1659 if (rc != NS_LDAP_SUCCESS) {
1660 cookie->err_rc = rc;
1661 return (-1);
1663 cookie->conn = conp;
1664 cookie->connectionId = connectionId;
1665 return (0);
1668 static int
1669 get_referral_session(ns_ldap_cookie_t *cookie)
1671 ConnectionID connectionId = -1;
1672 Connection *conp = NULL;
1673 int rc;
1674 int fail_if_new_pwd_reqd = 1;
1676 if (cookie->connectionId > -1) {
1677 DropConnection(cookie->connectionId, cookie->i_flags);
1678 cookie->connectionId = -1;
1681 /* set it up to use a connection opened for referral */
1682 if (cookie->conn_user != NULL) {
1683 /* If using a MT connection, return it. */
1684 if (cookie->conn_user->conn_mt != NULL)
1685 __s_api_conn_mt_return(cookie->conn_user);
1686 cookie->conn_user->referral = B_TRUE;
1689 rc = __s_api_getConnection(cookie->refpos->refHost, 0,
1690 cookie->i_auth, &connectionId, &conp,
1691 &cookie->errorp, fail_if_new_pwd_reqd,
1692 cookie->nopasswd_acct_mgmt, cookie->conn_user);
1695 * If password control attached in *cookie->errorp,
1696 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1697 * free the error structure (we do not need
1698 * the sec_to_expired info).
1699 * Reset rc to NS_LDAP_SUCCESS.
1701 if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1702 (void) __ns_ldap_freeError(
1703 &cookie->errorp);
1704 cookie->errorp = NULL;
1705 rc = NS_LDAP_SUCCESS;
1708 if (rc != NS_LDAP_SUCCESS) {
1709 cookie->err_rc = rc;
1710 return (-1);
1712 cookie->conn = conp;
1713 cookie->connectionId = connectionId;
1714 return (0);
1717 static int
1718 paging_supported(ns_ldap_cookie_t *cookie)
1720 int rc;
1722 cookie->listType = 0;
1723 rc = __s_api_isCtrlSupported(cookie->conn,
1724 LDAP_CONTROL_VLVREQUEST);
1725 if (rc == NS_LDAP_SUCCESS) {
1726 cookie->listType = VLVCTRLFLAG;
1727 return (1);
1729 rc = __s_api_isCtrlSupported(cookie->conn,
1730 LDAP_CONTROL_SIMPLE_PAGE);
1731 if (rc == NS_LDAP_SUCCESS) {
1732 cookie->listType = SIMPLEPAGECTRLFLAG;
1733 return (1);
1735 return (0);
1738 typedef struct servicesorttype {
1739 char *service;
1740 ns_srvsidesort_t type;
1741 } servicesorttype_t;
1743 static servicesorttype_t *sort_type = NULL;
1744 static int sort_type_size = 0;
1745 static int sort_type_hwm = 0;
1746 static mutex_t sort_type_mutex = DEFAULTMUTEX;
1749 static ns_srvsidesort_t
1750 get_srvsidesort_type(char *service)
1752 int i;
1753 ns_srvsidesort_t type = SSS_UNKNOWN;
1755 if (service == NULL)
1756 return (type);
1758 (void) mutex_lock(&sort_type_mutex);
1759 if (sort_type != NULL) {
1760 for (i = 0; i < sort_type_hwm; i++) {
1761 if (strcmp(sort_type[i].service, service) == 0) {
1762 type = sort_type[i].type;
1763 break;
1767 (void) mutex_unlock(&sort_type_mutex);
1768 return (type);
1771 static void
1772 update_srvsidesort_type(char *service, ns_srvsidesort_t type)
1774 int i, size;
1775 servicesorttype_t *tmp;
1777 if (service == NULL)
1778 return;
1780 (void) mutex_lock(&sort_type_mutex);
1782 for (i = 0; i < sort_type_hwm; i++) {
1783 if (strcmp(sort_type[i].service, service) == 0) {
1784 sort_type[i].type = type;
1785 (void) mutex_unlock(&sort_type_mutex);
1786 return;
1789 if (sort_type == NULL) {
1790 size = 10;
1791 tmp = malloc(size * sizeof (servicesorttype_t));
1792 if (tmp == NULL) {
1793 (void) mutex_unlock(&sort_type_mutex);
1794 return;
1796 sort_type = tmp;
1797 sort_type_size = size;
1798 } else if (sort_type_hwm >= sort_type_size) {
1799 size = sort_type_size + 10;
1800 tmp = reallocarray(sort_type, size, sizeof (servicesorttype_t));
1801 if (tmp == NULL) {
1802 (void) mutex_unlock(&sort_type_mutex);
1803 return;
1805 sort_type = tmp;
1806 sort_type_size = size;
1808 sort_type[sort_type_hwm].service = strdup(service);
1809 if (sort_type[sort_type_hwm].service == NULL) {
1810 (void) mutex_unlock(&sort_type_mutex);
1811 return;
1813 sort_type[sort_type_hwm].type = type;
1814 sort_type_hwm++;
1816 (void) mutex_unlock(&sort_type_mutex);
1819 static int
1820 setup_vlv_params(ns_ldap_cookie_t *cookie)
1822 LDAPControl **ctrls;
1823 LDAPsortkey **sortkeylist;
1824 LDAPControl *sortctrl = NULL;
1825 LDAPControl *vlvctrl = NULL;
1826 LDAPVirtualList vlist;
1827 char *sortattr;
1828 int rc;
1829 int free_sort = FALSE;
1831 _freeControlList(&cookie->p_serverctrls);
1833 if (cookie->sortTypeTry == SSS_UNKNOWN)
1834 cookie->sortTypeTry = get_srvsidesort_type(cookie->service);
1835 if (cookie->sortTypeTry == SSS_UNKNOWN)
1836 cookie->sortTypeTry = SSS_SINGLE_ATTR;
1838 if (cookie->sortTypeTry == SSS_SINGLE_ATTR) {
1839 if ((cookie->i_flags & NS_LDAP_NOMAP) == 0 &&
1840 cookie->i_sortattr) {
1841 sortattr = __ns_ldap_mapAttribute(cookie->service,
1842 cookie->i_sortattr);
1843 free_sort = TRUE;
1844 } else if (cookie->i_sortattr) {
1845 sortattr = (char *)cookie->i_sortattr;
1846 } else {
1847 sortattr = "cn";
1849 } else {
1850 sortattr = "cn uid";
1853 rc = ldap_create_sort_keylist(&sortkeylist, sortattr);
1854 if (free_sort)
1855 free(sortattr);
1856 if (rc != LDAP_SUCCESS) {
1857 (void) ldap_get_option(cookie->conn->ld,
1858 LDAP_OPT_ERROR_NUMBER, &rc);
1859 return (rc);
1861 rc = ldap_create_sort_control(cookie->conn->ld,
1862 sortkeylist, 1, &sortctrl);
1863 ldap_free_sort_keylist(sortkeylist);
1864 if (rc != LDAP_SUCCESS) {
1865 (void) ldap_get_option(cookie->conn->ld,
1866 LDAP_OPT_ERROR_NUMBER, &rc);
1867 return (rc);
1870 vlist.ldvlist_index = cookie->index;
1871 vlist.ldvlist_size = 0;
1873 vlist.ldvlist_before_count = 0;
1874 vlist.ldvlist_after_count = LISTPAGESIZE-1;
1875 vlist.ldvlist_attrvalue = NULL;
1876 vlist.ldvlist_extradata = NULL;
1878 rc = ldap_create_virtuallist_control(cookie->conn->ld,
1879 &vlist, &vlvctrl);
1880 if (rc != LDAP_SUCCESS) {
1881 ldap_control_free(sortctrl);
1882 (void) ldap_get_option(cookie->conn->ld, LDAP_OPT_ERROR_NUMBER,
1883 &rc);
1884 return (rc);
1887 ctrls = (LDAPControl **)calloc(3, sizeof (LDAPControl *));
1888 if (ctrls == NULL) {
1889 ldap_control_free(sortctrl);
1890 ldap_control_free(vlvctrl);
1891 return (LDAP_NO_MEMORY);
1894 ctrls[0] = sortctrl;
1895 ctrls[1] = vlvctrl;
1897 cookie->p_serverctrls = ctrls;
1898 return (LDAP_SUCCESS);
1901 static int
1902 setup_simplepg_params(ns_ldap_cookie_t *cookie)
1904 LDAPControl **ctrls;
1905 LDAPControl *pgctrl = NULL;
1906 int rc;
1908 _freeControlList(&cookie->p_serverctrls);
1910 rc = ldap_create_page_control(cookie->conn->ld, LISTPAGESIZE,
1911 cookie->ctrlCookie, '\0', &pgctrl);
1912 if (rc != LDAP_SUCCESS) {
1913 (void) ldap_get_option(cookie->conn->ld, LDAP_OPT_ERROR_NUMBER,
1914 &rc);
1915 return (rc);
1918 ctrls = (LDAPControl **)calloc(2, sizeof (LDAPControl *));
1919 if (ctrls == NULL) {
1920 ldap_control_free(pgctrl);
1921 return (LDAP_NO_MEMORY);
1923 ctrls[0] = pgctrl;
1924 cookie->p_serverctrls = ctrls;
1925 return (LDAP_SUCCESS);
1928 static void
1929 proc_result_referrals(ns_ldap_cookie_t *cookie)
1931 int errCode, i, rc;
1932 char **referrals = NULL;
1935 * Only follow one level of referrals, i.e.
1936 * if already in referral mode, do nothing
1938 if (cookie->refpos == NULL) {
1939 cookie->new_state = END_RESULT;
1940 rc = ldap_parse_result(cookie->conn->ld,
1941 cookie->resultMsg,
1942 &errCode, NULL,
1943 NULL, &referrals,
1944 NULL, 0);
1945 if (rc != NS_LDAP_SUCCESS) {
1946 (void) ldap_get_option(cookie->conn->ld,
1947 LDAP_OPT_ERROR_NUMBER,
1948 &cookie->err_rc);
1949 cookie->new_state = LDAP_ERROR;
1950 return;
1952 if (errCode == LDAP_REFERRAL) {
1953 for (i = 0; referrals[i] != NULL;
1954 i++) {
1955 /* add to referral list */
1956 rc = __s_api_addRefInfo(
1957 &cookie->reflist,
1958 referrals[i],
1959 cookie->basedn,
1960 &cookie->scope,
1961 cookie->filter,
1962 cookie->conn->ld);
1963 if (rc != NS_LDAP_SUCCESS) {
1964 cookie->new_state =
1965 ERROR;
1966 break;
1969 ldap_value_free(referrals);
1974 static void
1975 proc_search_references(ns_ldap_cookie_t *cookie)
1977 char **refurls = NULL;
1978 int i, rc;
1981 * Only follow one level of referrals, i.e.
1982 * if already in referral mode, do nothing
1984 if (cookie->refpos == NULL) {
1985 refurls = ldap_get_reference_urls(
1986 cookie->conn->ld,
1987 cookie->resultMsg);
1988 if (refurls == NULL) {
1989 (void) ldap_get_option(cookie->conn->ld,
1990 LDAP_OPT_ERROR_NUMBER,
1991 &cookie->err_rc);
1992 cookie->new_state = LDAP_ERROR;
1993 return;
1995 for (i = 0; refurls[i] != NULL; i++) {
1996 /* add to referral list */
1997 rc = __s_api_addRefInfo(
1998 &cookie->reflist,
1999 refurls[i],
2000 cookie->basedn,
2001 &cookie->scope,
2002 cookie->filter,
2003 cookie->conn->ld);
2004 if (rc != NS_LDAP_SUCCESS) {
2005 cookie->new_state =
2006 ERROR;
2007 break;
2010 /* free allocated storage */
2011 for (i = 0; refurls[i] != NULL; i++)
2012 free(refurls[i]);
2016 static ns_state_t
2017 multi_result(ns_ldap_cookie_t *cookie)
2019 char errstr[MAXERROR];
2020 char *err;
2021 ns_ldap_error_t **errorp = NULL;
2022 LDAPControl **retCtrls = NULL;
2023 int i, rc;
2024 int errCode;
2025 int finished = 0;
2026 unsigned long target_posp = 0;
2027 unsigned long list_size = 0;
2028 unsigned int count = 0;
2029 char **referrals = NULL;
2031 if (cookie->listType == VLVCTRLFLAG) {
2032 rc = ldap_parse_result(cookie->conn->ld, cookie->resultMsg,
2033 &errCode, NULL, NULL, &referrals, &retCtrls, 0);
2034 if (rc != LDAP_SUCCESS) {
2035 (void) ldap_get_option(cookie->conn->ld,
2036 LDAP_OPT_ERROR_NUMBER,
2037 &cookie->err_rc);
2038 (void) sprintf(errstr,
2039 gettext("LDAP ERROR (%d): %s.\n"),
2040 cookie->err_rc,
2041 gettext(ldap_err2string(cookie->err_rc)));
2042 err = strdup(errstr);
2043 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2045 cookie->err_rc = NS_LDAP_INTERNAL;
2046 cookie->errorp = *errorp;
2047 return (LDAP_ERROR);
2049 if (errCode == LDAP_REFERRAL) {
2050 for (i = 0; referrals[i] != NULL;
2051 i++) {
2052 /* add to referral list */
2053 rc = __s_api_addRefInfo(
2054 &cookie->reflist,
2055 referrals[i],
2056 cookie->basedn,
2057 &cookie->scope,
2058 cookie->filter,
2059 cookie->conn->ld);
2060 if (rc != NS_LDAP_SUCCESS) {
2061 ldap_value_free(
2062 referrals);
2063 if (retCtrls)
2064 ldap_controls_free(
2065 retCtrls);
2066 return (ERROR);
2069 ldap_value_free(referrals);
2070 if (retCtrls)
2071 ldap_controls_free(retCtrls);
2072 return (END_RESULT);
2074 if (retCtrls) {
2075 rc = ldap_parse_virtuallist_control(
2076 cookie->conn->ld, retCtrls,
2077 &target_posp, &list_size, &errCode);
2078 if (rc == LDAP_SUCCESS) {
2080 * AD does not return valid target_posp
2081 * and list_size
2083 if (target_posp != 0 && list_size != 0) {
2084 cookie->index =
2085 target_posp + LISTPAGESIZE;
2086 if (cookie->index > list_size)
2087 finished = 1;
2088 } else {
2089 if (cookie->entryCount < LISTPAGESIZE)
2090 finished = 1;
2091 else
2092 cookie->index +=
2093 cookie->entryCount;
2096 ldap_controls_free(retCtrls);
2097 retCtrls = NULL;
2099 else
2100 finished = 1;
2101 } else if (cookie->listType == SIMPLEPAGECTRLFLAG) {
2102 rc = ldap_parse_result(cookie->conn->ld, cookie->resultMsg,
2103 &errCode, NULL, NULL, &referrals, &retCtrls, 0);
2104 if (rc != LDAP_SUCCESS) {
2105 (void) ldap_get_option(cookie->conn->ld,
2106 LDAP_OPT_ERROR_NUMBER,
2107 &cookie->err_rc);
2108 (void) sprintf(errstr,
2109 gettext("LDAP ERROR (%d): %s.\n"),
2110 cookie->err_rc,
2111 gettext(ldap_err2string(cookie->err_rc)));
2112 err = strdup(errstr);
2113 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2115 cookie->err_rc = NS_LDAP_INTERNAL;
2116 cookie->errorp = *errorp;
2117 return (LDAP_ERROR);
2119 if (errCode == LDAP_REFERRAL) {
2120 for (i = 0; referrals[i] != NULL;
2121 i++) {
2122 /* add to referral list */
2123 rc = __s_api_addRefInfo(
2124 &cookie->reflist,
2125 referrals[i],
2126 cookie->basedn,
2127 &cookie->scope,
2128 cookie->filter,
2129 cookie->conn->ld);
2130 if (rc != NS_LDAP_SUCCESS) {
2131 ldap_value_free(
2132 referrals);
2133 if (retCtrls)
2134 ldap_controls_free(
2135 retCtrls);
2136 return (ERROR);
2139 ldap_value_free(referrals);
2140 if (retCtrls)
2141 ldap_controls_free(retCtrls);
2142 return (END_RESULT);
2144 if (retCtrls) {
2145 if (cookie->ctrlCookie)
2146 ber_bvfree(cookie->ctrlCookie);
2147 cookie->ctrlCookie = NULL;
2148 rc = ldap_parse_page_control(
2149 cookie->conn->ld, retCtrls,
2150 &count, &cookie->ctrlCookie);
2151 if (rc == LDAP_SUCCESS) {
2152 if ((cookie->ctrlCookie == NULL) ||
2153 (cookie->ctrlCookie->bv_val == NULL) ||
2154 (cookie->ctrlCookie->bv_len == 0))
2155 finished = 1;
2157 ldap_controls_free(retCtrls);
2158 retCtrls = NULL;
2160 else
2161 finished = 1;
2163 if (!finished && cookie->listType == VLVCTRLFLAG)
2164 return (NEXT_VLV);
2165 if (!finished && cookie->listType == SIMPLEPAGECTRLFLAG)
2166 return (NEXT_PAGE);
2167 if (finished)
2168 return (END_RESULT);
2169 return (ERROR);
2173 * clear_results(ns_ldap_cookie_t):
2175 * Attempt to obtain remnants of ldap responses and free them. If remnants are
2176 * not obtained within a certain time period tell the server we wish to abandon
2177 * the request.
2179 * Note that we do not initially tell the server to abandon the request as that
2180 * can be an expensive operation for the server, while it is cheap for us to
2181 * just flush the input.
2183 * If something was to remain in libldap queue as a result of some error then
2184 * it would be freed later during drop connection call or when no other
2185 * requests share the connection.
2187 static void
2188 clear_results(ns_ldap_cookie_t *cookie)
2190 int rc;
2191 if (cookie->conn != NULL && cookie->conn->ld != NULL &&
2192 (cookie->connectionId != -1 ||
2193 (cookie->conn_user != NULL &&
2194 cookie->conn_user->conn_mt != NULL)) &&
2195 cookie->msgId != 0) {
2197 * We need to cleanup the rest of response (if there is such)
2198 * and LDAP abandon is too heavy for LDAP servers, so we will
2199 * wait for the rest of response till timeout and "process" it.
2201 rc = ldap_result(cookie->conn->ld, cookie->msgId, LDAP_MSG_ALL,
2202 (struct timeval *)&cookie->search_timeout,
2203 &cookie->resultMsg);
2204 if (rc != -1 && rc != 0 && cookie->resultMsg != NULL) {
2205 (void) ldap_msgfree(cookie->resultMsg);
2206 cookie->resultMsg = NULL;
2210 * If there was timeout then we will send ABANDON request to
2211 * LDAP server to decrease load.
2213 if (rc == 0)
2214 (void) ldap_abandon_ext(cookie->conn->ld, cookie->msgId,
2215 NULL, NULL);
2216 /* Disassociate cookie with msgId */
2217 cookie->msgId = 0;
2222 * This state machine performs one or more LDAP searches to a given
2223 * directory server using service search descriptors and schema
2224 * mapping as appropriate. The approximate pseudocode for
2225 * this routine is the following:
2226 * Given the current configuration [set/reset connection etc.]
2227 * and the current service search descriptor list
2228 * or default search filter parameters
2229 * foreach (service search filter) {
2230 * initialize the filter [via filter_init if appropriate]
2231 * get a valid session/connection (preferably the current one)
2232 * Recover if the connection is lost
2233 * perform the search
2234 * foreach (result entry) {
2235 * process result [via callback if appropriate]
2236 * save result for caller if accepted.
2237 * exit and return all collected if allResults found;
2240 * return collected results and exit
2243 static
2244 ns_state_t
2245 search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
2247 char errstr[MAXERROR];
2248 char *err;
2249 int rc, ret;
2250 int rc_save;
2251 ns_ldap_entry_t *nextEntry;
2252 ns_ldap_error_t *error = NULL;
2253 ns_ldap_error_t **errorp;
2254 struct timeval tv;
2256 errorp = &error;
2257 cookie->state = state;
2258 errstr[0] = '\0';
2260 for (;;) {
2261 switch (cookie->state) {
2262 case CLEAR_RESULTS:
2263 clear_results(cookie);
2264 cookie->new_state = EXIT;
2265 break;
2266 case GET_ACCT_MGMT_INFO:
2268 * Set the flag to get ldap account management controls.
2270 cookie->nopasswd_acct_mgmt = 1;
2271 cookie->new_state = INIT;
2272 break;
2273 case EXIT:
2274 /* state engine/connection cleaned up in delete */
2275 if (cookie->attribute) {
2276 __s_api_free2dArray(cookie->attribute);
2277 cookie->attribute = NULL;
2279 if (cookie->reflist) {
2280 __s_api_deleteRefInfo(cookie->reflist);
2281 cookie->reflist = NULL;
2283 return (EXIT);
2284 case INIT:
2285 cookie->sdpos = NULL;
2286 cookie->new_state = NEXT_SEARCH_DESCRIPTOR;
2287 if (cookie->attribute) {
2288 __s_api_free2dArray(cookie->attribute);
2289 cookie->attribute = NULL;
2291 if ((cookie->i_flags & NS_LDAP_NOMAP) == 0 &&
2292 cookie->i_attr) {
2293 cookie->attribute =
2294 __ns_ldap_mapAttributeList(
2295 cookie->service,
2296 cookie->i_attr);
2298 break;
2299 case REINIT:
2300 /* Check if we've reached MAX retries. */
2301 cookie->retries++;
2302 if (cookie->retries > NS_LIST_TRY_MAX - 1) {
2303 cookie->new_state = LDAP_ERROR;
2304 break;
2308 * Even if we still have retries left, check
2309 * if retry is possible.
2311 if (cookie->conn_user != NULL) {
2312 int retry;
2313 ns_conn_mgmt_t *cmg;
2314 cmg = cookie->conn_user->conn_mgmt;
2315 retry = cookie->conn_user->retry;
2316 if (cmg != NULL && cmg->cfg_reloaded == 1)
2317 retry = 1;
2318 if (retry == 0) {
2319 cookie->new_state = LDAP_ERROR;
2320 break;
2324 * Free results if any, reset to the first
2325 * search descriptor and start a new session.
2327 if (cookie->resultMsg != NULL) {
2328 (void) ldap_msgfree(cookie->resultMsg);
2329 cookie->resultMsg = NULL;
2331 (void) __ns_ldap_freeError(&cookie->errorp);
2332 (void) __ns_ldap_freeResult(&cookie->result);
2333 cookie->sdpos = cookie->sdlist;
2334 cookie->err_from_result = 0;
2335 cookie->err_rc = 0;
2336 cookie->new_state = NEXT_SESSION;
2337 break;
2338 case NEXT_SEARCH_DESCRIPTOR:
2339 /* get next search descriptor */
2340 if (cookie->sdpos == NULL) {
2341 cookie->sdpos = cookie->sdlist;
2342 cookie->new_state = GET_SESSION;
2343 } else {
2344 cookie->sdpos++;
2345 cookie->new_state = NEXT_SEARCH;
2347 if (*cookie->sdpos == NULL)
2348 cookie->new_state = EXIT;
2349 break;
2350 case GET_SESSION:
2351 if (get_current_session(cookie) < 0)
2352 cookie->new_state = NEXT_SESSION;
2353 else
2354 cookie->new_state = NEXT_SEARCH;
2355 break;
2356 case NEXT_SESSION:
2357 if (get_next_session(cookie) < 0)
2358 cookie->new_state = RESTART_SESSION;
2359 else
2360 cookie->new_state = NEXT_SEARCH;
2361 break;
2362 case RESTART_SESSION:
2363 if (cookie->i_flags & NS_LDAP_HARD) {
2364 cookie->new_state = NEXT_SESSION;
2365 break;
2367 (void) sprintf(errstr,
2368 gettext("Session error no available conn.\n"),
2369 state);
2370 err = strdup(errstr);
2371 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2373 cookie->err_rc = NS_LDAP_INTERNAL;
2374 cookie->errorp = *errorp;
2375 cookie->new_state = EXIT;
2376 break;
2377 case NEXT_SEARCH:
2378 /* setup referrals search if necessary */
2379 if (cookie->refpos) {
2380 if (setup_referral_search(cookie) < 0) {
2381 cookie->new_state = EXIT;
2382 break;
2384 } else if (setup_next_search(cookie) < 0) {
2385 cookie->new_state = EXIT;
2386 break;
2388 /* only do VLV/PAGE on scopes onelevel/subtree */
2389 if (paging_supported(cookie)) {
2390 if (cookie->use_paging &&
2391 (cookie->scope != LDAP_SCOPE_BASE)) {
2392 cookie->index = 1;
2393 if (cookie->listType == VLVCTRLFLAG)
2394 cookie->new_state = NEXT_VLV;
2395 else
2396 cookie->new_state = NEXT_PAGE;
2397 break;
2400 cookie->new_state = ONE_SEARCH;
2401 break;
2402 case NEXT_VLV:
2403 rc = setup_vlv_params(cookie);
2404 if (rc != LDAP_SUCCESS) {
2405 cookie->err_rc = rc;
2406 cookie->new_state = LDAP_ERROR;
2407 break;
2409 cookie->next_state = MULTI_RESULT;
2410 cookie->new_state = DO_SEARCH;
2411 break;
2412 case NEXT_PAGE:
2413 rc = setup_simplepg_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 ONE_SEARCH:
2423 cookie->next_state = NEXT_RESULT;
2424 cookie->new_state = DO_SEARCH;
2425 break;
2426 case DO_SEARCH:
2427 cookie->entryCount = 0;
2428 rc = ldap_search_ext(cookie->conn->ld,
2429 cookie->basedn,
2430 cookie->scope,
2431 cookie->filter,
2432 cookie->attribute,
2434 cookie->p_serverctrls,
2435 NULL,
2436 &cookie->search_timeout, 0,
2437 &cookie->msgId);
2438 if (rc != LDAP_SUCCESS) {
2439 if (rc == LDAP_BUSY ||
2440 rc == LDAP_UNAVAILABLE ||
2441 rc == LDAP_UNWILLING_TO_PERFORM ||
2442 rc == LDAP_CONNECT_ERROR ||
2443 rc == LDAP_SERVER_DOWN) {
2445 if (cookie->reinit_on_retriable_err) {
2446 cookie->err_rc = rc;
2447 cookie->new_state = REINIT;
2448 } else
2449 cookie->new_state =
2450 NEXT_SESSION;
2453 * If not able to reach the
2454 * server, inform the ldap
2455 * cache manager that the
2456 * server should be removed
2457 * from it's server list.
2458 * Thus, the manager will not
2459 * return this server on the next
2460 * get-server request and will
2461 * also reduce the server list
2462 * refresh TTL, so that it will
2463 * find out sooner when the server
2464 * is up again.
2466 if ((rc == LDAP_CONNECT_ERROR ||
2467 rc == LDAP_SERVER_DOWN) &&
2468 (cookie->conn_user == NULL ||
2469 cookie->conn_user->conn_mt ==
2470 NULL)) {
2471 ret = __s_api_removeServer(
2472 cookie->conn->serverAddr);
2473 if (ret == NS_CACHE_NOSERVER &&
2474 cookie->conn_auth_type
2475 == NS_LDAP_AUTH_NONE) {
2477 * Couldn't remove
2478 * server from server
2479 * list.
2480 * Exit to avoid
2481 * potential infinite
2482 * loop.
2484 cookie->err_rc = rc;
2485 cookie->new_state =
2486 LDAP_ERROR;
2488 if (cookie->connectionId > -1) {
2490 * NS_LDAP_NEW_CONN
2491 * indicates that the
2492 * connection should
2493 * be deleted, not
2494 * kept alive
2496 DropConnection(
2497 cookie->
2498 connectionId,
2499 NS_LDAP_NEW_CONN);
2500 cookie->connectionId =
2503 } else if ((rc == LDAP_CONNECT_ERROR ||
2504 rc == LDAP_SERVER_DOWN) &&
2505 cookie->conn_user != NULL) {
2506 if (cookie->
2507 reinit_on_retriable_err) {
2509 * MT connection not
2510 * usable, close it
2511 * before REINIT.
2512 * rc has already
2513 * been saved in
2514 * cookie->err_rc above.
2516 __s_api_conn_mt_close(
2517 cookie->conn_user,
2519 &cookie->errorp);
2520 } else {
2522 * MT connection not
2523 * usable, close it in
2524 * the LDAP_ERROR state.
2525 * A retry will be done
2526 * next if allowed.
2528 cookie->err_rc = rc;
2529 cookie->new_state =
2530 LDAP_ERROR;
2533 break;
2535 cookie->err_rc = rc;
2536 cookie->new_state = LDAP_ERROR;
2537 break;
2539 cookie->new_state = cookie->next_state;
2540 break;
2541 case NEXT_RESULT:
2543 * Caller (e.g. __ns_ldap_list_batch_add)
2544 * does not want to block on ldap_result().
2545 * Therefore we execute ldap_result() with
2546 * a zeroed timeval.
2548 if (cookie->no_wait == B_TRUE)
2549 (void) memset(&tv, 0, sizeof (tv));
2550 else
2551 tv = cookie->search_timeout;
2552 rc = ldap_result(cookie->conn->ld, cookie->msgId,
2553 LDAP_MSG_ONE,
2554 &tv,
2555 &cookie->resultMsg);
2556 if (rc == LDAP_RES_SEARCH_RESULT) {
2557 cookie->new_state = END_RESULT;
2558 /* check and process referrals info */
2559 if (cookie->followRef)
2560 proc_result_referrals(
2561 cookie);
2562 (void) ldap_msgfree(cookie->resultMsg);
2563 cookie->resultMsg = NULL;
2564 break;
2566 /* handle referrals if necessary */
2567 if (rc == LDAP_RES_SEARCH_REFERENCE) {
2568 if (cookie->followRef)
2569 proc_search_references(cookie);
2570 (void) ldap_msgfree(cookie->resultMsg);
2571 cookie->resultMsg = NULL;
2572 break;
2574 if (rc != LDAP_RES_SEARCH_ENTRY) {
2575 switch (rc) {
2576 case 0:
2577 if (cookie->no_wait == B_TRUE) {
2578 (void) ldap_msgfree(
2579 cookie->resultMsg);
2580 cookie->resultMsg = NULL;
2581 return (cookie->new_state);
2583 rc = LDAP_TIMEOUT;
2584 break;
2585 case -1:
2586 rc = ldap_get_lderrno(cookie->conn->ld,
2587 NULL, NULL);
2588 break;
2589 default:
2590 rc = ldap_result2error(cookie->conn->ld,
2591 cookie->resultMsg, 1);
2592 break;
2594 if ((rc == LDAP_TIMEOUT ||
2595 rc == LDAP_SERVER_DOWN) &&
2596 (cookie->conn_user == NULL ||
2597 cookie->conn_user->conn_mt == NULL)) {
2598 if (rc == LDAP_TIMEOUT)
2599 (void) __s_api_removeServer(
2600 cookie->conn->serverAddr);
2601 if (cookie->connectionId > -1) {
2602 DropConnection(
2603 cookie->connectionId,
2604 NS_LDAP_NEW_CONN);
2605 cookie->connectionId = -1;
2607 cookie->err_from_result = 1;
2609 (void) ldap_msgfree(cookie->resultMsg);
2610 cookie->resultMsg = NULL;
2611 if (rc == LDAP_BUSY ||
2612 rc == LDAP_UNAVAILABLE ||
2613 rc == LDAP_UNWILLING_TO_PERFORM) {
2614 if (cookie->reinit_on_retriable_err) {
2615 cookie->err_rc = rc;
2616 cookie->err_from_result = 1;
2617 cookie->new_state = REINIT;
2618 } else
2619 cookie->new_state =
2620 NEXT_SESSION;
2621 break;
2623 if ((rc == LDAP_CONNECT_ERROR ||
2624 rc == LDAP_SERVER_DOWN) &&
2625 cookie->reinit_on_retriable_err) {
2626 ns_ldap_error_t *errorp = NULL;
2627 cookie->err_rc = rc;
2628 cookie->err_from_result = 1;
2629 cookie->new_state = REINIT;
2630 if (cookie->conn_user != NULL)
2631 __s_api_conn_mt_close(
2632 cookie->conn_user,
2633 rc, &errorp);
2634 if (errorp != NULL) {
2635 (void) __ns_ldap_freeError(
2636 &cookie->errorp);
2637 cookie->errorp = errorp;
2639 break;
2641 cookie->err_rc = rc;
2642 cookie->new_state = LDAP_ERROR;
2643 break;
2645 /* else LDAP_RES_SEARCH_ENTRY */
2646 /* get account management response control */
2647 if (cookie->nopasswd_acct_mgmt == 1) {
2648 rc = ldap_get_entry_controls(cookie->conn->ld,
2649 cookie->resultMsg,
2650 &(cookie->resultctrl));
2651 if (rc != LDAP_SUCCESS) {
2652 cookie->new_state = LDAP_ERROR;
2653 cookie->err_rc = rc;
2654 break;
2657 rc = __s_api_getEntry(cookie);
2658 (void) ldap_msgfree(cookie->resultMsg);
2659 cookie->resultMsg = NULL;
2660 if (rc != NS_LDAP_SUCCESS) {
2661 cookie->new_state = LDAP_ERROR;
2662 break;
2664 cookie->new_state = PROCESS_RESULT;
2665 cookie->next_state = NEXT_RESULT;
2666 break;
2667 case MULTI_RESULT:
2668 if (cookie->no_wait == B_TRUE)
2669 (void) memset(&tv, 0, sizeof (tv));
2670 else
2671 tv = cookie->search_timeout;
2672 rc = ldap_result(cookie->conn->ld, cookie->msgId,
2673 LDAP_MSG_ONE,
2674 &tv,
2675 &cookie->resultMsg);
2676 if (rc == LDAP_RES_SEARCH_RESULT) {
2677 rc = ldap_result2error(cookie->conn->ld,
2678 cookie->resultMsg, 0);
2679 if (rc == LDAP_ADMINLIMIT_EXCEEDED &&
2680 cookie->listType == VLVCTRLFLAG &&
2681 cookie->sortTypeTry == SSS_SINGLE_ATTR) {
2682 /* Try old "cn uid" server side sort */
2683 cookie->sortTypeTry = SSS_CN_UID_ATTRS;
2684 cookie->new_state = NEXT_VLV;
2685 (void) ldap_msgfree(cookie->resultMsg);
2686 cookie->resultMsg = NULL;
2687 break;
2689 if (rc != LDAP_SUCCESS) {
2690 cookie->err_rc = rc;
2691 cookie->new_state = LDAP_ERROR;
2692 (void) ldap_msgfree(cookie->resultMsg);
2693 cookie->resultMsg = NULL;
2694 break;
2696 cookie->new_state = multi_result(cookie);
2697 (void) ldap_msgfree(cookie->resultMsg);
2698 cookie->resultMsg = NULL;
2699 break;
2701 /* handle referrals if necessary */
2702 if (rc == LDAP_RES_SEARCH_REFERENCE &&
2703 cookie->followRef) {
2704 proc_search_references(cookie);
2705 (void) ldap_msgfree(cookie->resultMsg);
2706 cookie->resultMsg = NULL;
2707 break;
2709 if (rc != LDAP_RES_SEARCH_ENTRY) {
2710 switch (rc) {
2711 case 0:
2712 if (cookie->no_wait == B_TRUE) {
2713 (void) ldap_msgfree(
2714 cookie->resultMsg);
2715 cookie->resultMsg = NULL;
2716 return (cookie->new_state);
2718 rc = LDAP_TIMEOUT;
2719 break;
2720 case -1:
2721 rc = ldap_get_lderrno(cookie->conn->ld,
2722 NULL, NULL);
2723 break;
2724 default:
2725 rc = ldap_result2error(cookie->conn->ld,
2726 cookie->resultMsg, 1);
2727 break;
2729 if ((rc == LDAP_TIMEOUT ||
2730 rc == LDAP_SERVER_DOWN) &&
2731 (cookie->conn_user == NULL ||
2732 cookie->conn_user->conn_mt == NULL)) {
2733 if (rc == LDAP_TIMEOUT)
2734 (void) __s_api_removeServer(
2735 cookie->conn->serverAddr);
2736 if (cookie->connectionId > -1) {
2737 DropConnection(
2738 cookie->connectionId,
2739 NS_LDAP_NEW_CONN);
2740 cookie->connectionId = -1;
2742 cookie->err_from_result = 1;
2744 (void) ldap_msgfree(cookie->resultMsg);
2745 cookie->resultMsg = NULL;
2746 if (rc == LDAP_BUSY ||
2747 rc == LDAP_UNAVAILABLE ||
2748 rc == LDAP_UNWILLING_TO_PERFORM) {
2749 if (cookie->reinit_on_retriable_err) {
2750 cookie->err_rc = rc;
2751 cookie->err_from_result = 1;
2752 cookie->new_state = REINIT;
2753 } else
2754 cookie->new_state =
2755 NEXT_SESSION;
2756 break;
2759 if ((rc == LDAP_CONNECT_ERROR ||
2760 rc == LDAP_SERVER_DOWN) &&
2761 cookie->reinit_on_retriable_err) {
2762 ns_ldap_error_t *errorp = NULL;
2763 cookie->err_rc = rc;
2764 cookie->err_from_result = 1;
2765 cookie->new_state = REINIT;
2766 if (cookie->conn_user != NULL)
2767 __s_api_conn_mt_close(
2768 cookie->conn_user,
2769 rc, &errorp);
2770 if (errorp != NULL) {
2771 (void) __ns_ldap_freeError(
2772 &cookie->errorp);
2773 cookie->errorp = errorp;
2775 break;
2777 cookie->err_rc = rc;
2778 cookie->new_state = LDAP_ERROR;
2779 break;
2781 /* else LDAP_RES_SEARCH_ENTRY */
2782 cookie->entryCount++;
2783 rc = __s_api_getEntry(cookie);
2784 (void) ldap_msgfree(cookie->resultMsg);
2785 cookie->resultMsg = NULL;
2786 if (rc != NS_LDAP_SUCCESS) {
2787 cookie->new_state = LDAP_ERROR;
2788 break;
2791 * If VLV search was successfull save the server
2792 * side sort type tried.
2794 if (cookie->listType == VLVCTRLFLAG)
2795 update_srvsidesort_type(cookie->service,
2796 cookie->sortTypeTry);
2798 cookie->new_state = PROCESS_RESULT;
2799 cookie->next_state = MULTI_RESULT;
2800 break;
2801 case PROCESS_RESULT:
2802 /* NOTE THIS STATE MAY BE PROCESSED BY CALLER */
2803 if (cookie->use_usercb && cookie->callback) {
2804 rc = 0;
2805 for (nextEntry = cookie->result->entry;
2806 nextEntry != NULL;
2807 nextEntry = nextEntry->next) {
2808 rc = (*cookie->callback)(nextEntry,
2809 cookie->userdata);
2811 if (rc == NS_LDAP_CB_DONE) {
2812 /* cb doesn't want any more data */
2813 rc = NS_LDAP_PARTIAL;
2814 cookie->err_rc = rc;
2815 break;
2816 } else if (rc != NS_LDAP_CB_NEXT) {
2817 /* invalid return code */
2818 rc = NS_LDAP_OP_FAILED;
2819 cookie->err_rc = rc;
2820 break;
2823 (void) __ns_ldap_freeResult(&cookie->result);
2824 cookie->result = NULL;
2826 if (rc != 0) {
2827 cookie->new_state = EXIT;
2828 break;
2830 /* NOTE PREVIOUS STATE SPECIFIES NEXT STATE */
2831 cookie->new_state = cookie->next_state;
2832 break;
2833 case END_PROCESS_RESULT:
2834 cookie->new_state = cookie->next_state;
2835 break;
2836 case END_RESULT:
2838 * XXX DO WE NEED THIS CASE?
2839 * if (search is complete) {
2840 * cookie->new_state = EXIT;
2841 * } else
2844 * entering referral mode if necessary
2846 if (cookie->followRef && cookie->reflist)
2847 cookie->new_state =
2848 NEXT_REFERRAL;
2849 else
2850 cookie->new_state =
2851 NEXT_SEARCH_DESCRIPTOR;
2852 break;
2853 case NEXT_REFERRAL:
2854 /* get next referral info */
2855 if (cookie->refpos == NULL)
2856 cookie->refpos =
2857 cookie->reflist;
2858 else
2859 cookie->refpos =
2860 cookie->refpos->next;
2861 /* check see if done with all referrals */
2862 if (cookie->refpos != NULL)
2863 cookie->new_state =
2864 GET_REFERRAL_SESSION;
2865 else {
2866 __s_api_deleteRefInfo(cookie->reflist);
2867 cookie->reflist = NULL;
2868 cookie->new_state =
2869 NEXT_SEARCH_DESCRIPTOR;
2870 if (cookie->conn_user != NULL)
2871 cookie->conn_user->referral = B_FALSE;
2873 break;
2874 case GET_REFERRAL_SESSION:
2875 if (get_referral_session(cookie) < 0)
2876 cookie->new_state = EXIT;
2877 else {
2878 cookie->new_state = NEXT_SEARCH;
2880 break;
2881 case LDAP_ERROR:
2882 rc_save = cookie->err_rc;
2883 if (cookie->err_from_result) {
2884 if (cookie->err_rc == LDAP_SERVER_DOWN) {
2885 (void) sprintf(errstr,
2886 gettext("LDAP ERROR (%d): "
2887 "Error occurred during"
2888 " receiving results. "
2889 "Connection to server lost."),
2890 cookie->err_rc);
2891 } else if (cookie->err_rc == LDAP_TIMEOUT) {
2892 (void) sprintf(errstr,
2893 gettext("LDAP ERROR (%d): "
2894 "Error occurred during"
2895 " receiving results. %s"
2896 "."), cookie->err_rc,
2897 ldap_err2string(
2898 cookie->err_rc));
2900 } else
2901 (void) sprintf(errstr,
2902 gettext("LDAP ERROR (%d): %s."),
2903 cookie->err_rc,
2904 ldap_err2string(cookie->err_rc));
2905 err = strdup(errstr);
2906 if (cookie->err_from_result) {
2907 if (cookie->err_rc == LDAP_SERVER_DOWN) {
2908 MKERROR(LOG_INFO, *errorp,
2909 cookie->err_rc, err, 0);
2910 } else {
2911 MKERROR(LOG_WARNING, *errorp,
2912 cookie->err_rc, err, 0);
2914 } else {
2915 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2916 err, 0);
2918 cookie->err_rc = NS_LDAP_INTERNAL;
2919 cookie->errorp = *errorp;
2920 if (cookie->conn_user != NULL) {
2921 if (rc_save == LDAP_SERVER_DOWN ||
2922 rc_save == LDAP_CONNECT_ERROR) {
2924 * MT connection is not usable,
2925 * close it.
2927 __s_api_conn_mt_close(cookie->conn_user,
2928 rc_save, &cookie->errorp);
2929 return (ERROR);
2932 return (ERROR);
2933 default:
2934 case ERROR:
2935 (void) sprintf(errstr,
2936 gettext("Internal State machine exit (%d).\n"),
2937 cookie->state);
2938 err = strdup(errstr);
2939 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2941 cookie->err_rc = NS_LDAP_INTERNAL;
2942 cookie->errorp = *errorp;
2943 return (ERROR);
2946 if (cookie->conn_user != NULL &&
2947 cookie->conn_user->bad_mt_conn == B_TRUE) {
2948 __s_api_conn_mt_close(cookie->conn_user, 0, NULL);
2949 cookie->err_rc = cookie->conn_user->ns_rc;
2950 cookie->errorp = cookie->conn_user->ns_error;
2951 cookie->conn_user->ns_error = NULL;
2952 return (ERROR);
2955 if (cycle == ONE_STEP) {
2956 return (cookie->new_state);
2958 cookie->state = cookie->new_state;
2960 /*NOTREACHED*/
2961 #if 0
2962 (void) sprintf(errstr,
2963 gettext("Unexpected State machine error.\n"));
2964 err = strdup(errstr);
2965 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err, NULL);
2966 cookie->err_rc = NS_LDAP_INTERNAL;
2967 cookie->errorp = *errorp;
2968 return (ERROR);
2969 #endif
2973 * For a lookup of shadow data, if shadow update is enabled,
2974 * check the calling process' privilege to ensure it's
2975 * allowed to perform such operation.
2977 static int
2978 check_shadow(ns_ldap_cookie_t *cookie, const char *service)
2980 char errstr[MAXERROR];
2981 char *err;
2982 boolean_t priv;
2983 /* caller */
2984 priv_set_t *ps;
2985 /* zone */
2986 priv_set_t *zs;
2989 * If service is "shadow", we may need
2990 * to use privilege credentials.
2992 if ((strcmp(service, "shadow") == 0) &&
2993 __ns_ldap_is_shadow_update_enabled()) {
2995 * Since we release admin credentials after
2996 * connection is closed and we do not cache
2997 * them, we allow any root or all zone
2998 * privilege process to read shadow data.
3000 priv = (geteuid() == 0);
3001 if (!priv) {
3002 /* caller */
3003 ps = priv_allocset();
3005 (void) getppriv(PRIV_EFFECTIVE, ps);
3006 zs = priv_str_to_set("zone", ",", NULL);
3007 priv = priv_isequalset(ps, zs);
3008 priv_freeset(ps);
3009 priv_freeset(zs);
3011 if (!priv) {
3012 (void) sprintf(errstr,
3013 gettext("Permission denied"));
3014 err = strdup(errstr);
3015 if (err == NULL)
3016 return (NS_LDAP_MEMORY);
3017 MKERROR(LOG_INFO, cookie->errorp, NS_LDAP_INTERNAL, err,
3019 return (NS_LDAP_INTERNAL);
3021 cookie->i_flags |= NS_LDAP_READ_SHADOW;
3023 * We do not want to reuse connection (hence
3024 * keep it open) with admin credentials.
3025 * If NS_LDAP_KEEP_CONN is set, reject the
3026 * request.
3028 if (cookie->i_flags & NS_LDAP_KEEP_CONN)
3029 return (NS_LDAP_INVALID_PARAM);
3030 cookie->i_flags |= NS_LDAP_NEW_CONN;
3033 return (NS_LDAP_SUCCESS);
3037 * internal function for __ns_ldap_list
3039 static int
3040 ldap_list(
3041 ns_ldap_list_batch_t *batch,
3042 const char *service,
3043 const char *filter,
3044 const char *sortattr,
3045 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3046 char **realfilter, const void *userdata),
3047 const char * const *attribute,
3048 const ns_cred_t *auth,
3049 const int flags,
3050 ns_ldap_result_t **rResult, /* return result entries */
3051 ns_ldap_error_t **errorp,
3052 int *rcp,
3053 int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3054 const void *userdata, ns_conn_user_t *conn_user)
3056 ns_ldap_cookie_t *cookie;
3057 ns_ldap_search_desc_t **sdlist = NULL;
3058 ns_ldap_search_desc_t *dptr;
3059 ns_ldap_error_t *error = NULL;
3060 char **dns = NULL;
3061 int scope;
3062 int rc;
3063 int from_result;
3065 *errorp = NULL;
3066 *rResult = NULL;
3067 *rcp = NS_LDAP_SUCCESS;
3070 * Sanity check - NS_LDAP_READ_SHADOW is for our
3071 * own internal use.
3073 if (flags & NS_LDAP_READ_SHADOW)
3074 return (NS_LDAP_INVALID_PARAM);
3076 /* Initialize State machine cookie */
3077 cookie = init_search_state_machine();
3078 if (cookie == NULL) {
3079 *rcp = NS_LDAP_MEMORY;
3080 return (NS_LDAP_MEMORY);
3082 cookie->conn_user = conn_user;
3084 /* see if need to follow referrals */
3085 rc = __s_api_toFollowReferrals(flags,
3086 &cookie->followRef, errorp);
3087 if (rc != NS_LDAP_SUCCESS) {
3088 delete_search_cookie(cookie);
3089 *rcp = rc;
3090 return (rc);
3093 /* get the service descriptor - or create a default one */
3094 rc = __s_api_get_SSD_from_SSDtoUse_service(service,
3095 &sdlist, &error);
3096 if (rc != NS_LDAP_SUCCESS) {
3097 delete_search_cookie(cookie);
3098 *errorp = error;
3099 *rcp = rc;
3100 return (rc);
3103 if (sdlist == NULL) {
3104 /* Create default service Desc */
3105 sdlist = (ns_ldap_search_desc_t **)calloc(2,
3106 sizeof (ns_ldap_search_desc_t *));
3107 if (sdlist == NULL) {
3108 delete_search_cookie(cookie);
3109 cookie = NULL;
3110 *rcp = NS_LDAP_MEMORY;
3111 return (NS_LDAP_MEMORY);
3113 dptr = (ns_ldap_search_desc_t *)
3114 calloc(1, sizeof (ns_ldap_search_desc_t));
3115 if (dptr == NULL) {
3116 free(sdlist);
3117 delete_search_cookie(cookie);
3118 cookie = NULL;
3119 *rcp = NS_LDAP_MEMORY;
3120 return (NS_LDAP_MEMORY);
3122 sdlist[0] = dptr;
3124 /* default base */
3125 rc = __s_api_getDNs(&dns, service, &cookie->errorp);
3126 if (rc != NS_LDAP_SUCCESS) {
3127 if (dns) {
3128 __s_api_free2dArray(dns);
3129 dns = NULL;
3131 *errorp = cookie->errorp;
3132 cookie->errorp = NULL;
3133 delete_search_cookie(cookie);
3134 cookie = NULL;
3135 *rcp = rc;
3136 return (rc);
3138 dptr->basedn = strdup(dns[0]);
3139 __s_api_free2dArray(dns);
3140 dns = NULL;
3142 /* default scope */
3143 scope = 0;
3144 rc = __s_api_getSearchScope(&scope, &cookie->errorp);
3145 dptr->scope = scope;
3148 cookie->sdlist = sdlist;
3151 * use VLV/PAGE control only if NS_LDAP_PAGE_CTRL is set
3153 if (flags & NS_LDAP_PAGE_CTRL)
3154 cookie->use_paging = TRUE;
3155 else
3156 cookie->use_paging = FALSE;
3158 /* Set up other arguments */
3159 cookie->userdata = userdata;
3160 if (init_filter_cb != NULL) {
3161 cookie->init_filter_cb = init_filter_cb;
3162 cookie->use_filtercb = 1;
3164 if (callback != NULL) {
3165 cookie->callback = callback;
3166 cookie->use_usercb = 1;
3169 /* check_shadow() may add extra value to cookie->i_flags */
3170 cookie->i_flags = flags;
3171 if (service) {
3172 cookie->service = strdup(service);
3173 if (cookie->service == NULL) {
3174 delete_search_cookie(cookie);
3175 cookie = NULL;
3176 *rcp = NS_LDAP_MEMORY;
3177 return (NS_LDAP_MEMORY);
3181 * If given, use the credential given by the caller, and
3182 * skip the credential check required for shadow update.
3184 if (auth == NULL) {
3185 rc = check_shadow(cookie, service);
3186 if (rc != NS_LDAP_SUCCESS) {
3187 *errorp = cookie->errorp;
3188 cookie->errorp = NULL;
3189 delete_search_cookie(cookie);
3190 cookie = NULL;
3191 *rcp = rc;
3192 return (rc);
3197 cookie->i_filter = strdup(filter);
3198 cookie->i_attr = attribute;
3199 cookie->i_auth = auth;
3200 cookie->i_sortattr = sortattr;
3202 if (batch != NULL) {
3203 cookie->batch = batch;
3204 cookie->reinit_on_retriable_err = B_TRUE;
3205 cookie->no_wait = B_TRUE;
3206 (void) search_state_machine(cookie, INIT, 0);
3207 cookie->no_wait = B_FALSE;
3208 rc = cookie->err_rc;
3210 if (rc == NS_LDAP_SUCCESS) {
3212 * Here rc == NS_LDAP_SUCCESS means that the state
3213 * machine init'ed successfully. The actual status
3214 * of the search will be determined by
3215 * __ns_ldap_list_batch_end(). Add the cookie to our
3216 * batch.
3218 cookie->caller_result = rResult;
3219 cookie->caller_errorp = errorp;
3220 cookie->caller_rc = rcp;
3221 cookie->next_cookie_in_batch = batch->cookie_list;
3222 batch->cookie_list = cookie;
3223 batch->nactive++;
3224 return (rc);
3227 * If state machine init failed then copy error to the caller
3228 * and delete the cookie.
3230 } else {
3231 (void) search_state_machine(cookie, INIT, 0);
3234 /* Copy results back to user */
3235 rc = cookie->err_rc;
3236 if (rc != NS_LDAP_SUCCESS) {
3237 if (conn_user != NULL && conn_user->ns_error != NULL) {
3238 *errorp = conn_user->ns_error;
3239 conn_user->ns_error = NULL;
3240 } else
3241 *errorp = cookie->errorp;
3243 *rResult = cookie->result;
3244 from_result = cookie->err_from_result;
3246 cookie->errorp = NULL;
3247 cookie->result = NULL;
3248 delete_search_cookie(cookie);
3249 cookie = NULL;
3251 if (from_result == 0 && *rResult == NULL)
3252 rc = NS_LDAP_NOTFOUND;
3253 *rcp = rc;
3254 return (rc);
3259 * __ns_ldap_list performs one or more LDAP searches to a given
3260 * directory server using service search descriptors and schema
3261 * mapping as appropriate. The operation may be retried a
3262 * couple of times in error situations.
3265 __ns_ldap_list(
3266 const char *service,
3267 const char *filter,
3268 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3269 char **realfilter, const void *userdata),
3270 const char * const *attribute,
3271 const ns_cred_t *auth,
3272 const int flags,
3273 ns_ldap_result_t **rResult, /* return result entries */
3274 ns_ldap_error_t **errorp,
3275 int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3276 const void *userdata)
3278 int mod_flags;
3280 * Strip the NS_LDAP_PAGE_CTRL option as this interface does not
3281 * support this. If you want to use this option call the API
3282 * __ns_ldap_list_sort() with has the sort attribute.
3284 mod_flags = flags & (~NS_LDAP_PAGE_CTRL);
3286 return (__ns_ldap_list_sort(service, filter, NULL, init_filter_cb,
3287 attribute, auth, mod_flags, rResult, errorp,
3288 callback, userdata));
3292 * __ns_ldap_list_sort performs one or more LDAP searches to a given
3293 * directory server using service search descriptors and schema
3294 * mapping as appropriate. The operation may be retried a
3295 * couple of times in error situations.
3298 __ns_ldap_list_sort(
3299 const char *service,
3300 const char *filter,
3301 const char *sortattr,
3302 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3303 char **realfilter, const void *userdata),
3304 const char * const *attribute,
3305 const ns_cred_t *auth,
3306 const int flags,
3307 ns_ldap_result_t **rResult, /* return result entries */
3308 ns_ldap_error_t **errorp,
3309 int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3310 const void *userdata)
3312 ns_conn_user_t *cu = NULL;
3313 int try_cnt = 0;
3314 int rc = NS_LDAP_SUCCESS, trc;
3316 for (;;) {
3317 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
3318 &try_cnt, &rc, errorp) == 0)
3319 break;
3320 rc = ldap_list(NULL, service, filter, sortattr, init_filter_cb,
3321 attribute, auth, flags, rResult, errorp, &trc, callback,
3322 userdata, cu);
3325 return (rc);
3329 * Create and initialize batch for native LDAP lookups
3332 __ns_ldap_list_batch_start(ns_ldap_list_batch_t **batch)
3334 *batch = calloc(1, sizeof (ns_ldap_list_batch_t));
3335 if (*batch == NULL)
3336 return (NS_LDAP_MEMORY);
3337 return (NS_LDAP_SUCCESS);
3342 * Add a LDAP search request to the batch.
3345 __ns_ldap_list_batch_add(
3346 ns_ldap_list_batch_t *batch,
3347 const char *service,
3348 const char *filter,
3349 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3350 char **realfilter, const void *userdata),
3351 const char * const *attribute,
3352 const ns_cred_t *auth,
3353 const int flags,
3354 ns_ldap_result_t **rResult, /* return result entries */
3355 ns_ldap_error_t **errorp,
3356 int *rcp,
3357 int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3358 const void *userdata)
3360 ns_conn_user_t *cu;
3361 int rc;
3362 int mod_flags;
3364 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, 0);
3365 if (cu == NULL) {
3366 if (rcp != NULL)
3367 *rcp = NS_LDAP_MEMORY;
3368 return (NS_LDAP_MEMORY);
3372 * Strip the NS_LDAP_PAGE_CTRL option as the batch interface does not
3373 * support this.
3375 mod_flags = flags & (~NS_LDAP_PAGE_CTRL);
3377 rc = ldap_list(batch, service, filter, NULL, init_filter_cb, attribute,
3378 auth, mod_flags, rResult, errorp, rcp, callback, userdata, cu);
3381 * Free the conn_user if the cookie was not batched. If the cookie
3382 * was batched then __ns_ldap_list_batch_end or release will free the
3383 * conn_user. The batch API instructs the search_state_machine
3384 * to reinit and retry (max 3 times) on retriable LDAP errors.
3386 if (rc != NS_LDAP_SUCCESS && cu != NULL) {
3387 if (cu->conn_mt != NULL)
3388 __s_api_conn_mt_return(cu);
3389 __s_api_conn_user_free(cu);
3391 return (rc);
3396 * Free batch.
3398 void
3399 __ns_ldap_list_batch_release(ns_ldap_list_batch_t *batch)
3401 ns_ldap_cookie_t *c, *next;
3403 for (c = batch->cookie_list; c != NULL; c = next) {
3404 next = c->next_cookie_in_batch;
3405 if (c->conn_user != NULL) {
3406 if (c->conn_user->conn_mt != NULL)
3407 __s_api_conn_mt_return(c->conn_user);
3408 __s_api_conn_user_free(c->conn_user);
3409 c->conn_user = NULL;
3411 delete_search_cookie(c);
3413 free(batch);
3416 #define LD_USING_STATE(st) \
3417 ((st == DO_SEARCH) || (st == MULTI_RESULT) || (st == NEXT_RESULT))
3420 * Process batch. Everytime this function is called it selects an
3421 * active cookie from the batch and single steps through the
3422 * search_state_machine for the selected cookie. If lookup associated
3423 * with the cookie is complete (success or error) then the cookie is
3424 * removed from the batch and its memory freed.
3426 * Returns 1 (if batch still has active cookies)
3427 * 0 (if batch has no more active cookies)
3428 * -1 (on errors, *rcp will contain the error code)
3430 * The caller should call this function in a loop as long as it returns 1
3431 * to process all the requests added to the batch. The results (and errors)
3432 * will be available in the locations provided by the caller at the time of
3433 * __ns_ldap_list_batch_add().
3435 static
3437 __ns_ldap_list_batch_process(ns_ldap_list_batch_t *batch, int *rcp)
3439 ns_ldap_cookie_t *c, *ptr, **prev;
3440 ns_state_t state;
3441 ns_ldap_error_t *errorp = NULL;
3442 int rc;
3444 /* Check if are already done */
3445 if (batch->nactive == 0)
3446 return (0);
3448 /* Get the next cookie from the batch */
3449 c = (batch->next_cookie == NULL) ?
3450 batch->cookie_list : batch->next_cookie;
3452 batch->next_cookie = c->next_cookie_in_batch;
3455 * Checks the status of the cookie's connection if it needs
3456 * to use that connection for ldap_search_ext or ldap_result.
3457 * If the connection is no longer good but worth retrying
3458 * then reinit the search_state_machine for this cookie
3459 * starting from the first search descriptor. REINIT will
3460 * clear any leftover results if max retries have not been
3461 * reached and redo the search (which may also involve
3462 * following referrals again).
3464 * Note that each cookie in the batch will make this
3465 * determination when it reaches one of the LD_USING_STATES.
3467 if (LD_USING_STATE(c->new_state) && c->conn_user != NULL) {
3468 rc = __s_api_setup_getnext(c->conn_user, &c->err_rc, &errorp);
3469 if (rc == LDAP_BUSY || rc == LDAP_UNAVAILABLE ||
3470 rc == LDAP_UNWILLING_TO_PERFORM) {
3471 if (errorp != NULL) {
3472 (void) __ns_ldap_freeError(&c->errorp);
3473 c->errorp = errorp;
3475 c->new_state = REINIT;
3476 } else if (rc == LDAP_CONNECT_ERROR ||
3477 rc == LDAP_SERVER_DOWN) {
3478 if (errorp != NULL) {
3479 (void) __ns_ldap_freeError(&c->errorp);
3480 c->errorp = errorp;
3482 c->new_state = REINIT;
3484 * MT connection is not usable,
3485 * close it before REINIT.
3487 __s_api_conn_mt_close(
3488 c->conn_user, rc, NULL);
3489 } else if (rc != NS_LDAP_SUCCESS) {
3490 if (rcp != NULL)
3491 *rcp = rc;
3492 *c->caller_result = NULL;
3493 *c->caller_errorp = errorp;
3494 *c->caller_rc = rc;
3495 return (-1);
3499 for (;;) {
3500 /* Single step through the search_state_machine */
3501 state = search_state_machine(c, c->new_state, ONE_STEP);
3502 switch (state) {
3503 case LDAP_ERROR:
3504 (void) search_state_machine(c, state, ONE_STEP);
3505 (void) search_state_machine(c, CLEAR_RESULTS, ONE_STEP);
3506 /* FALLTHROUGH */
3507 case ERROR:
3508 case EXIT:
3509 *c->caller_result = c->result;
3510 *c->caller_errorp = c->errorp;
3511 *c->caller_rc =
3512 (c->result == NULL && c->err_from_result == 0)
3513 ? NS_LDAP_NOTFOUND : c->err_rc;
3514 c->result = NULL;
3515 c->errorp = NULL;
3516 /* Remove the cookie from the batch */
3517 ptr = batch->cookie_list;
3518 prev = &batch->cookie_list;
3519 while (ptr != NULL) {
3520 if (ptr == c) {
3521 *prev = ptr->next_cookie_in_batch;
3522 break;
3524 prev = &ptr->next_cookie_in_batch;
3525 ptr = ptr->next_cookie_in_batch;
3527 /* Delete cookie and decrement active cookie count */
3528 if (c->conn_user != NULL) {
3529 if (c->conn_user->conn_mt != NULL)
3530 __s_api_conn_mt_return(c->conn_user);
3531 __s_api_conn_user_free(c->conn_user);
3532 c->conn_user = NULL;
3534 delete_search_cookie(c);
3535 batch->nactive--;
3536 break;
3537 case NEXT_RESULT:
3538 case MULTI_RESULT:
3540 * This means that search_state_machine needs to do
3541 * another ldap_result() for the cookie in question.
3542 * We only do at most one ldap_result() per call in
3543 * this function and therefore we return. This allows
3544 * the caller to process results from other cookies
3545 * in the batch without getting tied up on just one
3546 * cookie.
3548 break;
3549 default:
3551 * This includes states that follow NEXT_RESULT or
3552 * MULTI_RESULT such as PROCESS_RESULT and
3553 * END_PROCESS_RESULT. We continue processing
3554 * this cookie till we reach either the error, exit
3555 * or the result states.
3557 continue;
3559 break;
3562 /* Return 0 if no more cookies left otherwise 1 */
3563 return ((batch->nactive > 0) ? 1 : 0);
3568 * Process all the active cookies in the batch and when none
3569 * remains finalize the batch.
3572 __ns_ldap_list_batch_end(ns_ldap_list_batch_t *batch)
3574 int rc = NS_LDAP_SUCCESS;
3575 while (__ns_ldap_list_batch_process(batch, &rc) > 0)
3577 __ns_ldap_list_batch_release(batch);
3578 return (rc);
3582 * find_domainname performs one or more LDAP searches to
3583 * find the value of the nisdomain attribute associated with
3584 * the input DN (with no retry).
3587 static int
3588 find_domainname(const char *dn, char **domainname, const ns_cred_t *cred,
3589 ns_ldap_error_t **errorp, ns_conn_user_t *conn_user)
3592 ns_ldap_cookie_t *cookie;
3593 ns_ldap_search_desc_t **sdlist;
3594 ns_ldap_search_desc_t *dptr;
3595 int rc;
3596 char **value;
3597 int flags = 0;
3599 *domainname = NULL;
3600 *errorp = NULL;
3602 /* Initialize State machine cookie */
3603 cookie = init_search_state_machine();
3604 if (cookie == NULL) {
3605 return (NS_LDAP_MEMORY);
3607 cookie->conn_user = conn_user;
3609 /* see if need to follow referrals */
3610 rc = __s_api_toFollowReferrals(flags,
3611 &cookie->followRef, errorp);
3612 if (rc != NS_LDAP_SUCCESS) {
3613 delete_search_cookie(cookie);
3614 return (rc);
3617 /* Create default service Desc */
3618 sdlist = (ns_ldap_search_desc_t **)calloc(2,
3619 sizeof (ns_ldap_search_desc_t *));
3620 if (sdlist == NULL) {
3621 delete_search_cookie(cookie);
3622 cookie = NULL;
3623 return (NS_LDAP_MEMORY);
3625 dptr = (ns_ldap_search_desc_t *)
3626 calloc(1, sizeof (ns_ldap_search_desc_t));
3627 if (dptr == NULL) {
3628 free(sdlist);
3629 delete_search_cookie(cookie);
3630 cookie = NULL;
3631 return (NS_LDAP_MEMORY);
3633 sdlist[0] = dptr;
3635 /* search base is dn */
3636 dptr->basedn = strdup(dn);
3638 /* search scope is base */
3639 dptr->scope = NS_LDAP_SCOPE_BASE;
3641 /* search filter is "nisdomain=*" */
3642 dptr->filter = strdup(_NIS_FILTER);
3644 cookie->sdlist = sdlist;
3645 cookie->i_filter = strdup(dptr->filter);
3646 cookie->i_attr = nis_domain_attrs;
3647 cookie->i_auth = cred;
3648 cookie->i_flags = 0;
3650 /* Process search */
3651 rc = search_state_machine(cookie, INIT, 0);
3653 /* Copy domain name if found */
3654 rc = cookie->err_rc;
3655 if (rc != NS_LDAP_SUCCESS) {
3656 if (conn_user != NULL && conn_user->ns_error != NULL) {
3657 *errorp = conn_user->ns_error;
3658 conn_user->ns_error = NULL;
3659 } else
3660 *errorp = cookie->errorp;
3662 if (cookie->result == NULL)
3663 rc = NS_LDAP_NOTFOUND;
3664 if (rc == NS_LDAP_SUCCESS) {
3665 value = __ns_ldap_getAttr(cookie->result->entry,
3666 _NIS_DOMAIN);
3667 if (value[0])
3668 *domainname = strdup(value[0]);
3669 else
3670 rc = NS_LDAP_NOTFOUND;
3672 if (cookie->result != NULL)
3673 (void) __ns_ldap_freeResult(&cookie->result);
3674 cookie->errorp = NULL;
3675 delete_search_cookie(cookie);
3676 cookie = NULL;
3677 return (rc);
3681 * __s_api_find_domainname performs one or more LDAP searches to
3682 * find the value of the nisdomain attribute associated with
3683 * the input DN (with retry).
3686 static int
3687 __s_api_find_domainname(const char *dn, char **domainname,
3688 const ns_cred_t *cred, ns_ldap_error_t **errorp)
3690 ns_conn_user_t *cu = NULL;
3691 int try_cnt = 0;
3692 int rc = NS_LDAP_SUCCESS;
3694 for (;;) {
3695 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
3696 &try_cnt, &rc, errorp) == 0)
3697 break;
3698 rc = find_domainname(dn, domainname, cred, errorp, cu);
3701 return (rc);
3704 static int
3705 firstEntry(
3706 const char *service,
3707 const char *filter,
3708 const char *sortattr,
3709 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3710 char **realfilter, const void *userdata),
3711 const char * const *attribute,
3712 const ns_cred_t *auth,
3713 const int flags,
3714 void **vcookie,
3715 ns_ldap_result_t **result,
3716 ns_ldap_error_t ** errorp,
3717 const void *userdata,
3718 ns_conn_user_t *conn_user)
3720 ns_ldap_cookie_t *cookie = NULL;
3721 ns_ldap_error_t *error = NULL;
3722 ns_state_t state;
3723 ns_ldap_search_desc_t **sdlist;
3724 ns_ldap_search_desc_t *dptr;
3725 char **dns = NULL;
3726 int scope;
3727 int rc;
3729 *errorp = NULL;
3730 *result = NULL;
3733 * Sanity check - NS_LDAP_READ_SHADOW is for our
3734 * own internal use.
3736 if (flags & NS_LDAP_READ_SHADOW)
3737 return (NS_LDAP_INVALID_PARAM);
3739 /* get the service descriptor - or create a default one */
3740 rc = __s_api_get_SSD_from_SSDtoUse_service(service,
3741 &sdlist, &error);
3742 if (rc != NS_LDAP_SUCCESS) {
3743 *errorp = error;
3744 return (rc);
3746 if (sdlist == NULL) {
3747 /* Create default service Desc */
3748 sdlist = (ns_ldap_search_desc_t **)calloc(2,
3749 sizeof (ns_ldap_search_desc_t *));
3750 if (sdlist == NULL) {
3751 return (NS_LDAP_MEMORY);
3753 dptr = (ns_ldap_search_desc_t *)
3754 calloc(1, sizeof (ns_ldap_search_desc_t));
3755 if (dptr == NULL) {
3756 free(sdlist);
3757 return (NS_LDAP_MEMORY);
3759 sdlist[0] = dptr;
3761 /* default base */
3762 rc = __s_api_getDNs(&dns, service, &error);
3763 if (rc != NS_LDAP_SUCCESS) {
3764 if (dns) {
3765 __s_api_free2dArray(dns);
3766 dns = NULL;
3768 if (sdlist) {
3769 (void) __ns_ldap_freeSearchDescriptors(
3770 &sdlist);
3772 sdlist = NULL;
3774 *errorp = error;
3775 return (rc);
3777 dptr->basedn = strdup(dns[0]);
3778 __s_api_free2dArray(dns);
3779 dns = NULL;
3781 /* default scope */
3782 scope = 0;
3783 cookie = init_search_state_machine();
3784 if (cookie == NULL) {
3785 if (sdlist) {
3786 (void) __ns_ldap_freeSearchDescriptors(&sdlist);
3787 sdlist = NULL;
3789 return (NS_LDAP_MEMORY);
3791 rc = __s_api_getSearchScope(&scope, &cookie->errorp);
3792 dptr->scope = scope;
3795 /* Initialize State machine cookie */
3796 if (cookie == NULL)
3797 cookie = init_search_state_machine();
3798 if (cookie == NULL) {
3799 if (sdlist) {
3800 (void) __ns_ldap_freeSearchDescriptors(&sdlist);
3801 sdlist = NULL;
3803 return (NS_LDAP_MEMORY);
3806 /* identify self as a getent user */
3807 cookie->conn_user = conn_user;
3809 cookie->sdlist = sdlist;
3811 /* see if need to follow referrals */
3812 rc = __s_api_toFollowReferrals(flags,
3813 &cookie->followRef, errorp);
3814 if (rc != NS_LDAP_SUCCESS) {
3815 delete_search_cookie(cookie);
3816 return (rc);
3820 * use VLV/PAGE control only if NS_LDAP_NO_PAGE_CTRL is not set
3822 if (flags & NS_LDAP_NO_PAGE_CTRL)
3823 cookie->use_paging = FALSE;
3824 else
3825 cookie->use_paging = TRUE;
3827 /* Set up other arguments */
3828 cookie->userdata = userdata;
3829 if (init_filter_cb != NULL) {
3830 cookie->init_filter_cb = init_filter_cb;
3831 cookie->use_filtercb = 1;
3833 cookie->use_usercb = 0;
3834 /* check_shadow() may add extra value to cookie->i_flags */
3835 cookie->i_flags = flags;
3836 if (service) {
3837 cookie->service = strdup(service);
3838 if (cookie->service == NULL) {
3839 delete_search_cookie(cookie);
3840 return (NS_LDAP_MEMORY);
3844 * If given, use the credential given by the caller, and
3845 * skip the credential check required for shadow update.
3847 if (auth == NULL) {
3848 rc = check_shadow(cookie, service);
3849 if (rc != NS_LDAP_SUCCESS) {
3850 *errorp = cookie->errorp;
3851 cookie->errorp = NULL;
3852 delete_search_cookie(cookie);
3853 cookie = NULL;
3854 return (rc);
3859 cookie->i_filter = strdup(filter);
3860 cookie->i_attr = attribute;
3861 cookie->i_sortattr = sortattr;
3862 cookie->i_auth = auth;
3864 state = INIT;
3865 for (;;) {
3866 state = search_state_machine(cookie, state, ONE_STEP);
3867 switch (state) {
3868 case PROCESS_RESULT:
3869 *result = cookie->result;
3870 cookie->result = NULL;
3871 *vcookie = (void *)cookie;
3872 return (NS_LDAP_SUCCESS);
3873 case LDAP_ERROR:
3874 state = search_state_machine(cookie, state, ONE_STEP);
3875 state = search_state_machine(cookie, CLEAR_RESULTS,
3876 ONE_STEP);
3877 /* FALLTHROUGH */
3878 case ERROR:
3879 rc = cookie->err_rc;
3880 if (conn_user != NULL && conn_user->ns_error != NULL) {
3881 *errorp = conn_user->ns_error;
3882 conn_user->ns_error = NULL;
3883 } else {
3884 *errorp = cookie->errorp;
3885 cookie->errorp = NULL;
3887 delete_search_cookie(cookie);
3888 return (rc);
3889 case EXIT:
3890 rc = cookie->err_rc;
3891 if (rc != NS_LDAP_SUCCESS) {
3892 *errorp = cookie->errorp;
3893 cookie->errorp = NULL;
3894 } else {
3895 rc = NS_LDAP_NOTFOUND;
3898 delete_search_cookie(cookie);
3899 return (rc);
3901 default:
3902 break;
3908 __ns_ldap_firstEntry(
3909 const char *service,
3910 const char *filter,
3911 const char *vlv_sort,
3912 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3913 char **realfilter, const void *userdata),
3914 const char * const *attribute,
3915 const ns_cred_t *auth,
3916 const int flags,
3917 void **vcookie,
3918 ns_ldap_result_t **result,
3919 ns_ldap_error_t ** errorp,
3920 const void *userdata)
3922 ns_conn_user_t *cu = NULL;
3923 int try_cnt = 0;
3924 int rc = NS_LDAP_SUCCESS;
3926 for (;;) {
3927 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_GETENT,
3928 &try_cnt, &rc, errorp) == 0)
3929 break;
3930 rc = firstEntry(service, filter, vlv_sort, init_filter_cb,
3931 attribute, auth, flags, vcookie, result, errorp, userdata,
3932 cu);
3934 return (rc);
3937 /*ARGSUSED2*/
3939 __ns_ldap_nextEntry(void *vcookie, ns_ldap_result_t **result,
3940 ns_ldap_error_t ** errorp)
3942 ns_ldap_cookie_t *cookie;
3943 ns_state_t state;
3944 int rc;
3946 cookie = (ns_ldap_cookie_t *)vcookie;
3947 cookie->result = NULL;
3948 *result = NULL;
3950 if (cookie->conn_user != NULL) {
3951 rc = __s_api_setup_getnext(cookie->conn_user,
3952 &cookie->err_rc, errorp);
3953 if (rc != NS_LDAP_SUCCESS)
3954 return (rc);
3957 state = END_PROCESS_RESULT;
3958 for (;;) {
3959 state = search_state_machine(cookie, state, ONE_STEP);
3960 switch (state) {
3961 case PROCESS_RESULT:
3962 *result = cookie->result;
3963 cookie->result = NULL;
3964 return (NS_LDAP_SUCCESS);
3965 case LDAP_ERROR:
3966 state = search_state_machine(cookie, state, ONE_STEP);
3967 state = search_state_machine(cookie, CLEAR_RESULTS,
3968 ONE_STEP);
3969 /* FALLTHROUGH */
3970 case ERROR:
3971 rc = cookie->err_rc;
3972 *errorp = cookie->errorp;
3973 cookie->errorp = NULL;
3974 return (rc);
3975 case EXIT:
3976 return (NS_LDAP_SUCCESS);
3982 __ns_ldap_endEntry(
3983 void **vcookie,
3984 ns_ldap_error_t ** errorp)
3986 ns_ldap_cookie_t *cookie;
3987 int rc;
3989 if (*vcookie == NULL)
3990 return (NS_LDAP_INVALID_PARAM);
3992 cookie = (ns_ldap_cookie_t *)(*vcookie);
3993 cookie->result = NULL;
3995 /* Complete search */
3996 rc = search_state_machine(cookie, CLEAR_RESULTS, 0);
3998 /* Copy results back to user */
3999 rc = cookie->err_rc;
4000 if (rc != NS_LDAP_SUCCESS)
4001 *errorp = cookie->errorp;
4003 cookie->errorp = NULL;
4004 if (cookie->conn_user != NULL) {
4005 if (cookie->conn_user->conn_mt != NULL)
4006 __s_api_conn_mt_return(cookie->conn_user);
4007 __s_api_conn_user_free(cookie->conn_user);
4009 delete_search_cookie(cookie);
4010 cookie = NULL;
4011 *vcookie = NULL;
4013 return (rc);
4018 __ns_ldap_freeResult(ns_ldap_result_t **result)
4021 ns_ldap_entry_t *curEntry = NULL;
4022 ns_ldap_entry_t *delEntry = NULL;
4023 int i;
4024 ns_ldap_result_t *res = *result;
4026 #ifdef DEBUG
4027 (void) fprintf(stderr, "__ns_ldap_freeResult START\n");
4028 #endif
4029 if (res == NULL)
4030 return (NS_LDAP_INVALID_PARAM);
4032 if (res->entry != NULL)
4033 curEntry = res->entry;
4035 for (i = 0; i < res->entries_count; i++) {
4036 if (curEntry != NULL) {
4037 delEntry = curEntry;
4038 curEntry = curEntry->next;
4039 __ns_ldap_freeEntry(delEntry);
4043 free(res);
4044 *result = NULL;
4045 return (NS_LDAP_SUCCESS);
4048 /*ARGSUSED*/
4050 __ns_ldap_auth(const ns_cred_t *auth,
4051 const int flags,
4052 ns_ldap_error_t **errorp,
4053 LDAPControl **serverctrls,
4054 LDAPControl **clientctrls)
4057 ConnectionID connectionId = -1;
4058 Connection *conp;
4059 int rc = 0;
4060 int do_not_fail_if_new_pwd_reqd = 0;
4061 int nopasswd_acct_mgmt = 0;
4062 ns_conn_user_t *conn_user;
4065 #ifdef DEBUG
4066 (void) fprintf(stderr, "__ns_ldap_auth START\n");
4067 #endif
4069 *errorp = NULL;
4070 if (!auth)
4071 return (NS_LDAP_INVALID_PARAM);
4073 conn_user = __s_api_conn_user_init(NS_CONN_USER_AUTH,
4074 NULL, B_FALSE);
4076 rc = __s_api_getConnection(NULL, flags | NS_LDAP_NEW_CONN,
4077 auth, &connectionId, &conp, errorp,
4078 do_not_fail_if_new_pwd_reqd, nopasswd_acct_mgmt,
4079 conn_user);
4081 if (conn_user != NULL)
4082 __s_api_conn_user_free(conn_user);
4084 if (rc == NS_LDAP_OP_FAILED && *errorp)
4085 (void) __ns_ldap_freeError(errorp);
4087 if (connectionId > -1)
4088 DropConnection(connectionId, flags);
4089 return (rc);
4092 char **
4093 __ns_ldap_getAttr(const ns_ldap_entry_t *entry, const char *attrname)
4095 int i;
4097 if (entry == NULL)
4098 return (NULL);
4099 for (i = 0; i < entry->attr_count; i++) {
4100 if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == 0)
4101 return (entry->attr_pair[i]->attrvalue);
4103 return (NULL);
4106 ns_ldap_attr_t *
4107 __ns_ldap_getAttrStruct(const ns_ldap_entry_t *entry, const char *attrname)
4109 int i;
4111 if (entry == NULL)
4112 return (NULL);
4113 for (i = 0; i < entry->attr_count; i++) {
4114 if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == 0)
4115 return (entry->attr_pair[i]);
4117 return (NULL);
4121 /*ARGSUSED*/
4123 __ns_ldap_uid2dn(const char *uid,
4124 char **userDN,
4125 const ns_cred_t *cred, /* cred is ignored */
4126 ns_ldap_error_t **errorp)
4128 ns_ldap_result_t *result = NULL;
4129 char *filter, *userdata;
4130 char errstr[MAXERROR];
4131 char **value;
4132 int rc = 0;
4133 int i = 0;
4134 size_t len;
4136 *errorp = NULL;
4137 *userDN = NULL;
4138 if ((uid == NULL) || (uid[0] == '\0'))
4139 return (NS_LDAP_INVALID_PARAM);
4141 while (uid[i] != '\0') {
4142 if (uid[i] == '=') {
4143 *userDN = strdup(uid);
4144 return (NS_LDAP_SUCCESS);
4146 i++;
4148 i = 0;
4149 while ((uid[i] != '\0') && (isdigit(uid[i])))
4150 i++;
4151 if (uid[i] == '\0') {
4152 len = strlen(UIDNUMFILTER) + strlen(uid) + 1;
4153 filter = (char *)malloc(len);
4154 if (filter == NULL) {
4155 *userDN = NULL;
4156 return (NS_LDAP_MEMORY);
4158 (void) snprintf(filter, len, UIDNUMFILTER, uid);
4160 len = strlen(UIDNUMFILTER_SSD) + strlen(uid) + 1;
4161 userdata = (char *)malloc(len);
4162 if (userdata == NULL) {
4163 *userDN = NULL;
4164 return (NS_LDAP_MEMORY);
4166 (void) snprintf(userdata, len, UIDNUMFILTER_SSD, uid);
4167 } else {
4168 len = strlen(UIDFILTER) + strlen(uid) + 1;
4169 filter = (char *)malloc(len);
4170 if (filter == NULL) {
4171 *userDN = NULL;
4172 return (NS_LDAP_MEMORY);
4174 (void) snprintf(filter, len, UIDFILTER, uid);
4176 len = strlen(UIDFILTER_SSD) + strlen(uid) + 1;
4177 userdata = (char *)malloc(len);
4178 if (userdata == NULL) {
4179 *userDN = NULL;
4180 return (NS_LDAP_MEMORY);
4182 (void) snprintf(userdata, len, UIDFILTER_SSD, uid);
4186 * we want to retrieve the DN as it appears in LDAP
4187 * hence the use of NS_LDAP_NOT_CVT_DN in flags
4189 rc = __ns_ldap_list("passwd", filter,
4190 __s_api_merge_SSD_filter,
4191 NULL, cred, NS_LDAP_NOT_CVT_DN,
4192 &result, errorp, NULL,
4193 userdata);
4194 free(filter);
4195 filter = NULL;
4196 free(userdata);
4197 userdata = NULL;
4198 if (rc != NS_LDAP_SUCCESS) {
4199 if (result) {
4200 (void) __ns_ldap_freeResult(&result);
4201 result = NULL;
4203 return (rc);
4205 if (result->entries_count > 1) {
4206 (void) __ns_ldap_freeResult(&result);
4207 result = NULL;
4208 *userDN = NULL;
4209 (void) sprintf(errstr,
4210 gettext("Too many entries are returned for %s"), uid);
4211 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
4213 return (NS_LDAP_INTERNAL);
4216 value = __ns_ldap_getAttr(result->entry, "dn");
4217 *userDN = strdup(value[0]);
4218 (void) __ns_ldap_freeResult(&result);
4219 result = NULL;
4220 return (NS_LDAP_SUCCESS);
4224 /*ARGSUSED*/
4226 __ns_ldap_host2dn(const char *host,
4227 const char *domain,
4228 char **hostDN,
4229 const ns_cred_t *cred, /* cred is ignored */
4230 ns_ldap_error_t **errorp)
4232 ns_ldap_result_t *result = NULL;
4233 char *filter, *userdata;
4234 char errstr[MAXERROR];
4235 char **value;
4236 int rc;
4237 size_t len;
4240 * XXX
4241 * the domain parameter needs to be used in case domain is not local, if
4242 * this routine is to support multi domain setups, it needs lots of work...
4244 *errorp = NULL;
4245 *hostDN = NULL;
4246 if ((host == NULL) || (host[0] == '\0'))
4247 return (NS_LDAP_INVALID_PARAM);
4249 len = strlen(HOSTFILTER) + strlen(host) + 1;
4250 filter = (char *)malloc(len);
4251 if (filter == NULL) {
4252 return (NS_LDAP_MEMORY);
4254 (void) snprintf(filter, len, HOSTFILTER, host);
4256 len = strlen(HOSTFILTER_SSD) + strlen(host) + 1;
4257 userdata = (char *)malloc(len);
4258 if (userdata == NULL) {
4259 return (NS_LDAP_MEMORY);
4261 (void) snprintf(userdata, len, HOSTFILTER_SSD, host);
4264 * we want to retrieve the DN as it appears in LDAP
4265 * hence the use of NS_LDAP_NOT_CVT_DN in flags
4267 rc = __ns_ldap_list("hosts", filter,
4268 __s_api_merge_SSD_filter,
4269 NULL, cred, NS_LDAP_NOT_CVT_DN, &result,
4270 errorp, NULL,
4271 userdata);
4272 free(filter);
4273 filter = NULL;
4274 free(userdata);
4275 userdata = NULL;
4276 if (rc != NS_LDAP_SUCCESS) {
4277 if (result) {
4278 (void) __ns_ldap_freeResult(&result);
4279 result = NULL;
4281 return (rc);
4284 if (result->entries_count > 1) {
4285 (void) __ns_ldap_freeResult(&result);
4286 result = NULL;
4287 *hostDN = NULL;
4288 (void) sprintf(errstr,
4289 gettext("Too many entries are returned for %s"), host);
4290 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
4292 return (NS_LDAP_INTERNAL);
4295 value = __ns_ldap_getAttr(result->entry, "dn");
4296 *hostDN = strdup(value[0]);
4297 (void) __ns_ldap_freeResult(&result);
4298 result = NULL;
4299 return (NS_LDAP_SUCCESS);
4302 /*ARGSUSED*/
4304 __ns_ldap_dn2domain(const char *dn,
4305 char **domain,
4306 const ns_cred_t *cred,
4307 ns_ldap_error_t **errorp)
4309 int rc, pnum, i, j, len = 0;
4310 char *newdn, **rdns = NULL;
4311 char **dns, *dn1;
4313 *errorp = NULL;
4315 if (domain == NULL)
4316 return (NS_LDAP_INVALID_PARAM);
4317 else
4318 *domain = NULL;
4320 if ((dn == NULL) || (dn[0] == '\0'))
4321 return (NS_LDAP_INVALID_PARAM);
4324 * break dn into rdns
4326 dn1 = strdup(dn);
4327 if (dn1 == NULL)
4328 return (NS_LDAP_MEMORY);
4329 rdns = ldap_explode_dn(dn1, 0);
4330 free(dn1);
4331 if (rdns == NULL || *rdns == NULL)
4332 return (NS_LDAP_INVALID_PARAM);
4334 for (i = 0; rdns[i]; i++)
4335 len += strlen(rdns[i]) + 1;
4336 pnum = i;
4338 newdn = (char *)malloc(len + 1);
4339 dns = (char **)calloc(pnum, sizeof (char *));
4340 if (newdn == NULL || dns == NULL) {
4341 free(newdn);
4342 ldap_value_free(rdns);
4343 return (NS_LDAP_MEMORY);
4346 /* construct a semi-normalized dn, newdn */
4347 *newdn = '\0';
4348 for (i = 0; rdns[i]; i++) {
4349 dns[i] = newdn + strlen(newdn);
4350 (void) strcat(newdn,
4351 __s_api_remove_rdn_space(rdns[i]));
4352 (void) strcat(newdn, ",");
4354 /* remove the last ',' */
4355 newdn[strlen(newdn) - 1] = '\0';
4356 ldap_value_free(rdns);
4359 * loop and find the domain name associated with newdn,
4360 * removing rdn one by one from left to right
4362 for (i = 0; i < pnum; i++) {
4364 if (*errorp)
4365 (void) __ns_ldap_freeError(errorp);
4368 * try cache manager first
4370 rc = __s_api_get_cachemgr_data(NS_CACHE_DN2DOMAIN,
4371 dns[i], domain);
4372 if (rc != NS_LDAP_SUCCESS) {
4374 * try ldap server second
4376 rc = __s_api_find_domainname(dns[i], domain,
4377 cred, errorp);
4378 } else {
4380 * skip the last one,
4381 * since it is already cached by ldap_cachemgr
4383 i--;
4385 if (rc == NS_LDAP_SUCCESS) {
4386 if (__s_api_nscd_proc()) {
4388 * If it's nscd, ask cache manager to save the
4389 * dn to domain mapping(s)
4391 for (j = 0; j <= i; j++) {
4392 (void) __s_api_set_cachemgr_data(
4393 NS_CACHE_DN2DOMAIN,
4394 dns[j],
4395 *domain);
4398 break;
4402 free(dns);
4403 free(newdn);
4404 if (rc != NS_LDAP_SUCCESS)
4405 rc = NS_LDAP_NOTFOUND;
4406 return (rc);
4409 /*ARGSUSED*/
4411 __ns_ldap_getServiceAuthMethods(const char *service,
4412 ns_auth_t ***auth,
4413 ns_ldap_error_t **errorp)
4415 char errstr[MAXERROR];
4416 int rc, i, done = 0;
4417 int slen;
4418 void **param;
4419 char **sam, *srv, *send;
4420 ns_auth_t **authpp = NULL, *ap;
4421 int cnt, max;
4422 ns_config_t *cfg;
4423 ns_ldap_error_t *error = NULL;
4425 if (errorp == NULL)
4426 return (NS_LDAP_INVALID_PARAM);
4427 *errorp = NULL;
4429 if ((service == NULL) || (service[0] == '\0') ||
4430 (auth == NULL))
4431 return (NS_LDAP_INVALID_PARAM);
4433 *auth = NULL;
4434 rc = __ns_ldap_getParam(NS_LDAP_SERVICE_AUTH_METHOD_P, &param, &error);
4435 if (rc != NS_LDAP_SUCCESS || param == NULL) {
4436 *errorp = error;
4437 return (rc);
4439 sam = (char **)param;
4441 cfg = __s_api_get_default_config();
4442 cnt = 0;
4444 slen = strlen(service);
4446 for (; *sam; sam++) {
4447 srv = *sam;
4448 if (strncasecmp(service, srv, slen) != 0)
4449 continue;
4450 srv += slen;
4451 if (*srv != COLONTOK)
4452 continue;
4453 send = srv;
4454 srv++;
4455 for (max = 1; (send = strchr(++send, SEMITOK)) != NULL;
4456 max++) {}
4457 authpp = (ns_auth_t **)calloc(++max, sizeof (ns_auth_t *));
4458 if (authpp == NULL) {
4459 (void) __ns_ldap_freeParam(&param);
4460 __s_api_release_config(cfg);
4461 return (NS_LDAP_MEMORY);
4463 while (!done) {
4464 send = strchr(srv, SEMITOK);
4465 if (send != NULL) {
4466 *send = '\0';
4467 send++;
4469 i = __s_get_enum_value(cfg, srv, NS_LDAP_AUTH_P);
4470 if (i == -1) {
4471 (void) __ns_ldap_freeParam(&param);
4472 (void) sprintf(errstr,
4473 gettext("Unsupported "
4474 "serviceAuthenticationMethod: %s.\n"), srv);
4475 MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX,
4476 strdup(errstr), 0);
4477 __s_api_release_config(cfg);
4478 return (NS_LDAP_CONFIG);
4480 ap = __s_api_AuthEnumtoStruct((EnumAuthType_t)i);
4481 if (ap == NULL) {
4482 (void) __ns_ldap_freeParam(&param);
4483 __s_api_release_config(cfg);
4484 return (NS_LDAP_MEMORY);
4486 authpp[cnt++] = ap;
4487 if (send == NULL)
4488 done = TRUE;
4489 else
4490 srv = send;
4494 *auth = authpp;
4495 (void) __ns_ldap_freeParam(&param);
4496 __s_api_release_config(cfg);
4497 return (NS_LDAP_SUCCESS);
4501 * This routine is called when certain scenario occurs
4502 * e.g.
4503 * service == auto_home
4504 * SSD = automount: ou = mytest,
4505 * NS_LDAP_MAPATTRIBUTE= auto_home: automountMapName=AAA
4506 * NS_LDAP_OBJECTCLASSMAP= auto_home:automountMap=MynisMap
4507 * NS_LDAP_OBJECTCLASSMAP= auto_home:automount=MynisObject
4509 * The automountMapName is prepended implicitely but is mapped
4510 * to AAA. So dn could appers as
4511 * dn: AAA=auto_home,ou=bar,dc=foo,dc=com
4512 * dn: automountKey=user_01,AAA=auto_home,ou=bar,dc=foo,dc=com
4513 * dn: automountKey=user_02,AAA=auto_home,ou=bar,dc=foo,dc=com
4514 * in the directory.
4515 * This function is called to covert the mapped attr back to
4516 * orig attr when the entries are searched and returned
4520 __s_api_convert_automountmapname(const char *service, char **dn,
4521 ns_ldap_error_t **errp) {
4523 char **mapping = NULL;
4524 char *mapped_attr = NULL;
4525 char *automountmapname = "automountMapName";
4526 char *buffer = NULL;
4527 int rc = NS_LDAP_SUCCESS;
4528 char errstr[MAXERROR];
4531 * dn is an input/out parameter, check it first
4534 if (service == NULL || dn == NULL || *dn == NULL)
4535 return (NS_LDAP_INVALID_PARAM);
4538 * Check to see if there is a mapped attribute for auto_xxx
4541 mapping = __ns_ldap_getMappedAttributes(service, automountmapname);
4544 * if no mapped attribute for auto_xxx, try automount
4547 if (mapping == NULL)
4548 mapping = __ns_ldap_getMappedAttributes(
4549 "automount", automountmapname);
4552 * if no mapped attribute is found, return SUCCESS (no op)
4555 if (mapping == NULL)
4556 return (NS_LDAP_SUCCESS);
4559 * if the mapped attribute is found and attr is not empty,
4560 * copy it
4563 if (mapping[0] != NULL) {
4564 mapped_attr = strdup(mapping[0]);
4565 __s_api_free2dArray(mapping);
4566 if (mapped_attr == NULL) {
4567 return (NS_LDAP_MEMORY);
4569 } else {
4570 __s_api_free2dArray(mapping);
4572 (void) snprintf(errstr, (2 * MAXERROR),
4573 gettext(
4574 "Attribute nisMapName is mapped to an "
4575 "empty string.\n"));
4577 MKERROR(LOG_ERR, *errp, NS_CONFIG_SYNTAX,
4578 strdup(errstr), 0);
4580 return (NS_LDAP_CONFIG);
4584 * Locate the mapped attribute in the dn
4585 * and replace it if it exists
4588 rc = __s_api_replace_mapped_attr_in_dn(
4589 (const char *) automountmapname, (const char *) mapped_attr,
4590 (const char *) *dn, &buffer);
4592 /* clean up */
4594 free(mapped_attr);
4597 * If mapped attr is found(buffer != NULL)
4598 * a new dn is returned
4599 * If no mapped attribute is in dn,
4600 * return NS_LDAP_SUCCESS (no op)
4601 * If no memory,
4602 * return NS_LDAP_MEMORY (no op)
4605 if (buffer != NULL) {
4606 free(*dn);
4607 *dn = buffer;
4610 return (rc);
4614 * If the mapped attr is found in the dn,
4615 * return NS_LDAP_SUCCESS and a new_dn.
4616 * If no mapped attr is found,
4617 * return NS_LDAP_SUCCESS and *new_dn == NULL
4618 * If there is not enough memory,
4619 * return NS_LDAP_MEMORY and *new_dn == NULL
4623 __s_api_replace_mapped_attr_in_dn(
4624 const char *orig_attr, const char *mapped_attr,
4625 const char *dn, char **new_dn) {
4627 char **dnArray = NULL;
4628 char *cur = NULL, *start = NULL;
4629 int i = 0, found = 0;
4630 int len = 0, orig_len = 0, mapped_len = 0;
4631 int dn_len = 0, tmp_len = 0;
4633 *new_dn = NULL;
4636 * seperate dn into individual componets
4637 * e.g.
4638 * "automountKey=user_01" , "automountMapName_test=auto_home", ...
4640 dnArray = ldap_explode_dn(dn, 0);
4643 * This will find "mapped attr=value" in dn.
4644 * It won't find match if mapped attr appears
4645 * in the value.
4647 for (i = 0; dnArray[i] != NULL; i++) {
4649 * This function is called when reading from
4650 * the directory so assume each component has "=".
4651 * Any ill formatted dn should be rejected
4652 * before adding to the directory
4654 cur = strchr(dnArray[i], '=');
4655 *cur = '\0';
4656 if (strcasecmp(mapped_attr, dnArray[i]) == 0)
4657 found = 1;
4658 *cur = '=';
4659 if (found) break;
4662 if (!found) {
4663 __s_api_free2dArray(dnArray);
4664 *new_dn = NULL;
4665 return (NS_LDAP_SUCCESS);
4668 * The new length is *dn length + (difference between
4669 * orig attr and mapped attr) + 1 ;
4670 * e.g.
4671 * automountKey=aa,automountMapName_test=auto_home,dc=foo,dc=com
4672 * ==>
4673 * automountKey=aa,automountMapName=auto_home,dc=foo,dc=com
4675 mapped_len = strlen(mapped_attr);
4676 orig_len = strlen(orig_attr);
4677 dn_len = strlen(dn);
4678 len = dn_len + orig_len - mapped_len + 1;
4679 *new_dn = (char *)calloc(1, len);
4680 if (*new_dn == NULL) {
4681 __s_api_free2dArray(dnArray);
4682 return (NS_LDAP_MEMORY);
4686 * Locate the mapped attr in the dn.
4687 * Use dnArray[i] instead of mapped_attr
4688 * because mapped_attr could appear in
4689 * the value
4692 cur = strstr(dn, dnArray[i]);
4693 __s_api_free2dArray(dnArray);
4694 /* copy the portion before mapped attr in dn */
4695 start = *new_dn;
4696 tmp_len = cur - dn;
4697 (void) memcpy((void *) start, (const void*) dn, tmp_len);
4700 * Copy the orig_attr. e.g. automountMapName
4701 * This replaces mapped attr with orig attr
4703 start = start + (cur - dn); /* move cursor in buffer */
4704 (void) memcpy((void *) start, (const void*) orig_attr, orig_len);
4707 * Copy the portion after mapped attr in dn
4709 cur = cur + mapped_len; /* move cursor in dn */
4710 start = start + orig_len; /* move cursor in buffer */
4711 (void) strcpy(start, cur);
4713 return (NS_LDAP_SUCCESS);
4717 * Validate Filter functions
4720 /* ***** Start of modified libldap.so.5 filter parser ***** */
4722 /* filter parsing routine forward references */
4723 static int adj_filter_list(char *str);
4724 static int adj_simple_filter(char *str);
4725 static int unescape_filterval(char *val);
4726 static int hexchar2int(char c);
4727 static int adj_substring_filter(char *val);
4731 * assumes string manipulation is in-line
4732 * and all strings are sufficient in size
4733 * return value is the position after 'c'
4736 static char *
4737 resync_str(char *str, char *next, char c)
4739 char *ret;
4741 ret = str + strlen(str);
4742 *next = c;
4743 if (ret == next)
4744 return (ret);
4745 (void) strcat(str, next);
4746 return (ret);
4749 static char *
4750 find_right_paren(char *s)
4752 int balance, escape;
4754 balance = 1;
4755 escape = 0;
4756 while (*s && balance) {
4757 if (escape == 0) {
4758 if (*s == '(')
4759 balance++;
4760 else if (*s == ')')
4761 balance--;
4763 if (*s == '\\' && ! escape)
4764 escape = 1;
4765 else
4766 escape = 0;
4767 if (balance)
4768 s++;
4771 return (*s ? s : NULL);
4774 static char *
4775 adj_complex_filter(char *str)
4777 char *next;
4780 * We have (x(filter)...) with str sitting on
4781 * the x. We have to find the paren matching
4782 * the one before the x and put the intervening
4783 * filters by calling adj_filter_list().
4786 str++;
4787 if ((next = find_right_paren(str)) == NULL)
4788 return (NULL);
4790 *next = '\0';
4791 if (adj_filter_list(str) == -1)
4792 return (NULL);
4793 next = resync_str(str, next, ')');
4794 next++;
4796 return (next);
4799 static int
4800 adj_filter(char *str)
4802 char *next;
4803 int parens, balance, escape;
4804 char *np, *cp, *dp;
4806 parens = 0;
4807 while (*str) {
4808 switch (*str) {
4809 case '(':
4810 str++;
4811 parens++;
4812 switch (*str) {
4813 case '&':
4814 if ((str = adj_complex_filter(str)) == NULL)
4815 return (-1);
4817 parens--;
4818 break;
4820 case '|':
4821 if ((str = adj_complex_filter(str)) == NULL)
4822 return (-1);
4824 parens--;
4825 break;
4827 case '!':
4828 if ((str = adj_complex_filter(str)) == NULL)
4829 return (-1);
4831 parens--;
4832 break;
4834 case '(':
4835 /* illegal ((case - generated by conversion */
4837 /* find missing close) */
4838 np = find_right_paren(str+1);
4840 /* error if not found */
4841 if (np == NULL)
4842 return (-1);
4844 /* remove redundant (and) */
4845 for (dp = str, cp = str+1; cp < np; ) {
4846 *dp++ = *cp++;
4848 cp++;
4849 while (*cp)
4850 *dp++ = *cp++;
4851 *dp = '\0';
4853 /* re-start test at original ( */
4854 parens--;
4855 str--;
4856 break;
4858 default:
4859 balance = 1;
4860 escape = 0;
4861 next = str;
4862 while (*next && balance) {
4863 if (escape == 0) {
4864 if (*next == '(')
4865 balance++;
4866 else if (*next == ')')
4867 balance--;
4869 if (*next == '\\' && ! escape)
4870 escape = 1;
4871 else
4872 escape = 0;
4873 if (balance)
4874 next++;
4876 if (balance != 0)
4877 return (-1);
4879 *next = '\0';
4880 if (adj_simple_filter(str) == -1) {
4881 return (-1);
4883 next = resync_str(str, next, ')');
4884 next++;
4885 str = next;
4886 parens--;
4887 break;
4889 break;
4891 case ')':
4892 str++;
4893 parens--;
4894 break;
4896 case ' ':
4897 str++;
4898 break;
4900 default: /* assume it's a simple type=value filter */
4901 next = strchr(str, '\0');
4902 if (adj_simple_filter(str) == -1) {
4903 return (-1);
4905 str = next;
4906 break;
4910 return (parens ? -1 : 0);
4915 * Put a list of filters like this "(filter1)(filter2)..."
4918 static int
4919 adj_filter_list(char *str)
4921 char *next;
4922 char save;
4924 while (*str) {
4925 while (*str && isspace(*str))
4926 str++;
4927 if (*str == '\0')
4928 break;
4930 if ((next = find_right_paren(str + 1)) == NULL)
4931 return (-1);
4932 save = *++next;
4934 /* now we have "(filter)" with str pointing to it */
4935 *next = '\0';
4936 if (adj_filter(str) == -1)
4937 return (-1);
4938 next = resync_str(str, next, save);
4940 str = next;
4943 return (0);
4948 * is_valid_attr - returns 1 if a is a syntactically valid left-hand side
4949 * of a filter expression, 0 otherwise. A valid string may contain only
4950 * letters, numbers, hyphens, semi-colons, colons and periods. examples:
4951 * cn
4952 * cn;lang-fr
4953 * 1.2.3.4;binary;dynamic
4954 * mail;dynamic
4955 * cn:dn:1.2.3.4
4957 * For compatibility with older servers, we also allow underscores in
4958 * attribute types, even through they are not allowed by the LDAPv3 RFCs.
4960 static int
4961 is_valid_attr(char *a)
4963 for (; *a; a++) {
4964 if (!isascii(*a)) {
4965 return (0);
4966 } else if (!isalnum(*a)) {
4967 switch (*a) {
4968 case '-':
4969 case '.':
4970 case ';':
4971 case ':':
4972 case '_':
4973 break; /* valid */
4974 default:
4975 return (0);
4979 return (1);
4982 static char *
4983 find_star(char *s)
4985 for (; *s; ++s) {
4986 switch (*s) {
4987 case '*':
4988 return (s);
4989 case '\\':
4990 ++s;
4991 if (hexchar2int(s[0]) >= 0 && hexchar2int(s[1]) >= 0)
4992 ++s;
4993 default:
4994 break;
4997 return (NULL);
5000 static int
5001 adj_simple_filter(char *str)
5003 char *s, *s2, *s3, filterop;
5004 char *value;
5005 int ftype = 0;
5006 int rc;
5008 rc = -1; /* pessimistic */
5010 if ((str = strdup(str)) == NULL) {
5011 return (rc);
5014 if ((s = strchr(str, '=')) == NULL) {
5015 goto free_and_return;
5017 value = s + 1;
5018 *s-- = '\0';
5019 filterop = *s;
5020 if (filterop == '<' || filterop == '>' || filterop == '~' ||
5021 filterop == ':') {
5022 *s = '\0';
5025 if (! is_valid_attr(str)) {
5026 goto free_and_return;
5029 switch (filterop) {
5030 case '<': /* LDAP_FILTER_LE */
5031 case '>': /* LDAP_FILTER_GE */
5032 case '~': /* LDAP_FILTER_APPROX */
5033 break;
5034 case ':': /* extended filter - v3 only */
5036 * extended filter looks like this:
5038 * [type][':dn'][':'oid]':='value
5040 * where one of type or :oid is required.
5043 s2 = s3 = NULL;
5044 if ((s2 = strrchr(str, ':')) == NULL) {
5045 goto free_and_return;
5047 if (strcasecmp(s2, ":dn") == 0) {
5048 *s2 = '\0';
5049 } else {
5050 *s2 = '\0';
5051 if ((s3 = strrchr(str, ':')) != NULL) {
5052 if (strcasecmp(s3, ":dn") != 0) {
5053 goto free_and_return;
5055 *s3 = '\0';
5058 if (unescape_filterval(value) < 0) {
5059 goto free_and_return;
5061 rc = 0;
5062 goto free_and_return;
5063 /* break; */
5064 default:
5065 if (find_star(value) == NULL) {
5066 ftype = 0; /* LDAP_FILTER_EQUALITY */
5067 } else if (strcmp(value, "*") == 0) {
5068 ftype = 1; /* LDAP_FILTER_PRESENT */
5069 } else {
5070 rc = adj_substring_filter(value);
5071 goto free_and_return;
5073 break;
5076 if (ftype != 0) { /* == LDAP_FILTER_PRESENT */
5077 rc = 0;
5078 } else if (unescape_filterval(value) >= 0) {
5079 rc = 0;
5081 if (rc != -1) {
5082 rc = 0;
5085 free_and_return:
5086 free(str);
5087 return (rc);
5092 * Check in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape
5093 * sequences within the null-terminated string 'val'.
5095 * If 'val' contains invalid escape sequences we return -1.
5096 * Otherwise return 1
5098 static int
5099 unescape_filterval(char *val)
5101 int escape, firstdigit;
5102 char *s;
5104 firstdigit = 0;
5105 escape = 0;
5106 for (s = val; *s; s++) {
5107 if (escape) {
5109 * first try LDAPv3 escape (hexadecimal) sequence
5111 if (hexchar2int(*s) < 0) {
5112 if (firstdigit) {
5114 * LDAPv2 (RFC1960) escape sequence
5116 escape = 0;
5117 } else {
5118 return (-1);
5121 if (firstdigit) {
5122 firstdigit = 0;
5123 } else {
5124 escape = 0;
5127 } else if (*s != '\\') {
5128 escape = 0;
5130 } else {
5131 escape = 1;
5132 firstdigit = 1;
5136 return (1);
5141 * convert character 'c' that represents a hexadecimal digit to an integer.
5142 * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned.
5143 * otherwise the converted value is returned.
5145 static int
5146 hexchar2int(char c)
5148 if (c >= '0' && c <= '9') {
5149 return (c - '0');
5151 if (c >= 'A' && c <= 'F') {
5152 return (c - 'A' + 10);
5154 if (c >= 'a' && c <= 'f') {
5155 return (c - 'a' + 10);
5157 return (-1);
5160 static int
5161 adj_substring_filter(char *val)
5163 char *nextstar;
5165 for (; val != NULL; val = nextstar) {
5166 if ((nextstar = find_star(val)) != NULL) {
5167 *nextstar++ = '\0';
5170 if (*val != '\0') {
5171 if (unescape_filterval(val) < 0) {
5172 return (-1);
5177 return (0);
5180 /* ***** End of modified libldap.so.5 filter parser ***** */
5184 * Walk filter, remove redundant parentheses in-line
5185 * verify that the filter is reasonable
5187 static int
5188 validate_filter(ns_ldap_cookie_t *cookie)
5190 char *filter = cookie->filter;
5191 int rc;
5193 /* Parse filter looking for illegal values */
5195 rc = adj_filter(filter);
5196 if (rc != 0) {
5197 return (NS_LDAP_OP_FAILED);
5200 /* end of filter checking */
5202 return (NS_LDAP_SUCCESS);
5206 * Set the account management request control that needs to be sent to server.
5207 * This control is required to get the account management information of
5208 * a user to do local account checking.
5210 static int
5211 setup_acctmgmt_params(ns_ldap_cookie_t *cookie)
5213 LDAPControl *req = NULL, **requestctrls;
5215 req = (LDAPControl *)malloc(sizeof (LDAPControl));
5217 if (req == NULL)
5218 return (NS_LDAP_MEMORY);
5220 /* fill in the fields of this new control */
5221 req->ldctl_iscritical = 1;
5222 req->ldctl_oid = strdup(NS_LDAP_ACCOUNT_USABLE_CONTROL);
5223 if (req->ldctl_oid == NULL) {
5224 free(req);
5225 return (NS_LDAP_MEMORY);
5227 req->ldctl_value.bv_len = 0;
5228 req->ldctl_value.bv_val = NULL;
5230 requestctrls = (LDAPControl **)calloc(2, sizeof (LDAPControl *));
5231 if (requestctrls == NULL) {
5232 ldap_control_free(req);
5233 return (NS_LDAP_MEMORY);
5236 requestctrls[0] = req;
5238 cookie->p_serverctrls = requestctrls;
5240 return (NS_LDAP_SUCCESS);
5244 * int get_new_acct_more_info(BerElement *ber,
5245 * AcctUsableResponse_t *acctResp)
5247 * Decode the more_info data from an Account Management control response,
5248 * when the account is not usable and when code style is from recent LDAP
5249 * servers (see below comments for parse_acct_cont_resp_msg() to get more
5250 * details on coding styles and ASN1 description).
5252 * Expected BER encoding: {tbtbtbtiti}
5253 * +t: tag is 0
5254 * +b: TRUE if inactive due to account inactivation
5255 * +t: tag is 1
5256 * +b: TRUE if password has been reset
5257 * +t: tag is 2
5258 * +b: TRUE if password is expired
5259 * +t: tag is 3
5260 * +i: contains num of remaining grace, 0 means no grace
5261 * +t: tag is 4
5262 * +i: contains num of seconds before auto-unlock. -1 means acct is locked
5263 * forever (i.e. until reset)
5265 * Asumptions:
5266 * - ber is not null
5267 * - acctResp is not null and is initialized with default values for the
5268 * fields in its AcctUsableResp.more_info structure
5269 * - the ber stream is received in the correct order, per the ASN1 description.
5270 * We do not check this order and make the asumption that it is correct.
5271 * Note that the ber stream may not (and will not in most cases) contain
5272 * all fields.
5274 static int
5275 get_new_acct_more_info(BerElement *ber, AcctUsableResponse_t *acctResp)
5277 int rc = NS_LDAP_SUCCESS;
5278 char errstr[MAXERROR];
5279 ber_tag_t rTag = LBER_DEFAULT;
5280 ber_len_t rLen = 0;
5281 ber_int_t rValue;
5282 char *last;
5283 int berRC = 0;
5286 * Look at what more_info BER element is/are left to be decoded.
5287 * look at each of them 1 by 1, without checking on their order
5288 * and possible multi values.
5290 for (rTag = ber_first_element(ber, &rLen, &last);
5291 rTag != LBER_END_OF_SEQORSET;
5292 rTag = ber_next_element(ber, &rLen, last)) {
5294 berRC = 0;
5295 switch (rTag) {
5296 case 0 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5297 /* inactive */
5298 berRC = ber_scanf(ber, "b", &rValue);
5299 if (berRC != LBER_ERROR) {
5300 (acctResp->AcctUsableResp).more_info.
5301 inactive = (rValue != 0) ? 1 : 0;
5303 break;
5305 case 1 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5306 /* reset */
5307 berRC = ber_scanf(ber, "b", &rValue);
5308 if (berRC != LBER_ERROR) {
5309 (acctResp->AcctUsableResp).more_info.reset
5310 = (rValue != 0) ? 1 : 0;
5312 break;
5314 case 2 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5315 /* expired */
5316 berRC = ber_scanf(ber, "b", &rValue);
5317 if (berRC != LBER_ERROR) {
5318 (acctResp->AcctUsableResp).more_info.expired
5319 = (rValue != 0) ? 1 : 0;
5321 break;
5323 case 3 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5324 /* remaining grace */
5325 berRC = ber_scanf(ber, "i", &rValue);
5326 if (berRC != LBER_ERROR) {
5327 (acctResp->AcctUsableResp).more_info.rem_grace
5328 = rValue;
5330 break;
5332 case 4 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5333 /* seconds before unlock */
5334 berRC = ber_scanf(ber, "i", &rValue);
5335 if (berRC != LBER_ERROR) {
5336 (acctResp->AcctUsableResp).more_info.
5337 sec_b4_unlock = rValue;
5339 break;
5341 default :
5342 (void) sprintf(errstr,
5343 gettext("invalid reason tag 0x%x"), rTag);
5344 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5345 rc = NS_LDAP_INTERNAL;
5346 break;
5348 if (berRC == LBER_ERROR) {
5349 (void) sprintf(errstr,
5350 gettext("error 0x%x decoding value for "
5351 "tag 0x%x"), berRC, rTag);
5352 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5353 rc = NS_LDAP_INTERNAL;
5355 if (rc != NS_LDAP_SUCCESS) {
5356 /* exit the for loop */
5357 break;
5361 return (rc);
5365 * int get_old_acct_opt_more_info(BerElement *ber,
5366 * AcctUsableResponse_t *acctResp)
5368 * Decode the optional more_info data from an Account Management control
5369 * response, when the account is not usable and when code style is from LDAP
5370 * server 5.2p4 (see below comments for parse_acct_cont_resp_msg() to get more
5371 * details on coding styles and ASN1 description).
5373 * Expected BER encoding: titi}
5374 * +t: tag is 2
5375 * +i: contains num of remaining grace, 0 means no grace
5376 * +t: tag is 3
5377 * +i: contains num of seconds before auto-unlock. -1 means acct is locked
5378 * forever (i.e. until reset)
5380 * Asumptions:
5381 * - ber is a valid BER element
5382 * - acctResp is initialized for the fields in its AcctUsableResp.more_info
5383 * structure
5385 static int
5386 get_old_acct_opt_more_info(ber_tag_t tag, BerElement *ber,
5387 AcctUsableResponse_t *acctResp)
5389 int rc = NS_LDAP_SUCCESS;
5390 char errstr[MAXERROR];
5391 ber_len_t len;
5392 int rem_grace, sec_b4_unlock;
5394 switch (tag) {
5395 case 2:
5396 /* decode and maybe 3 is following */
5397 if ((tag = ber_scanf(ber, "i", &rem_grace)) == LBER_ERROR) {
5398 (void) sprintf(errstr, gettext("Can not get "
5399 "rem_grace"));
5400 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5401 rc = NS_LDAP_INTERNAL;
5402 break;
5404 (acctResp->AcctUsableResp).more_info.rem_grace = rem_grace;
5406 if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5407 /* this is a success case, break to exit */
5408 (void) sprintf(errstr, gettext("No more "
5409 "optional data"));
5410 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5411 break;
5414 if (tag == 3) {
5415 if (ber_scanf(ber, "i", &sec_b4_unlock) == LBER_ERROR) {
5416 (void) sprintf(errstr,
5417 gettext("Can not get sec_b4_unlock "
5418 "- 1st case"));
5419 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5420 rc = NS_LDAP_INTERNAL;
5421 break;
5423 (acctResp->AcctUsableResp).more_info.sec_b4_unlock =
5424 sec_b4_unlock;
5425 } else { /* unknown tag */
5426 (void) sprintf(errstr, gettext("Unknown tag "
5427 "- 1st case"));
5428 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5429 rc = NS_LDAP_INTERNAL;
5430 break;
5432 break;
5434 case 3:
5435 if (ber_scanf(ber, "i", &sec_b4_unlock) == LBER_ERROR) {
5436 (void) sprintf(errstr, gettext("Can not get "
5437 "sec_b4_unlock - 2nd case"));
5438 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5439 rc = NS_LDAP_INTERNAL;
5440 break;
5442 (acctResp->AcctUsableResp).more_info.sec_b4_unlock =
5443 sec_b4_unlock;
5444 break;
5446 default: /* unknown tag */
5447 (void) sprintf(errstr, gettext("Unknown tag - 2nd case"));
5448 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5449 rc = NS_LDAP_INTERNAL;
5450 break;
5453 return (rc);
5457 * **** This function needs to be moved to libldap library ****
5458 * parse_acct_cont_resp_msg() parses the message received by server according to
5459 * following format (ASN1 notation):
5461 * ACCOUNT_USABLE_RESPONSE::= CHOICE {
5462 * is_available [0] INTEGER,
5463 * ** seconds before expiration **
5464 * is_not_available [1] more_info
5466 * more_info::= SEQUENCE {
5467 * inactive [0] BOOLEAN DEFAULT FALSE,
5468 * reset [1] BOOLEAN DEFAULT FALSE,
5469 * expired [2] BOOLEAN DEFAULT FALSE,
5470 * remaining_grace [3] INTEGER OPTIONAL,
5471 * seconds_before_unlock [4] INTEGER OPTIONAL
5475 * #define used to make the difference between coding style as done
5476 * by LDAP server 5.2p4 and newer LDAP servers. There are 4 values:
5477 * - DS52p4_USABLE: 5.2p4 coding style, account is usable
5478 * - DS52p4_NOT_USABLE: 5.2p4 coding style, account is not usable
5479 * - NEW_USABLE: newer LDAP servers coding style, account is usable
5480 * - NEW_NOT_USABLE: newer LDAP servers coding style, account is not usable
5482 * An account would be considered not usable if for instance:
5483 * - it's been made inactive in the LDAP server
5484 * - or its password was reset in the LDAP server database
5485 * - or its password expired
5486 * - or the account has been locked, possibly forever
5488 #define DS52p4_USABLE 0x00
5489 #define DS52p4_NOT_USABLE 0x01
5490 #define NEW_USABLE 0x00 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE
5491 #define NEW_NOT_USABLE 0x01 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED
5492 static int
5493 parse_acct_cont_resp_msg(LDAPControl **ectrls, AcctUsableResponse_t *acctResp)
5495 int rc = NS_LDAP_SUCCESS;
5496 BerElement *ber;
5497 ber_tag_t tag;
5498 ber_len_t len;
5499 int i;
5500 char errstr[MAXERROR];
5501 /* used for any coding style when account is usable */
5502 int seconds_before_expiry;
5503 /* used for 5.2p4 coding style when account is not usable */
5504 int inactive, reset, expired;
5506 if (ectrls == NULL) {
5507 (void) sprintf(errstr, gettext("Invalid ectrls parameter"));
5508 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5509 return (NS_LDAP_INVALID_PARAM);
5512 for (i = 0; ectrls[i] != NULL; i++) {
5513 if (strcmp(ectrls[i]->ldctl_oid, NS_LDAP_ACCOUNT_USABLE_CONTROL)
5514 == 0) {
5515 break;
5519 if (ectrls[i] == NULL) {
5520 /* Ldap control is not found */
5521 (void) sprintf(errstr, gettext("Account Usable Control "
5522 "not found"));
5523 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5524 return (NS_LDAP_NOTFOUND);
5527 /* Allocate a BER element from the control value and parse it. */
5528 if ((ber = ber_init(&ectrls[i]->ldctl_value)) == NULL)
5529 return (NS_LDAP_MEMORY);
5531 if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5532 /* Ldap decoding error */
5533 (void) sprintf(errstr, gettext("Error decoding 1st tag"));
5534 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5535 ber_free(ber, 1);
5536 return (NS_LDAP_INTERNAL);
5539 switch (tag) {
5540 case DS52p4_USABLE:
5541 case NEW_USABLE:
5542 acctResp->choice = 0;
5543 if (ber_scanf(ber, "i", &seconds_before_expiry)
5544 == LBER_ERROR) {
5545 /* Ldap decoding error */
5546 (void) sprintf(errstr, gettext("Can not get "
5547 "seconds_before_expiry"));
5548 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5549 rc = NS_LDAP_INTERNAL;
5550 break;
5552 /* ber_scanf() succeeded */
5553 (acctResp->AcctUsableResp).seconds_before_expiry =
5554 seconds_before_expiry;
5555 break;
5557 case DS52p4_NOT_USABLE:
5558 acctResp->choice = 1;
5559 if (ber_scanf(ber, "{bbb", &inactive, &reset, &expired)
5560 == LBER_ERROR) {
5561 /* Ldap decoding error */
5562 (void) sprintf(errstr, gettext("Can not get "
5563 "inactive/reset/expired"));
5564 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5565 rc = NS_LDAP_INTERNAL;
5566 break;
5568 /* ber_scanf() succeeded */
5569 (acctResp->AcctUsableResp).more_info.inactive =
5570 ((inactive == 0) ? 0 : 1);
5571 (acctResp->AcctUsableResp).more_info.reset =
5572 ((reset == 0) ? 0 : 1);
5573 (acctResp->AcctUsableResp).more_info.expired =
5574 ((expired == 0) ? 0 : 1);
5575 (acctResp->AcctUsableResp).more_info.rem_grace = 0;
5576 (acctResp->AcctUsableResp).more_info.sec_b4_unlock = 0;
5578 if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5579 /* this is a success case, break to exit */
5580 (void) sprintf(errstr, gettext("No optional data"));
5581 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5582 break;
5586 * Look at what optional more_info BER element is/are
5587 * left to be decoded.
5589 rc = get_old_acct_opt_more_info(tag, ber, acctResp);
5590 break;
5592 case NEW_NOT_USABLE:
5593 acctResp->choice = 1;
5595 * Recent LDAP servers won't code more_info data for default
5596 * values (see above comments on ASN1 description for what
5597 * fields have default values & what fields are optional).
5599 (acctResp->AcctUsableResp).more_info.inactive = 0;
5600 (acctResp->AcctUsableResp).more_info.reset = 0;
5601 (acctResp->AcctUsableResp).more_info.expired = 0;
5602 (acctResp->AcctUsableResp).more_info.rem_grace = 0;
5603 (acctResp->AcctUsableResp).more_info.sec_b4_unlock = 0;
5605 if (len == 0) {
5607 * Nothing else to decode; this is valid and we
5608 * use default values set above.
5610 (void) sprintf(errstr, gettext("more_info is "
5611 "empty, using default values"));
5612 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5613 break;
5617 * Look at what more_info BER element is/are left to
5618 * be decoded.
5620 rc = get_new_acct_more_info(ber, acctResp);
5621 break;
5623 default:
5624 (void) sprintf(errstr, gettext("unknwon coding style "
5625 "(tag: 0x%x)"), tag);
5626 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5627 rc = NS_LDAP_INTERNAL;
5628 break;
5631 ber_free(ber, 1);
5632 return (rc);
5636 * internal function for __ns_ldap_getAcctMgmt()
5638 static int
5639 getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp,
5640 ns_conn_user_t *conn_user)
5642 int scope, rc;
5643 char ldapfilter[1024];
5644 ns_ldap_cookie_t *cookie;
5645 ns_ldap_search_desc_t **sdlist = NULL;
5646 ns_ldap_search_desc_t *dptr;
5647 ns_ldap_error_t *error = NULL;
5648 char **dns = NULL;
5649 char service[] = "shadow";
5651 if (user == NULL || acctResp == NULL)
5652 return (NS_LDAP_INVALID_PARAM);
5654 /* Initialize State machine cookie */
5655 cookie = init_search_state_machine();
5656 if (cookie == NULL)
5657 return (NS_LDAP_MEMORY);
5658 cookie->conn_user = conn_user;
5660 /* see if need to follow referrals */
5661 rc = __s_api_toFollowReferrals(0,
5662 &cookie->followRef, &error);
5663 if (rc != NS_LDAP_SUCCESS) {
5664 (void) __ns_ldap_freeError(&error);
5665 goto out;
5668 /* get the service descriptor - or create a default one */
5669 rc = __s_api_get_SSD_from_SSDtoUse_service(service,
5670 &sdlist, &error);
5671 if (rc != NS_LDAP_SUCCESS) {
5672 (void) __ns_ldap_freeError(&error);
5673 goto out;
5676 if (sdlist == NULL) {
5677 /* Create default service Desc */
5678 sdlist = (ns_ldap_search_desc_t **)calloc(2,
5679 sizeof (ns_ldap_search_desc_t *));
5680 if (sdlist == NULL) {
5681 rc = NS_LDAP_MEMORY;
5682 goto out;
5684 dptr = (ns_ldap_search_desc_t *)
5685 calloc(1, sizeof (ns_ldap_search_desc_t));
5686 if (dptr == NULL) {
5687 free(sdlist);
5688 rc = NS_LDAP_MEMORY;
5689 goto out;
5691 sdlist[0] = dptr;
5693 /* default base */
5694 rc = __s_api_getDNs(&dns, service, &cookie->errorp);
5695 if (rc != NS_LDAP_SUCCESS) {
5696 if (dns) {
5697 __s_api_free2dArray(dns);
5698 dns = NULL;
5700 (void) __ns_ldap_freeError(&(cookie->errorp));
5701 cookie->errorp = NULL;
5702 goto out;
5704 dptr->basedn = strdup(dns[0]);
5705 if (dptr->basedn == NULL) {
5706 free(sdlist);
5707 free(dptr);
5708 if (dns) {
5709 __s_api_free2dArray(dns);
5710 dns = NULL;
5712 rc = NS_LDAP_MEMORY;
5713 goto out;
5715 __s_api_free2dArray(dns);
5716 dns = NULL;
5718 /* default scope */
5719 scope = 0;
5720 rc = __s_api_getSearchScope(&scope, &cookie->errorp);
5721 dptr->scope = scope;
5724 cookie->sdlist = sdlist;
5726 cookie->service = strdup(service);
5727 if (cookie->service == NULL) {
5728 rc = NS_LDAP_MEMORY;
5729 goto out;
5732 /* search for entries for this particular uid */
5733 (void) snprintf(ldapfilter, sizeof (ldapfilter), "(uid=%s)", user);
5734 cookie->i_filter = strdup(ldapfilter);
5735 if (cookie->i_filter == NULL) {
5736 rc = NS_LDAP_MEMORY;
5737 goto out;
5740 /* create the control request */
5741 if ((rc = setup_acctmgmt_params(cookie)) != NS_LDAP_SUCCESS)
5742 goto out;
5744 /* Process search */
5745 rc = search_state_machine(cookie, GET_ACCT_MGMT_INFO, 0);
5747 /* Copy results back to user */
5748 rc = cookie->err_rc;
5749 if (rc != NS_LDAP_SUCCESS)
5750 (void) __ns_ldap_freeError(&(cookie->errorp));
5752 if (cookie->result == NULL)
5753 goto out;
5755 if ((rc = parse_acct_cont_resp_msg(cookie->resultctrl, acctResp))
5756 != NS_LDAP_SUCCESS)
5757 goto out;
5759 rc = NS_LDAP_SUCCESS;
5761 out:
5762 delete_search_cookie(cookie);
5764 return (rc);
5768 * __ns_ldap_getAcctMgmt() is called from pam account management stack
5769 * for retrieving accounting information of users with no user password -
5770 * eg. rlogin, rsh, etc. This function uses the account management control
5771 * request to do a search on the server for the user in question. The
5772 * response control returned from the server is got from the cookie.
5773 * Input params: username of whose account mgmt information is to be got
5774 * pointer to hold the parsed account management information
5775 * Return values: NS_LDAP_SUCCESS on success or appropriate error
5776 * code on failure
5779 __ns_ldap_getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp)
5781 ns_conn_user_t *cu = NULL;
5782 int try_cnt = 0;
5783 int rc = NS_LDAP_SUCCESS;
5784 ns_ldap_error_t *error = NULL;
5786 for (;;) {
5787 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
5788 &try_cnt, &rc, &error) == 0)
5789 break;
5790 rc = getAcctMgmt(user, acctResp, cu);
5792 return (rc);