ns_reads.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <libintl.h>
#include <ctype.h>
#include <syslog.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include "ns_sldap.h"
#include "ns_internal.h"
#include "ns_cache_door.h"
#define _NIS_FILTER "nisdomain=*"
#define _NIS_DOMAIN "nisdomain"
static const char *nis_domain_attrs[] = {
_NIS_DOMAIN,
(char *)NULL
};
static int validate_filter(ns_ldap_cookie_t *cookie);
void
__ns_ldap_freeEntry(ns_ldap_entry_t *ep)
{
int j, k = 0;
if (ep == NULL)
return;
if (ep->attr_pair == NULL) {
free(ep);
return;
}
for (j = 0; j < ep->attr_count; j++) {
if (ep->attr_pair[j] == NULL)
continue;
if (ep->attr_pair[j]->attrname)
free(ep->attr_pair[j]->attrname);
if (ep->attr_pair[j]->attrvalue) {
for (k = 0; (k < ep->attr_pair[j]->value_count) &&
(ep->attr_pair[j]->attrvalue[k]); k++) {
free(ep->attr_pair[j]->attrvalue[k]);
}
free(ep->attr_pair[j]->attrvalue);
}
free(ep->attr_pair[j]);
}
free(ep->attr_pair);
free(ep);
}
static void
_freeControlList(LDAPControl ***ctrls)
{
LDAPControl **ctrl;
if (ctrls == NULL || *ctrls == NULL)
return;
for (ctrl = *ctrls; *ctrl != NULL; ctrl++)
ldap_control_free(*ctrl);
free(*ctrls);
*ctrls = NULL;
}
/*
* Convert attribute type in a RDN that has an attribute mapping to the
* original mappped type.
* e.g.
* cn<->cn-st and iphostnumber<->iphostnumber-st
* cn-st=aaa+iphostnumber-st=10.10.01.01
* is mapped to
* cn=aaa+iphostnumber=10.10.01.01
*
* Input - service: e.g. hosts, passwd etc.
* rdn: RDN
* Return: NULL - No attribute mapping in the RDN
* Non-NULL - The attribute type(s) in the RDN are mapped and
* the memory is allocated for the new rdn.
*
*/
static char *
_cvtRDN(const char *service, const char *rdn) {
char **attrs, **mapped_attrs, **mapp, *type, *value, *attr;
char *new_rdn = NULL;
int nAttr = 0, i, attr_mapped, len = 0;
/* Break down "type=value\0" pairs. Assume RDN is normalized */
if ((attrs = ldap_explode_rdn(rdn, 0)) == NULL)
return (NULL);
for (nAttr = 0; attrs[nAttr] != NULL; nAttr++);
if ((mapped_attrs = (char **)calloc(nAttr, sizeof (char *))) == NULL) {
ldap_value_free(attrs);
return (NULL);
}
attr_mapped = 0;
for (i = 0; i < nAttr; i++) {
/* Parse type=value pair */
if ((type = strtok_r(attrs[i], "=", &value)) == NULL ||
value == NULL)
goto cleanup;
/* Reverse map: e.g. cn-sm -> cn */
mapp = __ns_ldap_getOrigAttribute(service, type);
if (mapp != NULL && mapp[0] != NULL) {
/* The attribute mapping is found */
type = mapp[0];
attr_mapped = 1;
/* "type=value\0" */
len = strlen(type) + strlen(value) + 2;
/* Reconstruct type=value pair. A string is allocated */
if ((attr = (char *)calloc(1, len)) == NULL) {
__s_api_free2dArray(mapp);
goto cleanup;
}
(void) snprintf(attr, len, "%s=%s",
type, value);
mapped_attrs[i] = attr;
} else {
/*
* No attribute mapping. attrs[i] is going to be copied
* later. Restore "type\0value\0" back to
* "type=value\0".
*/
type[strlen(type)] = '=';
}
__s_api_free2dArray(mapp);
}
if (attr_mapped == 0)
/* No attribute mapping. Don't bother to reconstruct RDN */
goto cleanup;
len = 0;
/* Reconstruct RDN from type=value pairs */
for (i = 0; i < nAttr; i++) {
if (mapped_attrs[i])
len += strlen(mapped_attrs[i]);
else
len += strlen(attrs[i]);
/* Add 1 for "+" */
len++;
}
if ((new_rdn = (char *)calloc(1, ++len)) == NULL)
goto cleanup;
for (i = 0; i < nAttr; i++) {
if (i > 0)
/* Add seperator */
(void) strlcat(new_rdn, "+", len);
if (mapped_attrs[i])
(void) strlcat(new_rdn, mapped_attrs[i], len);
else
(void) strlcat(new_rdn, attrs[i], len);
}
cleanup:
ldap_value_free(attrs);
if (mapped_attrs) {
if (attr_mapped) {
for (i = 0; i < nAttr; i++) {
if (mapped_attrs[i])
free(mapped_attrs[i]);
}
}
free(mapped_attrs);
}
return (new_rdn);
}
/*
* Convert attribute type in a DN that has an attribute mapping to the
* original mappped type.
* e.g
* The mappings are cn<->cn-sm, iphostnumber<->iphostnumber-sm
*
* dn: cn-sm=aaa+iphostnumber-sm=9.9.9.9,dc=central,dc=sun,dc=com
* is converted to
* dn: cn=aaa+iphostnumber=9.9.9.9,dc=central,dc=sun,dc=com
*
* Input - service: e.g. hosts, passwd etc.
* dn: the value of a distinguished name
* Return - NULL: error
* non-NULL: A converted DN and the memory is allocated
*/
static char *
_cvtDN(const char *service, const char *dn) {
char **mapped_rdns;
char **rdns, *new_rdn, *new_dn = NULL;
int nRdn = 0, i, len = 0, rdn_mapped;
if (service == NULL || dn == NULL)
return (NULL);
if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
return (NULL);
for (nRdn = 0; rdns[nRdn] != NULL; nRdn++);
if ((mapped_rdns = (char **)calloc(nRdn, sizeof (char *))) == NULL) {
ldap_value_free(rdns);
return (NULL);
}
rdn_mapped = 0;
/* Break down RDNs in a DN */
for (i = 0; i < nRdn; i++) {
if ((new_rdn = _cvtRDN(service, rdns[i])) != NULL) {
mapped_rdns[i] = new_rdn;
rdn_mapped = 1;
}
}
if (rdn_mapped == 0) {
/*
* No RDN contains any attribute mapping.
* Don't bother to reconstruct DN from RDN. Copy DN directly.
*/
new_dn = strdup(dn);
goto cleanup;
}
/*
* Reconstruct dn from RDNs.
* Calculate the length first.
*/
for (i = 0; i < nRdn; i++) {
if (mapped_rdns[i])
len += strlen(mapped_rdns[i]);
else
len += strlen(rdns[i]);
/* add 1 for ',' */
len ++;
}
if ((new_dn = (char *)calloc(1, ++len)) == NULL)
goto cleanup;
for (i = 0; i < nRdn; i++) {
if (i > 0)
/* Add seperator */
(void) strlcat(new_dn, ",", len);
if (mapped_rdns[i])
(void) strlcat(new_dn, mapped_rdns[i], len);
else
(void) strlcat(new_dn, rdns[i], len);
}
cleanup:
ldap_value_free(rdns);
if (mapped_rdns) {
if (rdn_mapped) {
for (i = 0; i < nRdn; i++) {
if (mapped_rdns[i])
free(mapped_rdns[i]);
}
}
free(mapped_rdns);
}
return (new_dn);
}
/*
* Convert a single ldap entry from a LDAPMessage
* into an ns_ldap_entry structure.
* Schema map the entry if specified in flags
*/
static int
__s_api_cvtEntry(LDAP *ld,
const char *service,
LDAPMessage *e,
int flags,
ns_ldap_entry_t **ret,
ns_ldap_error_t **error)
{
ns_ldap_entry_t *ep = NULL;
ns_ldap_attr_t **ap = NULL;
BerElement *ber;
char *attr = NULL;
char **vals = NULL;
char **mapping;
char *dn;
int nAttrs = 0;
int i, j, k = 0;
char **gecos_mapping = NULL;
int gecos_val_index[3] = { -1, -1, -1};
char errstr[MAXERROR];
int schema_mapping_existed = FALSE;
int gecos_mapping_existed = FALSE;
int gecos_attr_matched;
int auto_service = FALSE;
int rc = NS_LDAP_SUCCESS;
if (e == NULL || ret == NULL || error == NULL)
return (NS_LDAP_INVALID_PARAM);
*error = NULL;
ep = (ns_ldap_entry_t *)calloc(1, sizeof (ns_ldap_entry_t));
if (ep == NULL)
return (NS_LDAP_MEMORY);
if (service != NULL &&
(strncasecmp(service, "auto_", 5) == 0 ||
strcasecmp(service, "automount") == 0))
auto_service = TRUE;
/*
* see if schema mapping existed for the given service
*/
mapping = __ns_ldap_getOrigAttribute(service,
NS_HASH_SCHEMA_MAPPING_EXISTED);
if (mapping) {
schema_mapping_existed = TRUE;
__s_api_free2dArray(mapping);
mapping = NULL;
} else if (auto_service) {
/*
* If service == auto_* and no
* schema mapping found
* then try automount
* There is certain case that schema mapping exist
* but __ns_ldap_getOrigAttribute(service,
* NS_HASH_SCHEMA_MAPPING_EXISTED);
* returns NULL.
* e.g.
* NS_LDAP_ATTRIBUTEMAP = automount:automountMapName=AAA
* NS_LDAP_OBJECTCLASSMAP = automount:automountMap=MynisMap
* NS_LDAP_OBJECTCLASSMAP = automount:automount=MynisObject
*
* Make a check for schema_mapping_existed here
* so later on __s_api_convert_automountmapname won't be called
* unnecessarily. It is also used for attribute mapping
* and objectclass mapping.
*/
mapping = __ns_ldap_getOrigAttribute("automount",
NS_HASH_SCHEMA_MAPPING_EXISTED);
if (mapping) {
schema_mapping_existed = TRUE;
__s_api_free2dArray(mapping);
mapping = NULL;
}
}
nAttrs = 1; /* start with 1 for the DN attr */
for (attr = ldap_first_attribute(ld, e, &ber); attr != NULL;
attr = ldap_next_attribute(ld, e, ber)) {
nAttrs++;
ldap_memfree(attr);
attr = NULL;
}
ber_free(ber, 0);
ber = NULL;
ep->attr_count = nAttrs;
/*
* add 1 for "gecos" 1 to N attribute mapping,
* just in case it is needed.
* ep->attr_count will be updated later if that is true.
*/
ap = (ns_ldap_attr_t **)calloc(ep->attr_count + 1,
sizeof (ns_ldap_attr_t *));
if (ap == NULL) {
__ns_ldap_freeEntry(ep);
ep = NULL;
return (NS_LDAP_MEMORY);
}
ep->attr_pair = ap;
/* DN attribute */
dn = ldap_get_dn(ld, e);
ap[0] = (ns_ldap_attr_t *)calloc(1, sizeof (ns_ldap_attr_t));
if (ap[0] == NULL) {
ldap_memfree(dn);
dn = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
return (NS_LDAP_MEMORY);
}
if ((ap[0]->attrname = strdup("dn")) == NULL) {
ldap_memfree(dn);
dn = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
return (NS_LDAP_INVALID_PARAM);
}
ap[0]->value_count = 1;
if ((ap[0]->attrvalue = (char **)
calloc(2, sizeof (char *))) == NULL) {
ldap_memfree(dn);
dn = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
return (NS_LDAP_MEMORY);
}
if (schema_mapping_existed)
ap[0]->attrvalue[0] = _cvtDN(service, dn);
else
ap[0]->attrvalue[0] = strdup(dn);
if (ap[0]->attrvalue[0] == NULL) {
ldap_memfree(dn);
dn = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
return (NS_LDAP_MEMORY);
}
ldap_memfree(dn);
dn = NULL;
if ((flags & NS_LDAP_NOMAP) == 0 && auto_service &&
schema_mapping_existed) {
rc = __s_api_convert_automountmapname(service,
&ap[0]->attrvalue[0],
error);
if (rc != NS_LDAP_SUCCESS) {
__ns_ldap_freeEntry(ep);
ep = NULL;
return (rc);
}
}
/* other attributes */
for (attr = ldap_first_attribute(ld, e, &ber), j = 1;
attr != NULL && j != nAttrs;
attr = ldap_next_attribute(ld, e, ber), j++) {
/* allocate new attr name */
if ((ap[j] = (ns_ldap_attr_t *)
calloc(1, sizeof (ns_ldap_attr_t))) == NULL) {
ber_free(ber, 0);
ber = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
if (gecos_mapping)
__s_api_free2dArray(gecos_mapping);
gecos_mapping = NULL;
return (NS_LDAP_MEMORY);
}
if ((flags & NS_LDAP_NOMAP) || schema_mapping_existed == FALSE)
mapping = NULL;
else
mapping = __ns_ldap_getOrigAttribute(service, attr);
if (mapping == NULL && auto_service &&
schema_mapping_existed &&
(flags & NS_LDAP_NOMAP) == 0)
/*
* if service == auto_* and no
* schema mapping found
* and schema_mapping_existed is TRUE
* and NS_LDAP_NOMAP is not set
* then try automount
* e.g.
* NS_LDAP_ATTRIBUTEMAP = automount:automountMapName=AAA
*/
mapping = __ns_ldap_getOrigAttribute("automount", attr);
if (mapping == NULL) {
if ((ap[j]->attrname = strdup(attr)) == NULL) {
ber_free(ber, 0);
ber = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
if (gecos_mapping)
__s_api_free2dArray(gecos_mapping);
gecos_mapping = NULL;
return (NS_LDAP_MEMORY);
}
} else {
/*
* for "gecos" 1 to N mapping,
* do not remove the mapped attribute,
* just create a new gecos attribute
* and append it to the end of the attribute list
*/
if (strcasecmp(mapping[0], "gecos") == 0) {
ap[j]->attrname = strdup(attr);
gecos_mapping_existed = TRUE;
} else
ap[j]->attrname = strdup(mapping[0]);
if (ap[j]->attrname == NULL) {
ber_free(ber, 0);
ber = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
if (gecos_mapping)
__s_api_free2dArray(gecos_mapping);
gecos_mapping = NULL;
return (NS_LDAP_MEMORY);
}
/*
* 1 to N attribute mapping processing
* is only done for "gecos"
*/
if (strcasecmp(mapping[0], "gecos") == 0) {
/*
* get attribute mapping for "gecos",
* need to know the number and order of the
* mapped attributes
*/
if (gecos_mapping == NULL) {
gecos_mapping =
__ns_ldap_getMappedAttributes(service,
mapping[0]);
if (gecos_mapping == NULL ||
gecos_mapping[0] == NULL) {
/*
* this should never happens,
* syslog the error
*/
(void) sprintf(errstr,
gettext(
"Attribute mapping "
"inconsistency "
"found for attributes "
"'%s' and '%s'."),
mapping[0], attr);
syslog(LOG_ERR, "libsldap: %s", errstr);
ber_free(ber, 0);
ber = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
__s_api_free2dArray(mapping);
mapping = NULL;
if (gecos_mapping)
__s_api_free2dArray(
gecos_mapping);
gecos_mapping = NULL;
return (NS_LDAP_INTERNAL);
}
}
/*
* is this attribute the 1st, 2nd, or
* 3rd attr in the mapping list?
*/
gecos_attr_matched = FALSE;
for (i = 0; i < 3 && gecos_mapping[i]; i++) {
if (gecos_mapping[i] &&
strcasecmp(gecos_mapping[i],
attr) == 0) {
gecos_val_index[i] = j;
gecos_attr_matched = TRUE;
break;
}
}
if (gecos_attr_matched == FALSE) {
/*
* Not match found.
* This should never happens,
* syslog the error
*/
(void) sprintf(errstr,
gettext(
"Attribute mapping "
"inconsistency "
"found for attributes "
"'%s' and '%s'."),
mapping[0], attr);
syslog(LOG_ERR, "libsldap: %s", errstr);
ber_free(ber, 0);
ber = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
__s_api_free2dArray(mapping);
mapping = NULL;
__s_api_free2dArray(gecos_mapping);
gecos_mapping = NULL;
return (NS_LDAP_INTERNAL);
}
}
__s_api_free2dArray(mapping);
mapping = NULL;
}
if ((vals = ldap_get_values(ld, e, attr)) != NULL) {
if ((ap[j]->value_count = ldap_count_values(vals)) == 0) {
ldap_value_free(vals);
vals = NULL;
continue;
} else {
ap[j]->attrvalue = (char **)
calloc(ap[j]->value_count+1, sizeof (char *));
if (ap[j]->attrvalue == NULL) {
ber_free(ber, 0);
ber = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
if (gecos_mapping)
__s_api_free2dArray(gecos_mapping);
gecos_mapping = NULL;
return (NS_LDAP_MEMORY);
}
}
/* map object classes if necessary */
if ((flags & NS_LDAP_NOMAP) == 0 &&
schema_mapping_existed && ap[j]->attrname &&
strcasecmp(ap[j]->attrname, "objectclass") == 0) {
for (k = 0; k < ap[j]->value_count; k++) {
mapping =
__ns_ldap_getOrigObjectClass(service, vals[k]);
if (mapping == NULL && auto_service)
/*
* if service == auto_* and no
* schema mapping found
* then try automount
*/
mapping =
__ns_ldap_getOrigObjectClass(
"automount", vals[k]);
if (mapping == NULL) {
ap[j]->attrvalue[k] = strdup(vals[k]);
} else {
ap[j]->attrvalue[k] = strdup(mapping[0]);
__s_api_free2dArray(mapping);
mapping = NULL;
}
if (ap[j]->attrvalue[k] == NULL) {
ber_free(ber, 0);
ber = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
if (gecos_mapping)
__s_api_free2dArray(gecos_mapping);
gecos_mapping = NULL;
return (NS_LDAP_MEMORY);
}
}
} else {
for (k = 0; k < ap[j]->value_count; k++) {
if ((ap[j]->attrvalue[k] = strdup(vals[k])) == NULL) {
ber_free(ber, 0);
ber = NULL;
__ns_ldap_freeEntry(ep);
ep = NULL;
if (gecos_mapping)
__s_api_free2dArray(gecos_mapping);
gecos_mapping = NULL;
return (NS_LDAP_MEMORY);
}
}
}
ap[j]->attrvalue[k] = NULL;
ldap_value_free(vals);
vals = NULL;
}
ldap_memfree(attr);
attr = NULL;
}
ber_free(ber, 0);
ber = NULL;
if (gecos_mapping) {
__s_api_free2dArray(gecos_mapping);
gecos_mapping = NULL;
}
/* special processing for gecos 1 to up to 3 attribute mapping */
if (schema_mapping_existed && gecos_mapping_existed) {
int f = -1;
for (i = 0; i < 3; i++) {
k = gecos_val_index[i];
/*
* f is the index of the first returned
* attribute which "gecos" attribute mapped to
*/
if (k != -1 && f == -1)
f = k;
if (k != -1 && ap[k]->value_count > 0 &&
ap[k]->attrvalue[0] &&
strlen(ap[k]->attrvalue[0]) > 0) {
if (k == f) {
/*
* Create and fill in the last reserved
* ap with the data from the "gecos"
* mapping attributes
*/
ap[nAttrs] = (ns_ldap_attr_t *)
calloc(1,
sizeof (ns_ldap_attr_t));
if (ap[nAttrs] == NULL) {
__ns_ldap_freeEntry(ep);
ep = NULL;
return (NS_LDAP_MEMORY);
}
ap[nAttrs]->attrvalue = (char **)calloc(
2, sizeof (char *));
if (ap[nAttrs]->attrvalue == NULL) {
__ns_ldap_freeEntry(ep);
ep = NULL;
return (NS_LDAP_MEMORY);
}
/* add 1 more for a possible "," */
ap[nAttrs]->attrvalue[0] =
(char *)calloc(
strlen(ap[f]->attrvalue[0]) +
2, 1);
if (ap[nAttrs]->attrvalue[0] == NULL) {
__ns_ldap_freeEntry(ep);
ep = NULL;
return (NS_LDAP_MEMORY);
}
(void) strcpy(ap[nAttrs]->attrvalue[0],
ap[f]->attrvalue[0]);
ap[nAttrs]->attrname = strdup("gecos");
if (ap[nAttrs]->attrname == NULL) {
__ns_ldap_freeEntry(ep);
ep = NULL;
return (NS_LDAP_MEMORY);
}
ap[nAttrs]->value_count = 1;
ep->attr_count = nAttrs + 1;
} else {
(void) strcat(ap[nAttrs]->attrvalue[0],
",");
ap[nAttrs]->attrvalue[0] =
(char *)realloc(
ap[nAttrs]->attrvalue[0],
strlen(ap[nAttrs]->
attrvalue[0]) + 2);
if (ap[nAttrs]->attrvalue[0] == NULL) {
__ns_ldap_freeEntry(ep);
ep = NULL;
return (NS_LDAP_MEMORY);
}
(void) strcat(ap[nAttrs]->attrvalue[0],
ap[k]->attrvalue[0]);
}
}
}
}
*ret = ep;
return (NS_LDAP_SUCCESS);
}
static int
__s_api_getEntry(ns_ldap_cookie_t *cookie)
{
ns_ldap_entry_t *curEntry = NULL;
int ret;
#ifdef DEBUG
(void) fprintf(stderr, "__s_api_getEntry START\n");
#endif
if (cookie->resultMsg == NULL) {
return (NS_LDAP_INVALID_PARAM);
}
ret = __s_api_cvtEntry(cookie->conn->ld, cookie->service,
cookie->resultMsg, cookie->i_flags,
&curEntry, &cookie->errorp);
if (ret != NS_LDAP_SUCCESS) {
return (ret);
}
if (cookie->result == NULL) {
cookie->result = (ns_ldap_result_t *)
calloc(1, sizeof (ns_ldap_result_t));
if (cookie->result == NULL) {
__ns_ldap_freeEntry(curEntry);
curEntry = NULL;
return (NS_LDAP_MEMORY);
}
cookie->result->entry = curEntry;
cookie->nextEntry = curEntry;
} else {
cookie->nextEntry->next = curEntry;
cookie->nextEntry = curEntry;
}
cookie->result->entries_count++;
return (NS_LDAP_SUCCESS);
}
static int
__s_api_get_cachemgr_data(const char *type,
const char *from, char **to)
{
union {
ldap_data_t s_d;
char s_b[DOORBUFFERSIZE];
} space;
ldap_data_t *sptr;
int ndata;
int adata;
int rc;
#ifdef DEBUG
(void) fprintf(stderr, "__s_api_get_cachemgr_data START\n");
#endif
if (from == NULL || from[0] == '\0' || to == NULL)
return (-1);
*to = NULL;
(void) memset(space.s_b, 0, DOORBUFFERSIZE);
space.s_d.ldap_call.ldap_callnumber = GETCACHE;
(void) snprintf(space.s_d.ldap_call.ldap_u.domainname,
DOORBUFFERSIZE - sizeof (space.s_d.ldap_call.ldap_callnumber),
"%s%s%s",
type,
DOORLINESEP,
from);
ndata = sizeof (space);
adata = sizeof (ldap_call_t) +
strlen(space.s_d.ldap_call.ldap_u.domainname) + 1;
sptr = &space.s_d;
rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
if (rc != SUCCESS)
return (-1);
else
*to = strdup(sptr->ldap_ret.ldap_u.buff);
return (NS_LDAP_SUCCESS);
}
static int
__s_api_set_cachemgr_data(const char *type,
const char *from, const char *to)
{
union {
ldap_data_t s_d;
char s_b[DOORBUFFERSIZE];
} space;
ldap_data_t *sptr;
int ndata;
int adata;
int rc;
#ifdef DEBUG
(void) fprintf(stderr, "__s_api_set_cachemgr_data START\n");
#endif
if ((from == NULL) || (from[0] == '\0') ||
(to == NULL) || (to[0] == '\0'))
return (-1);
(void) memset(space.s_b, 0, DOORBUFFERSIZE);
space.s_d.ldap_call.ldap_callnumber = SETCACHE;
(void) snprintf(space.s_d.ldap_call.ldap_u.domainname,
DOORBUFFERSIZE - sizeof (space.s_d.ldap_call.ldap_callnumber),
"%s%s%s%s%s",
type,
DOORLINESEP,
from,
DOORLINESEP,
to);
ndata = sizeof (space);
adata = sizeof (ldap_call_t) +
strlen(space.s_d.ldap_call.ldap_u.domainname) + 1;
sptr = &space.s_d;
rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
if (rc != SUCCESS)
return (-1);
return (NS_LDAP_SUCCESS);
}
static char *
__s_api_remove_rdn_space(char *rdn)
{
char *tf, *tl, *vf, *vl, *eqsign;
/* if no space(s) to remove, return */
if (strchr(rdn, SPACETOK) == NULL)
return (rdn);
/* if no '=' separator, return */
eqsign = strchr(rdn, '=');
if (eqsign == NULL)
return (rdn);
tf = rdn;
tl = eqsign - 1;
vf = eqsign + 1;
vl = rdn + strlen(rdn) - 1;
/* now two strings, type and value */
*eqsign = '\0';
/* remove type's leading spaces */
while (tf < tl && *tf == SPACETOK)
tf++;
/* remove type's trailing spaces */
while (tf < tl && *tl == SPACETOK)
tl--;
/* add '=' separator back */
*(++tl) = '=';
/* remove value's leading spaces */
while (vf < vl && *vf == SPACETOK)
vf++;
/* remove value's trailing spaces */
while (vf < vl && *vl == SPACETOK)
*vl-- = '\0';
/* move value up if necessary */
if (vf != tl + 1)
(void) strcpy(tl + 1, vf);
return (tf);
}
static
ns_ldap_cookie_t *
init_search_state_machine()
{
ns_ldap_cookie_t *cookie;
ns_config_t *cfg;
cookie = (ns_ldap_cookie_t *)calloc(1, sizeof (ns_ldap_cookie_t));
if (cookie == NULL)
return (NULL);
cookie->state = INIT;
/* assign other state variables */
cfg = __s_api_loadrefresh_config();
cookie->connectionId = -1;
if (cfg == NULL ||
cfg->paramList[NS_LDAP_SEARCH_TIME_P].ns_ptype == NS_UNKNOWN) {
cookie->search_timeout.tv_sec = NS_DEFAULT_SEARCH_TIMEOUT;
} else {
cookie->search_timeout.tv_sec =
cfg->paramList[NS_LDAP_SEARCH_TIME_P].ns_i;
}
if (cfg != NULL)
__s_api_release_config(cfg);
cookie->search_timeout.tv_usec = 0;
return (cookie);
}
static void
delete_search_cookie(ns_ldap_cookie_t *cookie)
{
if (cookie == NULL)
return;
if (cookie->connectionId > -1)
DropConnection(cookie->connectionId, cookie->i_flags);
if (cookie->filter)
free(cookie->filter);
if (cookie->i_filter)
free(cookie->i_filter);
if (cookie->service)
free(cookie->service);
if (cookie->sdlist)
(void) __ns_ldap_freeSearchDescriptors(&(cookie->sdlist));
if (cookie->result)
(void) __ns_ldap_freeResult(&cookie->result);
if (cookie->attribute)
__s_api_free2dArray(cookie->attribute);
if (cookie->errorp)
(void) __ns_ldap_freeError(&cookie->errorp);
if (cookie->reflist)
__s_api_deleteRefInfo(cookie->reflist);
if (cookie->basedn)
free(cookie->basedn);
if (cookie->ctrlCookie)
ber_bvfree(cookie->ctrlCookie);
_freeControlList(&cookie->p_serverctrls);
free(cookie);
}
static int
get_mapped_filter(ns_ldap_cookie_t *cookie, char **new_filter)
{
typedef struct filter_mapping_info {
char oc_or_attr;
char *name_start;
char *name_end;
char *veq_pos;
char *from_name;
char *to_name;
char **mapping;
} filter_mapping_info_t;
char *c, *last_copied;
char *filter_c, *filter_c_next;
char *key, *tail, *head;
char errstr[MAXERROR];
int num_eq = 0, num_veq = 0;
int in_quote = FALSE;
int i, j, oc_len, len;
int at_least_one = FALSE;
filter_mapping_info_t **info, *info1;
char **mapping;
char *service, *filter, *err;
int auto_service = FALSE;
if (cookie == NULL || new_filter == NULL)
return (NS_LDAP_INVALID_PARAM);
*new_filter = NULL;
service = cookie->service;
filter = cookie->filter;
/*
* count the number of '=' char
*/
for (c = filter; *c; c++) {
if (*c == TOKENSEPARATOR)
num_eq++;
}
if (service != NULL && strncasecmp(service, "auto_", 5) == 0)
auto_service = TRUE;
/*
* See if schema mapping existed for the given service.
* If not, just return success.
*/
mapping = __ns_ldap_getOrigAttribute(service,
NS_HASH_SCHEMA_MAPPING_EXISTED);
if (mapping == NULL && auto_service)
/*
* if service == auto_* and no
* schema mapping found
* then try automount
*/
mapping = __ns_ldap_getOrigAttribute(
"automount", NS_HASH_SCHEMA_MAPPING_EXISTED);
if (mapping)
__s_api_free2dArray(mapping);
else
return (NS_LDAP_SUCCESS);
/*
* no '=' sign, just say OK and return nothing
*/
if (num_eq == 0)
return (NS_LDAP_SUCCESS);
/*
* Make a copy of the filter string
* for saving the name of the objectclasses or
* attributes that need to be passed to the
* objectclass or attribute mapping functions.
* pointer "info->from_name" points to the locations
* within this string.
*
* The input filter string, filter, will be used
* to indicate where these names start and end.
* pointers "info->name_start" and "info->name_end"
* point to locations within the input filter string,
* and are used at the end of this function to
* merge the original filter data with the
* mapped objectclass or attribute names.
*/
filter_c = strdup(filter);
if (filter_c == NULL)
return (NS_LDAP_MEMORY);
filter_c_next = filter_c;
/*
* get memory for info arrays
*/
info = (filter_mapping_info_t **)calloc(num_eq + 1,
sizeof (filter_mapping_info_t *));
if (info == NULL) {
free(filter_c);
return (NS_LDAP_MEMORY);
}
/*
* find valid '=' for further processing,
* ignore the "escaped =" (.i.e. "\="), or
* "=" in quoted string
*/
for (c = filter_c; *c; c++) {
switch (*c) {
case TOKENSEPARATOR:
if (!in_quote) {
info1 = (filter_mapping_info_t *)calloc(1,
sizeof (filter_mapping_info_t));
if (!info1) {
free(filter_c);
for (i = 0; i < num_veq; i++)
free(info[i]);
free(info);
return (NS_LDAP_MEMORY);
}
info[num_veq] = info1;
/*
* remember the location of this "="
*/
info[num_veq++]->veq_pos = c;
}
break;
case QUOTETOK:
/*
* switch on/off the in_quote mode
*/
in_quote = (in_quote == FALSE);
break;
case '\\':
/*
* ignore escape characters
* don't skip if next char is '\0'
*/
if (!in_quote)
if (*(++c) == '\0')
c--;
break;
}
}
/*
* for each valid "=" found, get the name to
* be mapped
*/
oc_len = strlen("objectclass");
for (i = 0; i < num_veq; i++) {
/*
* look at the left side of "=" to see
* if assertion is "objectclass=<ocname>"
* or "<attribute name>=<attribute value>"
*
* first skip spaces before "=".
* Note that filter_c_next may not point to the
* start of the filter string. For i > 0,
* it points to the end of the last name processed + 2
*/
for (tail = info[i]->veq_pos; (tail > filter_c_next) &&
(*(tail - 1) == SPACETOK); tail--);
/*
* mark the end of the left side string (the key)
*/
*tail = '\0';
info[i]->name_end = tail - filter_c - 1 + filter;
/*
* find the start of the key
*/
key = filter_c_next;
for (c = tail; filter_c_next <= c; c--) {
/* OPARATOK is '(' */
if (*c == OPARATOK ||
*c == SPACETOK) {
key = c + 1;
break;
}
}
info[i]->name_start = key - filter_c + filter;
if ((key + oc_len) <= tail) {
if (strncasecmp(key, "objectclass",
oc_len) == 0) {
/*
* assertion is "objectclass=ocname",
* ocname is the one needs to be mapped
*
* skip spaces after "=" to find start
* of the ocname
*/
head = info[i]->veq_pos;
for (head = info[i]->veq_pos + 1;
*head && *head == SPACETOK; head++);
/* ignore empty ocname */
if (!(*head))
continue;
info[i]->name_start = head - filter_c +
filter;
/*
* now find the end of the ocname
*/
for (c = head; ; c++) {
/* CPARATOK is ')' */
if (*c == CPARATOK ||
*c == '\0' ||
*c == SPACETOK) {
*c = '\0';
info[i]->name_end =
c - filter_c - 1 +
filter;
filter_c_next = c + 1;
info[i]->oc_or_attr = 'o';
info[i]->from_name = head;
break;
}
}
}
}
/*
* assertion is not "objectclass=ocname",
* assume assertion is "<key> = <value>",
* <key> is the one needs to be mapped
*/
if (info[i]->from_name == NULL && strlen(key) > 0) {
info[i]->oc_or_attr = 'a';
info[i]->from_name = key;
}
}
/* perform schema mapping */
for (i = 0; i < num_veq; i++) {
if (info[i]->from_name == NULL)
continue;
if (info[i]->oc_or_attr == 'a')
info[i]->mapping =
__ns_ldap_getMappedAttributes(service,
info[i]->from_name);
else
info[i]->mapping =
__ns_ldap_getMappedObjectClass(service,
info[i]->from_name);
if (info[i]->mapping == NULL && auto_service) {
/*
* If no mapped attribute/objectclass is found
* and service == auto*
* try to find automount's
* mapped attribute/objectclass
*/
if (info[i]->oc_or_attr == 'a')
info[i]->mapping =
__ns_ldap_getMappedAttributes("automount",
info[i]->from_name);
else
info[i]->mapping =
__ns_ldap_getMappedObjectClass("automount",
info[i]->from_name);
}
if (info[i]->mapping == NULL ||
info[i]->mapping[0] == NULL) {
info[i]->to_name = NULL;
} else if (info[i]->mapping[1] == NULL) {
info[i]->to_name = info[i]->mapping[0];
at_least_one = TRUE;
} else {
__s_api_free2dArray(info[i]->mapping);
/*
* multiple mapping
* not allowed
*/
(void) sprintf(errstr,
gettext(
"Multiple attribute or objectclass "
"mapping for '%s' in filter "
"'%s' not allowed."),
info[i]->from_name, filter);
err = strdup(errstr);
if (err)
MKERROR(LOG_WARNING, cookie->errorp,
NS_CONFIG_SYNTAX,
err, NULL);
free(filter_c);
for (j = 0; j < num_veq; j++) {
if (info[j]->mapping)
__s_api_free2dArray(
info[j]->mapping);
free(info[j]);
}
free(info);
return (NS_LDAP_CONFIG);
}
}
if (at_least_one) {
len = strlen(filter);
last_copied = filter - 1;
for (i = 0; i < num_veq; i++) {
if (info[i]->to_name)
len += strlen(info[i]->to_name);
}
*new_filter = (char *)calloc(1, len);
if (*new_filter == NULL) {
free(filter_c);
for (j = 0; j < num_veq; j++) {
if (info[j]->mapping)
__s_api_free2dArray(
info[j]->mapping);
free(info[j]);
}
free(info);
return (NS_LDAP_MEMORY);
}
for (i = 0; i < num_veq; i++) {
if (info[i]->to_name != NULL &&
info[i]->to_name != NULL) {
/*
* copy the original filter data
* between the last name and current
* name
*/
if ((last_copied + 1) != info[i]->name_start)
(void) strncat(*new_filter, last_copied + 1,
info[i]->name_start -
last_copied - 1);
/* the data is copied */
last_copied = info[i]->name_end;
/*
* replace the name with
* the mapped name
*/
(void) strcat(*new_filter, info[i]->to_name);
}
/* copy the filter data after the last name */
if (i == (num_veq -1) &&
info[i]->name_end <
(filter + strlen(filter)))
(void) strncat(*new_filter, last_copied + 1,
filter + strlen(filter) -
last_copied - 1);
}
}
/* free memory */
free(filter_c);
for (j = 0; j < num_veq; j++) {
if (info[j]->mapping)
__s_api_free2dArray(info[j]->mapping);
free(info[j]);
}
free(info);
return (NS_LDAP_SUCCESS);
}
static int
setup_next_search(ns_ldap_cookie_t *cookie)
{
ns_ldap_search_desc_t *dptr;
int scope;
char *filter, *str;
int baselen;
int rc;
void **param;
dptr = *cookie->sdpos;
scope = cookie->i_flags & (NS_LDAP_SCOPE_BASE |
NS_LDAP_SCOPE_ONELEVEL |
NS_LDAP_SCOPE_SUBTREE);
if (scope)
cookie->scope = scope;
else
cookie->scope = dptr->scope;
switch (cookie->scope) {
case NS_LDAP_SCOPE_BASE:
cookie->scope = LDAP_SCOPE_BASE;
break;
case NS_LDAP_SCOPE_ONELEVEL:
cookie->scope = LDAP_SCOPE_ONELEVEL;
break;
case NS_LDAP_SCOPE_SUBTREE:
cookie->scope = LDAP_SCOPE_SUBTREE;
break;
}
filter = NULL;
if (cookie->use_filtercb && cookie->init_filter_cb &&
dptr->filter && strlen(dptr->filter) > 0) {
(*cookie->init_filter_cb)(dptr, &filter,
cookie->userdata);
}
if (filter == NULL) {
if (cookie->i_filter == NULL) {
cookie->err_rc = NS_LDAP_INVALID_PARAM;
return (-1);
} else {
cookie->filter = strdup(cookie->i_filter);
if (cookie->filter == NULL) {
cookie->err_rc = NS_LDAP_MEMORY;
return (-1);
}
}
} else {
cookie->filter = strdup(filter);
free(filter);
if (cookie->filter == NULL) {
cookie->err_rc = NS_LDAP_MEMORY;
return (-1);
}
}
/*
* perform attribute/objectclass mapping on filter
*/
filter = NULL;
if (cookie->service) {
rc = get_mapped_filter(cookie, &filter);
if (rc != NS_LDAP_SUCCESS) {
cookie->err_rc = rc;
return (-1);
} else {
/*
* get_mapped_filter returns
* NULL filter pointer, if
* no mapping was done
*/
if (filter) {
free(cookie->filter);
cookie->filter = filter;
}
}
}
/*
* validate filter to make sure it's legal
* [remove redundant ()'s]
*/
rc = validate_filter(cookie);
if (rc != NS_LDAP_SUCCESS) {
cookie->err_rc = rc;
return (-1);
}
baselen = strlen(dptr->basedn);
if (baselen > 0 && dptr->basedn[baselen-1] == COMMATOK) {
rc = __ns_ldap_getParam(NS_LDAP_SEARCH_BASEDN_P,
(void ***)&param, &cookie->errorp);
if (rc != NS_LDAP_SUCCESS) {
cookie->err_rc = rc;
return (-1);
}
str = ((char **)param)[0];
baselen += strlen(str)+1;
cookie->basedn = (char *)malloc(baselen);
if (cookie->basedn == NULL) {
cookie->err_rc = NS_LDAP_MEMORY;
return (-1);
}
(void) strcpy(cookie->basedn, dptr->basedn);
(void) strcat(cookie->basedn, str);
(void) __ns_ldap_freeParam(&param);
} else {
cookie->basedn = strdup(dptr->basedn);
}
return (0);
}
static int
setup_referral_search(ns_ldap_cookie_t *cookie)
{
ns_referral_info_t *ref;
ref = cookie->refpos;
cookie->scope = ref->refScope;
if (cookie->filter) {
free(cookie->filter);
}
cookie->filter = strdup(ref->refFilter);
if (cookie->basedn) {
free(cookie->basedn);
}
cookie->basedn = strdup(ref->refDN);
if (cookie->filter == NULL || cookie->basedn == NULL) {
cookie->err_rc = NS_LDAP_MEMORY;
return (-1);
}
return (0);
}
static int
get_current_session(ns_ldap_cookie_t *cookie)
{
ConnectionID connectionId = -1;
Connection *conp = NULL;
int rc;
int fail_if_new_pwd_reqd = 1;
rc = __s_api_getConnection(NULL, cookie->i_flags,
cookie->i_auth, &connectionId, &conp,
&cookie->errorp, fail_if_new_pwd_reqd);
/*
* If password control attached in *cookie->errorp,
* e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
* free the error structure (we do not need
* the sec_to_expired info).
* Reset rc to NS_LDAP_SUCCESS.
*/
if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
(void) __ns_ldap_freeError(
&cookie->errorp);
cookie->errorp = NULL;
rc = NS_LDAP_SUCCESS;
}
if (rc != NS_LDAP_SUCCESS) {
cookie->err_rc = rc;
return (-1);
}
cookie->conn = conp;
cookie->connectionId = connectionId;
return (0);
}
static int
get_next_session(ns_ldap_cookie_t *cookie)
{
ConnectionID connectionId = -1;
Connection *conp = NULL;
int rc;
int fail_if_new_pwd_reqd = 1;
if (cookie->connectionId > -1)
DropConnection(cookie->connectionId, cookie->i_flags);
rc = __s_api_getConnection(NULL, cookie->i_flags,
cookie->i_auth, &connectionId, &conp,
&cookie->errorp, fail_if_new_pwd_reqd);
/*
* If password control attached in *cookie->errorp,
* e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
* free the error structure (we do not need
* the sec_to_expired info).
* Reset rc to NS_LDAP_SUCCESS.
*/
if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
(void) __ns_ldap_freeError(
&cookie->errorp);
cookie->errorp = NULL;
rc = NS_LDAP_SUCCESS;
}
if (rc != NS_LDAP_SUCCESS) {
cookie->err_rc = rc;
return (-1);
}
cookie->conn = conp;
cookie->connectionId = connectionId;
return (0);
}
static int
get_referral_session(ns_ldap_cookie_t *cookie)
{
ConnectionID connectionId = -1;
Connection *conp = NULL;
int rc;
int fail_if_new_pwd_reqd = 1;
if (cookie->connectionId > -1)
DropConnection(cookie->connectionId, cookie->i_flags);
rc = __s_api_getConnection(cookie->refpos->refHost, 0,
cookie->i_auth, &connectionId, &conp,
&cookie->errorp, fail_if_new_pwd_reqd);
/*
* If password control attached in *cookie->errorp,
* e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
* free the error structure (we do not need
* the sec_to_expired info).
* Reset rc to NS_LDAP_SUCCESS.
*/
if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
(void) __ns_ldap_freeError(
&cookie->errorp);
cookie->errorp = NULL;
rc = NS_LDAP_SUCCESS;
}
if (rc != NS_LDAP_SUCCESS) {
cookie->err_rc = rc;
return (-1);
}
cookie->conn = conp;
cookie->connectionId = connectionId;
return (0);
}
static int
paging_supported(ns_ldap_cookie_t *cookie)
{
int rc;
cookie->listType = 0;
rc = __s_api_isCtrlSupported(cookie->conn,
LDAP_CONTROL_VLVREQUEST);
if (rc == NS_LDAP_SUCCESS) {
cookie->listType = VLVCTRLFLAG;
return (1);
}
rc = __s_api_isCtrlSupported(cookie->conn,
LDAP_CONTROL_SIMPLE_PAGE);
if (rc == NS_LDAP_SUCCESS) {
cookie->listType = SIMPLEPAGECTRLFLAG;
return (1);
}
return (0);
}
static int
setup_vlv_params(ns_ldap_cookie_t *cookie)
{
LDAPControl **ctrls;
LDAPsortkey **sortkeylist;
LDAPControl *sortctrl = NULL;
LDAPControl *vlvctrl = NULL;
LDAPVirtualList vlist;
int rc;
_freeControlList(&cookie->p_serverctrls);
rc = ldap_create_sort_keylist(&sortkeylist, SORTKEYLIST);
if (rc != LDAP_SUCCESS) {
(void) ldap_get_option(cookie->conn->ld,
LDAP_OPT_ERROR_NUMBER, &rc);
return (rc);
}
rc = ldap_create_sort_control(cookie->conn->ld,
sortkeylist, 1, &sortctrl);
ldap_free_sort_keylist(sortkeylist);
if (rc != LDAP_SUCCESS) {
(void) ldap_get_option(cookie->conn->ld,
LDAP_OPT_ERROR_NUMBER, &rc);
return (rc);
}
vlist.ldvlist_index = cookie->index;
vlist.ldvlist_size = 0;
vlist.ldvlist_before_count = 0;
vlist.ldvlist_after_count = LISTPAGESIZE-1;
vlist.ldvlist_attrvalue = NULL;
vlist.ldvlist_extradata = NULL;
rc = ldap_create_virtuallist_control(cookie->conn->ld,
&vlist, &vlvctrl);
if (rc != LDAP_SUCCESS) {
ldap_control_free(sortctrl);
(void) ldap_get_option(cookie->conn->ld, LDAP_OPT_ERROR_NUMBER,
&rc);
return (rc);
}
ctrls = (LDAPControl **)calloc(3, sizeof (LDAPControl *));
if (ctrls == NULL) {
ldap_control_free(sortctrl);
ldap_control_free(vlvctrl);
return (LDAP_NO_MEMORY);
}
ctrls[0] = sortctrl;
ctrls[1] = vlvctrl;
cookie->p_serverctrls = ctrls;
return (LDAP_SUCCESS);
}
static int
setup_simplepg_params(ns_ldap_cookie_t *cookie)
{
LDAPControl **ctrls;
LDAPControl *pgctrl = NULL;
int rc;
_freeControlList(&cookie->p_serverctrls);
rc = ldap_create_page_control(cookie->conn->ld, LISTPAGESIZE,
cookie->ctrlCookie, (char)0, &pgctrl);
if (rc != LDAP_SUCCESS) {
(void) ldap_get_option(cookie->conn->ld, LDAP_OPT_ERROR_NUMBER,
&rc);
return (rc);
}
ctrls = (LDAPControl **)calloc(2, sizeof (LDAPControl *));
if (ctrls == NULL) {
ldap_control_free(pgctrl);
return (LDAP_NO_MEMORY);
}
ctrls[0] = pgctrl;
cookie->p_serverctrls = ctrls;
return (LDAP_SUCCESS);
}
static void
proc_result_referrals(ns_ldap_cookie_t *cookie)
{
int errCode, i, rc;
char **referrals = NULL;
/*
* Only follow one level of referrals, i.e.
* if already in referral mode, do nothing
*/
if (cookie->refpos == NULL) {
cookie->new_state = END_RESULT;
rc = ldap_parse_result(cookie->conn->ld,
cookie->resultMsg,
&errCode, NULL,
NULL, &referrals,
NULL, 0);
if (rc != NS_LDAP_SUCCESS) {
(void) ldap_get_option(cookie->conn->ld,
LDAP_OPT_ERROR_NUMBER,
&cookie->err_rc);
cookie->new_state = LDAP_ERROR;
return;
}
if (errCode == LDAP_REFERRAL) {
for (i = 0; referrals[i] != NULL;
i++) {
/* add to referral list */
rc = __s_api_addRefInfo(
&cookie->reflist,
referrals[i],
cookie->basedn,
&cookie->scope,
cookie->filter,
cookie->conn->ld);
if (rc != NS_LDAP_SUCCESS) {
cookie->new_state =
ERROR;
break;
}
}
ldap_value_free(referrals);
}
}
}
static void
proc_search_references(ns_ldap_cookie_t *cookie)
{
char **refurls = NULL;
int i, rc;
/*
* Only follow one level of referrals, i.e.
* if already in referral mode, do nothing
*/
if (cookie->refpos == NULL) {
refurls = ldap_get_reference_urls(
cookie->conn->ld,
cookie->resultMsg);
if (refurls == NULL) {
(void) ldap_get_option(cookie->conn->ld,
LDAP_OPT_ERROR_NUMBER,
&cookie->err_rc);
cookie->new_state = LDAP_ERROR;
return;
}
for (i = 0; refurls[i] != NULL; i++) {
/* add to referral list */
rc = __s_api_addRefInfo(
&cookie->reflist,
refurls[i],
cookie->basedn,
&cookie->scope,
cookie->filter,
cookie->conn->ld);
if (rc != NS_LDAP_SUCCESS) {
cookie->new_state =
ERROR;
break;
}
}
/* free allocated storage */
for (i = 0; refurls[i] != NULL; i++)
free(refurls[i]);
}
}
static ns_state_t
multi_result(ns_ldap_cookie_t *cookie)
{
char errstr[MAXERROR];
char *err;
ns_ldap_error_t **errorp = NULL;
LDAPControl **retCtrls = NULL;
int i, rc;
int errCode;
int finished = 0;
unsigned long target_posp = 0;
unsigned long list_size = 0;
unsigned int count = 0;
char **referrals = NULL;
if (cookie->listType == VLVCTRLFLAG) {
rc = ldap_parse_result(cookie->conn->ld, cookie->resultMsg,
&errCode, NULL, NULL, &referrals, &retCtrls, 0);
if (rc != LDAP_SUCCESS) {
(void) ldap_get_option(cookie->conn->ld,
LDAP_OPT_ERROR_NUMBER,
&cookie->err_rc);
(void) sprintf(errstr,
gettext("LDAP ERROR (%d): %s.\n"),
cookie->err_rc,
gettext(ldap_err2string(cookie->err_rc)));
err = strdup(errstr);
MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
NULL);
cookie->err_rc = NS_LDAP_INTERNAL;
cookie->errorp = *errorp;
return (LDAP_ERROR);
}
if (errCode == LDAP_REFERRAL) {
for (i = 0; referrals[i] != NULL;
i++) {
/* add to referral list */
rc = __s_api_addRefInfo(
&cookie->reflist,
referrals[i],
cookie->basedn,
&cookie->scope,
cookie->filter,
cookie->conn->ld);
if (rc != NS_LDAP_SUCCESS) {
ldap_value_free(
referrals);
if (retCtrls)
ldap_controls_free(
retCtrls);
return (ERROR);
}
}
ldap_value_free(referrals);
if (retCtrls)
ldap_controls_free(retCtrls);
return (END_RESULT);
}
if (retCtrls) {
rc = ldap_parse_virtuallist_control(
cookie->conn->ld, retCtrls,
&target_posp, &list_size, &errCode);
if (rc == LDAP_SUCCESS) {
cookie->index = target_posp + LISTPAGESIZE;
if (cookie->index >= list_size) {
finished = 1;
}
}
ldap_controls_free(retCtrls);
retCtrls = NULL;
}
else
finished = 1;
} else if (cookie->listType == SIMPLEPAGECTRLFLAG) {
rc = ldap_parse_result(cookie->conn->ld, cookie->resultMsg,
&errCode, NULL, NULL, &referrals, &retCtrls, 0);
if (rc != LDAP_SUCCESS) {
(void) ldap_get_option(cookie->conn->ld,
LDAP_OPT_ERROR_NUMBER,
&cookie->err_rc);
(void) sprintf(errstr,
gettext("LDAP ERROR (%d): %s.\n"),
cookie->err_rc,
gettext(ldap_err2string(cookie->err_rc)));
err = strdup(errstr);
MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
NULL);
cookie->err_rc = NS_LDAP_INTERNAL;
cookie->errorp = *errorp;
return (LDAP_ERROR);
}
if (errCode == LDAP_REFERRAL) {
for (i = 0; referrals[i] != NULL;
i++) {
/* add to referral list */
rc = __s_api_addRefInfo(
&cookie->reflist,
referrals[i],
cookie->basedn,
&cookie->scope,
cookie->filter,
cookie->conn->ld);
if (rc != NS_LDAP_SUCCESS) {
ldap_value_free(
referrals);
if (retCtrls)
ldap_controls_free(
retCtrls);
return (ERROR);
}
}
ldap_value_free(referrals);
if (retCtrls)
ldap_controls_free(retCtrls);
return (END_RESULT);
}
if (retCtrls) {
if (cookie->ctrlCookie)
ber_bvfree(cookie->ctrlCookie);
cookie->ctrlCookie = NULL;
rc = ldap_parse_page_control(
cookie->conn->ld, retCtrls,
&count, &cookie->ctrlCookie);
if (rc == LDAP_SUCCESS) {
if ((cookie->ctrlCookie == NULL) ||
(cookie->ctrlCookie->bv_val == NULL) ||
(*cookie->ctrlCookie->bv_val == NULL))
finished = 1;
}
ldap_controls_free(retCtrls);
retCtrls = NULL;
}
else
finished = 1;
}
if (!finished && cookie->listType == VLVCTRLFLAG)
return (NEXT_VLV);
if (!finished && cookie->listType == SIMPLEPAGECTRLFLAG)
return (NEXT_PAGE);
if (finished)
return (END_RESULT);
return (ERROR);
}
/*
* This state machine performs one or more LDAP searches to a given
* directory server using service search descriptors and schema
* mapping as appropriate. The approximate pseudocode for
* this routine is the following:
* Given the current configuration [set/reset connection etc.]
* and the current service search descriptor list
* or default search filter parameters
* foreach (service search filter) {
* initialize the filter [via filter_init if appropriate]
* get a valid session/connection (preferably the current one)
* Recover if the connection is lost
* perform the search
* foreach (result entry) {
* process result [via callback if appropriate]
* save result for caller if accepted.
* exit and return all collected if allResults found;
* }
* }
* return collected results and exit
*/
static
ns_state_t
search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
{
char errstr[MAXERROR];
char *err;
int rc;
ns_ldap_entry_t *nextEntry;
ns_ldap_error_t *error = NULL;
ns_ldap_error_t **errorp;
errorp = &error;
cookie->err_rc = 0;
cookie->state = state;
for (;;) {
switch (cookie->state) {
case EXIT:
/* state engine/connection cleaned up in delete */
if (cookie->attribute) {
__s_api_free2dArray(cookie->attribute);
cookie->attribute = NULL;
}
if (cookie->reflist) {
__s_api_deleteRefInfo(cookie->reflist);
cookie->reflist = NULL;
}
return (EXIT);
case INIT:
cookie->sdpos = NULL;
cookie->new_state = NEXT_SEARCH_DESCRIPTOR;
if (cookie->attribute) {
__s_api_free2dArray(cookie->attribute);
cookie->attribute = NULL;
}
if ((cookie->i_flags & NS_LDAP_NOMAP) == 0 &&
cookie->i_attr) {
cookie->attribute =
__ns_ldap_mapAttributeList(
cookie->service,
cookie->i_attr);
}
break;
case NEXT_SEARCH_DESCRIPTOR:
/* get next search descriptor */
if (cookie->sdpos == NULL) {
cookie->sdpos = cookie->sdlist;
cookie->new_state = GET_SESSION;
} else {
cookie->sdpos++;
cookie->new_state = NEXT_SEARCH;
}
if (*cookie->sdpos == NULL)
cookie->new_state = EXIT;
break;
case GET_SESSION:
if (get_current_session(cookie) < 0)
cookie->new_state = NEXT_SESSION;
else
cookie->new_state = NEXT_SEARCH;
break;
case NEXT_SESSION:
if (get_next_session(cookie) < 0)
cookie->new_state = RESTART_SESSION;
else
cookie->new_state = NEXT_SEARCH;
break;
case RESTART_SESSION:
if (cookie->i_flags & NS_LDAP_HARD) {
cookie->new_state = NEXT_SESSION;
break;
}
(void) sprintf(errstr,
gettext("Session error no available conn.\n"),
state);
err = strdup(errstr);
MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
NULL);
cookie->err_rc = NS_LDAP_INTERNAL;
cookie->errorp = *errorp;
cookie->new_state = EXIT;
break;
case NEXT_SEARCH:
/* setup referrals search if necessary */
if (cookie->refpos) {
if (setup_referral_search(cookie) < 0) {
cookie->new_state = EXIT;
break;
}
} else if (setup_next_search(cookie) < 0) {
cookie->new_state = EXIT;
break;
}
/* only do VLV/PAGE on scopes onelevel/subtree */
if (paging_supported(cookie)) {
if (cookie->use_paging &&
(cookie->scope != LDAP_SCOPE_BASE)) {
cookie->index = 1;
if (cookie->listType == VLVCTRLFLAG)
cookie->new_state = NEXT_VLV;
else
cookie->new_state = NEXT_PAGE;
break;
}
}
cookie->new_state = ONE_SEARCH;
break;
case NEXT_VLV:
rc = setup_vlv_params(cookie);
if (rc != LDAP_SUCCESS) {
cookie->err_rc = rc;
cookie->new_state = LDAP_ERROR;
break;
}
cookie->next_state = MULTI_RESULT;
cookie->new_state = DO_SEARCH;
break;
case NEXT_PAGE:
rc = setup_simplepg_params(cookie);
if (rc != LDAP_SUCCESS) {
cookie->err_rc = rc;
cookie->new_state = LDAP_ERROR;
break;
}
cookie->next_state = MULTI_RESULT;
cookie->new_state = DO_SEARCH;
break;
case ONE_SEARCH:
cookie->next_state = NEXT_RESULT;
cookie->new_state = DO_SEARCH;
break;
case DO_SEARCH:
rc = ldap_search_ext(cookie->conn->ld,
cookie->basedn,
cookie->scope,
cookie->filter,
cookie->attribute,
0,
cookie->p_serverctrls,
NULL,
&cookie->search_timeout, 0,
&cookie->msgId);
if (rc != LDAP_SUCCESS) {
if (rc == LDAP_BUSY ||
rc == LDAP_UNAVAILABLE ||
rc == LDAP_UNWILLING_TO_PERFORM ||
rc == LDAP_CONNECT_ERROR ||
rc == LDAP_SERVER_DOWN) {
cookie->new_state = NEXT_SESSION;
/*
* If not able to reach the
* server, inform the ldap
* cache manager that the
* server should be removed
* from it's server list.
* Thus, the manager will not
* return this server on the next
* get-server request and will
* also reduce the server list
* refresh TTL, so that it will
* find out sooner when the server
* is up again.
*/
if (rc == LDAP_CONNECT_ERROR ||
rc == LDAP_SERVER_DOWN) {
__s_api_removeServer(
cookie->conn->serverAddr);
if (cookie->connectionId > -1) {
DropConnection(
cookie->connectionId,
NS_LDAP_NEW_CONN);
cookie->connectionId = -1;
}
}
break;
}
cookie->err_rc = rc;
cookie->new_state = LDAP_ERROR;
break;
}
cookie->new_state = cookie->next_state;
break;
case NEXT_RESULT:
rc = ldap_result(cookie->conn->ld, cookie->msgId,
LDAP_MSG_ONE,
(struct timeval *)&cookie->search_timeout,
&cookie->resultMsg);
if (rc == LDAP_RES_SEARCH_RESULT) {
cookie->new_state = END_RESULT;
/* check and process referrals info */
if (cookie->followRef)
proc_result_referrals(
cookie);
(void) ldap_msgfree(cookie->resultMsg);
cookie->resultMsg = NULL;
break;
}
/* handle referrals if necessary */
if (rc == LDAP_RES_SEARCH_REFERENCE) {
if (cookie->followRef)
proc_search_references(cookie);
(void) ldap_msgfree(cookie->resultMsg);
cookie->resultMsg = NULL;
break;
}
if (rc != LDAP_RES_SEARCH_ENTRY) {
switch (rc) {
case 0:
rc = LDAP_TIMEOUT;
break;
case -1:
rc = ldap_get_lderrno(cookie->conn->ld,
NULL, NULL);
break;
default:
rc = ldap_result2error(cookie->conn->ld,
cookie->resultMsg, 1);
break;
}
(void) ldap_msgfree(cookie->resultMsg);
cookie->resultMsg = NULL;
if (rc == LDAP_BUSY ||
rc == LDAP_UNAVAILABLE ||
rc == LDAP_UNWILLING_TO_PERFORM ||
rc == LDAP_SERVER_DOWN) {
cookie->new_state = NEXT_SESSION;
break;
}
cookie->err_rc = rc;
cookie->new_state = LDAP_ERROR;
break;
}
/* else LDAP_RES_SEARCH_ENTRY */
rc = __s_api_getEntry(cookie);
(void) ldap_msgfree(cookie->resultMsg);
cookie->resultMsg = NULL;
if (rc != NS_LDAP_SUCCESS) {
cookie->new_state = LDAP_ERROR;
break;
}
cookie->new_state = PROCESS_RESULT;
cookie->next_state = NEXT_RESULT;
break;
case MULTI_RESULT:
rc = ldap_result(cookie->conn->ld, cookie->msgId,
LDAP_MSG_ONE,
(struct timeval *)&cookie->search_timeout,
&cookie->resultMsg);
if (rc == LDAP_RES_SEARCH_RESULT) {
rc = ldap_result2error(cookie->conn->ld,
cookie->resultMsg, 0);
if (rc != LDAP_SUCCESS) {
cookie->err_rc = rc;
cookie->new_state = LDAP_ERROR;
(void) ldap_msgfree(cookie->resultMsg);
break;
}
cookie->new_state = multi_result(cookie);
(void) ldap_msgfree(cookie->resultMsg);
cookie->resultMsg = NULL;
break;
}
/* handle referrals if necessary */
if (rc == LDAP_RES_SEARCH_REFERENCE &&
cookie->followRef) {
proc_search_references(cookie);
(void) ldap_msgfree(cookie->resultMsg);
cookie->resultMsg = NULL;
break;
}
if (rc != LDAP_RES_SEARCH_ENTRY) {
switch (rc) {
case 0:
rc = LDAP_TIMEOUT;
break;
case -1:
rc = ldap_get_lderrno(cookie->conn->ld,
NULL, NULL);
break;
default:
rc = ldap_result2error(cookie->conn->ld,
cookie->resultMsg, 1);
break;
}
(void) ldap_msgfree(cookie->resultMsg);
cookie->resultMsg = NULL;
if (rc == LDAP_BUSY ||
rc == LDAP_UNAVAILABLE ||
rc == LDAP_UNWILLING_TO_PERFORM ||
rc == LDAP_SERVER_DOWN) {
cookie->new_state = NEXT_SESSION;
break;
}
cookie->err_rc = rc;
cookie->new_state = LDAP_ERROR;
break;
}
/* else LDAP_RES_SEARCH_ENTRY */
rc = __s_api_getEntry(cookie);
(void) ldap_msgfree(cookie->resultMsg);
cookie->resultMsg = NULL;
if (rc != NS_LDAP_SUCCESS) {
cookie->new_state = LDAP_ERROR;
break;
}
cookie->new_state = PROCESS_RESULT;
cookie->next_state = MULTI_RESULT;
break;
case PROCESS_RESULT:
/* NOTE THIS STATE MAY BE PROCESSED BY CALLER */
if (cookie->use_usercb && cookie->callback) {
rc = 0;
for (nextEntry = cookie->result->entry;
nextEntry != NULL;
nextEntry = nextEntry->next) {
rc = (*cookie->callback)(nextEntry,
cookie->userdata);
if (rc == NS_LDAP_CB_DONE) {
/* cb doesn't want any more data */
rc = NS_LDAP_PARTIAL;
cookie->err_rc = rc;
break;
} else if (rc != NS_LDAP_CB_NEXT) {
/* invalid return code */
rc = NS_LDAP_OP_FAILED;
cookie->err_rc = rc;
break;
}
}
(void) __ns_ldap_freeResult(&cookie->result);
cookie->result = NULL;
}
if (rc != 0) {
cookie->new_state = EXIT;
break;
}
/* NOTE PREVIOUS STATE SPECIFIES NEXT STATE */
cookie->new_state = cookie->next_state;
break;
case END_PROCESS_RESULT:
cookie->new_state = cookie->next_state;
break;
case END_RESULT:
/*
* XXX DO WE NEED THIS CASE?
* if (search is complete) {
* cookie->new_state = EXIT;
* } else
*/
/*
* entering referral mode if necessary
*/
if (cookie->followRef && cookie->reflist)
cookie->new_state =
NEXT_REFERRAL;
else
cookie->new_state =
NEXT_SEARCH_DESCRIPTOR;
break;
case NEXT_REFERRAL:
/* get next referral info */
if (cookie->refpos == NULL)
cookie->refpos =
cookie->reflist;
else
cookie->refpos =
cookie->refpos->next;
/* check see if done with all referrals */
if (cookie->refpos != NULL)
cookie->new_state =
GET_REFERRAL_SESSION;
else {
__s_api_deleteRefInfo(cookie->reflist);
cookie->reflist = NULL;
cookie->new_state =
NEXT_SEARCH_DESCRIPTOR;
}
break;
case GET_REFERRAL_SESSION:
if (get_referral_session(cookie) < 0)
cookie->new_state = EXIT;
else {
cookie->new_state = NEXT_SEARCH;
}
break;
case LDAP_ERROR:
(void) sprintf(errstr,
gettext("LDAP ERROR (%d): %s."),
cookie->err_rc,
ldap_err2string(cookie->err_rc));
err = strdup(errstr);
MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
NULL);
cookie->err_rc = NS_LDAP_INTERNAL;
cookie->errorp = *errorp;
return (ERROR);
default:
case ERROR:
(void) sprintf(errstr,
gettext("Internal State machine exit (%d).\n"),
cookie->state);
err = strdup(errstr);
MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
NULL);
cookie->err_rc = NS_LDAP_INTERNAL;
cookie->errorp = *errorp;
return (ERROR);
}
if (cycle == ONE_STEP) {
return (cookie->new_state);
}
cookie->state = cookie->new_state;
}
/*NOTREACHED*/
#if 0
(void) sprintf(errstr,
gettext("Unexpected State machine error.\n"));
err = strdup(errstr);
MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err, NULL);
cookie->err_rc = NS_LDAP_INTERNAL;
cookie->errorp = *errorp;
return (ERROR);
#endif
}
/*
* __ns_ldap_list performs one or more LDAP searches to a given
* directory server using service search descriptors and schema
* mapping as appropriate.
*/
int
__ns_ldap_list(
const char *service,
const char *filter,
int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
char **realfilter, const void *userdata),
const char * const *attribute,
const ns_cred_t *auth,
const int flags,
ns_ldap_result_t **rResult, /* return result entries */
ns_ldap_error_t **errorp,
int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
const void *userdata)
{
ns_ldap_cookie_t *cookie;
ns_ldap_search_desc_t **sdlist = NULL;
ns_ldap_search_desc_t *dptr;
ns_ldap_error_t *error = NULL;
char **dns = NULL;
int scope;
int rc;
*errorp = NULL;
/* Initialize State machine cookie */
cookie = init_search_state_machine();
if (cookie == NULL) {
return (NS_LDAP_MEMORY);
}
/* see if need to follow referrals */
rc = __s_api_toFollowReferrals(flags,
&cookie->followRef, errorp);
if (rc != NS_LDAP_SUCCESS) {
delete_search_cookie(cookie);
return (rc);
}
/* get the service descriptor - or create a default one */
rc = __s_api_get_SSD_from_SSDtoUse_service(service,
&sdlist, errorp);
if (rc != NS_LDAP_SUCCESS) {
delete_search_cookie(cookie);
*errorp = error;
return (rc);
}
if (sdlist == NULL) {
/* Create default service Desc */
sdlist = (ns_ldap_search_desc_t **)calloc(2,
sizeof (ns_ldap_search_desc_t *));
if (sdlist == NULL) {
delete_search_cookie(cookie);
cookie = NULL;
return (NS_LDAP_MEMORY);
}
dptr = (ns_ldap_search_desc_t *)
calloc(1, sizeof (ns_ldap_search_desc_t));
if (dptr == NULL) {
free(sdlist);
delete_search_cookie(cookie);
cookie = NULL;
return (NS_LDAP_MEMORY);
}
sdlist[0] = dptr;
/* default base */
rc = __s_api_getDNs(&dns, service, &cookie->errorp);
if (rc != NS_LDAP_SUCCESS) {
if (dns) {
__s_api_free2dArray(dns);
dns = NULL;
}
*errorp = cookie->errorp;
cookie->errorp = NULL;
delete_search_cookie(cookie);
cookie = NULL;
return (rc);
}
dptr->basedn = strdup(dns[0]);
__s_api_free2dArray(dns);
dns = NULL;
/* default scope */
scope = 0;
rc = __s_api_getSearchScope(&scope, &cookie->errorp);
dptr->scope = scope;
}
cookie->sdlist = sdlist;
/*
* use VLV/PAGE control only if NS_LDAP_PAGE_CTRL is set
*/
if (flags & NS_LDAP_PAGE_CTRL)
cookie->use_paging = TRUE;
else
cookie->use_paging = FALSE;
/* Set up other arguments */
cookie->userdata = userdata;
if (init_filter_cb != NULL) {
cookie->init_filter_cb = init_filter_cb;
cookie->use_filtercb = 1;
}
if (callback != NULL) {
cookie->callback = callback;
cookie->use_usercb = 1;
}
if (service) {
cookie->service = strdup(service);
if (cookie->service == NULL) {
delete_search_cookie(cookie);
cookie = NULL;
return (NS_LDAP_MEMORY);
}
}
cookie->i_filter = strdup(filter);
cookie->i_attr = attribute;
cookie->i_auth = auth;
cookie->i_flags = flags;
/* Process search */
rc = search_state_machine(cookie, INIT, 0);
/* Copy results back to user */
rc = cookie->err_rc;
if (rc != NS_LDAP_SUCCESS)
*errorp = cookie->errorp;
*rResult = cookie->result;
cookie->errorp = NULL;
cookie->result = NULL;
delete_search_cookie(cookie);
cookie = NULL;
if (*rResult == NULL)
rc = NS_LDAP_NOTFOUND;
return (rc);
}
/*
* __s_api_find_domainname performs one or more LDAP searches to
* find the value of the nisdomain attribute associated with
* the input DN
*/
static int
__s_api_find_domainname(
const char *dn,
char **domainname,
const ns_cred_t *cred,
ns_ldap_error_t **errorp)
{
ns_ldap_cookie_t *cookie;
ns_ldap_search_desc_t **sdlist;
ns_ldap_search_desc_t *dptr;
int rc;
char **value;
int flags = 0;
*domainname = NULL;
*errorp = NULL;
/* Initialize State machine cookie */
cookie = init_search_state_machine();
if (cookie == NULL) {
return (NS_LDAP_MEMORY);
}
/* see if need to follow referrals */
rc = __s_api_toFollowReferrals(flags,
&cookie->followRef, errorp);
if (rc != NS_LDAP_SUCCESS) {
delete_search_cookie(cookie);
return (rc);
}
/* Create default service Desc */
sdlist = (ns_ldap_search_desc_t **)calloc(2,
sizeof (ns_ldap_search_desc_t *));
if (sdlist == NULL) {
delete_search_cookie(cookie);
cookie = NULL;
return (NS_LDAP_MEMORY);
}
dptr = (ns_ldap_search_desc_t *)
calloc(1, sizeof (ns_ldap_search_desc_t));
if (dptr == NULL) {
free(sdlist);
delete_search_cookie(cookie);
cookie = NULL;
return (NS_LDAP_MEMORY);
}
sdlist[0] = dptr;
/* search base is dn */
dptr->basedn = strdup(dn);
/* search scope is base */
dptr->scope = NS_LDAP_SCOPE_BASE;
/* search filter is "nisdomain=*" */
dptr->filter = strdup(_NIS_FILTER);
cookie->sdlist = sdlist;
cookie->i_filter = strdup(dptr->filter);
cookie->i_attr = nis_domain_attrs;
cookie->i_auth = cred;
cookie->i_flags = 0;
/* Process search */
rc = search_state_machine(cookie, INIT, 0);
/* Copy domain name if found */
rc = cookie->err_rc;
if (rc != NS_LDAP_SUCCESS)
*errorp = cookie->errorp;
if (cookie->result == NULL)
rc = NS_LDAP_NOTFOUND;
if (rc == NS_LDAP_SUCCESS) {
value = __ns_ldap_getAttr(cookie->result->entry,
_NIS_DOMAIN);
if (value[0])
*domainname = strdup(value[0]);
else
rc = NS_LDAP_NOTFOUND;
}
if (cookie->result != NULL)
(void) __ns_ldap_freeResult(&cookie->result);
cookie->errorp = NULL;
delete_search_cookie(cookie);
cookie = NULL;
return (rc);
}
int
__ns_ldap_firstEntry(
const char *service,
const char *filter,
int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
char **realfilter, const void *userdata),
const char * const *attribute,
const ns_cred_t *auth,
const int flags,
void **vcookie,
ns_ldap_result_t **result,
ns_ldap_error_t ** errorp,
const void *userdata)
{
ns_ldap_cookie_t *cookie = NULL;
ns_ldap_error_t *error = NULL;
ns_state_t state;
ns_ldap_search_desc_t **sdlist;
ns_ldap_search_desc_t *dptr;
char **dns = NULL;
int scope;
int rc;
*errorp = NULL;
*result = NULL;
/* get the service descriptor - or create a default one */
rc = __s_api_get_SSD_from_SSDtoUse_service(service,
&sdlist, errorp);
if (rc != NS_LDAP_SUCCESS) {
*errorp = error;
return (rc);
}
if (sdlist == NULL) {
/* Create default service Desc */
sdlist = (ns_ldap_search_desc_t **)calloc(2,
sizeof (ns_ldap_search_desc_t *));
if (sdlist == NULL) {
return (NS_LDAP_MEMORY);
}
dptr = (ns_ldap_search_desc_t *)
calloc(1, sizeof (ns_ldap_search_desc_t));
if (dptr == NULL) {
free(sdlist);
return (NS_LDAP_MEMORY);
}
sdlist[0] = dptr;
/* default base */
rc = __s_api_getDNs(&dns, service, &error);
if (rc != NS_LDAP_SUCCESS) {
if (dns) {
__s_api_free2dArray(dns);
dns = NULL;
}
if (sdlist) {
(void) __ns_ldap_freeSearchDescriptors(
&sdlist);
sdlist = NULL;
}
*errorp = error;
return (rc);
}
dptr->basedn = strdup(dns[0]);
__s_api_free2dArray(dns);
dns = NULL;
/* default scope */
scope = 0;
cookie = init_search_state_machine();
if (cookie == NULL) {
if (sdlist) {
(void) __ns_ldap_freeSearchDescriptors(&sdlist);
sdlist = NULL;
}
return (NS_LDAP_MEMORY);
}
rc = __s_api_getSearchScope(&scope, &cookie->errorp);
dptr->scope = scope;
}
/* Initialize State machine cookie */
if (cookie == NULL)
cookie = init_search_state_machine();
if (cookie == NULL) {
if (sdlist) {
(void) __ns_ldap_freeSearchDescriptors(&sdlist);
sdlist = NULL;
}
return (NS_LDAP_MEMORY);
}
cookie->sdlist = sdlist;
/* see if need to follow referrals */
rc = __s_api_toFollowReferrals(flags,
&cookie->followRef, errorp);
if (rc != NS_LDAP_SUCCESS) {
delete_search_cookie(cookie);
return (rc);
}
/*
* use VLV/PAGE control only if NS_LDAP_NO_PAGE_CTRL is not set
*/
if (flags & NS_LDAP_NO_PAGE_CTRL)
cookie->use_paging = FALSE;
else
cookie->use_paging = TRUE;
/* Set up other arguments */
cookie->userdata = userdata;
if (init_filter_cb != NULL) {
cookie->init_filter_cb = init_filter_cb;
cookie->use_filtercb = 1;
}
cookie->use_usercb = 0;
if (service) {
cookie->service = strdup(service);
if (cookie->service == NULL) {
delete_search_cookie(cookie);
return (NS_LDAP_MEMORY);
}
}
cookie->i_filter = strdup(filter);
cookie->i_attr = attribute;
cookie->i_auth = auth;
cookie->i_flags = flags;
state = INIT;
for (;;) {
state = search_state_machine(cookie, state, ONE_STEP);
switch (state) {
case PROCESS_RESULT:
*result = cookie->result;
cookie->result = NULL;
*vcookie = (void *)cookie;
return (NS_LDAP_SUCCESS);
case ERROR:
case LDAP_ERROR:
rc = cookie->err_rc;
*errorp = cookie->errorp;
cookie->errorp = NULL;
delete_search_cookie(cookie);
return (rc);
case EXIT:
rc = cookie->err_rc;
if (rc != NS_LDAP_SUCCESS) {
*errorp = cookie->errorp;
cookie->errorp = NULL;
} else {
rc = NS_LDAP_NOTFOUND;
}
delete_search_cookie(cookie);
return (rc);
default:
break;
}
}
}
/*ARGSUSED2*/
int
__ns_ldap_nextEntry(
void *vcookie,
ns_ldap_result_t **result,
ns_ldap_error_t ** errorp)
{
ns_ldap_cookie_t *cookie;
ns_state_t state;
int rc;
cookie = (ns_ldap_cookie_t *)vcookie;
cookie->result = NULL;
*result = NULL;
state = END_PROCESS_RESULT;
for (;;) {
state = search_state_machine(cookie, state, ONE_STEP);
switch (state) {
case PROCESS_RESULT:
*result = cookie->result;
cookie->result = NULL;
return (NS_LDAP_SUCCESS);
case ERROR:
case LDAP_ERROR:
rc = cookie->err_rc;
*errorp = cookie->errorp;
cookie->errorp = NULL;
return (rc);
case EXIT:
return (NS_LDAP_SUCCESS);
}
}
}
int
__ns_ldap_endEntry(
void **vcookie,
ns_ldap_error_t ** errorp)
{
ns_ldap_cookie_t *cookie;
int rc;
if (*vcookie == NULL)
return (NS_LDAP_INVALID_PARAM);
cookie = (ns_ldap_cookie_t *)(*vcookie);
cookie->result = NULL;
/* Complete search */
rc = search_state_machine(cookie, EXIT, 0);
/* Copy results back to user */
rc = cookie->err_rc;
if (rc != NS_LDAP_SUCCESS)
*errorp = cookie->errorp;
cookie->errorp = NULL;
delete_search_cookie(cookie);
cookie = NULL;
*vcookie = NULL;
return (rc);
}
int
__ns_ldap_freeResult(ns_ldap_result_t **result)
{
ns_ldap_entry_t *curEntry = NULL;
ns_ldap_entry_t *delEntry = NULL;
int i;
ns_ldap_result_t *res = *result;
#ifdef DEBUG
(void) fprintf(stderr, "__ns_ldap_freeResult START\n");
#endif
if (res == NULL)
return (NS_LDAP_INVALID_PARAM);
if (res->entry != NULL)
curEntry = res->entry;
for (i = 0; i < res->entries_count; i++) {
if (curEntry != NULL) {
delEntry = curEntry;
curEntry = curEntry->next;
__ns_ldap_freeEntry(delEntry);
}
}
free(res);
*result = NULL;
return (NS_LDAP_SUCCESS);
}
/*ARGSUSED*/
int
__ns_ldap_auth(const ns_cred_t *auth,
const int flags,
ns_ldap_error_t **errorp,
LDAPControl **serverctrls,
LDAPControl **clientctrls)
{
ConnectionID connectionId = -1;
Connection *conp;
int rc = 0;
int do_not_fail_if_new_pwd_reqd = 0;
#ifdef DEBUG
(void) fprintf(stderr, "__ns_ldap_auth START\n");
#endif
*errorp = NULL;
if (!auth)
return (NS_LDAP_INVALID_PARAM);
rc = __s_api_getConnection(NULL, flags,
auth, &connectionId, &conp, errorp,
do_not_fail_if_new_pwd_reqd);
if (rc == NS_LDAP_OP_FAILED && *errorp)
(void) __ns_ldap_freeError(errorp);
if (connectionId > -1)
DropConnection(connectionId, flags);
return (rc);
}
char **
__ns_ldap_getAttr(const ns_ldap_entry_t *entry, const char *attrname)
{
int i;
if (entry == NULL)
return (NULL);
for (i = 0; i < entry->attr_count; i++) {
if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == NULL)
return (entry->attr_pair[i]->attrvalue);
}
return (NULL);
}
/*ARGSUSED*/
int
__ns_ldap_uid2dn(const char *uid,
char **userDN,
const ns_cred_t *cred, /* cred is ignored */
ns_ldap_error_t **errorp)
{
ns_ldap_result_t *result = NULL;
char *filter, *userdata;
char errstr[MAXERROR];
char **value;
int rc = 0;
int i = 0;
size_t len;
*errorp = NULL;
*userDN = NULL;
if ((uid == NULL) || (uid[0] == '\0'))
return (NS_LDAP_INVALID_PARAM);
while (uid[i] != '\0') {
if (uid[i] == '=') {
*userDN = strdup(uid);
return (NS_LDAP_SUCCESS);
}
i++;
}
i = 0;
while ((uid[i] != '\0') && (isdigit(uid[i])))
i++;
if (uid[i] == '\0') {
len = strlen(UIDNUMFILTER) + strlen(uid) + 1;
filter = (char *)malloc(len);
if (filter == NULL) {
*userDN = NULL;
return (NS_LDAP_MEMORY);
}
(void) snprintf(filter, len, UIDNUMFILTER, uid);
len = strlen(UIDNUMFILTER_SSD) + strlen(uid) + 1;
userdata = (char *)malloc(len);
if (userdata == NULL) {
*userDN = NULL;
return (NS_LDAP_MEMORY);
}
(void) snprintf(userdata, len, UIDNUMFILTER_SSD, uid);
} else {
len = strlen(UIDFILTER) + strlen(uid) + 1;
filter = (char *)malloc(len);
if (filter == NULL) {
*userDN = NULL;
return (NS_LDAP_MEMORY);
}
(void) snprintf(filter, len, UIDFILTER, uid);
len = strlen(UIDFILTER_SSD) + strlen(uid) + 1;
userdata = (char *)malloc(len);
if (userdata == NULL) {
*userDN = NULL;
return (NS_LDAP_MEMORY);
}
(void) snprintf(userdata, len, UIDFILTER_SSD, uid);
}
rc = __ns_ldap_list("passwd", filter,
__s_api_merge_SSD_filter,
NULL, cred, 0,
&result, errorp, NULL,
userdata);
free(filter);
filter = NULL;
free(userdata);
userdata = NULL;
if (rc != NS_LDAP_SUCCESS) {
if (result) {
(void) __ns_ldap_freeResult(&result);
result = NULL;
}
return (rc);
}
if (result->entries_count > 1) {
(void) __ns_ldap_freeResult(&result);
result = NULL;
*userDN = NULL;
(void) sprintf(errstr,
gettext("Too many entries are returned for %s"), uid);
MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
NULL);
return (NS_LDAP_INTERNAL);
}
value = __ns_ldap_getAttr(result->entry, "dn");
*userDN = strdup(value[0]);
(void) __ns_ldap_freeResult(&result);
result = NULL;
return (NS_LDAP_SUCCESS);
}
/*ARGSUSED*/
int
__ns_ldap_host2dn(const char *host,
const char *domain,
char **hostDN,
const ns_cred_t *cred, /* cred is ignored */
ns_ldap_error_t **errorp)
{
ns_ldap_result_t *result = NULL;
char *filter, *userdata;
char errstr[MAXERROR];
char **value;
int rc;
size_t len;
/*
* XXX
* the domain parameter needs to be used in case domain is not local, if
* this routine is to support multi domain setups, it needs lots of work...
*/
*errorp = NULL;
*hostDN = NULL;
if ((host == NULL) || (host[0] == '\0'))
return (NS_LDAP_INVALID_PARAM);
len = strlen(HOSTFILTER) + strlen(host) + 1;
filter = (char *)malloc(len);
if (filter == NULL) {
return (NS_LDAP_MEMORY);
}
(void) snprintf(filter, len, HOSTFILTER, host);
len = strlen(HOSTFILTER_SSD) + strlen(host) + 1;
userdata = (char *)malloc(len);
if (userdata == NULL) {
return (NS_LDAP_MEMORY);
}
(void) snprintf(userdata, len, HOSTFILTER_SSD, host);
rc = __ns_ldap_list("hosts", filter,
__s_api_merge_SSD_filter,
NULL, cred, 0, &result,
errorp, NULL,
userdata);
free(filter);
filter = NULL;
free(userdata);
userdata = NULL;
if (rc != NS_LDAP_SUCCESS) {
if (result) {
(void) __ns_ldap_freeResult(&result);
result = NULL;
}
return (rc);
}
if (result->entries_count > 1) {
(void) __ns_ldap_freeResult(&result);
result = NULL;
*hostDN = NULL;
(void) sprintf(errstr,
gettext("Too many entries are returned for %s"), host);
MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
NULL);
return (NS_LDAP_INTERNAL);
}
value = __ns_ldap_getAttr(result->entry, "dn");
*hostDN = strdup(value[0]);
(void) __ns_ldap_freeResult(&result);
result = NULL;
return (NS_LDAP_SUCCESS);
}
/*ARGSUSED*/
int
__ns_ldap_dn2domain(const char *dn,
char **domain,
const ns_cred_t *cred,
ns_ldap_error_t **errorp)
{
int rc, pnum, i, j, len = 0;
char *newdn, **rdns = NULL;
char **dns, *dn1;
*errorp = NULL;
if (domain == NULL)
return (NS_LDAP_INVALID_PARAM);
else
*domain = NULL;
if ((dn == NULL) || (dn[0] == '\0'))
return (NS_LDAP_INVALID_PARAM);
/*
* break dn into rdns
*/
dn1 = strdup(dn);
if (dn1 == NULL)
return (NS_LDAP_MEMORY);
rdns = ldap_explode_dn(dn1, 0);
free(dn1);
if (rdns == NULL || *rdns == NULL)
return (NS_LDAP_INVALID_PARAM);
for (i = 0; rdns[i]; i++)
len += strlen(rdns[i]) + 1;
pnum = i;
newdn = (char *)malloc(len + 1);
dns = (char **)calloc(pnum, sizeof (char *));
if (newdn == NULL || dns == NULL) {
if (newdn)
free(newdn);
ldap_value_free(rdns);
return (NS_LDAP_MEMORY);
}
/* construct a semi-normalized dn, newdn */
*newdn = '\0';
for (i = 0; rdns[i]; i++) {
dns[i] = newdn + strlen(newdn);
(void) strcat(newdn,
__s_api_remove_rdn_space(rdns[i]));
(void) strcat(newdn, ",");
}
/* remove the last ',' */
newdn[strlen(newdn) - 1] = '\0';
ldap_value_free(rdns);
/*
* loop and find the domain name associated with newdn,
* removing rdn one by one from left to right
*/
for (i = 0; i < pnum; i++) {
if (*errorp)
(void) __ns_ldap_freeError(errorp);
/*
* try cache manager first
*/
rc = __s_api_get_cachemgr_data(NS_CACHE_DN2DOMAIN,
dns[i], domain);
if (rc != NS_LDAP_SUCCESS) {
/*
* try ldap server second
*/
rc = __s_api_find_domainname(dns[i], domain,
cred, errorp);
} else {
/*
* skip the last one,
* since it is already cached by ldap_cachemgr
*/
i--;
}
if (rc == NS_LDAP_SUCCESS) {
/*
* ask cache manager to save the
* dn to domain mapping(s)
*/
for (j = 0; j <= i; j++) {
(void) __s_api_set_cachemgr_data(
NS_CACHE_DN2DOMAIN,
dns[j],
*domain);
}
break;
}
}
free(dns);
free(newdn);
if (rc != NS_LDAP_SUCCESS)
rc = NS_LDAP_NOTFOUND;
return (rc);
}
/*ARGSUSED*/
int
__ns_ldap_getServiceAuthMethods(const char *service,
ns_auth_t ***auth,
ns_ldap_error_t **errorp)
{
char errstr[MAXERROR];
int rc, i, done = 0;
int slen;
void **param;
char **sam, *srv, *send;
ns_auth_t **authpp = NULL, *ap;
int cnt, max;
ns_config_t *cfg;
ns_ldap_error_t *error = NULL;
if (errorp == NULL)
return (NS_LDAP_INVALID_PARAM);
*errorp = NULL;
if ((service == NULL) || (service[0] == '\0') ||
(auth == NULL))
return (NS_LDAP_INVALID_PARAM);
*auth = NULL;
rc = __ns_ldap_getParam(NS_LDAP_SERVICE_AUTH_METHOD_P, &param, &error);
if (rc != NS_LDAP_SUCCESS || param == NULL) {
*errorp = error;
return (rc);
}
sam = (char **)param;
cfg = __s_api_get_default_config();
cnt = 0;
slen = strlen(service);
for (; *sam; sam++) {
srv = *sam;
if (strncasecmp(service, srv, slen) != 0)
continue;
srv += slen;
if (*srv != COLONTOK)
continue;
send = srv;
srv++;
for (max = 1; (send = strchr(++send, SEMITOK)) != NULL;
max++) {}
authpp = (ns_auth_t **)calloc(++max, sizeof (ns_auth_t *));
if (authpp == NULL) {
(void) __ns_ldap_freeParam(&param);
__s_api_release_config(cfg);
return (NS_LDAP_MEMORY);
}
while (!done) {
send = strchr(srv, SEMITOK);
if (send != NULL) {
*send = '\0';
send++;
}
i = __s_get_enum_value(cfg, srv, NS_LDAP_AUTH_P);
if (i == -1) {
(void) __ns_ldap_freeParam(&param);
(void) sprintf(errstr,
gettext("Unsupported serviceAuthenticationMethod: %s.\n"), srv);
MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX,
strdup(errstr), NULL);
__s_api_release_config(cfg);
return (NS_LDAP_CONFIG);
}
ap = __s_api_AuthEnumtoStruct((EnumAuthType_t)i);
if (ap == NULL) {
(void) __ns_ldap_freeParam(&param);
__s_api_release_config(cfg);
return (NS_LDAP_MEMORY);
}
authpp[cnt++] = ap;
if (send == NULL)
done = TRUE;
else
srv = send;
}
}
*auth = authpp;
(void) __ns_ldap_freeParam(&param);
__s_api_release_config(cfg);
return (NS_LDAP_SUCCESS);
}
/*
* This routine is called when certain scenario occurs
* e.g.
* service == auto_home
* SSD = automount: ou = mytest,
* NS_LDAP_MAPATTRIBUTE= auto_home: automountMapName=AAA
* NS_LDAP_OBJECTCLASSMAP= auto_home:automountMap=MynisMap
* NS_LDAP_OBJECTCLASSMAP= auto_home:automount=MynisObject
*
* The automountMapName is prepended implicitely but is mapped
* to AAA. So dn could appers as
* dn: AAA=auto_home,ou=bar,dc=foo,dc=com
* dn: automountKey=user_01,AAA=auto_home,ou=bar,dc=foo,dc=com
* dn: automountKey=user_02,AAA=auto_home,ou=bar,dc=foo,dc=com
* in the directory.
* This function is called to covert the mapped attr back to
* orig attr when the entries are searched and returned
*/
int
__s_api_convert_automountmapname(const char *service, char **dn,
ns_ldap_error_t **errp) {
char **mapping = NULL;
char *mapped_attr = NULL;
char *automountmapname = "automountMapName";
char *buffer = NULL;
int rc = NS_LDAP_SUCCESS;
char errstr[MAXERROR];
/*
* dn is an input/out parameter, check it first
*/
if (service == NULL || dn == NULL || *dn == NULL)
return (NS_LDAP_INVALID_PARAM);
/*
* Check to see if there is a mapped attribute for auto_xxx
*/
mapping = __ns_ldap_getMappedAttributes(service, automountmapname);
/*
* if no mapped attribute for auto_xxx, try automount
*/
if (mapping == NULL)
mapping = __ns_ldap_getMappedAttributes(
"automount", automountmapname);
/*
* if no mapped attribute is found, return SUCCESS (no op)
*/
if (mapping == NULL)
return (NS_LDAP_SUCCESS);
/*
* if the mapped attribute is found and attr is not empty,
* copy it
*/
if (mapping[0] != NULL) {
mapped_attr = strdup(mapping[0]);
__s_api_free2dArray(mapping);
if (mapped_attr == NULL) {
return (NS_LDAP_MEMORY);
}
} else {
__s_api_free2dArray(mapping);
(void) snprintf(errstr, (2 * MAXERROR),
gettext(
"Attribute nisMapName is mapped to an "
"empty string.\n"));
MKERROR(LOG_ERR, *errp, NS_CONFIG_SYNTAX,
strdup(errstr), NULL);
return (NS_LDAP_CONFIG);
}
/*
* Locate the mapped attribute in the dn
* and replace it if it exists
*/
rc = __s_api_replace_mapped_attr_in_dn(
(const char *) automountmapname, (const char *) mapped_attr,
(const char *) *dn, &buffer);
/* clean up */
free(mapped_attr);
/*
* If mapped attr is found(buffer != NULL)
* a new dn is returned
* If no mapped attribute is in dn,
* return NS_LDAP_SUCCESS (no op)
* If no memory,
* return NS_LDAP_MEMORY (no op)
*/
if (buffer != NULL) {
free(*dn);
*dn = buffer;
}
return (rc);
}
/*
* If the mapped attr is found in the dn,
* return NS_LDAP_SUCCESS and a new_dn.
* If no mapped attr is found,
* return NS_LDAP_SUCCESS and *new_dn == NULL
* If there is not enough memory,
* return NS_LDAP_MEMORY and *new_dn == NULL
*/
int
__s_api_replace_mapped_attr_in_dn(
const char *orig_attr, const char *mapped_attr,
const char *dn, char **new_dn) {
char **dnArray = NULL;
char *cur = NULL, *start = NULL;
int i = 0, found = 0;
int len = 0, orig_len = 0, mapped_len = 0;
int dn_len = 0, tmp_len = 0;
*new_dn = NULL;
/*
* seperate dn into individual componets
* e.g.
* "automountKey=user_01" , "automountMapName_test=auto_home", ...
*/
dnArray = ldap_explode_dn(dn, 0);
/*
* This will find "mapped attr=value" in dn.
* It won't find match if mapped attr appears
* in the value.
*/
for (i = 0; dnArray[i] != NULL; i++) {
/*
* This function is called when reading from
* the directory so assume each component has "=".
* Any ill formatted dn should be rejected
* before adding to the directory
*/
cur = strchr(dnArray[i], '=');
*cur = '\0';
if (strcasecmp(mapped_attr, dnArray[i]) == 0)
found = 1;
*cur = '=';
if (found) break;
}
if (!found) {
__s_api_free2dArray(dnArray);
*new_dn = NULL;
return (NS_LDAP_SUCCESS);
}
/*
* The new length is *dn length + (difference between
* orig attr and mapped attr) + 1 ;
* e.g.
* automountKey=aa,automountMapName_test=auto_home,dc=foo,dc=com
* ==>
* automountKey=aa,automountMapName=auto_home,dc=foo,dc=com
*/
mapped_len = strlen(mapped_attr);
orig_len = strlen(orig_attr);
dn_len = strlen(dn);
len = dn_len + orig_len - mapped_len + 1;
*new_dn = (char *)calloc(1, len);
if (*new_dn == NULL) {
__s_api_free2dArray(dnArray);
return (NS_LDAP_MEMORY);
}
/*
* Locate the mapped attr in the dn.
* Use dnArray[i] instead of mapped_attr
* because mapped_attr could appear in
* the value
*/
cur = strstr(dn, dnArray[i]);
__s_api_free2dArray(dnArray);
/* copy the portion before mapped attr in dn */
start = *new_dn;
tmp_len = cur - dn;
(void) memcpy((void *) start, (const void*) dn, tmp_len);
/*
* Copy the orig_attr. e.g. automountMapName
* This replaces mapped attr with orig attr
*/
start = start + (cur - dn); /* move cursor in buffer */
(void) memcpy((void *) start, (const void*) orig_attr, orig_len);
/*
* Copy the portion after mapped attr in dn
*/
cur = cur + mapped_len; /* move cursor in dn */
start = start + orig_len; /* move cursor in buffer */
(void) strcpy(start, cur);
return (NS_LDAP_SUCCESS);
}
/*
* Validate Filter functions
*/
/* ***** Start of modified libldap.so.5 filter parser ***** */
/* filter parsing routine forward references */
static int adj_filter_list(char *str);
static int adj_simple_filter(char *str);
static int unescape_filterval(char *val);
static int hexchar2int(char c);
static int adj_substring_filter(char *val);
/*
* assumes string manipulation is in-line
* and all strings are sufficient in size
* return value is the position after 'c'
*/
static char *
resync_str(char *str, char *next, char c)
{
char *ret;
ret = str + strlen(str);
*next = c;
if (ret == next)
return (ret);
(void) strcat(str, next);
return (ret);
}
static char *
find_right_paren(char *s)
{
int balance, escape;
balance = 1;
escape = 0;
while (*s && balance) {
if (escape == 0) {
if (*s == '(')
balance++;
else if (*s == ')')
balance--;
}
if (*s == '\\' && ! escape)
escape = 1;
else
escape = 0;
if (balance)
s++;
}
return (*s ? s : NULL);
}
static char *
adj_complex_filter(char *str)
{
char *next;
/*
* We have (x(filter)...) with str sitting on
* the x. We have to find the paren matching
* the one before the x and put the intervening
* filters by calling adj_filter_list().
*/
str++;
if ((next = find_right_paren(str)) == NULL)
return (NULL);
*next = '\0';
if (adj_filter_list(str) == -1)
return (NULL);
next = resync_str(str, next, ')');
next++;
return (next);
}
static int
adj_filter(char *str)
{
char *next;
int parens, balance, escape;
char *np, *cp, *dp;
parens = 0;
while (*str) {
switch (*str) {
case '(':
str++;
parens++;
switch (*str) {
case '&':
if ((str = adj_complex_filter(str)) == NULL)
return (-1);
parens--;
break;
case '|':
if ((str = adj_complex_filter(str)) == NULL)
return (-1);
parens--;
break;
case '!':
if ((str = adj_complex_filter(str)) == NULL)
return (-1);
parens--;
break;
case '(':
/* illegal ((case - generated by conversion */
/* find missing close) */
np = find_right_paren(str+1);
/* error if not found */
if (np == NULL)
return (-1);
/* remove redundant (and) */
for (dp = str, cp = str+1; cp < np; ) {
*dp++ = *cp++;
}
cp++;
while (*cp)
*dp++ = *cp++;
*dp = '\0';
/* re-start test at original ( */
parens--;
str--;
break;
default:
balance = 1;
escape = 0;
next = str;
while (*next && balance) {
if (escape == 0) {
if (*next == '(')
balance++;
else if (*next == ')')
balance--;
}
if (*next == '\\' && ! escape)
escape = 1;
else
escape = 0;
if (balance)
next++;
}
if (balance != 0)
return (-1);
*next = '\0';
if (adj_simple_filter(str) == -1) {
return (-1);
}
next = resync_str(str, next, ')');
next++;
str = next;
parens--;
break;
}
break;
case ')':
str++;
parens--;
break;
case ' ':
str++;
break;
default: /* assume it's a simple type=value filter */
next = strchr(str, '\0');
if (adj_simple_filter(str) == -1) {
return (-1);
}
str = next;
break;
}
}
return (parens ? -1 : 0);
}
/*
* Put a list of filters like this "(filter1)(filter2)..."
*/
static int
adj_filter_list(char *str)
{
char *next;
char save;
while (*str) {
while (*str && isspace(*str))
str++;
if (*str == '\0')
break;
if ((next = find_right_paren(str + 1)) == NULL)
return (-1);
save = *++next;
/* now we have "(filter)" with str pointing to it */
*next = '\0';
if (adj_filter(str) == -1)
return (-1);
next = resync_str(str, next, save);
str = next;
}
return (0);
}
/*
* is_valid_attr - returns 1 if a is a syntactically valid left-hand side
* of a filter expression, 0 otherwise. A valid string may contain only
* letters, numbers, hyphens, semi-colons, colons and periods. examples:
* cn
* cn;lang-fr
* 1.2.3.4;binary;dynamic
* mail;dynamic
* cn:dn:1.2.3.4
*
* For compatibility with older servers, we also allow underscores in
* attribute types, even through they are not allowed by the LDAPv3 RFCs.
*/
static int
is_valid_attr(char *a)
{
for (; *a; a++) {
if (!isascii(*a)) {
return (0);
} else if (!isalnum(*a)) {
switch (*a) {
case '-':
case '.':
case ';':
case ':':
case '_':
break; /* valid */
default:
return (0);
}
}
}
return (1);
}
static char *
find_star(char *s)
{
for (; *s; ++s) {
switch (*s) {
case '*':
return (s);
case '\\':
++s;
if (hexchar2int(s[0]) >= 0 && hexchar2int(s[1]) >= 0)
++s;
default:
break;
}
}
return (NULL);
}
static int
adj_simple_filter(char *str)
{
char *s, *s2, *s3, filterop;
char *value;
int ftype = 0;
int rc;
rc = -1; /* pessimistic */
if ((str = strdup(str)) == NULL) {
return (rc);
}
if ((s = strchr(str, '=')) == NULL) {
goto free_and_return;
}
value = s + 1;
*s-- = '\0';
filterop = *s;
if (filterop == '<' || filterop == '>' || filterop == '~' ||
filterop == ':') {
*s = '\0';
}
if (! is_valid_attr(str)) {
goto free_and_return;
}
switch (filterop) {
case '<': /* LDAP_FILTER_LE */
case '>': /* LDAP_FILTER_GE */
case '~': /* LDAP_FILTER_APPROX */
break;
case ':': /* extended filter - v3 only */
/*
* extended filter looks like this:
*
* [type][':dn'][':'oid]':='value
*
* where one of type or :oid is required.
*
*/
s2 = s3 = NULL;
if ((s2 = strrchr(str, ':')) == NULL) {
goto free_and_return;
}
if (strcasecmp(s2, ":dn") == 0) {
*s2 = '\0';
} else {
*s2 = '\0';
if ((s3 = strrchr(str, ':')) != NULL) {
if (strcasecmp(s3, ":dn") != 0) {
goto free_and_return;
}
*s3 = '\0';
}
}
if (unescape_filterval(value) < 0) {
goto free_and_return;
}
rc = 0;
goto free_and_return;
/* break; */
default:
if (find_star(value) == NULL) {
ftype = 0; /* LDAP_FILTER_EQUALITY */
} else if (strcmp(value, "*") == 0) {
ftype = 1; /* LDAP_FILTER_PRESENT */
} else {
rc = adj_substring_filter(value);
goto free_and_return;
}
break;
}
if (ftype != 0) { /* == LDAP_FILTER_PRESENT */
rc = 0;
} else if (unescape_filterval(value) >= 0) {
rc = 0;
}
if (rc != -1) {
rc = 0;
}
free_and_return:
free(str);
return (rc);
}
/*
* Check in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape
* sequences within the null-terminated string 'val'.
*
* If 'val' contains invalid escape sequences we return -1.
* Otherwise return 1
*/
static int
unescape_filterval(char *val)
{
int escape, firstdigit;
char *s;
firstdigit = 0;
escape = 0;
for (s = val; *s; s++) {
if (escape) {
/*
* first try LDAPv3 escape (hexadecimal) sequence
*/
if (hexchar2int(*s) < 0) {
if (firstdigit) {
/*
* LDAPv2 (RFC1960) escape sequence
*/
escape = 0;
} else {
return (-1);
}
}
if (firstdigit) {
firstdigit = 0;
} else {
escape = 0;
}
} else if (*s != '\\') {
escape = 0;
} else {
escape = 1;
firstdigit = 1;
}
}
return (1);
}
/*
* convert character 'c' that represents a hexadecimal digit to an integer.
* if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned.
* otherwise the converted value is returned.
*/
static int
hexchar2int(char c)
{
if (c >= '0' && c <= '9') {
return (c - '0');
}
if (c >= 'A' && c <= 'F') {
return (c - 'A' + 10);
}
if (c >= 'a' && c <= 'f') {
return (c - 'a' + 10);
}
return (-1);
}
static int
adj_substring_filter(char *val)
{
char *nextstar;
for (; val != NULL; val = nextstar) {
if ((nextstar = find_star(val)) != NULL) {
*nextstar++ = '\0';
}
if (*val != '\0') {
if (unescape_filterval(val) < 0) {
return (-1);
}
}
}
return (0);
}
/* ***** End of modified libldap.so.5 filter parser ***** */
/*
* Walk filter, remove redundant parentheses in-line
* verify that the filter is reasonable
*/
static int
validate_filter(ns_ldap_cookie_t *cookie)
{
char *filter = cookie->filter;
int rc;
/* Parse filter looking for illegal values */
rc = adj_filter(filter);
if (rc != 0) {
return (NS_LDAP_OP_FAILED);
}
/* end of filter checking */
return (NS_LDAP_SUCCESS);
}