xref: /illumos-gate/usr/src/lib/libnisdb/ldap_ldap.c (revision e3ae4b35c024af1196582063ecee3ab79367227d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2001-2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <lber.h>
28 #include <ldap.h>
29 #include <string.h>
30 
31 #include "ldap_util.h"
32 #include "ldap_op.h"
33 #include "ldap_attr.h"
34 #include "ldap_ldap.h"
35 
36 
37 static __nis_value_t *
38 evalMappingElement(__nis_mapping_element_t *e, __nis_rule_value_t *rvIn) {
39 	__nis_rule_value_t	*rv = rvIn;
40 	int			freeRv = 0;
41 	__nis_value_t		*val;
42 
43 	if (rv == 0) {
44 		rv = initRuleValue(1, 0);
45 		if (rv == 0)
46 			return (0);
47 		freeRv = 1;
48 	}
49 
50 	val = getMappingElement(e, mit_any, rv, NULL);
51 
52 	if (freeRv)
53 		freeRuleValue(rv, 1);
54 
55 	return (val);
56 }
57 
58 __nis_value_t *
59 lookupLDAP(__nis_search_triple_t *t, char *attrName, __nis_rule_value_t *rv,
60 		__nis_object_dn_t *def, int *np_ldap_stat) {
61 	__nis_value_t		*val, *eVal = 0;
62 	char			*base, *filter;
63 	__nis_ldap_search_t	*ls;
64 	char			*attrs[2];
65 	int			scope, i, stat, nrv = 0, freeBase = 0;
66 	char			*myself = "lookupLDAP";
67 
68 	if (t == 0 || slen(attrName) <= 0)
69 		return (0);
70 
71 	if (t->element != 0) {
72 		/* Evaluate t->element to get the t->attrs value */
73 
74 		eVal = evalMappingElement(t->element, rv);
75 
76 		if (eVal == 0)
77 			return (0);
78 
79 		if (eVal->type != vt_string || eVal->numVals <= 0) {
80 			freeValue(eVal, 1);
81 			{
82 				char	*ename = "<unknown>";
83 
84 				eVal = evalMappingElement(t->element, 0);
85 				if (eVal != 0 && eVal->type == vt_string &&
86 					eVal->numVals == 1 &&
87 					eVal->val[0].length > 0 &&
88 					eVal->val[0].value != 0)
89 					ename = eVal->val[0].value;
90 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
91 			"%s: %s: unable to evaluate filter expression \"%s\"",
92 					myself, attrName, ename);
93 				freeValue(eVal, 1);
94 			}
95 			return (0);
96 		}
97 
98 		filter = eVal->val[0].value;
99 	} else {
100 		filter = t->attrs;
101 	}
102 
103 	if (slen(t->base) > 0) {
104 		base = appendBase(t->base, (def != 0) ? def->read.base : 0,
105 					&stat, 0);
106 		if (stat != 0) {
107 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
108 				"%s: %s: error appending \"%s\" to \"%s\"",
109 				myself, attrName, NIL(def->read.base),
110 				NIL(t->base));
111 			return (0);
112 		}
113 		freeBase = 1;
114 	} else {
115 		if (def == 0 || def->read.scope == LDAP_SCOPE_UNKNOWN ||
116 				slen(def->read.base) <= 0) {
117 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
118 				"%s: %s: no supplied or default search base",
119 				myself, attrName);
120 			freeValue(eVal, 1);
121 			return (0);
122 		}
123 		base = def->read.base;
124 	}
125 
126 	if (slen(filter) > 0)
127 		scope = t->scope;
128 	else
129 		scope = LDAP_SCOPE_BASE;
130 
131 	attrs[0] = attrName;
132 	attrs[1] = 0;
133 
134 	ls = buildLdapSearch(base, scope, 0, 0, filter, attrs, 0, 0);
135 	if (ls == 0) {
136 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
137 	"%s: %s: error building LDAP search information for \"%s?%s?%s\"",
138 			myself, attrName, NIL(base), getScope(scope),
139 			NIL(filter));
140 		freeValue(eVal, 1);
141 		if (freeBase)
142 			sfree(base);
143 		return (0);
144 	}
145 
146 	rv = ldapSearch(ls, &nrv, 0, &stat);
147 	freeLdapSearch(ls);
148 
149 	/*
150 	 * If ldapSearch returns LDAP_NO_SUCH_OBJECT, then entry that
151 	 * looked for is not there in LDAP, so return NP_LDAP_NO_VALUE
152 	 * in np_ldap_stat.
153 	 */
154 
155 	if (np_ldap_stat != NULL && stat == LDAP_NO_SUCH_OBJECT)
156 		*np_ldap_stat = NP_LDAP_NO_VALUE;
157 
158 	if (rv == 0) {
159 		logmsg(MSG_NOTIMECHECK,
160 			(stat == LDAP_NO_SUCH_OBJECT)?LOG_DEBUG:LOG_ERR,
161 			"%s: %s: LDAP error %d (%s) for \"%s?%s?%s\"",
162 			myself, attrName, stat, ldap_err2string(stat),
163 			NIL(base), getScope(scope), NIL(filter));
164 		if (freeBase)
165 			sfree(base);
166 		freeValue(eVal, 1);
167 		return (0);
168 	}
169 
170 	if (freeBase)
171 		sfree(base);
172 	freeValue(eVal, 1);
173 	eVal = 0;
174 
175 	for (i = 0, val = 0; i < nrv; i++) {
176 		int	j;
177 		for (j = 0; j < rv[i].numAttrs; j++) {
178 			if (strcasecmp(attrName, rv[i].attrName[j]) == 0) {
179 				eVal = concatenateValues(val,
180 							&rv[i].attrVal[j]);
181 				freeValue(val, 1);
182 				if (eVal == 0) {
183 					freeRuleValue(rv, nrv);
184 					return (0);
185 				}
186 				val = eVal;
187 				break;
188 			}
189 		}
190 	}
191 
192 	freeRuleValue(rv, nrv);
193 	return (val);
194 }
195 
196 /*
197  * Store 'val' at the LDAP location indicated by 'item'. As usual,
198  * val->numVals == -1 indicates deletion.
199  *
200  * The 'index' and 'numIndexes' parameters are used as follows:
201  *
202  *	index < 0 || index >= numIndexes
203  *		Illegal
204  *
205  *	index >= val->numVals
206  *		Store val->val[val->numVals-1]
207  *
208  *	item->repeat == 0 || index < numIndexes
209  *		Store val->val[index]
210  *
211  *	Else (repeat != 0 && index == numIndexes-1)
212  *		Store val->val[index...val->numVals-1]
213  *
214  * 'defDN' should be the default object DN specification, primarily
215  * used when the item search triple is invalid. Also, the defDN->write.base
216  * value is appended to the item search base if the latter is empty, or ends
217  * in a comma.
218  *
219  * If the item search triple is invalid, 'dn' must contain the DN(s)
220  * of the LDAP entry to be modified. If the search triple is valid,
221  * the DN(s) is(are) either:
222  *	Derived via an LDAP search on the search triple 'attrs' or
223  *      'element' fields, or (if neither of those fields is set)
224  *	assumed to be the search triple base.
225  *
226  * Returns LDAP_SUCCESS when successful, or an appropriate LDAP
227  * error status otherwise.
228  */
229 int
230 storeLDAP(__nis_mapping_item_t *item, int index, int numIndexes,
231 		__nis_value_t *val, __nis_object_dn_t *defDN,
232 		char **dn, int numDN) {
233 	__nis_ldap_search_t	ls;
234 	int			stat, i, ix, six, nix;
235 	int			freeDN = 0;
236 	char			*locDN[1];
237 	__nis_rule_value_t	*rv;
238 	char			*defBase = 0;
239 	char			*myself = "storeLDAP";
240 
241 	if (item == 0 || item->type != mit_ldap || item->name == 0 ||
242 			index < 0 || index >= numIndexes ||
243 			val == 0 || val->numVals < -1 || val->numVals == 0)
244 		return (LDAP_PARAM_ERROR);
245 
246 	if (defDN != 0 && slen(defDN->write.base) > 0)
247 		defBase = defDN->write.base;
248 
249 	ls.numFilterComps = 0;
250 	ls.filterComp = 0;
251 	ls.numAttrs = 0;
252 	ls.attrs = 0;
253 	ls.isDN = 0;
254 
255 	if (item->searchSpec.triple.scope == LDAP_SCOPE_UNKNOWN) {
256 		/* If 'defDN' is NULL, we don't know where to write */
257 		if (defDN == 0)
258 			return (LDAP_PARAM_ERROR);
259 		/*
260 		 * Check if we're supposed to write. Since we want the
261 		 * admin to be able to use the nisplusLDAPobjectDN attribute
262 		 * as an on/off switch, we don't flag failure as an error.
263 		 */
264 		if (defDN != 0 && defDN->write.scope == LDAP_SCOPE_UNKNOWN) {
265 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
266 				"%s: write not enabled for \"%s\"",
267 				myself, NIL(item->name));
268 			return (LDAP_SUCCESS);
269 		}
270 	} else {
271 		/*
272 		 * Attempt to get a DN from the search triple.
273 		 */
274 
275 		if (slen(item->searchSpec.triple.base) > 0)
276 			ls.base = item->searchSpec.triple.base;
277 		else
278 			ls.base = defBase;
279 		ls.base = appendBase(ls.base, defBase, &stat, 0);
280 		if (stat != 0)
281 			return (0);
282 		ls.scope = item->searchSpec.triple.scope;
283 
284 		/*
285 		 * If the search triple specifies a filter, we use the
286 		 * base, scope and filter to get an entry to supply the
287 		 * DN. Otherwise, the triple.base is assumed to be the DN.
288 		 */
289 		if (slen(item->searchSpec.triple.attrs) > 0 ||
290 				item->searchSpec.triple.element != 0) {
291 			__nis_value_t		*eVal = 0;
292 			__nis_rule_value_t	*rvDN;
293 			int			nv = 0;
294 
295 			if (item->searchSpec.triple.element != 0) {
296 				eVal = evalMappingElement(
297 					item->searchSpec.triple.element, 0);
298 
299 				if (eVal == 0) {
300 					sfree(ls.base);
301 					return (0);
302 				}
303 
304 				if (eVal->type != vt_string ||
305 						eVal->numVals <= 0) {
306 					sfree(ls.base);
307 					freeValue(eVal, 1);
308 					return (0);
309 				}
310 
311 				ls.filter = eVal->val[0].value;
312 			} else {
313 				ls.filter = item->searchSpec.triple.attrs;
314 			}
315 
316 			rvDN = ldapSearch(&ls, &nv, 0, &stat);
317 			sfree(ls.base);
318 			freeValue(eVal, 1);
319 			if (rvDN == 0 || nv <= 0)
320 				return (stat);
321 
322 			/* Look for DNs */
323 			dn = findDNs(myself, rvDN, nv, 0, &numDN);
324 			freeRuleValue(rvDN, nv);
325 			if (dn == 0 || numDN <= 0) {
326 				freeDNs(dn, numDN);
327 				return (LDAP_NO_MEMORY);
328 			}
329 			freeDN = 1;
330 		} else if (slen(item->searchSpec.triple.base) > 0) {
331 			locDN[0] = item->searchSpec.triple.base;
332 			dn = locDN;
333 			numDN = 1;
334 		}
335 	}
336 
337 	/* We must have at least one DN to continue */
338 	if (dn == 0 || numDN < 1) {
339 		if (freeDN)
340 			freeDNs(dn, numDN);
341 		return (LDAP_PARAM_ERROR);
342 	}
343 
344 	if (val->numVals > 0) {
345 		/* Make a rule-value describing the modification */
346 		rv = am(myself, sizeof (*rv));
347 		if (rv == 0)
348 			return (LDAP_NO_MEMORY);
349 		rv->attrName = am(myself, sizeof (rv->attrName[0]));
350 		rv->attrVal = am(myself, sizeof (rv->attrVal[0]));
351 		if (rv->attrName == 0 || rv->attrVal == 0) {
352 			if (freeDN)
353 				freeDNs(dn, numDN);
354 			freeRuleValue(rv, 1);
355 			return (LDAP_NO_MEMORY);
356 		}
357 
358 		/*
359 		 * What's the start index in val->val[], and how many elements
360 		 * should we copy ?
361 		 */
362 		if (index < val->numVals)
363 			six = index;
364 		else
365 			six = val->numVals - 1;
366 		if (item->repeat && index == (numIndexes - 1))
367 			nix = 1 + (six - (val->numVals - 1));
368 		else
369 			nix = 1;
370 
371 		rv->attrName[0] = sdup(myself, T, item->name);
372 		rv->attrVal[0].val = am(myself,
373 				nix * sizeof (rv->attrVal[0].val[0]));
374 		if (rv->attrName[0] == 0 || rv->attrVal[0].val == 0) {
375 			if (freeDN)
376 				freeDNs(dn, numDN);
377 			freeRuleValue(rv, 1);
378 			return (LDAP_NO_MEMORY);
379 		}
380 		rv->numAttrs = 1;
381 		for (ix = six; ix < nix; ix++) {
382 			rv->attrVal[0].numVals++;
383 			rv->attrVal[0].val[ix-six].value =
384 					am(myself, val->val[ix].length);
385 			if (rv->attrVal[0].val[ix-six].value == 0 &&
386 					val->val[ix].value != 0) {
387 				if (freeDN)
388 					freeDNs(dn, numDN);
389 				freeRuleValue(rv, 1);
390 				return (LDAP_NO_MEMORY);
391 			}
392 			rv->attrVal[0].val[ix-six].length =
393 				val->val[ix].length;
394 			if (rv->attrVal[0].val[ix-six].length > 0) {
395 				(void) memcpy(rv->attrVal[0].val[ix-six].value,
396 					val->val[ix].value,
397 					rv->attrVal[0].val[ix-six].length);
398 			}
399 		}
400 		rv->attrVal[0].type = val->type;
401 	} else {
402 		/*
403 		 * We already rejected val->numvals < -1 and val->numVals == 0
404 		 * in the initial sanity check, so it must be -1. This means
405 		 * deletion, which we indicate to ldapModify() by supplying
406 		 * a NULL rule-value pointer.
407 		 */
408 		rv = 0;
409 	}
410 
411 	/* For each DN */
412 	for (i = 0; i < numDN; i++) {
413 		stat = ldapModify(dn[i], rv, item->searchSpec.triple.attrs, 0);
414 		if (stat != LDAP_SUCCESS)
415 			break;
416 	}
417 
418 	if (freeDN)
419 		freeDNs(dn, numDN);
420 	freeRuleValue(rv, 1);
421 
422 	return (stat);
423 }
424