2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * DESCRIPTION: Contains dit_access interface support functions.
2N/A */
2N/A#include <sys/systeminfo.h>
2N/A#include <unistd.h>
2N/A#include <stdlib.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/systeminfo.h>
2N/A#include <unistd.h>
2N/A#include <stdlib.h>
2N/A#include <syslog.h>
2N/A#include <ndbm.h>
2N/A#include <strings.h>
2N/A#include <errno.h>
2N/A#include "../ldap_util.h"
2N/A#include "../ldap_map.h"
2N/A#include "../ldap_parse.h"
2N/A#include "../ldap_structs.h"
2N/A#include "../ldap_val.h"
2N/A#include "../ldap_ruleval.h"
2N/A#include "../ldap_op.h"
2N/A#include "../ldap_attr.h"
2N/A#include "../ldap_nisdbquery.h"
2N/A#include "../nisdb_mt.h"
2N/A#include "shim.h"
2N/A#include "yptol.h"
2N/A#include "dit_access_utils.h"
2N/A
2N/A#define YPMULTI "YP_MULTI_"
2N/A#define YPMULTISZ 9 /* == strlen(YPMULTI) */
2N/A
2N/A/*
2N/A * Returns 'map,domain.'
2N/A */
2N/Achar *
2N/AgetFullMapName(char *map, char *domain) {
2N/A const char *myself = "getFullMapName";
2N/A char *objPath;
2N/A if (map == 0 || domain == 0) {
2N/A return (0);
2N/A }
2N/A objPath = scat(myself, T, scat(myself, F, map, ","),
2N/A scat(myself, F, domain, "."));
2N/A
2N/A return (objPath);
2N/A}
2N/A
2N/A/*
2N/A * Convert string to __nis_value_t
2N/A */
2N/A__nis_value_t *stringToValue(char *dptr, int dsize) {
2N/A const char *myself = "stringToValue";
2N/A char *emptystr = "";
2N/A __nis_value_t *val;
2N/A
2N/A if ((val = am(myself, sizeof (*val))) == 0) {
2N/A return (0);
2N/A }
2N/A
2N/A val->type = vt_string;
2N/A val->repeat = 0;
2N/A val->numVals = 1;
2N/A if ((val->val = am(myself, sizeof (val->val[0]))) == 0) {
2N/A sfree(val);
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * Null strings or strings with length 0 are treated
2N/A * as empty strings with length 1
2N/A */
2N/A if (dptr == 0 || dsize <= 0) {
2N/A dptr = emptystr;
2N/A dsize = 1;
2N/A }
2N/A
2N/A val->val->length = dsize;
2N/A if (dptr[dsize - 1] != '\0') {
2N/A val->val->length = dsize + 1;
2N/A }
2N/A
2N/A val->val->value = am(myself, val->val->length);
2N/A if (val->val->value == 0) {
2N/A freeValue(val, 1);
2N/A return (0);
2N/A }
2N/A (void) memcpy(val->val->value, dptr, dsize);
2N/A
2N/A return (val);
2N/A}
2N/A
2N/A/*
2N/A * Returns an array of rule-values corresponding to the
2N/A * splitfields.
2N/A */
2N/A__nis_rule_value_t *
2N/AprocessSplitField(__nis_table_mapping_t *sf, __nis_value_t *inVal,
2N/A int *nv, int *statP) {
2N/A
2N/A char *sepset;
2N/A __nis_rule_value_t *rvq;
2N/A __nis_mapping_format_t *ftmp;
2N/A __nis_value_t **valA, *tempVal;
2N/A int i, j, res, numVals, oldlen, count;
2N/A char *str, *oldstr;
2N/A const char *myself = "processSplitField";
2N/A
2N/A /* sf will be non NULL */
2N/A
2N/A if (inVal == 0 || inVal->type != vt_string) {
2N/A *statP = MAP_PARAM_ERROR;
2N/A return (0);
2N/A }
2N/A
2N/A /* Get the separator list */
2N/A sepset = sf->separatorStr;
2N/A
2N/A /* Initialize rule-value */
2N/A rvq = 0;
2N/A count = 0;
2N/A
2N/A if ((tempVal = stringToValue(inVal->val->value,
2N/A inVal->val->length)) == 0) {
2N/A *statP = MAP_NO_MEMORY;
2N/A return (0);
2N/A }
2N/A
2N/A str = oldstr = tempVal->val->value;
2N/A oldlen = tempVal->val->length;
2N/A
2N/A while (str) {
2N/A tempVal->val->value = str;
2N/A tempVal->val->length = strlen(str) + 1;
2N/A
2N/A /* Loop to check which format matches str */
2N/A for (i = 0; i <= sf->numSplits; i++) {
2N/A valA = matchMappingItem(sf->e[i].element.match.fmt,
2N/A tempVal, &numVals, sepset, &str);
2N/A if (valA == 0) {
2N/A /* The format didn't match. Try the next one */
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * If we are here means we had a match.
2N/A * Each new set of values obtained from the match is
2N/A * added to a new rule-value. This is to preserve the
2N/A * the distinction between each set.
2N/A */
2N/A rvq = growRuleValue(count, count + 1, rvq, 0);
2N/A if (rvq == 0) {
2N/A *statP = MAP_INTERNAL_ERROR;
2N/A for (j = 0; j < numVals; j++)
2N/A freeValue(valA[j], 1);
2N/A sfree(valA);
2N/A tempVal->val->value = oldstr;
2N/A tempVal->val->length = oldlen;
2N/A freeValue(tempVal, 1);
2N/A return (0);
2N/A }
2N/A count++;
2N/A
2N/A for (j = 0; j < numVals; j++) {
2N/A res = addCol2RuleValue(vt_string,
2N/A sf->e[i].element.match.item[j].name,
2N/A valA[j]->val->value,
2N/A valA[j]->val->length,
2N/A &rvq[count - 1]);
2N/A if (res == -1) {
2N/A *statP = MAP_INTERNAL_ERROR;
2N/A for (; j < numVals; j++)
2N/A freeValue(valA[j], 1);
2N/A sfree(valA);
2N/A tempVal->val->value = oldstr;
2N/A tempVal->val->length = oldlen;
2N/A freeValue(tempVal, 1);
2N/A freeRuleValue(rvq, count);
2N/A return (0);
2N/A }
2N/A freeValue(valA[j], 1);
2N/A }
2N/A sfree(valA);
2N/A
2N/A /*
2N/A * Since we had a match, break out of this loop
2N/A * to parse remainder of str
2N/A */
2N/A break;
2N/A }
2N/A
2N/A /* Didn't find any match, so get out of the loop */
2N/A if (i > sf->numSplits) {
2N/A str = 0;
2N/A break;
2N/A }
2N/A
2N/A /* Skip the separators before looping back */
2N/A if (str) {
2N/A str = str + strspn(str, sepset);
2N/A if (*str == '\0')
2N/A break;
2N/A }
2N/A }
2N/A
2N/A tempVal->val->value = oldstr;
2N/A tempVal->val->length = oldlen;
2N/A freeValue(tempVal, 1);
2N/A
2N/A if (str == 0) {
2N/A freeRuleValue(rvq, count);
2N/A return (0);
2N/A }
2N/A
2N/A if (nv != 0)
2N/A *nv = count;
2N/A
2N/A return (rvq);
2N/A}
2N/A
2N/A/*
2N/A * Convert the datum to an array of RuleValues
2N/A */
2N/A__nis_rule_value_t *
2N/AdatumToRuleValue(datum *key, datum *value, __nis_table_mapping_t *t,
2N/A int *nv, char *domain, bool_t readonly, int *statP) {
2N/A
2N/A char *dptr = value->dptr;
2N/A __nis_rule_value_t *rvq, *subrvq, *newrvq;
2N/A __nis_value_t *val;
2N/A __nis_value_t **valA;
2N/A __nis_table_mapping_t *sf;
2N/A int valueLen, comLen, numVals, nr, count = 1;
2N/A int i, j, k, l, af;
2N/A char *ipaddr, *ipvalue;
2N/A
2N/A /* At this point, 't' is always non NULL */
2N/A
2N/A /* Initialize rule-value */
2N/A if ((rvq = initRuleValue(1, 0)) == 0) {
2N/A *statP = MAP_INTERNAL_ERROR;
2N/A return (0);
2N/A }
2N/A
2N/A /* Add domainname to rule-value */
2N/A if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
2N/A rvq)) {
2N/A freeRuleValue(rvq, 1);
2N/A *statP = MAP_INTERNAL_ERROR;
2N/A return (0);
2N/A }
2N/A
2N/A /* Handle key */
2N/A if (key != 0) {
2N/A /* Add field=value pair for N2LKEY */
2N/A i = addCol2RuleValue(vt_string, N2LKEY, key->dptr, key->dsize,
2N/A rvq);
2N/A
2N/A /* For readonly, add field=value pair for N2LSEARCHKEY */
2N/A if (readonly == TRUE && i == 0) {
2N/A i = addCol2RuleValue(vt_string, N2LSEARCHKEY, key->dptr,
2N/A key->dsize, rvq);
2N/A }
2N/A if (i) {
2N/A freeRuleValue(rvq, 1);
2N/A *statP = MAP_INTERNAL_ERROR;
2N/A return (0);
2N/A }
2N/A
2N/A /* Add field=value pairs for IP addresses */
2N/A if (checkIPaddress(key->dptr, key->dsize, &ipaddr) > 0) {
2N/A /* If key is IPaddress, use preferred format */
2N/A ipvalue = ipaddr;
2N/A valueLen = strlen(ipaddr);
2N/A i = addCol2RuleValue(vt_string, N2LIPKEY, ipvalue,
2N/A valueLen, rvq);
2N/A } else {
2N/A /* If not, use original value for N2LSEARCHIPKEY */
2N/A ipaddr = 0;
2N/A ipvalue = key->dptr;
2N/A valueLen = key->dsize;
2N/A i = 0;
2N/A }
2N/A
2N/A if (readonly == TRUE && i == 0) {
2N/A i = addCol2RuleValue(vt_string, N2LSEARCHIPKEY, ipvalue,
2N/A valueLen, rvq);
2N/A }
2N/A sfree(ipaddr);
2N/A if (i) {
2N/A freeRuleValue(rvq, 1);
2N/A *statP = MAP_INTERNAL_ERROR;
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A /* Handle datum value */
2N/A if (value != 0 && t->e) {
2N/A valueLen = value->dsize;
2N/A /*
2N/A * Extract the comment, if any, and add it to
2N/A * the rule-value.
2N/A */
2N/A if (t->commentChar != '\0') {
2N/A /*
2N/A * We loop on value->dsize because value->dptr
2N/A * may not be NULL-terminated.
2N/A */
2N/A for (i = 0; i < value->dsize; i++) {
2N/A if (dptr[i] == t->commentChar) {
2N/A valueLen = i;
2N/A comLen = value->dsize - i - 1;
2N/A if (comLen == 0)
2N/A break;
2N/A if (addCol2RuleValue(vt_string,
2N/A N2LCOMMENT, dptr + i + 1,
2N/A comLen, rvq)) {
2N/A freeRuleValue(rvq, 1);
2N/A *statP = MAP_INTERNAL_ERROR;
2N/A return (0);
2N/A }
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* Skip trailing whitespaces */
2N/A for (; valueLen > 0 && (dptr[valueLen - 1] == ' ' ||
2N/A dptr[valueLen - 1] == '\t'); valueLen--);
2N/A
2N/A /*
2N/A * At this point valueLen is the effective length of
2N/A * the data. Convert value into __nis_value_t so that
2N/A * we can use the matchMappingItem function to break it
2N/A * into fields.
2N/A */
2N/A if ((val = stringToValue(dptr, valueLen)) == 0) {
2N/A freeRuleValue(rvq, 1);
2N/A *statP = MAP_NO_MEMORY;
2N/A return (0);
2N/A }
2N/A
2N/A /* Perform namefield match */
2N/A valA = matchMappingItem(t->e->element.match.fmt, val,
2N/A &numVals, 0, 0);
2N/A if (valA == 0) {
2N/A freeValue(val, 1);
2N/A freeRuleValue(rvq, 1);
2N/A *statP = MAP_NAMEFIELD_MATCH_ERROR;
2N/A return (0);
2N/A }
2N/A
2N/A /* We don't need val anymore, so free it */
2N/A freeValue(val, 1);
2N/A
2N/A /*
2N/A * Since matchMappingItem only returns us an array of
2N/A * __nis_value_t's, we need to associate each value
2N/A * in the array with the corresponding item name.
2N/A * This code assumes that numVals will be less than or
2N/A * equal to the number of item names associated with
2N/A * the format.
2N/A * These name=value pairs are added to rvq.
2N/A */
2N/A for (i = 0, *statP = SUCCESS; i < numVals; i++) {
2N/A for (j = 0; j < count; j++) {
2N/A if (addCol2RuleValue(vt_string,
2N/A t->e->element.match.item[i].name,
2N/A valA[i]->val->value,
2N/A valA[i]->val->length, &rvq[j])) {
2N/A *statP = MAP_INTERNAL_ERROR;
2N/A break;
2N/A }
2N/A }
2N/A if (*statP == MAP_INTERNAL_ERROR)
2N/A break;
2N/A
2N/A /*
2N/A * Check if splitField exists for the field.
2N/A * Since splitfields are also stored as mapping
2N/A * structures, we need to get the hash table entry
2N/A * corresponding to the splitfield name
2N/A */
2N/A sf = mappingFromMap(t->e->element.match.item[i].name,
2N/A domain, statP);
2N/A if (*statP == MAP_NO_MEMORY)
2N/A break;
2N/A *statP = SUCCESS;
2N/A if (sf == 0)
2N/A continue;
2N/A
2N/A /*
2N/A * Process and add splitFields to rule-value rvq
2N/A */
2N/A subrvq = processSplitField(sf, valA[i], &nr, statP);
2N/A
2N/A if (subrvq == 0) {
2N/A /* statP would have been set */
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * We merge 'count' rule-values in rvq with 'nr'
2N/A * rule-values from subrvq to give us a whopping
2N/A * 'count * nr' rule-values
2N/A */
2N/A
2N/A /* Initialize the new rule-value array */
2N/A if ((newrvq = initRuleValue(count * nr, 0)) == 0) {
2N/A *statP = MAP_INTERNAL_ERROR;
2N/A freeRuleValue(subrvq, nr);
2N/A break;
2N/A }
2N/A
2N/A for (j = 0, l = 0; j < nr; j++) {
2N/A for (k = 0; k < count; k++, l++) {
2N/A if ((mergeRuleValue(&newrvq[l],
2N/A &rvq[k]) == -1) ||
2N/A (mergeRuleValue(
2N/A &newrvq[l],
2N/A &subrvq[j]) == -1)) {
2N/A *statP = MAP_INTERNAL_ERROR;
2N/A for (i = 0; i < numVals; i++)
2N/A freeValue(valA[i], 1);
2N/A sfree(valA);
2N/A freeRuleValue(rvq, count);
2N/A freeRuleValue(newrvq,
2N/A count * nr);
2N/A freeRuleValue(subrvq, nr);
2N/A return (0);
2N/A }
2N/A }
2N/A }
2N/A
2N/A freeRuleValue(rvq, count);
2N/A rvq = newrvq;
2N/A count = l;
2N/A freeRuleValue(subrvq, nr);
2N/A
2N/A }
2N/A
2N/A /* We don't need valA anymore, so free it */
2N/A for (i = 0; i < numVals; i++)
2N/A freeValue(valA[i], 1);
2N/A sfree(valA);
2N/A
2N/A if (*statP != SUCCESS) {
2N/A freeRuleValue(rvq, count);
2N/A return (0);
2N/A }
2N/A
2N/A } /* if value */
2N/A
2N/A if (nv != 0)
2N/A *nv = count;
2N/A return (rvq);
2N/A
2N/A}
2N/A
2N/A/*
2N/A * Generate name=values pairs for splitfield names
2N/A *
2N/A * Consider Example:
2N/A * nisLDAPnameFields club:
2N/A * ("%s %s %s", name, code, members)
2N/A * nisLDAPsplitField members:
2N/A * ("(%s,%s,%s)", host, user, domain),
2N/A * ("%s", group)
2N/A * On entry,
2N/A * - rv is an array of numVals rule-values each containing
2N/A * name=value pairs for names occuring in nisLDAPsplitField.
2N/A * (i.e host, user, domain, group)
2N/A * - trv contains name=value pairs for names occuring in
2N/A * nisLDAPnameFields. (i.e name, code but not members)
2N/A *
2N/A * For every name in nisLDAPnamefields that is a splitfield,
2N/A * this function applies the data in rv to the corresponding
2N/A * splitfield formats (accessed thru t), to generate a single
2N/A * string value for the corresponding splitfield (members).
2N/A * This new name=value pair is then added to trv.
2N/A * Besides, any uninitialized namefield names are set to empty strings.
2N/A */
2N/Asuc_code
2N/AaddSplitFieldValues(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
2N/A __nis_rule_value_t *trv, int numVals, char *domain) {
2N/A __nis_table_mapping_t *sf;
2N/A __nis_value_t *val;
2N/A int i, j, k, nitems, res, statP;
2N/A char *str, *tempstr;
2N/A char delim[2] = {0, 0};
2N/A char *emptystr = "";
2N/A const char *myself = "addSplitFieldValues";
2N/A
2N/A if (trv == 0)
2N/A return (MAP_INTERNAL_ERROR);
2N/A
2N/A if (t->e == 0)
2N/A return (SUCCESS);
2N/A
2N/A nitems = t->e->element.match.numItems;
2N/A
2N/A /*
2N/A * Procedure:
2N/A * - Check each name in nisLDAPnamefield
2N/A * - if it's a splifield, construct its value and add it to trv
2N/A * - if not, check if it has a value
2N/A * - if not, add empty string
2N/A */
2N/A for (i = 0, sf = 0; i < nitems; i++) {
2N/A if (rv) {
2N/A /*
2N/A * str will eventually contain the single string
2N/A * value for the corresponding splitfield.
2N/A * No point initializing str if rv == 0 because
2N/A * splitfield cannot be constructed without rv.
2N/A * So, only initialized here.
2N/A */
2N/A str = 0;
2N/A
2N/A /* Check if it's a splitfield name */
2N/A sf = mappingFromMap(t->e->element.match.item[i].name,
2N/A domain, &statP);
2N/A
2N/A /*
2N/A * Return only incase of memory allocation failure.
2N/A * The other error case (MAP_NO_MAPPING_EXISTS),
2N/A * indicates that the item name is not a splitfieldname
2N/A * i.e it's a namefieldname. This case is handled by
2N/A * the following if (sf == 0)
2N/A */
2N/A if (statP == MAP_NO_MEMORY)
2N/A return (statP);
2N/A }
2N/A
2N/A if (sf == 0) {
2N/A /*
2N/A * Not a splitfield name. Verify if it has a value
2N/A */
2N/A if (findVal(t->e->element.match.item[i].name,
2N/A trv, mit_nis) == 0) {
2N/A /* if not, use empty string */
2N/A res = addCol2RuleValue(vt_string,
2N/A t->e->element.match.item[i].name,
2N/A emptystr, 0, trv);
2N/A if (res == -1) {
2N/A return (MAP_INTERNAL_ERROR);
2N/A }
2N/A }
2N/A /*
2N/A * If rv == 0 then sf == 0 so we will continue here
2N/A * i.e. does not matter that str is not yet set up.
2N/A */
2N/A continue;
2N/A }
2N/A
2N/A /* Code to construct a single value */
2N/A
2N/A /* Use the first separator character as the delimiter */
2N/A delim[0] = sf->separatorStr[0];
2N/A
2N/A for (j = 0; j < numVals; j++) {
2N/A /* sf->numSplits is zero-based */
2N/A for (k = 0; k <= sf->numSplits; k++) {
2N/A val = getMappingFormatArray(
2N/A sf->e[k].element.match.fmt, &rv[j],
2N/A fa_item,
2N/A sf->e[k].element.match.numItems,
2N/A sf->e[k].element.match.item);
2N/A if (val == 0)
2N/A continue;
2N/A if (val->numVals > 0) {
2N/A if (str) {
2N/A tempstr = scat(myself,
2N/A 0, str, delim);
2N/A sfree(str);
2N/A if (tempstr)
2N/A str = tempstr;
2N/A else {
2N/A freeValue(val, 1);
2N/A return (MAP_NO_MEMORY);
2N/A }
2N/A }
2N/A tempstr = scat(myself, 0, str,
2N/A val->val->value);
2N/A sfree(str);
2N/A if (tempstr)
2N/A str = tempstr;
2N/A else {
2N/A freeValue(val, 1);
2N/A return (MAP_NO_MEMORY);
2N/A }
2N/A }
2N/A freeValue(val, 1);
2N/A }
2N/A }
2N/A if (str == 0)
2N/A str = emptystr;
2N/A
2N/A res = addCol2RuleValue(vt_string,
2N/A t->e->element.match.item[i].name,
2N/A str, strlen(str), trv);
2N/A
2N/A if (str != emptystr)
2N/A sfree(str);
2N/A
2N/A if (res == -1) {
2N/A return (MAP_INTERNAL_ERROR);
2N/A }
2N/A }
2N/A
2N/A return (SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Updates 'rv' with NIS name=value pairs suitable to
2N/A * construct datum from namefield information.
2N/A * Some part based on createNisPlusEntry (from ldap_nisdbquery.c)
2N/A * This code assumes that from a given LDAP entry, applying the
2N/A * mapping rules, would give us one or more NIS entries, differing
2N/A * only in key.
2N/A */
2N/Asuc_code
2N/AbuildNISRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
2N/A char *domain) {
2N/A int r, i, j, k, l, index, nrq, res, len;
2N/A int numItems, splitname, count, statP;
2N/A __nis_value_t *rval;
2N/A __nis_mapping_item_t *litem;
2N/A __nis_mapping_rule_t *rl;
2N/A __nis_rule_value_t *rvq;
2N/A char *value, *emptystr = "";
2N/A
2N/A statP = SUCCESS;
2N/A
2N/A /* Initialize default base */
2N/A __nisdb_get_tsd()->searchBase = t->objectDN->read.base;
2N/A
2N/A /* Initialize rule-value rvq */
2N/A rvq = 0;
2N/A count = 0;
2N/A
2N/A /* Add domainname to rule-value */
2N/A if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
2N/A rv)) {
2N/A return (MAP_INTERNAL_ERROR);
2N/A }
2N/A
2N/A for (r = 0; r < t->numRulesFromLDAP; r++) {
2N/A rl = t->ruleFromLDAP[r];
2N/A
2N/A /* Set escapeFlag if RHS is "dn" to remove escape chars */
2N/A if (rl->rhs.numElements == 1 &&
2N/A rl->rhs.element->type == me_item &&
2N/A rl->rhs.element->element.item.type == mit_ldap &&
2N/A strcasecmp(rl->rhs.element->element.item.name, "dn")
2N/A == 0) {
2N/A __nisdb_get_tsd()->escapeFlag = '2';
2N/A }
2N/A
2N/A rval = buildRvalue(&rl->rhs, mit_ldap, rv, NULL);
2N/A
2N/A /* Reset escapeFlag */
2N/A __nisdb_get_tsd()->escapeFlag = '\0';
2N/A
2N/A if (rval == 0) {
2N/A continue;
2N/A }
2N/A
2N/A if (rval->numVals <= 0) {
2N/A /* Treat as invalid */
2N/A freeValue(rval, 1);
2N/A continue;
2N/A }
2N/A
2N/A litem = buildLvalue(&rl->lhs, &rval, &numItems);
2N/A if (litem == 0) {
2N/A /* This will take care of numItems == 0 */
2N/A freeValue(rval, 1);
2N/A continue;
2N/A }
2N/A
2N/A if (rval->numVals > 1) {
2N/A if (numItems == 1 && litem->repeat)
2N/A nrq = rval->numVals;
2N/A else if (numItems > 1 && rval->repeat)
2N/A nrq = 1 + ((rval->numVals-1)/numItems);
2N/A else
2N/A nrq = 1;
2N/A } else
2N/A nrq = 1;
2N/A
2N/A /* Set splitname if splitfield names are specified */
2N/A for (i = 0; i < numItems; i++) {
2N/A if (strcasecmp(litem[i].name, N2LKEY) == 0 ||
2N/A strcasecmp(litem[i].name, N2LIPKEY) == 0 ||
2N/A strcasecmp(litem[i].name, N2LCOMMENT) == 0)
2N/A continue;
2N/A for (j = 0; j < t->numColumns; j++) {
2N/A if (strcmp(litem[i].name, t->column[j]) == 0)
2N/A break;
2N/A }
2N/A if (j == t->numColumns)
2N/A break;
2N/A }
2N/A
2N/A splitname = (i < numItems)?1:0;
2N/A
2N/A for (j = 0; j < nrq; j++) {
2N/A if (splitname == 1) {
2N/A /*
2N/A * Put every value of splitfieldname in a new
2N/A * rule-value. Helps generating splitfields.
2N/A */
2N/A rvq = growRuleValue(count, count + 1, rvq, 0);
2N/A if (rvq == 0) {
2N/A freeRuleValue(rvq, count);
2N/A freeValue(rval, 1);
2N/A freeMappingItem(litem, numItems);
2N/A return (MAP_INTERNAL_ERROR);
2N/A }
2N/A count++;
2N/A }
2N/A
2N/A for (k = j % nrq, l = 0; l < numItems; k += nrq, l++) {
2N/A /* If we run out of values, use empty strings */
2N/A if (k >= rval->numVals) {
2N/A value = emptystr;
2N/A len = 0;
2N/A } else {
2N/A value = rval->val[k].value;
2N/A len = rval->val[k].length;
2N/A }
2N/A res = (splitname == 1)?addCol2RuleValue(
2N/A vt_string, litem[l].name, value,
2N/A len, &rvq[count - 1]):0;
2N/A if (res != -1)
2N/A res = addCol2RuleValue(vt_string,
2N/A litem[l].name, value, len, rv);
2N/A if (res == -1) {
2N/A freeRuleValue(rvq, count);
2N/A freeValue(rval, 1);
2N/A freeMappingItem(litem, numItems);
2N/A return (MAP_INTERNAL_ERROR);
2N/A }
2N/A }
2N/A }
2N/A freeValue(rval, 1);
2N/A rval = 0;
2N/A freeMappingItem(litem, numItems);
2N/A litem = 0;
2N/A numItems = 0;
2N/A } /* for r < t->numRulesFromLDAP */
2N/A
2N/A statP = addSplitFieldValues(t, rvq, rv, count, domain);
2N/A
2N/A if (rvq)
2N/A freeRuleValue(rvq, count);
2N/A
2N/A if (verifyIndexMatch(t, 0, rv, 0, 0) == 0)
2N/A return (MAP_INDEXLIST_ERROR);
2N/A return (statP);
2N/A
2N/A} /* end of buildNISRuleValue */
2N/A
2N/A/*
2N/A * Convert rule-value to datum using namefield information
2N/A */
2N/Adatum *
2N/AruleValueToDatum(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *statP) {
2N/A __nis_value_t *val;
2N/A datum *value;
2N/A char *str, *cstr, commentSep[3] = {' ', 0, 0};
2N/A const char *myself = "ruleValueToDatum";
2N/A
2N/A /* No error yet */
2N/A *statP = 0;
2N/A
2N/A /* Return empty datum if no namefield information available */
2N/A if (t->e == 0) {
2N/A if ((value = am(myself, sizeof (*value))) == 0)
2N/A *statP = MAP_NO_MEMORY;
2N/A return (value);
2N/A }
2N/A
2N/A val = getMappingFormatArray(t->e->element.match.fmt, rv,
2N/A fa_item, t->e->element.match.numItems,
2N/A t->e->element.match.item);
2N/A
2N/A if (val && val->val && val->val->value) {
2N/A if ((value = am(myself, sizeof (*value))) == 0) {
2N/A *statP = MAP_NO_MEMORY;
2N/A freeValue(val, 1);
2N/A return (0);
2N/A }
2N/A
2N/A /* Strip trailing whitespaces */
2N/A cstr = (char *)val->val->value + val->val->length;
2N/A for (; cstr >= (char *)val->val->value &&
2N/A (*cstr == ' ' || *cstr == '\t'); *cstr-- = '\0');
2N/A
2N/A if (t->commentChar != '\0' &&
2N/A (str = findVal(N2LCOMMENT, rv, mit_nis)) != 0 &&
2N/A *str != '\0') {
2N/A commentSep[1] = t->commentChar;
2N/A cstr = scat(myself, F, commentSep, str);
2N/A if (cstr) {
2N/A value->dptr = scat(myself, F,
2N/A val->val->value, cstr);
2N/A sfree(cstr);
2N/A }
2N/A } else {
2N/A value->dptr = sdup(myself, T, val->val->value);
2N/A }
2N/A freeValue(val, 1);
2N/A if (value->dptr) {
2N/A value->dsize = strlen(value->dptr);
2N/A return (value);
2N/A } else {
2N/A *statP = MAP_NO_MEMORY;
2N/A sfree(value);
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A *statP = MAP_NAMEFIELD_MATCH_ERROR;
2N/A return (0);
2N/A}
2N/A
2N/Adatum *
2N/AgetKeyFromRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *nv,
2N/A int *statP, bool_t xlate_to_lcase)
2N/A{
2N/A int i, j, k;
2N/A datum *key = 0;
2N/A char *dptr;
2N/A char *str;
2N/A const char *myself = "getKeyFromRuleValue";
2N/A
2N/A /* No error yet */
2N/A *statP = 0;
2N/A
2N/A if (rv == 0 || nv == 0)
2N/A return (0);
2N/A
2N/A for (i = 0; i < rv->numColumns; i++) {
2N/A if (rv->colName[i] == 0)
2N/A continue;
2N/A if (strcasecmp(N2LKEY, rv->colName[i]) == 0 ||
2N/A strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
2N/A if ((*nv = rv->colVal[i].numVals) == 0)
2N/A return (0);
2N/A if ((key = am(myself, sizeof (key[0]) * *nv)) == 0) {
2N/A *statP = MAP_NO_MEMORY;
2N/A return (0);
2N/A }
2N/A for (j = 0; j < *nv; j++) {
2N/A if ((str = rv->colVal[i].val[j].value) == 0) {
2N/A key[j].dsize = 0;
2N/A key[j].dptr = 0;
2N/A } else {
2N/A if (verifyIndexMatch(t, 0, 0,
2N/A rv->colName[i], str) == 0) {
2N/A key[j].dsize = 0;
2N/A key[j].dptr = 0;
2N/A continue;
2N/A }
2N/A
2N/A key[j].dsize = strlen(str);
2N/A key[j].dptr = am(myself,
2N/A key[j].dsize + 1);
2N/A if (key[j].dptr == 0) {
2N/A *statP = MAP_NO_MEMORY;
2N/A for (--j; j >= 0; j--)
2N/A sfree(key[j].dptr);
2N/A sfree(key);
2N/A return (0);
2N/A }
2N/A
2N/A /* transliterate key to lowercase */
2N/A if (xlate_to_lcase == TRUE) {
2N/A
2N/A /*
2N/A * For multi-homed
2N/A * entries, skip over
2N/A * "YP_MULTI_" prefix.
2N/A */
2N/A k = 0;
2N/A dptr = key[j].dptr;
2N/A if (strncmp(YPMULTI, str,
2N/A YPMULTISZ) == 0) {
2N/A k = YPMULTISZ;
2N/A bcopy(str, dptr,
2N/A YPMULTISZ);
2N/A }
2N/A while (k < key[j].dsize) {
2N/A dptr[k] =
2N/A (char)tolower(
2N/A (int)(uchar_t)
2N/A str[k]);
2N/A k++;
2N/A }
2N/A } else {
2N/A bcopy(str, key[j].dptr,
2N/A key[j].dsize);
2N/A }
2N/A }
2N/A }
2N/A return (key);
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Get the mapping structure corresponding to `map,domain.'
2N/A */
2N/A__nis_table_mapping_t *
2N/AmappingFromMap(char *map, char *domain, int *statP) {
2N/A char *mapPath;
2N/A __nis_table_mapping_t *t;
2N/A
2N/A /* No error yet */
2N/A *statP = 0;
2N/A
2N/A /* Construct map,domain. */
2N/A if ((mapPath = getFullMapName(map, domain)) == 0) {
2N/A *statP = MAP_NO_MEMORY;
2N/A return (0);
2N/A }
2N/A
2N/A /* Get the hash table entry for the mapPath */
2N/A if ((t = __nis_find_item_mt(mapPath, &ldapMappingList, 1, 0))
2N/A == 0) {
2N/A *statP = MAP_NO_MAPPING_EXISTS;
2N/A }
2N/A sfree(mapPath);
2N/A return (t);
2N/A}
2N/A
2N/A/*
2N/A * Verify at least one key value obtained from DIT matches the search key
2N/A * RETURNS: 1 MATCH
2N/A * 0 NO MATCH
2N/A * -1 NO KEY FOUND
2N/A */
2N/Astatic int
2N/AverifyKey(char *key, __nis_rule_value_t *rv) {
2N/A int i, j;
2N/A char *sipkey, *str;
2N/A
2N/A for (i = 0; i < rv->numColumns; i++) {
2N/A if (rv->colName[i] == 0)
2N/A continue;
2N/A if (strcasecmp(N2LKEY, rv->colName[i]) == 0) {
2N/A if (rv->colVal[i].val == 0)
2N/A return (0);
2N/A for (j = 0; j < rv->colVal[i].numVals; j++) {
2N/A str = (char *)rv->colVal[i].val[j].value;
2N/A if (str && strcmp(str, key) == 0)
2N/A return (1);
2N/A }
2N/A return (0);
2N/A } else if (strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
2N/A if (checkIPaddress(key, strlen(key), &sipkey) > 0) {
2N/A if (rv->colVal[i].val == 0)
2N/A return (0);
2N/A for (j = 0; j < rv->colVal[i].numVals; j++) {
2N/A str = rv->colVal[i].val[j].value;
2N/A if (str && strcmp(str, sipkey) == 0) {
2N/A sfree(sipkey);
2N/A return (1);
2N/A }
2N/A }
2N/A sfree(sipkey);
2N/A }
2N/A return (0);
2N/A }
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Read (i.e get and map) a single NIS entry from the LDAP DIT
2N/A */
2N/Abool_t
2N/AsingleReadFromDIT(char *map, char *domain, datum *key, datum *value,
2N/A int *statP) {
2N/A __nis_table_mapping_t *t;
2N/A __nis_rule_value_t *rv_request = 0, *rv_result = 0;
2N/A __nis_ldap_search_t *ls;
2N/A __nis_object_dn_t *objectDN = NULL;
2N/A int i, rc, nr = 0, nv = 0;
2N/A datum *datval = 0;
2N/A char *skey, *str, *sipkey;
2N/A const char *myself = "singleReadFromDIT";
2N/A
2N/A *statP = SUCCESS;
2N/A
2N/A if (!map || !domain || !key || !value) {
2N/A *statP = MAP_PARAM_ERROR;
2N/A return (FALSE);
2N/A }
2N/A
2N/A
2N/A /* Get the mapping information for the map */
2N/A if ((t = mappingFromMap(map, domain, statP)) == 0) {
2N/A /*
2N/A * No problem. We don't handle this map and domain. Maybe it's
2N/A * handled by a service other than NIS.
2N/A */
2N/A return (FALSE);
2N/A }
2N/A
2N/A /* NULL-terminated version of datum key for logging */
2N/A if ((skey = am(myself, key->dsize + 1)) == 0) {
2N/A *statP = MAP_NO_MEMORY;
2N/A return (FALSE);
2N/A }
2N/A (void) memcpy(skey, key->dptr, key->dsize);
2N/A
2N/A if ((str = getFullMapName(map, domain)) == 0) {
2N/A *statP = MAP_NO_MEMORY;
2N/A return (FALSE);
2N/A }
2N/A
2N/A /* For each alternate mapping */
2N/A for (; t != 0; t = t->next) {
2N/A /* Verify objName */
2N/A if (strcmp(str, t->objName) != 0) {
2N/A continue;
2N/A }
2N/A
2N/A /* Verify if key matches the index */
2N/A if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
2N/A verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
2N/A continue;
2N/A
2N/A /* Check if rulesFromLDAP are provided */
2N/A if (t->numRulesFromLDAP == 0) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_INFO,
2N/A "%s: No rulesFromLDAP information available "
2N/A "for %s (%s)", myself, t->dbId, map);
2N/A continue;
2N/A }
2N/A
2N/A /* Convert key into rule-value */
2N/A if ((rv_request = datumToRuleValue(key, 0, t, 0, domain, TRUE,
2N/A statP)) == 0) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: Conversion error %d (NIS to name=value "
2N/A "pairs) for NIS key (%s) for %s (%s)",
2N/A myself, *statP, skey, t->dbId, map);
2N/A continue;
2N/A }
2N/A /* Convert rule-value into ldap request */
2N/A for (objectDN = t->objectDN; objectDN &&
2N/A objectDN->read.base;
2N/A objectDN = objectDN->next) {
2N/A ls = createLdapRequest(t, rv_request, 0, 1, NULL,
2N/A objectDN);
2N/A if (ls == 0) {
2N/A *statP = MAP_CREATE_LDAP_REQUEST_ERROR;
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: Failed to create ldapSearch "
2N/A "request for "
2N/A "NIS key (%s) for %s (%s) "
2N/A "for base %s",
2N/A myself, skey, t->dbId, map,
2N/A objectDN->read.base);
2N/A continue;
2N/A }
2N/A ls->timeout.tv_sec = SINGLE_ACCESS_TIMEOUT_SEC;
2N/A ls->timeout.tv_usec = SINGLE_ACCESS_TIMEOUT_USEC;
2N/A /* Query LDAP */
2N/A nr = (ls->isDN)?0:-1;
2N/A rv_result = ldapSearch(ls, &nr, 0, statP);
2N/A freeLdapSearch(ls);
2N/A if (rv_result == 0) {
2N/A if (*statP == LDAP_NO_SUCH_OBJECT) {
2N/A /* Entry does not exist in */
2N/A /* the ldap server */
2N/A }
2N/A continue;
2N/A }
2N/A freeRuleValue(rv_request, 1);
2N/A rv_request = 0;
2N/A
2N/A /* if result > 1, first match will be returned */
2N/A if (nr > 1) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_INFO,
2N/A "%s: %d ldapSearch results "
2N/A "for NIS key (%s) "
2N/A "for %s (%s) for base %s. "
2N/A "First match will be returned ",
2N/A myself, nr, skey, t->dbId, map,
2N/A objectDN->read.base);
2N/A }
2N/A
2N/A for (i = 0; i < nr; i++) {
2N/A /* Convert LDAP data to NIS equivalents */
2N/A *statP = buildNISRuleValue(t, &rv_result[i],
2N/A domain);
2N/A if (*statP == MAP_INDEXLIST_ERROR)
2N/A continue;
2N/A
2N/A if (*statP != SUCCESS) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_WARNING,
2N/A "%s: Conversion error %d (LDAP to "
2N/A "name=value pairs) for NIS key (%s) "
2N/A "for %s (%s) for base %s", myself,
2N/A *statP, skey,
2N/A t->dbId, map, objectDN->read.base);
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Check if 'key' from the ldap result matches the key
2N/A * provided by our caller
2N/A */
2N/A if ((rc = verifyKey(skey, &rv_result[i]))
2N/A == -1) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_INFO,
2N/A "%s: Cannot verify key from ldap "
2N/A "result for NIS key (%s) for %s (%s) "
2N/A "for base %s",
2N/A myself, skey, t->dbId, map,
2N/A objectDN->read.base);
2N/A continue;
2N/A }
2N/A
2N/A if (rc == 1) {
2N/A datval = ruleValueToDatum(t,
2N/A &rv_result[i], statP);
2N/A if (datval == 0) {
2N/A logmsg(MSG_NOTIMECHECK,
2N/A LOG_WARNING,
2N/A "%s: Conversion error %d "
2N/A "(name=value pairs to NIS) "
2N/A "for NIS key (%s) for %s (%s)"
2N/A " for base %s",
2N/A myself,
2N/A *statP, skey, t->dbId, map,
2N/A objectDN->read.base);
2N/A continue;
2N/A }
2N/A if (value) {
2N/A value->dptr = datval->dptr;
2N/A value->dsize = datval->dsize;
2N/A }
2N/A sfree(datval);
2N/A sfree(skey);
2N/A freeRuleValue(rv_result, nr);
2N/A rv_result = 0;
2N/A *statP = SUCCESS;
2N/A
2N/A /* Free full map name */
2N/A sfree(str);
2N/A
2N/A return (TRUE);
2N/A }
2N/A }
2N/A freeRuleValue(rv_result, nr);
2N/A rv_result = 0;
2N/A } /* end of for over objectDN */
2N/A
2N/A if (rv_request != 0) {
2N/A freeRuleValue(rv_request, 1);
2N/A rv_request = 0;
2N/A }
2N/A if (rv_result != 0) {
2N/A freeRuleValue(rv_result, nr);
2N/A rv_result = 0;
2N/A }
2N/A }
2N/A sfree(skey);
2N/A *statP = MAP_NO_MATCHING_KEY;
2N/A
2N/A /* Free full map name */
2N/A sfree(str);
2N/A
2N/A return (FALSE);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Maps and writes a single NIS entry to the LDAP DIT
2N/A */
2N/Aint
2N/AsingleWriteToDIT(char *map, char *domain, datum *key, datum *value,
2N/A bool_t replace) {
2N/A __nis_table_mapping_t *t;
2N/A __nis_rule_value_t *rv, *frv;
2N/A __nis_ldap_search_t *ls;
2N/A int statP = SUCCESS, flag;
2N/A int nv, nr, i, rc, collapse;
2N/A char *dn = 0, *skey, *svalue, *str;
2N/A const char *myself = "singleWriteToDIT";
2N/A char *dptr;
2N/A
2N/A if (!map || !domain || !key || !value) {
2N/A return (MAP_PARAM_ERROR);
2N/A }
2N/A
2N/A /* Return SUCCESS for empty or whitespace key */
2N/A for (i = 0; i < key->dsize && ((dptr = key->dptr)[i] == 0 ||
2N/A dptr[i] == ' ' || dptr[i] == '\t'); i++);
2N/A if (i >= key->dsize)
2N/A return (SUCCESS);
2N/A
2N/A /* Get the mapping information for the map */
2N/A if ((t = mappingFromMap(map, domain, &statP)) == 0) {
2N/A /*
2N/A * No problem. We don't handle this map and domain. Maybe it's
2N/A * handled by a service other than NIS.
2N/A */
2N/A return (statP);
2N/A }
2N/A
2N/A /* NULL-terminated version of key and value for logging */
2N/A if ((skey = am(myself, key->dsize + 1)) == 0)
2N/A return (MAP_NO_MEMORY);
2N/A (void) memcpy(skey, key->dptr, key->dsize);
2N/A
2N/A if ((svalue = am(myself, value->dsize + 1)) == 0) {
2N/A sfree(skey);
2N/A return (MAP_NO_MEMORY);
2N/A }
2N/A (void) memcpy(svalue, value->dptr, value->dsize);
2N/A
2N/A if ((str = getFullMapName(map, domain)) == 0) {
2N/A sfree(skey);
2N/A sfree(svalue);
2N/A return (MAP_NO_MEMORY);
2N/A }
2N/A
2N/A /* For each alternate mapping */
2N/A for (flag = 0; t != 0; t = t->next) {
2N/A /* Verify objName */
2N/A if (strcmp(str, t->objName) != 0) {
2N/A continue;
2N/A }
2N/A
2N/A /* Verify if key matches the index */
2N/A if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
2N/A verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
2N/A continue;
2N/A
2N/A /* Check the writespecs */
2N/A if (t->objectDN->write.base == 0) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_INFO,
2N/A "%s: No baseDN in writespec. Write disabled "
2N/A "for %s (%s)", myself, t->dbId, map);
2N/A continue;
2N/A }
2N/A
2N/A /* Check if rulesToLDAP are provided */
2N/A if (t->numRulesToLDAP == 0) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_INFO,
2N/A "%s: No rulesToLDAP. Write disabled for "
2N/A "%s (%s)", myself, t->dbId, map);
2N/A continue;
2N/A }
2N/A
2N/A /* Set flag to indicate write is enabled */
2N/A flag = 1;
2N/A
2N/A /* Convert key and value into an array of rule-values */
2N/A if ((rv = datumToRuleValue(key, value, t, &nv, domain, FALSE,
2N/A &statP)) == 0) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: Conversion error %d (NIS to name=value "
2N/A "pairs) for NIS data (key=%s, value=%s) "
2N/A "for %s (%s)",
2N/A myself, statP, skey, svalue, t->dbId, map);
2N/A sfree(skey);
2N/A sfree(svalue);
2N/A
2N/A /* Free full map name */
2N/A sfree(str);
2N/A
2N/A return (statP);
2N/A }
2N/A
2N/A /* Convert NIS data to LDAP equivalents for each rule-value */
2N/A for (i = 0; i < nv; i++) {
2N/A /* Verify indexlist with name=value pairs */
2N/A if (verifyIndexMatch(t, 0, &rv[i], 0, 0) == 0)
2N/A break;
2N/A
2N/A /* Create LDAP request and LDAP name=value pairs */
2N/A if ((ls = createLdapRequest(t, &rv[i],
2N/A 0, 0, NULL, NULL)) == 0) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: Conversion error (name=value pairs"
2N/A " to LDAP) for NIS data "
2N/A "(key=%s, value=%s) for %s (%s)",
2N/A myself, skey, svalue, t->dbId, map);
2N/A freeRuleValue(rv, nv);
2N/A sfree(skey);
2N/A sfree(svalue);
2N/A
2N/A /* Free full map name */
2N/A sfree(str);
2N/A
2N/A return (MAP_CREATE_LDAP_REQUEST_ERROR);
2N/A }
2N/A freeLdapSearch(ls);
2N/A /* printRuleValue(&rv[i]); */
2N/A }
2N/A
2N/A /* If i < nv then this alternate mapping isn't the one */
2N/A if (i < nv)
2N/A continue;
2N/A
2N/A /*
2N/A * Merge rule-values with the same DN so that we have
2N/A * one ldap write request for each DN
2N/A */
2N/A nr = nv;
2N/A frv = mergeRuleValueWithSameDN(rv, &nr);
2N/A freeRuleValue(rv, nv);
2N/A if (frv == 0) {
2N/A if (nr == -1) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: Unable to merge LDAP write "
2N/A "requests to same DN for NIS data "
2N/A "(key=%s, value=%s) for %s (%s)",
2N/A myself, skey, svalue, t->dbId, map);
2N/A statP = MAP_INTERNAL_ERROR;
2N/A } else if (nr == 0) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_WARNING,
2N/A "%s: Cannot generate write DN due to "
2N/A "missing information for NIS data "
2N/A "(key=%s, value=%s) for %s (%s)",
2N/A myself, skey, svalue, t->dbId, map);
2N/A statP = MAP_NO_DN;
2N/A }
2N/A sfree(skey);
2N/A sfree(svalue);
2N/A
2N/A /* Free full map name */
2N/A sfree(str);
2N/A
2N/A return (statP);
2N/A }
2N/A
2N/A /* Write to the LDAP server */
2N/A for (collapse = 0, i = 0; i < nr; i++) {
2N/A if ((dn = findVal("dn", &frv[i], mit_ldap)) != 0) {
2N/A if (replace == FALSE) {
2N/A /* ldap add */
2N/A rc = ldapAdd(dn, &frv[i],
2N/A t->objectDN->write.attrs, 0);
2N/A } else {
2N/A /* ldap modify with addFirst set */
2N/A rc = ldapModify(dn, &frv[i],
2N/A t->objectDN->write.attrs, 1);
2N/A }
2N/A
2N/A /* if we get err=20, collapse and try again */
2N/A if (!collapse &&
2N/A (rc == LDAP_TYPE_OR_VALUE_EXISTS) &&
2N/A (collapseRuleValue(&frv[i]) == 1)) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_WARNING,
2N/A "%s: Ignoring values differing "
2N/A "in case from NIS data (key=%s,"
2N/A " value=%s) for (dn: %s) for "
2N/A "%s (%s)", myself, skey,
2N/A svalue, dn, t->dbId, map);
2N/A collapse = 1;
2N/A i--;
2N/A continue;
2N/A }
2N/A
2N/A collapse = 0;
2N/A if (rc != LDAP_SUCCESS) {
2N/A /* Log error */
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: %s error %d (%s) for "
2N/A "(dn: %s) for NIS data "
2N/A "(key=%s, value=%s) "
2N/A "for %s (%s)",
2N/A myself, (replace == TRUE) ?
2N/A "ldapModify" : "ldapAdd", rc,
2N/A ldap_err2string(rc), dn, skey,
2N/A svalue, t->dbId, map);
2N/A
2N/A /* Dumping failed call may be useful */
2N/A /* printRuleValue(&frv[i]); */
2N/A
2N/A /*
2N/A * Return the error code and let wrapper
2N/A * sort out if mapping should continue
2N/A * or abort.
2N/A */
2N/A statP = rc;
2N/A sfree(skey);
2N/A sfree(svalue);
2N/A freeRuleValue(frv, nr);
2N/A
2N/A /* Free full map name */
2N/A sfree(str);
2N/A
2N/A return (statP);
2N/A }
2N/A }
2N/A }
2N/A
2N/A freeRuleValue(frv, nr);
2N/A }
2N/A
2N/A sfree(skey);
2N/A sfree(svalue);
2N/A
2N/A /* Free full map name */
2N/A sfree(str);
2N/A
2N/A return ((flag)?SUCCESS:MAP_WRITE_DISABLED);
2N/A}
2N/A
2N/Asuc_code
2N/AcollapseRuleValue(__nis_rule_value_t *rv) {
2N/A int i, j, k, flag;
2N/A
2N/A /* Using 'val' to appease cstyle's 80 chars/line limit */
2N/A __nis_value_t *val;
2N/A
2N/A for (i = 0, flag = 0; i < rv->numAttrs; i++) {
2N/A val = &rv->attrVal[i];
2N/A for (j = 1; j < val->numVals; j++) {
2N/A for (k = 0; k < j; k++) {
2N/A if (val->val[j].length != val->val[k].length)
2N/A continue;
2N/A if (val->val[k].length == 0)
2N/A continue;
2N/A if (strncasecmp(val->val[j].value,
2N/A val->val[k].value,
2N/A val->val[j].length) != 0)
2N/A continue;
2N/A flag = 1;
2N/A sfree(val->val[j].value);
2N/A
2N/A#ifdef ORDER_NOT_IMPORTANT
2N/A val->val[j--] = val->val[--val->numVals];
2N/A#else
2N/A /* Order needs to be maintained */
2N/A for (k = j + 1; k < val->numVals; k++)
2N/A val->val[k - 1] = val->val[k];
2N/A j--;
2N/A val->numVals--;
2N/A#endif
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A return (flag);
2N/A}
2N/A
2N/A/* ObjectClass lookup table */
2N/Astatic struct {
2N/A const char *attrType;
2N/A const char *objectClass;
2N/A} oc_lookup[] = {
2N/A { "o", "objectclass=organization"},
2N/A { "organizationname", "objectclass=organization"},
2N/A { "2.5.4.10", "objectclass=organization"},
2N/A { "ou", "objectclass=organizationalunit"},
2N/A { "organizationalunitname", "objectclass=organizationalunit"},
2N/A { "2.5.4.11", "objectclass=organizationalunit"},
2N/A { "c", "objectclass=country"},
2N/A { "countryname", "objectclass=country"},
2N/A { "2.5.4.6", "objectclass=country"},
2N/A { "dc", "objectclass=domain"},
2N/A { "domaincomponent", "objectclass=domain"},
2N/A { "0.9.2342.19200300.100.1.25", "objectclass=domain"},
2N/A { "nismapname", "objectclass=nismap"},
2N/A { "1.3.6.1.1.1.1.26", "objectclass=nismap"},
2N/A { "automountmapname", "objectclass=automountmap"},
2N/A { "1.3.6.1.1.1.1.31", "objectclass=automountmap"},
2N/A { 0, 0}
2N/A};
2N/A
2N/A/*
2N/A * Returns the name of the objectclass to which the object
2N/A * represented by the given 'rdn' will most likely belong to.
2N/A * The return value is in static memory so it should not be
2N/A * freed
2N/A */
2N/Aconst char *
2N/AgetObjectClass(char *rdn) {
2N/A
2N/A char *attrtype, *p;
2N/A int len, i;
2N/A
2N/A /* Skip leading whitespaces */
2N/A for (p = rdn; *p == ' ' || *p == '\t'; p++);
2N/A if (*p == '\0')
2N/A return (0);
2N/A attrtype = p;
2N/A
2N/A /* Find '=' */
2N/A if ((p = strchr(attrtype, '=')) == 0 || p == attrtype ||
2N/A *(p - 1) == '\\')
2N/A return (0);
2N/A
2N/A /*
2N/A * Skip trailing whitespaces in attrtype
2N/A * Don't worry, p won't decrease beyond attrtype
2N/A */
2N/A for (--p; *p == ' ' || *p == '\t'; p--);
2N/A len = p - attrtype + 1;
2N/A
2N/A for (i = 0; oc_lookup[i].attrType; i++)
2N/A if (!strncasecmp(oc_lookup[i].attrType, attrtype, len))
2N/A /* Check length is right */
2N/A if (len == strlen(oc_lookup[i].attrType))
2N/A return (oc_lookup[i].objectClass);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Split 'dn' into rdn and parentdn based on the first
2N/A * occurrence of unescaped 'comma' or 'semicolon'. rdn
2N/A * lies on the LHS while parentdn lies on the RHS of the
2N/A * split. If none found, then an empty string ("") is
2N/A * assigned to parentdn
2N/A */
2N/Aint
2N/AsplitDN(char *dn, char **rdn, char **parentdn) {
2N/A char *value, *name;
2N/A const char *myself = "splitDN";
2N/A
2N/A if ((name = sdup(myself, T, dn)) == 0)
2N/A return (-1);
2N/A
2N/A for (value = name; *value != '\0'; value++) {
2N/A if (*value == ',' || *value == ';')
2N/A if (value == name || *(value - 1) != '\\')
2N/A break;
2N/A }
2N/A
2N/A if (*value != '\0') {
2N/A *value = '\0';
2N/A value++;
2N/A } else
2N/A value = 0;
2N/A
2N/A if (parentdn) {
2N/A if ((*parentdn = sdup(myself, T, value)) == 0) {
2N/A sfree(name);
2N/A return (-1);
2N/A }
2N/A }
2N/A if (rdn)
2N/A *rdn = name;
2N/A else
2N/A sfree(name);
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : makeNISObject()
2N/A *
2N/A * DESCRIPTION: Sets up a nis Object in the DIT.
2N/A *
2N/A * GIVEN :
2N/A * Case 1: Both 'domain' and 'dn' are non-NULL
2N/A * Create nisDomainObject with the given information
2N/A * Case 2: Only 'domain' is non-NULL
2N/A * Obtain the 'dn' from the nisLDAPdomainContext list
2N/A * Create nisDomainObject with the above information
2N/A * Case 3: Only 'dn' is non-NULL
2N/A * Create an object with the 'dn'
2N/A * Here we guess the objectclass attribute, based on
2N/A * oc_lookup table
2N/A * Case 4: Both 'domain' and 'dn' are NULL
2N/A * Error
2N/A *
2N/A * RETURNS : SUCCESS = It worked
2N/A * FAILURE = There was a problem.
2N/A */
2N/Asuc_code
2N/AmakeNISObject(char *domain, char *dn) {
2N/A __nis_rule_value_t *rv;
2N/A __nis_ldap_search_t *ls;
2N/A int i, rc, nr, add_rc;
2N/A char *val;
2N/A const char *myself = "makeNISObject";
2N/A
2N/A if (!dn && !domain)
2N/A return (FAILURE);
2N/A
2N/A /*
2N/A * If only 'domain' name is provided, then
2N/A * try to find dn from the nisLDAPdomainContext
2N/A * list generated by the parser
2N/A */
2N/A if (!dn) {
2N/A for (i = 0; i < ypDomains.numDomains; i++) {
2N/A if (ypDomains.domainLabels[i] == 0)
2N/A continue;
2N/A if (strcasecmp(domain, ypDomains.domainLabels[i])
2N/A == 0) {
2N/A dn = ypDomains.domains[i];
2N/A break;
2N/A }
2N/A }
2N/A if (!dn)
2N/A return (FAILURE);
2N/A }
2N/A
2N/A /*
2N/A * If only 'dn' is given, then it means that the
2N/A * caller simply wants to a create an entry for
2N/A * that 'dn'.
2N/A *
2N/A * If 'domain' is given, then check if the 'dn'
2N/A * has already been set up as a nis domain object.
2N/A * If not, see if we can make it become one.
2N/A */
2N/A if (domain) {
2N/A /*
2N/A * Check to see if the nis domain object has
2N/A * already been set up
2N/A */
2N/A ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0,
2N/A "objectclass=*", 0, 0, 0);
2N/A if (ls == 0) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: Unable to create ldapSearch "
2N/A "request for dn: %s", myself, dn);
2N/A return (FAILURE);
2N/A }
2N/A nr = -1;
2N/A rv = ldapSearch(ls, &nr, 0, &rc);
2N/A freeLdapSearch(ls);
2N/A if (rc == LDAP_SUCCESS) {
2N/A val = findVal("nisDomain", rv, mit_ldap);
2N/A if (val != NULL) {
2N/A /*
2N/A * Yes, nis domain object found. Check
2N/A * to see if the domain names match.
2N/A * If so, we are done. If not, log
2N/A * a warning message, and return SUCCESS.
2N/A */
2N/A if (strcasecmp(val, domain) == 0) {
2N/A freeRuleValue(rv, nr);
2N/A return (SUCCESS);
2N/A } else {
2N/A logmsg(MSG_NOTIMECHECK,
2N/A LOG_WARNING,
2N/A "%s: Entry (dn: %s) already "
2N/A "contains a nis domain name "
2N/A "(%s). The domain name (%s) "
2N/A "is not added.",
2N/A myself, dn, val, domain);
2N/A freeRuleValue(rv, nr);
2N/A return (SUCCESS);
2N/A }
2N/A } else {
2N/A freeRuleValue(rv, nr);
2N/A /*
2N/A * Entry for the 'dn' exists, but it
2N/A * is not a nis domain object yet.
2N/A * Add the nisDoamin attribute and
2N/A * the nisDomainObject objectclass to
2N/A * the entry.
2N/A */
2N/A if ((rv = initRuleValue(1, 0)) == 0)
2N/A return (FAILURE);
2N/A
2N/A if (addSAttr2RuleValue("nisDomain",
2N/A domain, rv) == -1) {
2N/A freeRuleValue(rv, 1);
2N/A return (FAILURE);
2N/A }
2N/A rc = ldapModify(dn, rv,
2N/A "objectclass=nisDomainObject",
2N/A 0);
2N/A freeRuleValue(rv, 1);
2N/A if (rc == LDAP_SUCCESS) {
2N/A logmsg(MSG_NOTIMECHECK,
2N/A LOG_INFO,
2N/A "%s: entry (dn: %s) "
2N/A "modified to be an "
2N/A "nis domain object",
2N/A myself, dn);
2N/A return (SUCCESS);
2N/A } else {
2N/A logmsg(MSG_NOTIMECHECK,
2N/A LOG_ERR,
2N/A "%s: unable to modify "
2N/A "entry (dn: %s) to be "
2N/A "a nis domain object: "
2N/A "ldapModify error %d (%s)",
2N/A myself, dn, rc,
2N/A ldap_err2string(rc));
2N/A return (FAILURE);
2N/A }
2N/A }
2N/A } else { /* search for 'dn' failed */
2N/A freeRuleValue(rv, nr);
2N/A
2N/A /*
2N/A * It is OK if no such object, otherwise
2N/A * log an error.
2N/A */
2N/A if (rc != LDAP_NO_SUCH_OBJECT) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: unable to retrieve "
2N/A "entry (dn: %s): "
2N/A "ldapSearch error %d (%s)",
2N/A myself, dn, rc,
2N/A ldap_err2string(rc));
2N/A return (FAILURE);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If the 'dn' is actually the naming context of
2N/A * the DIT, we should be able to make it a nis domain
2N/A * object without worrying about missing parent
2N/A * entries. If unable to add the entry for the 'dn'
2N/A * due to missing parent entries, fall through
2N/A * to create them and then add the nis domain object.
2N/A */
2N/A if (addNISObject(domain, dn, &add_rc) == SUCCESS)
2N/A return (SUCCESS);
2N/A else if (add_rc != LDAP_NO_SUCH_OBJECT)
2N/A return (FAILURE);
2N/A }
2N/A
2N/A /* Create parent */
2N/A if (addParent(dn, NULL) == FAILURE)
2N/A return (FAILURE);
2N/A
2N/A if (addNISObject(domain, dn, NULL) == FAILURE)
2N/A return (FAILURE);
2N/A
2N/A return (SUCCESS);
2N/A}
2N/A
2N/Asuc_code
2N/AaddParent(char *dn, char **attr) {
2N/A __nis_rule_value_t *rv;
2N/A __nis_ldap_search_t *ls;
2N/A int rc, nr;
2N/A char *parentdn = 0, *rdn = 0;
2N/A const char *myself = "addParent";
2N/A
2N/A /* Obtain parentdn */
2N/A if (splitDN(dn, &rdn, &parentdn) == -1)
2N/A return (FAILURE);
2N/A if (!parentdn) {
2N/A sfree(rdn);
2N/A return (FAILURE);
2N/A }
2N/A
2N/A /* Check if parentdn exists */
2N/A ls = buildLdapSearch(parentdn, LDAP_SCOPE_BASE, 0, 0,
2N/A "objectclass=*", 0, 0, 0);
2N/A if (ls == 0) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: Unable to create ldapSearch request for "
2N/A "parent (dn: %s) of (dn: %s)",
2N/A myself, parentdn, dn);
2N/A sfree(parentdn);
2N/A sfree(rdn);
2N/A return (FAILURE);
2N/A }
2N/A nr = -1;
2N/A rv = ldapSearch(ls, &nr, 0, &rc);
2N/A freeLdapSearch(ls);
2N/A freeRuleValue(rv, nr);
2N/A
2N/A /* Create parent if it doesn't exists */
2N/A if (rc == LDAP_NO_SUCH_OBJECT) {
2N/A if (makeNISObject(0, parentdn) == FAILURE) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: Unable to create parent (dn: %s) of "
2N/A "(dn: %s) in the DIT", myself, parentdn, dn);
2N/A sfree(parentdn);
2N/A sfree(rdn);
2N/A return (FAILURE);
2N/A }
2N/A }
2N/A sfree(parentdn);
2N/A
2N/A if (attr && rdn)
2N/A *attr = (char *)getObjectClass(rdn);
2N/A sfree(rdn);
2N/A
2N/A return (SUCCESS);
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * FUNCTION : is_fatal_error()
2N/A *
2N/A * DESCRIPTION: Works out if a failed mapping operation should be retried.
2N/A *
2N/A * INPUTS : Result code from operation
2N/A *
2N/A * OUTPUTS : TRUE = Fatal error, don't retry.
2N/A * FALSE = Temporary error, retry.
2N/A */
2N/Abool_t
2N/Ais_fatal_error(int res)
2N/A{
2N/A
2N/A if (0 > res)
2N/A /* An internal mapping error. Not going to go away. */
2N/A return (TRUE);
2N/A
2N/A switch (res) {
2N/A case (LDAP_PROTOCOL_ERROR):
2N/A case (LDAP_TIMELIMIT_EXCEEDED):
2N/A case (LDAP_PARTIAL_RESULTS):
2N/A case (LDAP_BUSY):
2N/A case (LDAP_UNAVAILABLE):
2N/A case (LDAP_UNWILLING_TO_PERFORM):
2N/A case (LDAP_OTHER):
2N/A case (LDAP_SERVER_DOWN):
2N/A case (LDAP_LOCAL_ERROR):
2N/A case (LDAP_TIMEOUT):
2N/A case (LDAP_NO_MEMORY):
2N/A /* Probably worth a retry */
2N/A return (FALSE);
2N/A
2N/A default:
2N/A return (TRUE);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : addNISObject()
2N/A *
2N/A * DESCRIPTION: Add a nis Object in the DIT.
2N/A *
2N/A * GIVEN :
2N/A * Case 1: 'dn' is NULL
2N/A * Error
2N/A * Case 2: 'domain' is non-NULL
2N/A * Create nisDomainObject with the given information
2N/A * Case 3: 'domain' is NULL
2N/A * Create an object with the 'dn'
2N/A * Here we guess the objectclass attribute, based on
2N/A * oc_lookup table
2N/A *
2N/A * RETURNS : SUCCESS = It worked
2N/A * FAILURE = There was a problem. If the ldap add
2N/A * operation failed, ldap_rc will be set
2N/A * to the ldap error code.
2N/A */
2N/Asuc_code
2N/AaddNISObject(char *domain, char *dn, int *ldap_rc) {
2N/A __nis_rule_value_t *rv;
2N/A int rc;
2N/A char *objClassAttrs = NULL, *attrs;
2N/A char *value, *svalue, *rdn = NULL;
2N/A const char *myself = "addNISObject";
2N/A
2N/A if (!dn)
2N/A return (FAILURE);
2N/A
2N/A if ((rv = initRuleValue(1, 0)) == 0)
2N/A return (FAILURE);
2N/A
2N/A if (ldap_rc)
2N/A *ldap_rc = -1;
2N/A
2N/A /*
2N/A * Add name=value pairs from RDN. Although this is not required
2N/A * for SunOne Directory Server, during openldap interoperabilty
2N/A * tests, it was found out that openldap server returned object
2N/A * class violation errors if MUST attributes were not specified
2N/A * explicitly.
2N/A */
2N/A if (splitDN(dn, &rdn, 0) == -1)
2N/A return (FAILURE);
2N/A if (rdn != NULL) {
2N/A objClassAttrs = (char *)getObjectClass(rdn);
2N/A if (objClassAttrs == NULL) {
2N/A sfree(rdn);
2N/A return (FAILURE);
2N/A }
2N/A
2N/A /*
2N/A * RDN can be composed of multiple name=value pairs
2N/A * concatenated by '+'. Hence, we need to determine each
2N/A * pair and add it to 'rv'
2N/A */
2N/A for (value = rdn, svalue = NULL; *value != '\0'; value++) {
2N/A if (*value == '+') {
2N/A /* Make sure it's not escaped */
2N/A if (value == rdn || *(value - 1) != '\\') {
2N/A /*
2N/A * We are at the start of the new
2N/A * pair. 'svalue' now contains the
2N/A * value for the previous pair. Add
2N/A * the previous pair to 'rv'
2N/A */
2N/A *value = '\0';
2N/A if (svalue &&
2N/A addSAttr2RuleValue(rdn, svalue, rv)
2N/A == -1) {
2N/A sfree(rdn);
2N/A freeRuleValue(rv, 1);
2N/A return (FAILURE);
2N/A }
2N/A svalue = NULL;
2N/A rdn = value + 1;
2N/A continue;
2N/A }
2N/A }
2N/A
2N/A if (*value == '=') {
2N/A if (value == rdn || *(value - 1) != '\\') {
2N/A /*
2N/A * 'rdn' now contains the name.
2N/A * Whatever follows till the next
2N/A * unescaped '+' or '\0' is the
2N/A * value for this pair.
2N/A */
2N/A *value = '\0';
2N/A svalue = value + 1;
2N/A continue;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * End of String. Add the previous name=value pair to 'rv'
2N/A */
2N/A if (svalue && addSAttr2RuleValue(rdn, svalue, rv) == -1) {
2N/A sfree(rdn);
2N/A freeRuleValue(rv, 1);
2N/A return (FAILURE);
2N/A }
2N/A sfree(rdn);
2N/A } else /* rdn == NULL */
2N/A return (FAILURE);
2N/A
2N/A /* Create the entry */
2N/A if (domain) {
2N/A if (addSAttr2RuleValue("nisDomain", domain, rv) == -1) {
2N/A freeRuleValue(rv, 1);
2N/A return (FAILURE);
2N/A }
2N/A attrs = scat(myself, F, "objectclass=nisdomainobject,",
2N/A objClassAttrs);
2N/A if (!attrs) {
2N/A freeRuleValue(rv, 1);
2N/A return (FAILURE);
2N/A }
2N/A rc = ldapAdd(dn, rv, attrs, 0);
2N/A sfree(attrs);
2N/A } else {
2N/A rc = ldapAdd(dn, rv, objClassAttrs, 0);
2N/A }
2N/A
2N/A if (rc == LDAP_SUCCESS)
2N/A logmsg(MSG_NOTIMECHECK, LOG_INFO,
2N/A "%s: Entry (dn: %s) added to DIT",
2N/A myself, dn);
2N/A else if (rc == LDAP_ALREADY_EXISTS)
2N/A /* Treat this as success */
2N/A rc = LDAP_SUCCESS;
2N/A else
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "%s: ldapAdd error %d (%s) for (dn: %s)",
2N/A myself, rc, ldap_err2string(rc), dn);
2N/A
2N/A freeRuleValue(rv, 1);
2N/A if (ldap_rc)
2N/A *ldap_rc = rc;
2N/A return ((rc == LDAP_SUCCESS)?SUCCESS:FAILURE);
2N/A}