Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / lib / libnisdb / yptol / dit_access_utils.c
blob8807976f7a071576f6b67a5b401fa8e84b169810
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2015 Gary Mills
23 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
27 * DESCRIPTION: Contains dit_access interface support functions.
29 #include <sys/systeminfo.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/systeminfo.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <syslog.h>
38 #include <ndbm.h>
39 #include <strings.h>
40 #include <errno.h>
41 #include <ctype.h>
42 #include "../ldap_util.h"
43 #include "../ldap_map.h"
44 #include "../ldap_parse.h"
45 #include "../ldap_structs.h"
46 #include "../ldap_val.h"
47 #include "../ldap_ruleval.h"
48 #include "../ldap_op.h"
49 #include "../ldap_attr.h"
50 #include "../ldap_nisdbquery.h"
51 #include "../nisdb_mt.h"
52 #include "shim.h"
53 #include "yptol.h"
54 #include "dit_access_utils.h"
56 #define YPMULTI "YP_MULTI_"
57 #define YPMULTISZ 9 /* == strlen(YPMULTI) */
60 * Returns 'map,domain.'
62 char *
63 getFullMapName(char *map, char *domain) {
64 char *myself = "getFullMapName";
65 char *objPath;
66 if (map == 0 || domain == 0) {
67 return (0);
69 objPath = scat(myself, T, scat(myself, F, map, ","),
70 scat(myself, F, domain, "."));
72 return (objPath);
76 * Convert string to __nis_value_t
78 __nis_value_t *stringToValue(char *dptr, int dsize) {
79 char *myself = "stringToValue";
80 char *emptystr = "";
81 __nis_value_t *val;
83 if ((val = am(myself, sizeof (*val))) == 0) {
84 return (0);
87 val->type = vt_string;
88 val->repeat = 0;
89 val->numVals = 1;
90 if ((val->val = am(myself, sizeof (val->val[0]))) == 0) {
91 sfree(val);
92 return (0);
96 * Null strings or strings with length 0 are treated
97 * as empty strings with length 1
99 if (dptr == 0 || dsize <= 0) {
100 dptr = emptystr;
101 dsize = 1;
104 val->val->length = dsize;
105 if (dptr[dsize - 1] != '\0') {
106 val->val->length = dsize + 1;
109 val->val->value = am(myself, val->val->length);
110 if (val->val->value == 0) {
111 freeValue(val, 1);
112 return (0);
114 (void) memcpy(val->val->value, dptr, dsize);
116 return (val);
120 * Returns an array of rule-values corresponding to the
121 * splitfields.
123 __nis_rule_value_t *
124 processSplitField(__nis_table_mapping_t *sf, __nis_value_t *inVal,
125 int *nv, int *statP) {
127 char *sepset;
128 __nis_rule_value_t *rvq;
129 __nis_value_t **valA, *tempVal;
130 int i, j, res, numVals, oldlen, count;
131 char *str, *oldstr;
133 /* sf will be non NULL */
135 if (inVal == 0 || inVal->type != vt_string) {
136 *statP = MAP_PARAM_ERROR;
137 return (0);
140 /* Get the separator list */
141 sepset = sf->separatorStr;
143 /* Initialize rule-value */
144 rvq = 0;
145 count = 0;
147 if ((tempVal = stringToValue(inVal->val->value,
148 inVal->val->length)) == 0) {
149 *statP = MAP_NO_MEMORY;
150 return (0);
153 str = oldstr = tempVal->val->value;
154 oldlen = tempVal->val->length;
156 while (str) {
157 tempVal->val->value = str;
158 tempVal->val->length = strlen(str) + 1;
160 /* Loop to check which format matches str */
161 for (i = 0; i <= sf->numSplits; i++) {
162 valA = matchMappingItem(sf->e[i].element.match.fmt,
163 tempVal, &numVals, sepset, &str);
164 if (valA == 0) {
165 /* The format didn't match. Try the next one */
166 continue;
170 * If we are here means we had a match.
171 * Each new set of values obtained from the match is
172 * added to a new rule-value. This is to preserve the
173 * the distinction between each set.
175 rvq = growRuleValue(count, count + 1, rvq, 0);
176 if (rvq == 0) {
177 *statP = MAP_INTERNAL_ERROR;
178 for (j = 0; j < numVals; j++)
179 freeValue(valA[j], 1);
180 sfree(valA);
181 tempVal->val->value = oldstr;
182 tempVal->val->length = oldlen;
183 freeValue(tempVal, 1);
184 return (0);
186 count++;
188 for (j = 0; j < numVals; j++) {
189 res = addCol2RuleValue(vt_string,
190 sf->e[i].element.match.item[j].name,
191 valA[j]->val->value,
192 valA[j]->val->length,
193 &rvq[count - 1]);
194 if (res == -1) {
195 *statP = MAP_INTERNAL_ERROR;
196 for (; j < numVals; j++)
197 freeValue(valA[j], 1);
198 sfree(valA);
199 tempVal->val->value = oldstr;
200 tempVal->val->length = oldlen;
201 freeValue(tempVal, 1);
202 freeRuleValue(rvq, count);
203 return (0);
205 freeValue(valA[j], 1);
207 sfree(valA);
210 * Since we had a match, break out of this loop
211 * to parse remainder of str
213 break;
216 /* Didn't find any match, so get out of the loop */
217 if (i > sf->numSplits) {
218 str = 0;
219 break;
222 /* Skip the separators before looping back */
223 if (str) {
224 str = str + strspn(str, sepset);
225 if (*str == '\0')
226 break;
230 tempVal->val->value = oldstr;
231 tempVal->val->length = oldlen;
232 freeValue(tempVal, 1);
234 if (str == 0) {
235 freeRuleValue(rvq, count);
236 return (0);
239 if (nv != 0)
240 *nv = count;
242 return (rvq);
246 * Convert the datum to an array of RuleValues
248 __nis_rule_value_t *
249 datumToRuleValue(datum *key, datum *value, __nis_table_mapping_t *t,
250 int *nv, char *domain, bool_t readonly, int *statP) {
252 __nis_rule_value_t *rvq, *subrvq, *newrvq;
253 __nis_value_t *val;
254 __nis_value_t **valA;
255 __nis_table_mapping_t *sf;
256 int valueLen, comLen, numVals, nr, count = 1;
257 int i, j, k, l;
258 char *ipaddr, *ipvalue;
260 /* At this point, 't' is always non NULL */
262 /* Initialize rule-value */
263 if ((rvq = initRuleValue(1, 0)) == 0) {
264 *statP = MAP_INTERNAL_ERROR;
265 return (0);
268 /* Add domainname to rule-value */
269 if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
270 rvq)) {
271 freeRuleValue(rvq, 1);
272 *statP = MAP_INTERNAL_ERROR;
273 return (0);
276 /* Handle key */
277 if (key != 0) {
278 /* Add field=value pair for N2LKEY */
279 i = addCol2RuleValue(vt_string, N2LKEY, key->dptr, key->dsize,
280 rvq);
282 /* For readonly, add field=value pair for N2LSEARCHKEY */
283 if (readonly == TRUE && i == 0) {
284 i = addCol2RuleValue(vt_string, N2LSEARCHKEY, key->dptr,
285 key->dsize, rvq);
287 if (i) {
288 freeRuleValue(rvq, 1);
289 *statP = MAP_INTERNAL_ERROR;
290 return (0);
293 /* Add field=value pairs for IP addresses */
294 if (checkIPaddress(key->dptr, key->dsize, &ipaddr) > 0) {
295 /* If key is IPaddress, use preferred format */
296 ipvalue = ipaddr;
297 valueLen = strlen(ipaddr);
298 i = addCol2RuleValue(vt_string, N2LIPKEY, ipvalue,
299 valueLen, rvq);
300 } else {
301 /* If not, use original value for N2LSEARCHIPKEY */
302 ipaddr = 0;
303 ipvalue = key->dptr;
304 valueLen = key->dsize;
305 i = 0;
308 if (readonly == TRUE && i == 0) {
309 i = addCol2RuleValue(vt_string, N2LSEARCHIPKEY, ipvalue,
310 valueLen, rvq);
312 sfree(ipaddr);
313 if (i) {
314 freeRuleValue(rvq, 1);
315 *statP = MAP_INTERNAL_ERROR;
316 return (0);
320 /* Handle datum value */
321 if (value != 0 && t->e) {
322 char *valstr = value->dptr;
323 valueLen = value->dsize;
325 * Extract the comment, if any, and add it to
326 * the rule-value.
328 if (t->commentChar != '\0') {
330 * We loop on value->dsize because valstr
331 * may not be NULL-terminated.
333 for (i = 0; i < value->dsize; i++) {
334 if (valstr[i] == t->commentChar) {
335 valueLen = i;
336 comLen = value->dsize - i - 1;
337 if (comLen == 0)
338 break;
339 if (addCol2RuleValue(vt_string,
340 N2LCOMMENT, valstr + i + 1,
341 comLen, rvq)) {
342 freeRuleValue(rvq, 1);
343 *statP = MAP_INTERNAL_ERROR;
344 return (0);
346 break;
351 /* Skip trailing whitespaces */
352 for (; valueLen > 0 && (valstr[valueLen - 1] == ' ' ||
353 valstr[valueLen - 1] == '\t'); valueLen--);
356 * At this point valueLen is the effective length of
357 * the data. Convert value into __nis_value_t so that
358 * we can use the matchMappingItem function to break it
359 * into fields.
361 if ((val = stringToValue(valstr, valueLen)) == 0) {
362 freeRuleValue(rvq, 1);
363 *statP = MAP_NO_MEMORY;
364 return (0);
367 /* Perform namefield match */
368 valA = matchMappingItem(t->e->element.match.fmt, val,
369 &numVals, 0, 0);
370 if (valA == 0) {
371 freeValue(val, 1);
372 freeRuleValue(rvq, 1);
373 *statP = MAP_NAMEFIELD_MATCH_ERROR;
374 return (0);
377 /* We don't need val anymore, so free it */
378 freeValue(val, 1);
381 * Since matchMappingItem only returns us an array of
382 * __nis_value_t's, we need to associate each value
383 * in the array with the corresponding item name.
384 * This code assumes that numVals will be less than or
385 * equal to the number of item names associated with
386 * the format.
387 * These name=value pairs are added to rvq.
389 for (i = 0, *statP = SUCCESS; i < numVals; i++) {
390 for (j = 0; j < count; j++) {
391 if (addCol2RuleValue(vt_string,
392 t->e->element.match.item[i].name,
393 valA[i]->val->value,
394 valA[i]->val->length, &rvq[j])) {
395 *statP = MAP_INTERNAL_ERROR;
396 break;
399 if (*statP == MAP_INTERNAL_ERROR)
400 break;
403 * Check if splitField exists for the field.
404 * Since splitfields are also stored as mapping
405 * structures, we need to get the hash table entry
406 * corresponding to the splitfield name
408 sf = mappingFromMap(t->e->element.match.item[i].name,
409 domain, statP);
410 if (*statP == MAP_NO_MEMORY)
411 break;
412 *statP = SUCCESS;
413 if (sf == 0)
414 continue;
417 * Process and add splitFields to rule-value rvq
419 subrvq = processSplitField(sf, valA[i], &nr, statP);
421 if (subrvq == 0) {
422 /* statP would have been set */
423 break;
427 * We merge 'count' rule-values in rvq with 'nr'
428 * rule-values from subrvq to give us a whopping
429 * 'count * nr' rule-values
432 /* Initialize the new rule-value array */
433 if ((newrvq = initRuleValue(count * nr, 0)) == 0) {
434 *statP = MAP_INTERNAL_ERROR;
435 freeRuleValue(subrvq, nr);
436 break;
439 for (j = 0, l = 0; j < nr; j++) {
440 for (k = 0; k < count; k++, l++) {
441 if ((mergeRuleValue(&newrvq[l],
442 &rvq[k]) == -1) ||
443 (mergeRuleValue(
444 &newrvq[l],
445 &subrvq[j]) == -1)) {
446 *statP = MAP_INTERNAL_ERROR;
447 for (i = 0; i < numVals; i++)
448 freeValue(valA[i], 1);
449 sfree(valA);
450 freeRuleValue(rvq, count);
451 freeRuleValue(newrvq,
452 count * nr);
453 freeRuleValue(subrvq, nr);
454 return (0);
459 freeRuleValue(rvq, count);
460 rvq = newrvq;
461 count = l;
462 freeRuleValue(subrvq, nr);
466 /* We don't need valA anymore, so free it */
467 for (i = 0; i < numVals; i++)
468 freeValue(valA[i], 1);
469 sfree(valA);
471 if (*statP != SUCCESS) {
472 freeRuleValue(rvq, count);
473 return (0);
476 } /* if value */
478 if (nv != 0)
479 *nv = count;
480 return (rvq);
485 * Generate name=values pairs for splitfield names
487 * Consider Example:
488 * nisLDAPnameFields club:
489 * ("%s %s %s", name, code, members)
490 * nisLDAPsplitField members:
491 * ("(%s,%s,%s)", host, user, domain),
492 * ("%s", group)
493 * On entry,
494 * - rv is an array of numVals rule-values each containing
495 * name=value pairs for names occuring in nisLDAPsplitField.
496 * (i.e host, user, domain, group)
497 * - trv contains name=value pairs for names occuring in
498 * nisLDAPnameFields. (i.e name, code but not members)
500 * For every name in nisLDAPnamefields that is a splitfield,
501 * this function applies the data in rv to the corresponding
502 * splitfield formats (accessed thru t), to generate a single
503 * string value for the corresponding splitfield (members).
504 * This new name=value pair is then added to trv.
505 * Besides, any uninitialized namefield names are set to empty strings.
507 suc_code
508 addSplitFieldValues(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
509 __nis_rule_value_t *trv, int numVals, char *domain) {
510 __nis_table_mapping_t *sf;
511 __nis_value_t *val;
512 int i, j, k, nitems, res, statP;
513 char *str, *tempstr;
514 char delim[2] = {0, 0};
515 char *emptystr = "";
516 char *myself = "addSplitFieldValues";
518 if (trv == 0)
519 return (MAP_INTERNAL_ERROR);
521 if (t->e == 0)
522 return (SUCCESS);
524 nitems = t->e->element.match.numItems;
527 * Procedure:
528 * - Check each name in nisLDAPnamefield
529 * - if it's a splifield, construct its value and add it to trv
530 * - if not, check if it has a value
531 * - if not, add empty string
533 for (i = 0, sf = 0; i < nitems; i++) {
534 if (rv) {
536 * str will eventually contain the single string
537 * value for the corresponding splitfield.
538 * No point initializing str if rv == 0 because
539 * splitfield cannot be constructed without rv.
540 * So, only initialized here.
542 str = 0;
544 /* Check if it's a splitfield name */
545 sf = mappingFromMap(t->e->element.match.item[i].name,
546 domain, &statP);
549 * Return only incase of memory allocation failure.
550 * The other error case (MAP_NO_MAPPING_EXISTS),
551 * indicates that the item name is not a splitfieldname
552 * i.e it's a namefieldname. This case is handled by
553 * the following if (sf == 0)
555 if (statP == MAP_NO_MEMORY)
556 return (statP);
559 if (sf == 0) {
561 * Not a splitfield name. Verify if it has a value
563 if (findVal(t->e->element.match.item[i].name,
564 trv, mit_nisplus) == 0) {
565 /* if not, use empty string */
566 res = addCol2RuleValue(vt_string,
567 t->e->element.match.item[i].name,
568 emptystr, 0, trv);
569 if (res == -1) {
570 return (MAP_INTERNAL_ERROR);
574 * If rv == 0 then sf == 0 so we will continue here
575 * i.e. does not matter that str is not yet set up.
577 continue;
580 /* Code to construct a single value */
582 /* Use the first separator character as the delimiter */
583 delim[0] = sf->separatorStr[0];
585 for (j = 0; j < numVals; j++) {
586 /* sf->numSplits is zero-based */
587 for (k = 0; k <= sf->numSplits; k++) {
588 val = getMappingFormatArray(
589 sf->e[k].element.match.fmt, &rv[j],
590 fa_item,
591 sf->e[k].element.match.numItems,
592 sf->e[k].element.match.item);
593 if (val == 0)
594 continue;
595 if (val->numVals > 0) {
596 if (str) {
597 tempstr = scat(myself,
598 0, str, delim);
599 sfree(str);
600 if (tempstr)
601 str = tempstr;
602 else {
603 freeValue(val, 1);
604 return (MAP_NO_MEMORY);
607 tempstr = scat(myself, 0, str,
608 val->val->value);
609 sfree(str);
610 if (tempstr)
611 str = tempstr;
612 else {
613 freeValue(val, 1);
614 return (MAP_NO_MEMORY);
617 freeValue(val, 1);
620 if (str == 0)
621 str = emptystr;
623 res = addCol2RuleValue(vt_string,
624 t->e->element.match.item[i].name,
625 str, strlen(str), trv);
627 if (str != emptystr)
628 sfree(str);
630 if (res == -1) {
631 return (MAP_INTERNAL_ERROR);
635 return (SUCCESS);
639 * Updates 'rv' with NIS name=value pairs suitable to
640 * construct datum from namefield information.
641 * Some part based on createNisPlusEntry (from ldap_nisdbquery.c)
642 * This code assumes that from a given LDAP entry, applying the
643 * mapping rules, would give us one or more NIS entries, differing
644 * only in key.
646 suc_code
647 buildNISRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
648 char *domain) {
649 int r, i, j, k, l, nrq, res, len;
650 int numItems, splitname, count, statP;
651 __nis_value_t *rval;
652 __nis_mapping_item_t *litem;
653 __nis_mapping_rule_t *rl;
654 __nis_rule_value_t *rvq;
655 char *value, *emptystr = "";
657 statP = SUCCESS;
659 /* Initialize default base */
660 __nisdb_get_tsd()->searchBase = t->objectDN->read.base;
662 /* Initialize rule-value rvq */
663 rvq = 0;
664 count = 0;
666 /* Add domainname to rule-value */
667 if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
668 rv)) {
669 return (MAP_INTERNAL_ERROR);
672 for (r = 0; r < t->numRulesFromLDAP; r++) {
673 rl = t->ruleFromLDAP[r];
675 /* Set escapeFlag if RHS is "dn" to remove escape chars */
676 if (rl->rhs.numElements == 1 &&
677 rl->rhs.element->type == me_item &&
678 rl->rhs.element->element.item.type == mit_ldap &&
679 strcasecmp(rl->rhs.element->element.item.name, "dn")
680 == 0) {
681 __nisdb_get_tsd()->escapeFlag = '2';
684 rval = buildRvalue(&rl->rhs, mit_ldap, rv, NULL);
686 /* Reset escapeFlag */
687 __nisdb_get_tsd()->escapeFlag = '\0';
689 if (rval == 0) {
690 continue;
693 if (rval->numVals <= 0) {
694 /* Treat as invalid */
695 freeValue(rval, 1);
696 continue;
699 litem = buildLvalue(&rl->lhs, &rval, &numItems);
700 if (litem == 0) {
701 /* This will take care of numItems == 0 */
702 freeValue(rval, 1);
703 continue;
706 if (rval->numVals > 1) {
707 if (numItems == 1 && litem->repeat)
708 nrq = rval->numVals;
709 else if (numItems > 1 && rval->repeat)
710 nrq = 1 + ((rval->numVals-1)/numItems);
711 else
712 nrq = 1;
713 } else
714 nrq = 1;
716 /* Set splitname if splitfield names are specified */
717 for (i = 0; i < numItems; i++) {
718 if (strcasecmp(litem[i].name, N2LKEY) == 0 ||
719 strcasecmp(litem[i].name, N2LIPKEY) == 0 ||
720 strcasecmp(litem[i].name, N2LCOMMENT) == 0)
721 continue;
722 for (j = 0; j < t->numColumns; j++) {
723 if (strcmp(litem[i].name, t->column[j]) == 0)
724 break;
726 if (j == t->numColumns)
727 break;
730 splitname = (i < numItems)?1:0;
732 for (j = 0; j < nrq; j++) {
733 if (splitname == 1) {
735 * Put every value of splitfieldname in a new
736 * rule-value. Helps generating splitfields.
738 rvq = growRuleValue(count, count + 1, rvq, 0);
739 if (rvq == 0) {
740 freeRuleValue(rvq, count);
741 freeValue(rval, 1);
742 freeMappingItem(litem, numItems);
743 return (MAP_INTERNAL_ERROR);
745 count++;
748 for (k = j % nrq, l = 0; l < numItems; k += nrq, l++) {
749 /* If we run out of values, use empty strings */
750 if (k >= rval->numVals) {
751 value = emptystr;
752 len = 0;
753 } else {
754 value = rval->val[k].value;
755 len = rval->val[k].length;
757 res = (splitname == 1)?addCol2RuleValue(
758 vt_string, litem[l].name, value,
759 len, &rvq[count - 1]):0;
760 if (res != -1)
761 res = addCol2RuleValue(vt_string,
762 litem[l].name, value, len, rv);
763 if (res == -1) {
764 freeRuleValue(rvq, count);
765 freeValue(rval, 1);
766 freeMappingItem(litem, numItems);
767 return (MAP_INTERNAL_ERROR);
771 freeValue(rval, 1);
772 rval = 0;
773 freeMappingItem(litem, numItems);
774 litem = 0;
775 numItems = 0;
776 } /* for r < t->numRulesFromLDAP */
778 statP = addSplitFieldValues(t, rvq, rv, count, domain);
780 if (rvq)
781 freeRuleValue(rvq, count);
783 if (verifyIndexMatch(t, 0, rv, 0, 0) == 0)
784 return (MAP_INDEXLIST_ERROR);
785 return (statP);
787 } /* end of buildNISRuleValue */
790 * Convert rule-value to datum using namefield information
792 datum *
793 ruleValueToDatum(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *statP) {
794 __nis_value_t *val;
795 datum *value;
796 char *str, *cstr, commentSep[3] = {' ', 0, 0};
797 char *myself = "ruleValueToDatum";
799 /* No error yet */
800 *statP = 0;
802 /* Return empty datum if no namefield information available */
803 if (t->e == 0) {
804 if ((value = am(myself, sizeof (*value))) == 0)
805 *statP = MAP_NO_MEMORY;
806 return (value);
809 val = getMappingFormatArray(t->e->element.match.fmt, rv,
810 fa_item, t->e->element.match.numItems,
811 t->e->element.match.item);
813 if (val && val->val && val->val->value) {
814 if ((value = am(myself, sizeof (*value))) == 0) {
815 *statP = MAP_NO_MEMORY;
816 freeValue(val, 1);
817 return (0);
820 /* Strip trailing whitespaces */
821 cstr = (char *)val->val->value + val->val->length;
822 for (; cstr >= (char *)val->val->value &&
823 (*cstr == ' ' || *cstr == '\t'); *cstr-- = '\0');
825 if (t->commentChar != '\0' &&
826 (str = findVal(N2LCOMMENT, rv, mit_nisplus)) != 0 &&
827 *str != '\0') {
828 commentSep[1] = t->commentChar;
829 cstr = scat(myself, F, commentSep, str);
830 if (cstr) {
831 value->dptr = scat(myself, F,
832 val->val->value, cstr);
833 sfree(cstr);
835 } else {
836 value->dptr = sdup(myself, T, val->val->value);
838 freeValue(val, 1);
839 if (value->dptr) {
840 value->dsize = strlen(value->dptr);
841 return (value);
842 } else {
843 *statP = MAP_NO_MEMORY;
844 sfree(value);
845 return (0);
849 *statP = MAP_NAMEFIELD_MATCH_ERROR;
850 return (0);
853 datum *
854 getKeyFromRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *nv,
855 int *statP, bool_t xlate_to_lcase)
857 int i, j, k;
858 datum *key = 0;
859 char *str;
860 char *myself = "getKeyFromRuleValue";
862 /* No error yet */
863 *statP = 0;
865 if (rv == 0 || nv == 0)
866 return (0);
868 for (i = 0; i < rv->numColumns; i++) {
869 if (rv->colName[i] == 0)
870 continue;
871 if (strcasecmp(N2LKEY, rv->colName[i]) == 0 ||
872 strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
873 if ((*nv = rv->colVal[i].numVals) == 0)
874 return (0);
875 if ((key = am(myself, sizeof (key[0]) * *nv)) == 0) {
876 *statP = MAP_NO_MEMORY;
877 return (0);
879 for (j = 0; j < *nv; j++) {
880 if ((str = rv->colVal[i].val[j].value) == 0) {
881 key[j].dsize = 0;
882 key[j].dptr = 0;
883 } else {
884 if (verifyIndexMatch(t, 0, 0,
885 rv->colName[i], str) == 0) {
886 key[j].dsize = 0;
887 key[j].dptr = 0;
888 continue;
891 key[j].dsize = strlen(str);
892 key[j].dptr = am(myself,
893 key[j].dsize + 1);
894 if (key[j].dptr == 0) {
895 *statP = MAP_NO_MEMORY;
896 for (--j; j >= 0; j--)
897 sfree(key[j].dptr);
898 sfree(key);
899 return (0);
902 /* transliterate key to lowercase */
903 if (xlate_to_lcase == TRUE) {
906 * For multi-homed
907 * entries, skip over
908 * "YP_MULTI_" prefix.
910 k = 0;
911 if (strncmp(YPMULTI, str,
912 YPMULTISZ) == 0) {
913 k = YPMULTISZ;
914 bcopy(str, key[j].dptr,
915 YPMULTISZ);
917 while (k < key[j].dsize) {
918 char *d = key[j].dptr;
919 d[k] =
920 (char)tolower(
921 (int)(uchar_t)
922 str[k]);
923 k++;
925 } else {
926 bcopy(str, key[j].dptr,
927 key[j].dsize);
931 return (key);
934 return (0);
938 * Get the mapping structure corresponding to `map,domain.'
940 __nis_table_mapping_t *
941 mappingFromMap(char *map, char *domain, int *statP) {
942 char *mapPath;
943 __nis_table_mapping_t *t;
945 /* No error yet */
946 *statP = 0;
948 /* Construct map,domain. */
949 if ((mapPath = getFullMapName(map, domain)) == 0) {
950 *statP = MAP_NO_MEMORY;
951 return (0);
954 /* Get the hash table entry for the mapPath */
955 if ((t = __nis_find_item_mt(mapPath, &ldapMappingList, 1, 0))
956 == 0) {
957 *statP = MAP_NO_MAPPING_EXISTS;
959 sfree(mapPath);
960 return (t);
964 * Verify at least one key value obtained from DIT matches the search key
965 * RETURNS: 1 MATCH
966 * 0 NO MATCH
967 * -1 NO KEY FOUND
969 static int
970 verifyKey(char *key, __nis_rule_value_t *rv) {
971 int i, j;
972 char *sipkey, *str;
974 for (i = 0; i < rv->numColumns; i++) {
975 if (rv->colName[i] == 0)
976 continue;
977 if (strcasecmp(N2LKEY, rv->colName[i]) == 0) {
978 if (rv->colVal[i].val == 0)
979 return (0);
980 for (j = 0; j < rv->colVal[i].numVals; j++) {
981 str = (char *)rv->colVal[i].val[j].value;
982 if (str && strcmp(str, key) == 0)
983 return (1);
985 return (0);
986 } else if (strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
987 if (checkIPaddress(key, strlen(key), &sipkey) > 0) {
988 if (rv->colVal[i].val == 0)
989 return (0);
990 for (j = 0; j < rv->colVal[i].numVals; j++) {
991 str = rv->colVal[i].val[j].value;
992 if (str && strcmp(str, sipkey) == 0) {
993 sfree(sipkey);
994 return (1);
997 sfree(sipkey);
999 return (0);
1002 return (-1);
1006 * Read (i.e get and map) a single NIS entry from the LDAP DIT
1008 bool_t
1009 singleReadFromDIT(char *map, char *domain, datum *key, datum *value,
1010 int *statP) {
1011 __nis_table_mapping_t *t;
1012 __nis_rule_value_t *rv_request = 0, *rv_result = 0;
1013 __nis_ldap_search_t *ls;
1014 __nis_object_dn_t *objectDN = NULL;
1015 int i, rc, nr = 0;
1016 datum *datval = 0;
1017 char *skey, *str;
1018 char *myself = "singleReadFromDIT";
1020 *statP = SUCCESS;
1022 if (!map || !domain || !key || !value) {
1023 *statP = MAP_PARAM_ERROR;
1024 return (FALSE);
1028 /* Get the mapping information for the map */
1029 if ((t = mappingFromMap(map, domain, statP)) == 0) {
1031 * No problem. We don't handle this map and domain. Maybe it's
1032 * handled by a service other than NIS.
1034 return (FALSE);
1037 /* NULL-terminated version of datum key for logging */
1038 if ((skey = am(myself, key->dsize + 1)) == 0) {
1039 *statP = MAP_NO_MEMORY;
1040 return (FALSE);
1042 (void) memcpy(skey, key->dptr, key->dsize);
1044 if ((str = getFullMapName(map, domain)) == 0) {
1045 *statP = MAP_NO_MEMORY;
1046 return (FALSE);
1049 /* For each alternate mapping */
1050 for (; t != 0; t = t->next) {
1051 /* Verify objName */
1052 if (strcmp(str, t->objName) != 0) {
1053 continue;
1056 /* Verify if key matches the index */
1057 if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
1058 verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
1059 continue;
1061 /* Check if rulesFromLDAP are provided */
1062 if (t->numRulesFromLDAP == 0) {
1063 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1064 "%s: No rulesFromLDAP information available "
1065 "for %s (%s)", myself, t->dbId, map);
1066 continue;
1069 /* Convert key into rule-value */
1070 if ((rv_request = datumToRuleValue(key, 0, t, 0, domain, TRUE,
1071 statP)) == 0) {
1072 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1073 "%s: Conversion error %d (NIS to name=value "
1074 "pairs) for NIS key (%s) for %s (%s)",
1075 myself, *statP, skey, t->dbId, map);
1076 continue;
1078 /* Convert rule-value into ldap request */
1079 for (objectDN = t->objectDN; objectDN &&
1080 objectDN->read.base;
1081 objectDN = objectDN->next) {
1082 ls = createLdapRequest(t, rv_request, 0, 1, NULL,
1083 objectDN);
1084 if (ls == 0) {
1085 *statP = MAP_CREATE_LDAP_REQUEST_ERROR;
1086 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1087 "%s: Failed to create ldapSearch "
1088 "request for "
1089 "NIS key (%s) for %s (%s) "
1090 "for base %s",
1091 myself, skey, t->dbId, map,
1092 objectDN->read.base);
1093 continue;
1095 ls->timeout.tv_sec = SINGLE_ACCESS_TIMEOUT_SEC;
1096 ls->timeout.tv_usec = SINGLE_ACCESS_TIMEOUT_USEC;
1097 /* Query LDAP */
1098 nr = (ls->isDN)?0:-1;
1099 rv_result = ldapSearch(ls, &nr, 0, statP);
1100 freeLdapSearch(ls);
1101 if (rv_result == 0) {
1102 if (*statP == LDAP_NO_SUCH_OBJECT) {
1103 /* Entry does not exist in */
1104 /* the ldap server */
1106 continue;
1108 freeRuleValue(rv_request, 1);
1109 rv_request = 0;
1111 /* if result > 1, first match will be returned */
1112 if (nr > 1) {
1113 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1114 "%s: %d ldapSearch results "
1115 "for NIS key (%s) "
1116 "for %s (%s) for base %s. "
1117 "First match will be returned ",
1118 myself, nr, skey, t->dbId, map,
1119 objectDN->read.base);
1122 for (i = 0; i < nr; i++) {
1123 /* Convert LDAP data to NIS equivalents */
1124 *statP = buildNISRuleValue(t, &rv_result[i],
1125 domain);
1126 if (*statP == MAP_INDEXLIST_ERROR)
1127 continue;
1129 if (*statP != SUCCESS) {
1130 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1131 "%s: Conversion error %d (LDAP to "
1132 "name=value pairs) for NIS key (%s) "
1133 "for %s (%s) for base %s", myself,
1134 *statP, skey,
1135 t->dbId, map, objectDN->read.base);
1136 continue;
1140 * Check if 'key' from the ldap result matches the key
1141 * provided by our caller
1143 if ((rc = verifyKey(skey, &rv_result[i]))
1144 == -1) {
1145 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1146 "%s: Cannot verify key from ldap "
1147 "result for NIS key (%s) for %s (%s) "
1148 "for base %s",
1149 myself, skey, t->dbId, map,
1150 objectDN->read.base);
1151 continue;
1154 if (rc == 1) {
1155 datval = ruleValueToDatum(t,
1156 &rv_result[i], statP);
1157 if (datval == 0) {
1158 logmsg(MSG_NOTIMECHECK,
1159 LOG_WARNING,
1160 "%s: Conversion error %d "
1161 "(name=value pairs to NIS) "
1162 "for NIS key (%s) for %s (%s)"
1163 " for base %s",
1164 myself,
1165 *statP, skey, t->dbId, map,
1166 objectDN->read.base);
1167 continue;
1169 if (value) {
1170 value->dptr = datval->dptr;
1171 value->dsize = datval->dsize;
1173 sfree(datval);
1174 sfree(skey);
1175 freeRuleValue(rv_result, nr);
1176 rv_result = 0;
1177 *statP = SUCCESS;
1179 /* Free full map name */
1180 sfree(str);
1182 return (TRUE);
1185 freeRuleValue(rv_result, nr);
1186 rv_result = 0;
1187 } /* end of for over objectDN */
1189 if (rv_request != 0) {
1190 freeRuleValue(rv_request, 1);
1191 rv_request = 0;
1193 if (rv_result != 0) {
1194 freeRuleValue(rv_result, nr);
1195 rv_result = 0;
1198 sfree(skey);
1199 *statP = MAP_NO_MATCHING_KEY;
1201 /* Free full map name */
1202 sfree(str);
1204 return (FALSE);
1209 * Maps and writes a single NIS entry to the LDAP DIT
1212 singleWriteToDIT(char *map, char *domain, datum *key, datum *value,
1213 bool_t replace) {
1214 __nis_table_mapping_t *t;
1215 __nis_rule_value_t *rv, *frv;
1216 __nis_ldap_search_t *ls;
1217 int statP = SUCCESS, flag;
1218 int nv, nr, i, rc, collapse;
1219 char *dn = 0, *skey, *svalue, *str;
1220 char *myself = "singleWriteToDIT";
1221 char *keystr;
1223 if (!map || !domain || !key || !value) {
1224 return (MAP_PARAM_ERROR);
1227 keystr = key->dptr;
1228 /* Return SUCCESS for empty or whitespace key */
1229 for (i = 0; i < key->dsize && (keystr[i] == 0 ||
1230 keystr[i] == ' ' || keystr[i] == '\t'); i++);
1231 if (i >= key->dsize)
1232 return (SUCCESS);
1234 /* Get the mapping information for the map */
1235 if ((t = mappingFromMap(map, domain, &statP)) == 0) {
1237 * No problem. We don't handle this map and domain. Maybe it's
1238 * handled by a service other than NIS.
1240 return (statP);
1243 /* NULL-terminated version of key and value for logging */
1244 if ((skey = am(myself, key->dsize + 1)) == 0)
1245 return (MAP_NO_MEMORY);
1246 (void) memcpy(skey, key->dptr, key->dsize);
1248 if ((svalue = am(myself, value->dsize + 1)) == 0) {
1249 sfree(skey);
1250 return (MAP_NO_MEMORY);
1252 (void) memcpy(svalue, value->dptr, value->dsize);
1254 if ((str = getFullMapName(map, domain)) == 0) {
1255 sfree(skey);
1256 sfree(svalue);
1257 return (MAP_NO_MEMORY);
1260 /* For each alternate mapping */
1261 for (flag = 0; t != 0; t = t->next) {
1262 /* Verify objName */
1263 if (strcmp(str, t->objName) != 0) {
1264 continue;
1267 /* Verify if key matches the index */
1268 if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
1269 verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
1270 continue;
1272 /* Check the writespecs */
1273 if (t->objectDN->write.base == 0) {
1274 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1275 "%s: No baseDN in writespec. Write disabled "
1276 "for %s (%s)", myself, t->dbId, map);
1277 continue;
1280 /* Check if rulesToLDAP are provided */
1281 if (t->numRulesToLDAP == 0) {
1282 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1283 "%s: No rulesToLDAP. Write disabled for "
1284 "%s (%s)", myself, t->dbId, map);
1285 continue;
1288 /* Set flag to indicate write is enabled */
1289 flag = 1;
1291 /* Convert key and value into an array of rule-values */
1292 if ((rv = datumToRuleValue(key, value, t, &nv, domain, FALSE,
1293 &statP)) == 0) {
1294 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1295 "%s: Conversion error %d (NIS to name=value "
1296 "pairs) for NIS data (key=%s, value=%s) "
1297 "for %s (%s)",
1298 myself, statP, skey, svalue, t->dbId, map);
1299 sfree(skey);
1300 sfree(svalue);
1302 /* Free full map name */
1303 sfree(str);
1305 return (statP);
1308 /* Convert NIS data to LDAP equivalents for each rule-value */
1309 for (i = 0; i < nv; i++) {
1310 /* Verify indexlist with name=value pairs */
1311 if (verifyIndexMatch(t, 0, &rv[i], 0, 0) == 0)
1312 break;
1314 /* Create LDAP request and LDAP name=value pairs */
1315 if ((ls = createLdapRequest(t, &rv[i],
1316 0, 0, NULL, NULL)) == 0) {
1317 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1318 "%s: Conversion error (name=value pairs"
1319 " to LDAP) for NIS data "
1320 "(key=%s, value=%s) for %s (%s)",
1321 myself, skey, svalue, t->dbId, map);
1322 freeRuleValue(rv, nv);
1323 sfree(skey);
1324 sfree(svalue);
1326 /* Free full map name */
1327 sfree(str);
1329 return (MAP_CREATE_LDAP_REQUEST_ERROR);
1331 freeLdapSearch(ls);
1332 /* printRuleValue(&rv[i]); */
1335 /* If i < nv then this alternate mapping isn't the one */
1336 if (i < nv)
1337 continue;
1340 * Merge rule-values with the same DN so that we have
1341 * one ldap write request for each DN
1343 nr = nv;
1344 frv = mergeRuleValueWithSameDN(rv, &nr);
1345 freeRuleValue(rv, nv);
1346 if (frv == 0) {
1347 if (nr == -1) {
1348 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1349 "%s: Unable to merge LDAP write "
1350 "requests to same DN for NIS data "
1351 "(key=%s, value=%s) for %s (%s)",
1352 myself, skey, svalue, t->dbId, map);
1353 statP = MAP_INTERNAL_ERROR;
1354 } else if (nr == 0) {
1355 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1356 "%s: Cannot generate write DN due to "
1357 "missing information for NIS data "
1358 "(key=%s, value=%s) for %s (%s)",
1359 myself, skey, svalue, t->dbId, map);
1360 statP = MAP_NO_DN;
1362 sfree(skey);
1363 sfree(svalue);
1365 /* Free full map name */
1366 sfree(str);
1368 return (statP);
1371 /* Write to the LDAP server */
1372 for (collapse = 0, i = 0; i < nr; i++) {
1373 if ((dn = findVal("dn", &frv[i], mit_ldap)) != 0) {
1374 if (replace == FALSE) {
1375 /* ldap add */
1376 rc = ldapAdd(dn, &frv[i],
1377 t->objectDN->write.attrs, 0);
1378 } else {
1379 /* ldap modify with addFirst set */
1380 rc = ldapModify(dn, &frv[i],
1381 t->objectDN->write.attrs, 1);
1384 /* if we get err=20, collapse and try again */
1385 if (!collapse &&
1386 (rc == LDAP_TYPE_OR_VALUE_EXISTS) &&
1387 (collapseRuleValue(&frv[i]) == 1)) {
1388 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1389 "%s: Ignoring values differing "
1390 "in case from NIS data (key=%s,"
1391 " value=%s) for (dn: %s) for "
1392 "%s (%s)", myself, skey,
1393 svalue, dn, t->dbId, map);
1394 collapse = 1;
1395 i--;
1396 continue;
1399 collapse = 0;
1400 if (rc != LDAP_SUCCESS) {
1401 /* Log error */
1402 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1403 "%s: %s error %d (%s) for "
1404 "(dn: %s) for NIS data "
1405 "(key=%s, value=%s) "
1406 "for %s (%s)",
1407 myself, (replace == TRUE) ?
1408 "ldapModify" : "ldapAdd", rc,
1409 ldap_err2string(rc), dn, skey,
1410 svalue, t->dbId, map);
1412 /* Dumping failed call may be useful */
1413 /* printRuleValue(&frv[i]); */
1416 * Return the error code and let wrapper
1417 * sort out if mapping should continue
1418 * or abort.
1420 statP = rc;
1421 sfree(skey);
1422 sfree(svalue);
1423 freeRuleValue(frv, nr);
1425 /* Free full map name */
1426 sfree(str);
1428 return (statP);
1433 freeRuleValue(frv, nr);
1436 sfree(skey);
1437 sfree(svalue);
1439 /* Free full map name */
1440 sfree(str);
1442 return ((flag)?SUCCESS:MAP_WRITE_DISABLED);
1445 suc_code
1446 collapseRuleValue(__nis_rule_value_t *rv) {
1447 int i, j, k, flag;
1449 /* Using 'val' to appease cstyle's 80 chars/line limit */
1450 __nis_value_t *val;
1452 for (i = 0, flag = 0; i < rv->numAttrs; i++) {
1453 val = &rv->attrVal[i];
1454 for (j = 1; j < val->numVals; j++) {
1455 for (k = 0; k < j; k++) {
1456 if (val->val[j].length != val->val[k].length)
1457 continue;
1458 if (val->val[k].length == 0)
1459 continue;
1460 if (strncasecmp(val->val[j].value,
1461 val->val[k].value,
1462 val->val[j].length) != 0)
1463 continue;
1464 flag = 1;
1465 sfree(val->val[j].value);
1467 #ifdef ORDER_NOT_IMPORTANT
1468 val->val[j--] = val->val[--val->numVals];
1469 #else
1470 /* Order needs to be maintained */
1471 for (k = j + 1; k < val->numVals; k++)
1472 val->val[k - 1] = val->val[k];
1473 j--;
1474 val->numVals--;
1475 #endif
1476 break;
1480 return (flag);
1483 /* ObjectClass lookup table */
1484 static struct {
1485 const char *attrType;
1486 const char *objectClass;
1487 } oc_lookup[] = {
1488 { "o", "objectclass=organization"},
1489 { "organizationname", "objectclass=organization"},
1490 { "2.5.4.10", "objectclass=organization"},
1491 { "ou", "objectclass=organizationalunit"},
1492 { "organizationalunitname", "objectclass=organizationalunit"},
1493 { "2.5.4.11", "objectclass=organizationalunit"},
1494 { "c", "objectclass=country"},
1495 { "countryname", "objectclass=country"},
1496 { "2.5.4.6", "objectclass=country"},
1497 { "dc", "objectclass=domain"},
1498 { "domaincomponent", "objectclass=domain"},
1499 { "0.9.2342.19200300.100.1.25", "objectclass=domain"},
1500 { "nismapname", "objectclass=nismap"},
1501 { "1.3.6.1.1.1.1.26", "objectclass=nismap"},
1502 { "automountmapname", "objectclass=automountmap"},
1503 { "1.3.6.1.1.1.1.31", "objectclass=automountmap"},
1504 { 0, 0}
1508 * Returns the name of the objectclass to which the object
1509 * represented by the given 'rdn' will most likely belong to.
1510 * The return value is in static memory so it should not be
1511 * freed
1513 const char *
1514 getObjectClass(char *rdn) {
1516 char *attrtype, *p;
1517 int len, i;
1519 /* Skip leading whitespaces */
1520 for (p = rdn; *p == ' ' || *p == '\t'; p++);
1521 if (*p == '\0')
1522 return (0);
1523 attrtype = p;
1525 /* Find '=' */
1526 if ((p = strchr(attrtype, '=')) == 0 || p == attrtype ||
1527 *(p - 1) == '\\')
1528 return (0);
1531 * Skip trailing whitespaces in attrtype
1532 * Don't worry, p won't decrease beyond attrtype
1534 for (--p; *p == ' ' || *p == '\t'; p--);
1535 len = p - attrtype + 1;
1537 for (i = 0; oc_lookup[i].attrType; i++)
1538 if (!strncasecmp(oc_lookup[i].attrType, attrtype, len))
1539 /* Check length is right */
1540 if (len == strlen(oc_lookup[i].attrType))
1541 return (oc_lookup[i].objectClass);
1543 return (0);
1547 * Split 'dn' into rdn and parentdn based on the first
1548 * occurrence of unescaped 'comma' or 'semicolon'. rdn
1549 * lies on the LHS while parentdn lies on the RHS of the
1550 * split. If none found, then an empty string ("") is
1551 * assigned to parentdn
1554 splitDN(char *dn, char **rdn, char **parentdn) {
1555 char *value, *name;
1556 char *myself = "splitDN";
1558 if ((name = sdup(myself, T, dn)) == 0)
1559 return (-1);
1561 for (value = name; *value != '\0'; value++) {
1562 if (*value == ',' || *value == ';')
1563 if (value == name || *(value - 1) != '\\')
1564 break;
1567 if (*value != '\0') {
1568 *value = '\0';
1569 value++;
1570 } else
1571 value = 0;
1573 if (parentdn) {
1574 if ((*parentdn = sdup(myself, T, value)) == 0) {
1575 sfree(name);
1576 return (-1);
1579 if (rdn)
1580 *rdn = name;
1581 else
1582 sfree(name);
1584 return (1);
1588 * FUNCTION : makeNISObject()
1590 * DESCRIPTION: Sets up a nis Object in the DIT.
1592 * GIVEN :
1593 * Case 1: Both 'domain' and 'dn' are non-NULL
1594 * Create nisDomainObject with the given information
1595 * Case 2: Only 'domain' is non-NULL
1596 * Obtain the 'dn' from the nisLDAPdomainContext list
1597 * Create nisDomainObject with the above information
1598 * Case 3: Only 'dn' is non-NULL
1599 * Create an object with the 'dn'
1600 * Here we guess the objectclass attribute, based on
1601 * oc_lookup table
1602 * Case 4: Both 'domain' and 'dn' are NULL
1603 * Error
1605 * RETURNS : SUCCESS = It worked
1606 * FAILURE = There was a problem.
1608 suc_code
1609 makeNISObject(char *domain, char *dn) {
1610 __nis_rule_value_t *rv;
1611 __nis_ldap_search_t *ls;
1612 int i, rc, nr, add_rc;
1613 char *val;
1614 char *myself = "makeNISObject";
1616 if (!dn && !domain)
1617 return (FAILURE);
1620 * If only 'domain' name is provided, then
1621 * try to find dn from the nisLDAPdomainContext
1622 * list generated by the parser
1624 if (!dn) {
1625 for (i = 0; i < ypDomains.numDomains; i++) {
1626 if (ypDomains.domainLabels[i] == 0)
1627 continue;
1628 if (strcasecmp(domain, ypDomains.domainLabels[i])
1629 == 0) {
1630 dn = ypDomains.domains[i];
1631 break;
1634 if (!dn)
1635 return (FAILURE);
1639 * If only 'dn' is given, then it means that the
1640 * caller simply wants to a create an entry for
1641 * that 'dn'.
1643 * If 'domain' is given, then check if the 'dn'
1644 * has already been set up as a nis domain object.
1645 * If not, see if we can make it become one.
1647 if (domain) {
1649 * Check to see if the nis domain object has
1650 * already been set up
1652 ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0,
1653 "objectclass=*", 0, 0, 0);
1654 if (ls == 0) {
1655 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1656 "%s: Unable to create ldapSearch "
1657 "request for dn: %s", myself, dn);
1658 return (FAILURE);
1660 nr = -1;
1661 rv = ldapSearch(ls, &nr, 0, &rc);
1662 freeLdapSearch(ls);
1663 if (rc == LDAP_SUCCESS) {
1664 val = findVal("nisDomain", rv, mit_ldap);
1665 if (val != NULL) {
1667 * Yes, nis domain object found. Check
1668 * to see if the domain names match.
1669 * If so, we are done. If not, log
1670 * a warning message, and return SUCCESS.
1672 if (strcasecmp(val, domain) == 0) {
1673 freeRuleValue(rv, nr);
1674 return (SUCCESS);
1675 } else {
1676 logmsg(MSG_NOTIMECHECK,
1677 LOG_WARNING,
1678 "%s: Entry (dn: %s) already "
1679 "contains a nis domain name "
1680 "(%s). The domain name (%s) "
1681 "is not added.",
1682 myself, dn, val, domain);
1683 freeRuleValue(rv, nr);
1684 return (SUCCESS);
1686 } else {
1687 freeRuleValue(rv, nr);
1689 * Entry for the 'dn' exists, but it
1690 * is not a nis domain object yet.
1691 * Add the nisDoamin attribute and
1692 * the nisDomainObject objectclass to
1693 * the entry.
1695 if ((rv = initRuleValue(1, 0)) == 0)
1696 return (FAILURE);
1698 if (addSAttr2RuleValue("nisDomain",
1699 domain, rv) == -1) {
1700 freeRuleValue(rv, 1);
1701 return (FAILURE);
1703 rc = ldapModify(dn, rv,
1704 "objectclass=nisDomainObject",
1706 freeRuleValue(rv, 1);
1707 if (rc == LDAP_SUCCESS) {
1708 logmsg(MSG_NOTIMECHECK,
1709 LOG_INFO,
1710 "%s: entry (dn: %s) "
1711 "modified to be an "
1712 "nis domain object",
1713 myself, dn);
1714 return (SUCCESS);
1715 } else {
1716 logmsg(MSG_NOTIMECHECK,
1717 LOG_ERR,
1718 "%s: unable to modify "
1719 "entry (dn: %s) to be "
1720 "a nis domain object: "
1721 "ldapModify error %d (%s)",
1722 myself, dn, rc,
1723 ldap_err2string(rc));
1724 return (FAILURE);
1727 } else { /* search for 'dn' failed */
1728 freeRuleValue(rv, nr);
1731 * It is OK if no such object, otherwise
1732 * log an error.
1734 if (rc != LDAP_NO_SUCH_OBJECT) {
1735 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1736 "%s: unable to retrieve "
1737 "entry (dn: %s): "
1738 "ldapSearch error %d (%s)",
1739 myself, dn, rc,
1740 ldap_err2string(rc));
1741 return (FAILURE);
1746 * If the 'dn' is actually the naming context of
1747 * the DIT, we should be able to make it a nis domain
1748 * object without worrying about missing parent
1749 * entries. If unable to add the entry for the 'dn'
1750 * due to missing parent entries, fall through
1751 * to create them and then add the nis domain object.
1753 if (addNISObject(domain, dn, &add_rc) == SUCCESS)
1754 return (SUCCESS);
1755 else if (add_rc != LDAP_NO_SUCH_OBJECT)
1756 return (FAILURE);
1759 /* Create parent */
1760 if (addParent(dn, NULL) == FAILURE)
1761 return (FAILURE);
1763 if (addNISObject(domain, dn, NULL) == FAILURE)
1764 return (FAILURE);
1766 return (SUCCESS);
1769 suc_code
1770 addParent(char *dn, char **attr) {
1771 __nis_rule_value_t *rv;
1772 __nis_ldap_search_t *ls;
1773 int rc, nr;
1774 char *parentdn = 0, *rdn = 0;
1775 char *myself = "addParent";
1777 /* Obtain parentdn */
1778 if (splitDN(dn, &rdn, &parentdn) == -1)
1779 return (FAILURE);
1780 if (!parentdn) {
1781 sfree(rdn);
1782 return (FAILURE);
1785 /* Check if parentdn exists */
1786 ls = buildLdapSearch(parentdn, LDAP_SCOPE_BASE, 0, 0,
1787 "objectclass=*", 0, 0, 0);
1788 if (ls == 0) {
1789 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1790 "%s: Unable to create ldapSearch request for "
1791 "parent (dn: %s) of (dn: %s)",
1792 myself, parentdn, dn);
1793 sfree(parentdn);
1794 sfree(rdn);
1795 return (FAILURE);
1797 nr = -1;
1798 rv = ldapSearch(ls, &nr, 0, &rc);
1799 freeLdapSearch(ls);
1800 freeRuleValue(rv, nr);
1802 /* Create parent if it doesn't exists */
1803 if (rc == LDAP_NO_SUCH_OBJECT) {
1804 if (makeNISObject(0, parentdn) == FAILURE) {
1805 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1806 "%s: Unable to create parent (dn: %s) of "
1807 "(dn: %s) in the DIT", myself, parentdn, dn);
1808 sfree(parentdn);
1809 sfree(rdn);
1810 return (FAILURE);
1813 sfree(parentdn);
1815 if (attr && rdn)
1816 *attr = (char *)getObjectClass(rdn);
1817 sfree(rdn);
1819 return (SUCCESS);
1825 * FUNCTION : is_fatal_error()
1827 * DESCRIPTION: Works out if a failed mapping operation should be retried.
1829 * INPUTS : Result code from operation
1831 * OUTPUTS : TRUE = Fatal error, don't retry.
1832 * FALSE = Temporary error, retry.
1834 bool_t
1835 is_fatal_error(int res)
1838 if (0 > res)
1839 /* An internal mapping error. Not going to go away. */
1840 return (TRUE);
1842 switch (res) {
1843 case (LDAP_PROTOCOL_ERROR):
1844 case (LDAP_TIMELIMIT_EXCEEDED):
1845 case (LDAP_PARTIAL_RESULTS):
1846 case (LDAP_BUSY):
1847 case (LDAP_UNAVAILABLE):
1848 case (LDAP_UNWILLING_TO_PERFORM):
1849 case (LDAP_OTHER):
1850 case (LDAP_SERVER_DOWN):
1851 case (LDAP_LOCAL_ERROR):
1852 case (LDAP_TIMEOUT):
1853 case (LDAP_NO_MEMORY):
1854 /* Probably worth a retry */
1855 return (FALSE);
1857 default:
1858 return (TRUE);
1863 * FUNCTION : addNISObject()
1865 * DESCRIPTION: Add a nis Object in the DIT.
1867 * GIVEN :
1868 * Case 1: 'dn' is NULL
1869 * Error
1870 * Case 2: 'domain' is non-NULL
1871 * Create nisDomainObject with the given information
1872 * Case 3: 'domain' is NULL
1873 * Create an object with the 'dn'
1874 * Here we guess the objectclass attribute, based on
1875 * oc_lookup table
1877 * RETURNS : SUCCESS = It worked
1878 * FAILURE = There was a problem. If the ldap add
1879 * operation failed, ldap_rc will be set
1880 * to the ldap error code.
1882 suc_code
1883 addNISObject(char *domain, char *dn, int *ldap_rc) {
1884 __nis_rule_value_t *rv;
1885 int rc;
1886 char *objClassAttrs = NULL, *attrs;
1887 char *value, *svalue, *rdn = NULL;
1888 char *myself = "addNISObject";
1890 if (!dn)
1891 return (FAILURE);
1893 if ((rv = initRuleValue(1, 0)) == 0)
1894 return (FAILURE);
1896 if (ldap_rc)
1897 *ldap_rc = -1;
1900 * Add name=value pairs from RDN. Although this is not required
1901 * for SunOne Directory Server, during openldap interoperabilty
1902 * tests, it was found out that openldap server returned object
1903 * class violation errors if MUST attributes were not specified
1904 * explicitly.
1906 if (splitDN(dn, &rdn, 0) == -1)
1907 return (FAILURE);
1908 if (rdn != NULL) {
1909 objClassAttrs = (char *)getObjectClass(rdn);
1910 if (objClassAttrs == NULL) {
1911 sfree(rdn);
1912 return (FAILURE);
1916 * RDN can be composed of multiple name=value pairs
1917 * concatenated by '+'. Hence, we need to determine each
1918 * pair and add it to 'rv'
1920 for (value = rdn, svalue = NULL; *value != '\0'; value++) {
1921 if (*value == '+') {
1922 /* Make sure it's not escaped */
1923 if (value == rdn || *(value - 1) != '\\') {
1925 * We are at the start of the new
1926 * pair. 'svalue' now contains the
1927 * value for the previous pair. Add
1928 * the previous pair to 'rv'
1930 *value = '\0';
1931 if (svalue &&
1932 addSAttr2RuleValue(rdn, svalue, rv)
1933 == -1) {
1934 sfree(rdn);
1935 freeRuleValue(rv, 1);
1936 return (FAILURE);
1938 svalue = NULL;
1939 rdn = value + 1;
1940 continue;
1944 if (*value == '=') {
1945 if (value == rdn || *(value - 1) != '\\') {
1947 * 'rdn' now contains the name.
1948 * Whatever follows till the next
1949 * unescaped '+' or '\0' is the
1950 * value for this pair.
1952 *value = '\0';
1953 svalue = value + 1;
1954 continue;
1960 * End of String. Add the previous name=value pair to 'rv'
1962 if (svalue && addSAttr2RuleValue(rdn, svalue, rv) == -1) {
1963 sfree(rdn);
1964 freeRuleValue(rv, 1);
1965 return (FAILURE);
1967 sfree(rdn);
1968 } else /* rdn == NULL */
1969 return (FAILURE);
1971 /* Create the entry */
1972 if (domain) {
1973 if (addSAttr2RuleValue("nisDomain", domain, rv) == -1) {
1974 freeRuleValue(rv, 1);
1975 return (FAILURE);
1977 attrs = scat(myself, F, "objectclass=nisdomainobject,",
1978 objClassAttrs);
1979 if (!attrs) {
1980 freeRuleValue(rv, 1);
1981 return (FAILURE);
1983 rc = ldapAdd(dn, rv, attrs, 0);
1984 sfree(attrs);
1985 } else {
1986 rc = ldapAdd(dn, rv, objClassAttrs, 0);
1989 if (rc == LDAP_SUCCESS)
1990 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1991 "%s: Entry (dn: %s) added to DIT",
1992 myself, dn);
1993 else if (rc == LDAP_ALREADY_EXISTS)
1994 /* Treat this as success */
1995 rc = LDAP_SUCCESS;
1996 else
1997 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1998 "%s: ldapAdd error %d (%s) for (dn: %s)",
1999 myself, rc, ldap_err2string(rc), dn);
2001 freeRuleValue(rv, 1);
2002 if (ldap_rc)
2003 *ldap_rc = rc;
2004 return ((rc == LDAP_SUCCESS)?SUCCESS:FAILURE);