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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2015 Gary Mills
23 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * DESCRIPTION: Contains dit_access interface support functions.
28 */
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"
55
56 #define YPMULTI "YP_MULTI_"
57 #define YPMULTISZ 9 /* == strlen(YPMULTI) */
58
59 /*
60 * Returns 'map,domain.'
61 */
62 char *
getFullMapName(char * map,char * domain)63 getFullMapName(char *map, char *domain) {
64 char *myself = "getFullMapName";
65 char *objPath;
66 if (map == 0 || domain == 0) {
67 return (0);
68 }
69 objPath = scat(myself, T, scat(myself, F, map, ","),
70 scat(myself, F, domain, "."));
71
72 return (objPath);
73 }
74
75 /*
76 * Convert string to __nis_value_t
77 */
stringToValue(char * dptr,int dsize)78 __nis_value_t *stringToValue(char *dptr, int dsize) {
79 char *myself = "stringToValue";
80 char *emptystr = "";
81 __nis_value_t *val;
82
83 if ((val = am(myself, sizeof (*val))) == 0) {
84 return (0);
85 }
86
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);
93 }
94
95 /*
96 * Null strings or strings with length 0 are treated
97 * as empty strings with length 1
98 */
99 if (dptr == 0 || dsize <= 0) {
100 dptr = emptystr;
101 dsize = 1;
102 }
103
104 val->val->length = dsize;
105 if (dptr[dsize - 1] != '\0') {
106 val->val->length = dsize + 1;
107 }
108
109 val->val->value = am(myself, val->val->length);
110 if (val->val->value == 0) {
111 freeValue(val, 1);
112 return (0);
113 }
114 (void) memcpy(val->val->value, dptr, dsize);
115
116 return (val);
117 }
118
119 /*
120 * Returns an array of rule-values corresponding to the
121 * splitfields.
122 */
123 __nis_rule_value_t *
processSplitField(__nis_table_mapping_t * sf,__nis_value_t * inVal,int * nv,int * statP)124 processSplitField(__nis_table_mapping_t *sf, __nis_value_t *inVal,
125 int *nv, int *statP) {
126
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;
132
133 /* sf will be non NULL */
134
135 if (inVal == 0 || inVal->type != vt_string) {
136 *statP = MAP_PARAM_ERROR;
137 return (0);
138 }
139
140 /* Get the separator list */
141 sepset = sf->separatorStr;
142
143 /* Initialize rule-value */
144 rvq = 0;
145 count = 0;
146
147 if ((tempVal = stringToValue(inVal->val->value,
148 inVal->val->length)) == 0) {
149 *statP = MAP_NO_MEMORY;
150 return (0);
151 }
152
153 str = oldstr = tempVal->val->value;
154 oldlen = tempVal->val->length;
155
156 while (str) {
157 tempVal->val->value = str;
158 tempVal->val->length = strlen(str) + 1;
159
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;
167 }
168
169 /*
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.
174 */
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);
185 }
186 count++;
187
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);
204 }
205 freeValue(valA[j], 1);
206 }
207 sfree(valA);
208
209 /*
210 * Since we had a match, break out of this loop
211 * to parse remainder of str
212 */
213 break;
214 }
215
216 /* Didn't find any match, so get out of the loop */
217 if (i > sf->numSplits) {
218 str = 0;
219 break;
220 }
221
222 /* Skip the separators before looping back */
223 if (str) {
224 str = str + strspn(str, sepset);
225 if (*str == '\0')
226 break;
227 }
228 }
229
230 tempVal->val->value = oldstr;
231 tempVal->val->length = oldlen;
232 freeValue(tempVal, 1);
233
234 if (str == 0) {
235 freeRuleValue(rvq, count);
236 return (0);
237 }
238
239 if (nv != 0)
240 *nv = count;
241
242 return (rvq);
243 }
244
245 /*
246 * Convert the datum to an array of RuleValues
247 */
248 __nis_rule_value_t *
datumToRuleValue(datum * key,datum * value,__nis_table_mapping_t * t,int * nv,char * domain,bool_t readonly,int * statP)249 datumToRuleValue(datum *key, datum *value, __nis_table_mapping_t *t,
250 int *nv, char *domain, bool_t readonly, int *statP) {
251
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;
259
260 /* At this point, 't' is always non NULL */
261
262 /* Initialize rule-value */
263 if ((rvq = initRuleValue(1, 0)) == 0) {
264 *statP = MAP_INTERNAL_ERROR;
265 return (0);
266 }
267
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);
274 }
275
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);
281
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);
286 }
287 if (i) {
288 freeRuleValue(rvq, 1);
289 *statP = MAP_INTERNAL_ERROR;
290 return (0);
291 }
292
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;
306 }
307
308 if (readonly == TRUE && i == 0) {
309 i = addCol2RuleValue(vt_string, N2LSEARCHIPKEY, ipvalue,
310 valueLen, rvq);
311 }
312 sfree(ipaddr);
313 if (i) {
314 freeRuleValue(rvq, 1);
315 *statP = MAP_INTERNAL_ERROR;
316 return (0);
317 }
318 }
319
320 /* Handle datum value */
321 if (value != 0 && t->e) {
322 valueLen = value->dsize;
323 /*
324 * Extract the comment, if any, and add it to
325 * the rule-value.
326 */
327 if (t->commentChar != '\0') {
328 /*
329 * We loop on value->dsize because value->dptr
330 * may not be NULL-terminated.
331 */
332 for (i = 0; i < value->dsize; i++) {
333 if (value->dptr[i] == t->commentChar) {
334 valueLen = i;
335 comLen = value->dsize - i - 1;
336 if (comLen == 0)
337 break;
338 if (addCol2RuleValue(vt_string,
339 N2LCOMMENT, value->dptr + i + 1,
340 comLen, rvq)) {
341 freeRuleValue(rvq, 1);
342 *statP = MAP_INTERNAL_ERROR;
343 return (0);
344 }
345 break;
346 }
347 }
348 }
349
350 /* Skip trailing whitespaces */
351 for (; valueLen > 0 && (value->dptr[valueLen - 1] == ' ' ||
352 value->dptr[valueLen - 1] == '\t'); valueLen--);
353
354 /*
355 * At this point valueLen is the effective length of
356 * the data. Convert value into __nis_value_t so that
357 * we can use the matchMappingItem function to break it
358 * into fields.
359 */
360 if ((val = stringToValue(value->dptr, valueLen)) == 0) {
361 freeRuleValue(rvq, 1);
362 *statP = MAP_NO_MEMORY;
363 return (0);
364 }
365
366 /* Perform namefield match */
367 valA = matchMappingItem(t->e->element.match.fmt, val,
368 &numVals, 0, 0);
369 if (valA == 0) {
370 freeValue(val, 1);
371 freeRuleValue(rvq, 1);
372 *statP = MAP_NAMEFIELD_MATCH_ERROR;
373 return (0);
374 }
375
376 /* We don't need val anymore, so free it */
377 freeValue(val, 1);
378
379 /*
380 * Since matchMappingItem only returns us an array of
381 * __nis_value_t's, we need to associate each value
382 * in the array with the corresponding item name.
383 * This code assumes that numVals will be less than or
384 * equal to the number of item names associated with
385 * the format.
386 * These name=value pairs are added to rvq.
387 */
388 for (i = 0, *statP = SUCCESS; i < numVals; i++) {
389 for (j = 0; j < count; j++) {
390 if (addCol2RuleValue(vt_string,
391 t->e->element.match.item[i].name,
392 valA[i]->val->value,
393 valA[i]->val->length, &rvq[j])) {
394 *statP = MAP_INTERNAL_ERROR;
395 break;
396 }
397 }
398 if (*statP == MAP_INTERNAL_ERROR)
399 break;
400
401 /*
402 * Check if splitField exists for the field.
403 * Since splitfields are also stored as mapping
404 * structures, we need to get the hash table entry
405 * corresponding to the splitfield name
406 */
407 sf = mappingFromMap(t->e->element.match.item[i].name,
408 domain, statP);
409 if (*statP == MAP_NO_MEMORY)
410 break;
411 *statP = SUCCESS;
412 if (sf == 0)
413 continue;
414
415 /*
416 * Process and add splitFields to rule-value rvq
417 */
418 subrvq = processSplitField(sf, valA[i], &nr, statP);
419
420 if (subrvq == 0) {
421 /* statP would have been set */
422 break;
423 }
424
425 /*
426 * We merge 'count' rule-values in rvq with 'nr'
427 * rule-values from subrvq to give us a whopping
428 * 'count * nr' rule-values
429 */
430
431 /* Initialize the new rule-value array */
432 if ((newrvq = initRuleValue(count * nr, 0)) == 0) {
433 *statP = MAP_INTERNAL_ERROR;
434 freeRuleValue(subrvq, nr);
435 break;
436 }
437
438 for (j = 0, l = 0; j < nr; j++) {
439 for (k = 0; k < count; k++, l++) {
440 if ((mergeRuleValue(&newrvq[l],
441 &rvq[k]) == -1) ||
442 (mergeRuleValue(
443 &newrvq[l],
444 &subrvq[j]) == -1)) {
445 *statP = MAP_INTERNAL_ERROR;
446 for (i = 0; i < numVals; i++)
447 freeValue(valA[i], 1);
448 sfree(valA);
449 freeRuleValue(rvq, count);
450 freeRuleValue(newrvq,
451 count * nr);
452 freeRuleValue(subrvq, nr);
453 return (0);
454 }
455 }
456 }
457
458 freeRuleValue(rvq, count);
459 rvq = newrvq;
460 count = l;
461 freeRuleValue(subrvq, nr);
462
463 }
464
465 /* We don't need valA anymore, so free it */
466 for (i = 0; i < numVals; i++)
467 freeValue(valA[i], 1);
468 sfree(valA);
469
470 if (*statP != SUCCESS) {
471 freeRuleValue(rvq, count);
472 return (0);
473 }
474
475 } /* if value */
476
477 if (nv != 0)
478 *nv = count;
479 return (rvq);
480
481 }
482
483 /*
484 * Generate name=values pairs for splitfield names
485 *
486 * Consider Example:
487 * nisLDAPnameFields club:
488 * ("%s %s %s", name, code, members)
489 * nisLDAPsplitField members:
490 * ("(%s,%s,%s)", host, user, domain),
491 * ("%s", group)
492 * On entry,
493 * - rv is an array of numVals rule-values each containing
494 * name=value pairs for names occuring in nisLDAPsplitField.
495 * (i.e host, user, domain, group)
496 * - trv contains name=value pairs for names occuring in
497 * nisLDAPnameFields. (i.e name, code but not members)
498 *
499 * For every name in nisLDAPnamefields that is a splitfield,
500 * this function applies the data in rv to the corresponding
501 * splitfield formats (accessed thru t), to generate a single
502 * string value for the corresponding splitfield (members).
503 * This new name=value pair is then added to trv.
504 * Besides, any uninitialized namefield names are set to empty strings.
505 */
506 suc_code
addSplitFieldValues(__nis_table_mapping_t * t,__nis_rule_value_t * rv,__nis_rule_value_t * trv,int numVals,char * domain)507 addSplitFieldValues(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
508 __nis_rule_value_t *trv, int numVals, char *domain) {
509 __nis_table_mapping_t *sf;
510 __nis_value_t *val;
511 int i, j, k, nitems, res, statP;
512 char *str, *tempstr;
513 char delim[2] = {0, 0};
514 char *emptystr = "";
515 char *myself = "addSplitFieldValues";
516
517 if (trv == 0)
518 return (MAP_INTERNAL_ERROR);
519
520 if (t->e == 0)
521 return (SUCCESS);
522
523 nitems = t->e->element.match.numItems;
524
525 /*
526 * Procedure:
527 * - Check each name in nisLDAPnamefield
528 * - if it's a splifield, construct its value and add it to trv
529 * - if not, check if it has a value
530 * - if not, add empty string
531 */
532 for (i = 0, sf = 0; i < nitems; i++) {
533 if (rv) {
534 /*
535 * str will eventually contain the single string
536 * value for the corresponding splitfield.
537 * No point initializing str if rv == 0 because
538 * splitfield cannot be constructed without rv.
539 * So, only initialized here.
540 */
541 str = 0;
542
543 /* Check if it's a splitfield name */
544 sf = mappingFromMap(t->e->element.match.item[i].name,
545 domain, &statP);
546
547 /*
548 * Return only incase of memory allocation failure.
549 * The other error case (MAP_NO_MAPPING_EXISTS),
550 * indicates that the item name is not a splitfieldname
551 * i.e it's a namefieldname. This case is handled by
552 * the following if (sf == 0)
553 */
554 if (statP == MAP_NO_MEMORY)
555 return (statP);
556 }
557
558 if (sf == 0) {
559 /*
560 * Not a splitfield name. Verify if it has a value
561 */
562 if (findVal(t->e->element.match.item[i].name,
563 trv, mit_nisplus) == 0) {
564 /* if not, use empty string */
565 res = addCol2RuleValue(vt_string,
566 t->e->element.match.item[i].name,
567 emptystr, 0, trv);
568 if (res == -1) {
569 return (MAP_INTERNAL_ERROR);
570 }
571 }
572 /*
573 * If rv == 0 then sf == 0 so we will continue here
574 * i.e. does not matter that str is not yet set up.
575 */
576 continue;
577 }
578
579 /* Code to construct a single value */
580
581 /* Use the first separator character as the delimiter */
582 delim[0] = sf->separatorStr[0];
583
584 for (j = 0; j < numVals; j++) {
585 /* sf->numSplits is zero-based */
586 for (k = 0; k <= sf->numSplits; k++) {
587 val = getMappingFormatArray(
588 sf->e[k].element.match.fmt, &rv[j],
589 fa_item,
590 sf->e[k].element.match.numItems,
591 sf->e[k].element.match.item);
592 if (val == 0)
593 continue;
594 if (val->numVals > 0) {
595 if (str) {
596 tempstr = scat(myself,
597 0, str, delim);
598 sfree(str);
599 if (tempstr)
600 str = tempstr;
601 else {
602 freeValue(val, 1);
603 return (MAP_NO_MEMORY);
604 }
605 }
606 tempstr = scat(myself, 0, str,
607 val->val->value);
608 sfree(str);
609 if (tempstr)
610 str = tempstr;
611 else {
612 freeValue(val, 1);
613 return (MAP_NO_MEMORY);
614 }
615 }
616 freeValue(val, 1);
617 }
618 }
619 if (str == 0)
620 str = emptystr;
621
622 res = addCol2RuleValue(vt_string,
623 t->e->element.match.item[i].name,
624 str, strlen(str), trv);
625
626 if (str != emptystr)
627 sfree(str);
628
629 if (res == -1) {
630 return (MAP_INTERNAL_ERROR);
631 }
632 }
633
634 return (SUCCESS);
635 }
636
637 /*
638 * Updates 'rv' with NIS name=value pairs suitable to
639 * construct datum from namefield information.
640 * Some part based on createNisPlusEntry (from ldap_nisdbquery.c)
641 * This code assumes that from a given LDAP entry, applying the
642 * mapping rules, would give us one or more NIS entries, differing
643 * only in key.
644 */
645 suc_code
buildNISRuleValue(__nis_table_mapping_t * t,__nis_rule_value_t * rv,char * domain)646 buildNISRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
647 char *domain) {
648 int r, i, j, k, l, nrq, res, len;
649 int numItems, splitname, count, statP;
650 __nis_value_t *rval;
651 __nis_mapping_item_t *litem;
652 __nis_mapping_rule_t *rl;
653 __nis_rule_value_t *rvq;
654 char *value, *emptystr = "";
655
656 statP = SUCCESS;
657
658 /* Initialize default base */
659 __nisdb_get_tsd()->searchBase = t->objectDN->read.base;
660
661 /* Initialize rule-value rvq */
662 rvq = 0;
663 count = 0;
664
665 /* Add domainname to rule-value */
666 if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
667 rv)) {
668 return (MAP_INTERNAL_ERROR);
669 }
670
671 for (r = 0; r < t->numRulesFromLDAP; r++) {
672 rl = t->ruleFromLDAP[r];
673
674 /* Set escapeFlag if RHS is "dn" to remove escape chars */
675 if (rl->rhs.numElements == 1 &&
676 rl->rhs.element->type == me_item &&
677 rl->rhs.element->element.item.type == mit_ldap &&
678 strcasecmp(rl->rhs.element->element.item.name, "dn")
679 == 0) {
680 __nisdb_get_tsd()->escapeFlag = '2';
681 }
682
683 rval = buildRvalue(&rl->rhs, mit_ldap, rv, NULL);
684
685 /* Reset escapeFlag */
686 __nisdb_get_tsd()->escapeFlag = '\0';
687
688 if (rval == 0) {
689 continue;
690 }
691
692 if (rval->numVals <= 0) {
693 /* Treat as invalid */
694 freeValue(rval, 1);
695 continue;
696 }
697
698 litem = buildLvalue(&rl->lhs, &rval, &numItems);
699 if (litem == 0) {
700 /* This will take care of numItems == 0 */
701 freeValue(rval, 1);
702 continue;
703 }
704
705 if (rval->numVals > 1) {
706 if (numItems == 1 && litem->repeat)
707 nrq = rval->numVals;
708 else if (numItems > 1 && rval->repeat)
709 nrq = 1 + ((rval->numVals-1)/numItems);
710 else
711 nrq = 1;
712 } else
713 nrq = 1;
714
715 /* Set splitname if splitfield names are specified */
716 for (i = 0; i < numItems; i++) {
717 if (strcasecmp(litem[i].name, N2LKEY) == 0 ||
718 strcasecmp(litem[i].name, N2LIPKEY) == 0 ||
719 strcasecmp(litem[i].name, N2LCOMMENT) == 0)
720 continue;
721 for (j = 0; j < t->numColumns; j++) {
722 if (strcmp(litem[i].name, t->column[j]) == 0)
723 break;
724 }
725 if (j == t->numColumns)
726 break;
727 }
728
729 splitname = (i < numItems)?1:0;
730
731 for (j = 0; j < nrq; j++) {
732 if (splitname == 1) {
733 /*
734 * Put every value of splitfieldname in a new
735 * rule-value. Helps generating splitfields.
736 */
737 rvq = growRuleValue(count, count + 1, rvq, 0);
738 if (rvq == 0) {
739 freeRuleValue(rvq, count);
740 freeValue(rval, 1);
741 freeMappingItem(litem, numItems);
742 return (MAP_INTERNAL_ERROR);
743 }
744 count++;
745 }
746
747 for (k = j % nrq, l = 0; l < numItems; k += nrq, l++) {
748 /* If we run out of values, use empty strings */
749 if (k >= rval->numVals) {
750 value = emptystr;
751 len = 0;
752 } else {
753 value = rval->val[k].value;
754 len = rval->val[k].length;
755 }
756 res = (splitname == 1)?addCol2RuleValue(
757 vt_string, litem[l].name, value,
758 len, &rvq[count - 1]):0;
759 if (res != -1)
760 res = addCol2RuleValue(vt_string,
761 litem[l].name, value, len, rv);
762 if (res == -1) {
763 freeRuleValue(rvq, count);
764 freeValue(rval, 1);
765 freeMappingItem(litem, numItems);
766 return (MAP_INTERNAL_ERROR);
767 }
768 }
769 }
770 freeValue(rval, 1);
771 rval = 0;
772 freeMappingItem(litem, numItems);
773 litem = 0;
774 numItems = 0;
775 } /* for r < t->numRulesFromLDAP */
776
777 statP = addSplitFieldValues(t, rvq, rv, count, domain);
778
779 if (rvq)
780 freeRuleValue(rvq, count);
781
782 if (verifyIndexMatch(t, 0, rv, 0, 0) == 0)
783 return (MAP_INDEXLIST_ERROR);
784 return (statP);
785
786 } /* end of buildNISRuleValue */
787
788 /*
789 * Convert rule-value to datum using namefield information
790 */
791 datum *
ruleValueToDatum(__nis_table_mapping_t * t,__nis_rule_value_t * rv,int * statP)792 ruleValueToDatum(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *statP) {
793 __nis_value_t *val;
794 datum *value;
795 char *str, *cstr, commentSep[3] = {' ', 0, 0};
796 char *myself = "ruleValueToDatum";
797
798 /* No error yet */
799 *statP = 0;
800
801 /* Return empty datum if no namefield information available */
802 if (t->e == 0) {
803 if ((value = am(myself, sizeof (*value))) == 0)
804 *statP = MAP_NO_MEMORY;
805 return (value);
806 }
807
808 val = getMappingFormatArray(t->e->element.match.fmt, rv,
809 fa_item, t->e->element.match.numItems,
810 t->e->element.match.item);
811
812 if (val && val->val && val->val->value) {
813 if ((value = am(myself, sizeof (*value))) == 0) {
814 *statP = MAP_NO_MEMORY;
815 freeValue(val, 1);
816 return (0);
817 }
818
819 /* Strip trailing whitespaces */
820 cstr = (char *)val->val->value + val->val->length;
821 for (; cstr >= (char *)val->val->value &&
822 (*cstr == ' ' || *cstr == '\t'); *cstr-- = '\0');
823
824 if (t->commentChar != '\0' &&
825 (str = findVal(N2LCOMMENT, rv, mit_nisplus)) != 0 &&
826 *str != '\0') {
827 commentSep[1] = t->commentChar;
828 cstr = scat(myself, F, commentSep, str);
829 if (cstr) {
830 value->dptr = scat(myself, F,
831 val->val->value, cstr);
832 sfree(cstr);
833 }
834 } else {
835 value->dptr = sdup(myself, T, val->val->value);
836 }
837 freeValue(val, 1);
838 if (value->dptr) {
839 value->dsize = strlen(value->dptr);
840 return (value);
841 } else {
842 *statP = MAP_NO_MEMORY;
843 sfree(value);
844 return (0);
845 }
846 }
847
848 *statP = MAP_NAMEFIELD_MATCH_ERROR;
849 return (0);
850 }
851
852 datum *
getKeyFromRuleValue(__nis_table_mapping_t * t,__nis_rule_value_t * rv,int * nv,int * statP,bool_t xlate_to_lcase)853 getKeyFromRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *nv,
854 int *statP, bool_t xlate_to_lcase)
855 {
856 int i, j, k;
857 datum *key = 0;
858 char *str;
859 char *myself = "getKeyFromRuleValue";
860
861 /* No error yet */
862 *statP = 0;
863
864 if (rv == 0 || nv == 0)
865 return (0);
866
867 for (i = 0; i < rv->numColumns; i++) {
868 if (rv->colName[i] == 0)
869 continue;
870 if (strcasecmp(N2LKEY, rv->colName[i]) == 0 ||
871 strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
872 if ((*nv = rv->colVal[i].numVals) == 0)
873 return (0);
874 if ((key = am(myself, sizeof (key[0]) * *nv)) == 0) {
875 *statP = MAP_NO_MEMORY;
876 return (0);
877 }
878 for (j = 0; j < *nv; j++) {
879 if ((str = rv->colVal[i].val[j].value) == 0) {
880 key[j].dsize = 0;
881 key[j].dptr = 0;
882 } else {
883 if (verifyIndexMatch(t, 0, 0,
884 rv->colName[i], str) == 0) {
885 key[j].dsize = 0;
886 key[j].dptr = 0;
887 continue;
888 }
889
890 key[j].dsize = strlen(str);
891 key[j].dptr = am(myself,
892 key[j].dsize + 1);
893 if (key[j].dptr == 0) {
894 *statP = MAP_NO_MEMORY;
895 for (--j; j >= 0; j--)
896 sfree(key[j].dptr);
897 sfree(key);
898 return (0);
899 }
900
901 /* transliterate key to lowercase */
902 if (xlate_to_lcase == TRUE) {
903
904 /*
905 * For multi-homed
906 * entries, skip over
907 * "YP_MULTI_" prefix.
908 */
909 k = 0;
910 if (strncmp(YPMULTI, str,
911 YPMULTISZ) == 0) {
912 k = YPMULTISZ;
913 bcopy(str, key[j].dptr,
914 YPMULTISZ);
915 }
916 while (k < key[j].dsize) {
917 key[j].dptr[k] =
918 (char)tolower(
919 (int)(uchar_t)
920 str[k]);
921 k++;
922 }
923 } else {
924 bcopy(str, key[j].dptr,
925 key[j].dsize);
926 }
927 }
928 }
929 return (key);
930 }
931 }
932 return (0);
933 }
934
935 /*
936 * Get the mapping structure corresponding to `map,domain.'
937 */
938 __nis_table_mapping_t *
mappingFromMap(char * map,char * domain,int * statP)939 mappingFromMap(char *map, char *domain, int *statP) {
940 char *mapPath;
941 __nis_table_mapping_t *t;
942
943 /* No error yet */
944 *statP = 0;
945
946 /* Construct map,domain. */
947 if ((mapPath = getFullMapName(map, domain)) == 0) {
948 *statP = MAP_NO_MEMORY;
949 return (0);
950 }
951
952 /* Get the hash table entry for the mapPath */
953 if ((t = __nis_find_item_mt(mapPath, &ldapMappingList, 1, 0))
954 == 0) {
955 *statP = MAP_NO_MAPPING_EXISTS;
956 }
957 sfree(mapPath);
958 return (t);
959 }
960
961 /*
962 * Verify at least one key value obtained from DIT matches the search key
963 * RETURNS: 1 MATCH
964 * 0 NO MATCH
965 * -1 NO KEY FOUND
966 */
967 static int
verifyKey(char * key,__nis_rule_value_t * rv)968 verifyKey(char *key, __nis_rule_value_t *rv) {
969 int i, j;
970 char *sipkey, *str;
971
972 for (i = 0; i < rv->numColumns; i++) {
973 if (rv->colName[i] == 0)
974 continue;
975 if (strcasecmp(N2LKEY, rv->colName[i]) == 0) {
976 if (rv->colVal[i].val == 0)
977 return (0);
978 for (j = 0; j < rv->colVal[i].numVals; j++) {
979 str = (char *)rv->colVal[i].val[j].value;
980 if (str && strcmp(str, key) == 0)
981 return (1);
982 }
983 return (0);
984 } else if (strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
985 if (checkIPaddress(key, strlen(key), &sipkey) > 0) {
986 if (rv->colVal[i].val == 0)
987 return (0);
988 for (j = 0; j < rv->colVal[i].numVals; j++) {
989 str = rv->colVal[i].val[j].value;
990 if (str && strcmp(str, sipkey) == 0) {
991 sfree(sipkey);
992 return (1);
993 }
994 }
995 sfree(sipkey);
996 }
997 return (0);
998 }
999 }
1000 return (-1);
1001 }
1002
1003 /*
1004 * Read (i.e get and map) a single NIS entry from the LDAP DIT
1005 */
1006 bool_t
singleReadFromDIT(char * map,char * domain,datum * key,datum * value,int * statP)1007 singleReadFromDIT(char *map, char *domain, datum *key, datum *value,
1008 int *statP) {
1009 __nis_table_mapping_t *t;
1010 __nis_rule_value_t *rv_request = 0, *rv_result = 0;
1011 __nis_ldap_search_t *ls;
1012 __nis_object_dn_t *objectDN = NULL;
1013 int i, rc, nr = 0;
1014 datum *datval = 0;
1015 char *skey, *str;
1016 char *myself = "singleReadFromDIT";
1017
1018 *statP = SUCCESS;
1019
1020 if (!map || !domain || !key || !value) {
1021 *statP = MAP_PARAM_ERROR;
1022 return (FALSE);
1023 }
1024
1025
1026 /* Get the mapping information for the map */
1027 if ((t = mappingFromMap(map, domain, statP)) == 0) {
1028 /*
1029 * No problem. We don't handle this map and domain. Maybe it's
1030 * handled by a service other than NIS.
1031 */
1032 return (FALSE);
1033 }
1034
1035 /* NULL-terminated version of datum key for logging */
1036 if ((skey = am(myself, key->dsize + 1)) == 0) {
1037 *statP = MAP_NO_MEMORY;
1038 return (FALSE);
1039 }
1040 (void) memcpy(skey, key->dptr, key->dsize);
1041
1042 if ((str = getFullMapName(map, domain)) == 0) {
1043 *statP = MAP_NO_MEMORY;
1044 return (FALSE);
1045 }
1046
1047 /* For each alternate mapping */
1048 for (; t != 0; t = t->next) {
1049 /* Verify objName */
1050 if (strcmp(str, t->objName) != 0) {
1051 continue;
1052 }
1053
1054 /* Verify if key matches the index */
1055 if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
1056 verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
1057 continue;
1058
1059 /* Check if rulesFromLDAP are provided */
1060 if (t->numRulesFromLDAP == 0) {
1061 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1062 "%s: No rulesFromLDAP information available "
1063 "for %s (%s)", myself, t->dbId, map);
1064 continue;
1065 }
1066
1067 /* Convert key into rule-value */
1068 if ((rv_request = datumToRuleValue(key, 0, t, 0, domain, TRUE,
1069 statP)) == 0) {
1070 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1071 "%s: Conversion error %d (NIS to name=value "
1072 "pairs) for NIS key (%s) for %s (%s)",
1073 myself, *statP, skey, t->dbId, map);
1074 continue;
1075 }
1076 /* Convert rule-value into ldap request */
1077 for (objectDN = t->objectDN; objectDN &&
1078 objectDN->read.base;
1079 objectDN = objectDN->next) {
1080 ls = createLdapRequest(t, rv_request, 0, 1, NULL,
1081 objectDN);
1082 if (ls == 0) {
1083 *statP = MAP_CREATE_LDAP_REQUEST_ERROR;
1084 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1085 "%s: Failed to create ldapSearch "
1086 "request for "
1087 "NIS key (%s) for %s (%s) "
1088 "for base %s",
1089 myself, skey, t->dbId, map,
1090 objectDN->read.base);
1091 continue;
1092 }
1093 ls->timeout.tv_sec = SINGLE_ACCESS_TIMEOUT_SEC;
1094 ls->timeout.tv_usec = SINGLE_ACCESS_TIMEOUT_USEC;
1095 /* Query LDAP */
1096 nr = (ls->isDN)?0:-1;
1097 rv_result = ldapSearch(ls, &nr, 0, statP);
1098 freeLdapSearch(ls);
1099 if (rv_result == 0) {
1100 if (*statP == LDAP_NO_SUCH_OBJECT) {
1101 /* Entry does not exist in */
1102 /* the ldap server */
1103 }
1104 continue;
1105 }
1106 freeRuleValue(rv_request, 1);
1107 rv_request = 0;
1108
1109 /* if result > 1, first match will be returned */
1110 if (nr > 1) {
1111 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1112 "%s: %d ldapSearch results "
1113 "for NIS key (%s) "
1114 "for %s (%s) for base %s. "
1115 "First match will be returned ",
1116 myself, nr, skey, t->dbId, map,
1117 objectDN->read.base);
1118 }
1119
1120 for (i = 0; i < nr; i++) {
1121 /* Convert LDAP data to NIS equivalents */
1122 *statP = buildNISRuleValue(t, &rv_result[i],
1123 domain);
1124 if (*statP == MAP_INDEXLIST_ERROR)
1125 continue;
1126
1127 if (*statP != SUCCESS) {
1128 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1129 "%s: Conversion error %d (LDAP to "
1130 "name=value pairs) for NIS key (%s) "
1131 "for %s (%s) for base %s", myself,
1132 *statP, skey,
1133 t->dbId, map, objectDN->read.base);
1134 continue;
1135 }
1136
1137 /*
1138 * Check if 'key' from the ldap result matches the key
1139 * provided by our caller
1140 */
1141 if ((rc = verifyKey(skey, &rv_result[i]))
1142 == -1) {
1143 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1144 "%s: Cannot verify key from ldap "
1145 "result for NIS key (%s) for %s (%s) "
1146 "for base %s",
1147 myself, skey, t->dbId, map,
1148 objectDN->read.base);
1149 continue;
1150 }
1151
1152 if (rc == 1) {
1153 datval = ruleValueToDatum(t,
1154 &rv_result[i], statP);
1155 if (datval == 0) {
1156 logmsg(MSG_NOTIMECHECK,
1157 LOG_WARNING,
1158 "%s: Conversion error %d "
1159 "(name=value pairs to NIS) "
1160 "for NIS key (%s) for %s (%s)"
1161 " for base %s",
1162 myself,
1163 *statP, skey, t->dbId, map,
1164 objectDN->read.base);
1165 continue;
1166 }
1167 if (value) {
1168 value->dptr = datval->dptr;
1169 value->dsize = datval->dsize;
1170 }
1171 sfree(datval);
1172 sfree(skey);
1173 freeRuleValue(rv_result, nr);
1174 rv_result = 0;
1175 *statP = SUCCESS;
1176
1177 /* Free full map name */
1178 sfree(str);
1179
1180 return (TRUE);
1181 }
1182 }
1183 freeRuleValue(rv_result, nr);
1184 rv_result = 0;
1185 } /* end of for over objectDN */
1186
1187 if (rv_request != 0) {
1188 freeRuleValue(rv_request, 1);
1189 rv_request = 0;
1190 }
1191 if (rv_result != 0) {
1192 freeRuleValue(rv_result, nr);
1193 rv_result = 0;
1194 }
1195 }
1196 sfree(skey);
1197 *statP = MAP_NO_MATCHING_KEY;
1198
1199 /* Free full map name */
1200 sfree(str);
1201
1202 return (FALSE);
1203 }
1204
1205
1206 /*
1207 * Maps and writes a single NIS entry to the LDAP DIT
1208 */
1209 int
singleWriteToDIT(char * map,char * domain,datum * key,datum * value,bool_t replace)1210 singleWriteToDIT(char *map, char *domain, datum *key, datum *value,
1211 bool_t replace) {
1212 __nis_table_mapping_t *t;
1213 __nis_rule_value_t *rv, *frv;
1214 __nis_ldap_search_t *ls;
1215 int statP = SUCCESS, flag;
1216 int nv, nr, i, rc, collapse;
1217 char *dn = 0, *skey, *svalue, *str;
1218 char *myself = "singleWriteToDIT";
1219
1220 if (!map || !domain || !key || !value) {
1221 return (MAP_PARAM_ERROR);
1222 }
1223
1224 /* Return SUCCESS for empty or whitespace key */
1225 for (i = 0; i < key->dsize && (key->dptr[i] == 0 ||
1226 key->dptr[i] == ' ' || key->dptr[i] == '\t'); i++);
1227 if (i >= key->dsize)
1228 return (SUCCESS);
1229
1230 /* Get the mapping information for the map */
1231 if ((t = mappingFromMap(map, domain, &statP)) == 0) {
1232 /*
1233 * No problem. We don't handle this map and domain. Maybe it's
1234 * handled by a service other than NIS.
1235 */
1236 return (statP);
1237 }
1238
1239 /* NULL-terminated version of key and value for logging */
1240 if ((skey = am(myself, key->dsize + 1)) == 0)
1241 return (MAP_NO_MEMORY);
1242 (void) memcpy(skey, key->dptr, key->dsize);
1243
1244 if ((svalue = am(myself, value->dsize + 1)) == 0) {
1245 sfree(skey);
1246 return (MAP_NO_MEMORY);
1247 }
1248 (void) memcpy(svalue, value->dptr, value->dsize);
1249
1250 if ((str = getFullMapName(map, domain)) == 0) {
1251 sfree(skey);
1252 sfree(svalue);
1253 return (MAP_NO_MEMORY);
1254 }
1255
1256 /* For each alternate mapping */
1257 for (flag = 0; t != 0; t = t->next) {
1258 /* Verify objName */
1259 if (strcmp(str, t->objName) != 0) {
1260 continue;
1261 }
1262
1263 /* Verify if key matches the index */
1264 if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
1265 verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
1266 continue;
1267
1268 /* Check the writespecs */
1269 if (t->objectDN->write.base == 0) {
1270 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1271 "%s: No baseDN in writespec. Write disabled "
1272 "for %s (%s)", myself, t->dbId, map);
1273 continue;
1274 }
1275
1276 /* Check if rulesToLDAP are provided */
1277 if (t->numRulesToLDAP == 0) {
1278 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1279 "%s: No rulesToLDAP. Write disabled for "
1280 "%s (%s)", myself, t->dbId, map);
1281 continue;
1282 }
1283
1284 /* Set flag to indicate write is enabled */
1285 flag = 1;
1286
1287 /* Convert key and value into an array of rule-values */
1288 if ((rv = datumToRuleValue(key, value, t, &nv, domain, FALSE,
1289 &statP)) == 0) {
1290 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1291 "%s: Conversion error %d (NIS to name=value "
1292 "pairs) for NIS data (key=%s, value=%s) "
1293 "for %s (%s)",
1294 myself, statP, skey, svalue, t->dbId, map);
1295 sfree(skey);
1296 sfree(svalue);
1297
1298 /* Free full map name */
1299 sfree(str);
1300
1301 return (statP);
1302 }
1303
1304 /* Convert NIS data to LDAP equivalents for each rule-value */
1305 for (i = 0; i < nv; i++) {
1306 /* Verify indexlist with name=value pairs */
1307 if (verifyIndexMatch(t, 0, &rv[i], 0, 0) == 0)
1308 break;
1309
1310 /* Create LDAP request and LDAP name=value pairs */
1311 if ((ls = createLdapRequest(t, &rv[i],
1312 0, 0, NULL, NULL)) == 0) {
1313 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1314 "%s: Conversion error (name=value pairs"
1315 " to LDAP) for NIS data "
1316 "(key=%s, value=%s) for %s (%s)",
1317 myself, skey, svalue, t->dbId, map);
1318 freeRuleValue(rv, nv);
1319 sfree(skey);
1320 sfree(svalue);
1321
1322 /* Free full map name */
1323 sfree(str);
1324
1325 return (MAP_CREATE_LDAP_REQUEST_ERROR);
1326 }
1327 freeLdapSearch(ls);
1328 /* printRuleValue(&rv[i]); */
1329 }
1330
1331 /* If i < nv then this alternate mapping isn't the one */
1332 if (i < nv)
1333 continue;
1334
1335 /*
1336 * Merge rule-values with the same DN so that we have
1337 * one ldap write request for each DN
1338 */
1339 nr = nv;
1340 frv = mergeRuleValueWithSameDN(rv, &nr);
1341 freeRuleValue(rv, nv);
1342 if (frv == 0) {
1343 if (nr == -1) {
1344 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1345 "%s: Unable to merge LDAP write "
1346 "requests to same DN for NIS data "
1347 "(key=%s, value=%s) for %s (%s)",
1348 myself, skey, svalue, t->dbId, map);
1349 statP = MAP_INTERNAL_ERROR;
1350 } else if (nr == 0) {
1351 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1352 "%s: Cannot generate write DN due to "
1353 "missing information for NIS data "
1354 "(key=%s, value=%s) for %s (%s)",
1355 myself, skey, svalue, t->dbId, map);
1356 statP = MAP_NO_DN;
1357 }
1358 sfree(skey);
1359 sfree(svalue);
1360
1361 /* Free full map name */
1362 sfree(str);
1363
1364 return (statP);
1365 }
1366
1367 /* Write to the LDAP server */
1368 for (collapse = 0, i = 0; i < nr; i++) {
1369 if ((dn = findVal("dn", &frv[i], mit_ldap)) != 0) {
1370 if (replace == FALSE) {
1371 /* ldap add */
1372 rc = ldapAdd(dn, &frv[i],
1373 t->objectDN->write.attrs, 0);
1374 } else {
1375 /* ldap modify with addFirst set */
1376 rc = ldapModify(dn, &frv[i],
1377 t->objectDN->write.attrs, 1);
1378 }
1379
1380 /* if we get err=20, collapse and try again */
1381 if (!collapse &&
1382 (rc == LDAP_TYPE_OR_VALUE_EXISTS) &&
1383 (collapseRuleValue(&frv[i]) == 1)) {
1384 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1385 "%s: Ignoring values differing "
1386 "in case from NIS data (key=%s,"
1387 " value=%s) for (dn: %s) for "
1388 "%s (%s)", myself, skey,
1389 svalue, dn, t->dbId, map);
1390 collapse = 1;
1391 i--;
1392 continue;
1393 }
1394
1395 collapse = 0;
1396 if (rc != LDAP_SUCCESS) {
1397 /* Log error */
1398 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1399 "%s: %s error %d (%s) for "
1400 "(dn: %s) for NIS data "
1401 "(key=%s, value=%s) "
1402 "for %s (%s)",
1403 myself, (replace == TRUE) ?
1404 "ldapModify" : "ldapAdd", rc,
1405 ldap_err2string(rc), dn, skey,
1406 svalue, t->dbId, map);
1407
1408 /* Dumping failed call may be useful */
1409 /* printRuleValue(&frv[i]); */
1410
1411 /*
1412 * Return the error code and let wrapper
1413 * sort out if mapping should continue
1414 * or abort.
1415 */
1416 statP = rc;
1417 sfree(skey);
1418 sfree(svalue);
1419 freeRuleValue(frv, nr);
1420
1421 /* Free full map name */
1422 sfree(str);
1423
1424 return (statP);
1425 }
1426 }
1427 }
1428
1429 freeRuleValue(frv, nr);
1430 }
1431
1432 sfree(skey);
1433 sfree(svalue);
1434
1435 /* Free full map name */
1436 sfree(str);
1437
1438 return ((flag)?SUCCESS:MAP_WRITE_DISABLED);
1439 }
1440
1441 suc_code
collapseRuleValue(__nis_rule_value_t * rv)1442 collapseRuleValue(__nis_rule_value_t *rv) {
1443 int i, j, k, flag;
1444
1445 /* Using 'val' to appease cstyle's 80 chars/line limit */
1446 __nis_value_t *val;
1447
1448 for (i = 0, flag = 0; i < rv->numAttrs; i++) {
1449 val = &rv->attrVal[i];
1450 for (j = 1; j < val->numVals; j++) {
1451 for (k = 0; k < j; k++) {
1452 if (val->val[j].length != val->val[k].length)
1453 continue;
1454 if (val->val[k].length == 0)
1455 continue;
1456 if (strncasecmp(val->val[j].value,
1457 val->val[k].value,
1458 val->val[j].length) != 0)
1459 continue;
1460 flag = 1;
1461 sfree(val->val[j].value);
1462
1463 #ifdef ORDER_NOT_IMPORTANT
1464 val->val[j--] = val->val[--val->numVals];
1465 #else
1466 /* Order needs to be maintained */
1467 for (k = j + 1; k < val->numVals; k++)
1468 val->val[k - 1] = val->val[k];
1469 j--;
1470 val->numVals--;
1471 #endif
1472 break;
1473 }
1474 }
1475 }
1476 return (flag);
1477 }
1478
1479 /* ObjectClass lookup table */
1480 static struct {
1481 const char *attrType;
1482 const char *objectClass;
1483 } oc_lookup[] = {
1484 { "o", "objectclass=organization"},
1485 { "organizationname", "objectclass=organization"},
1486 { "2.5.4.10", "objectclass=organization"},
1487 { "ou", "objectclass=organizationalunit"},
1488 { "organizationalunitname", "objectclass=organizationalunit"},
1489 { "2.5.4.11", "objectclass=organizationalunit"},
1490 { "c", "objectclass=country"},
1491 { "countryname", "objectclass=country"},
1492 { "2.5.4.6", "objectclass=country"},
1493 { "dc", "objectclass=domain"},
1494 { "domaincomponent", "objectclass=domain"},
1495 { "0.9.2342.19200300.100.1.25", "objectclass=domain"},
1496 { "nismapname", "objectclass=nismap"},
1497 { "1.3.6.1.1.1.1.26", "objectclass=nismap"},
1498 { "automountmapname", "objectclass=automountmap"},
1499 { "1.3.6.1.1.1.1.31", "objectclass=automountmap"},
1500 { 0, 0}
1501 };
1502
1503 /*
1504 * Returns the name of the objectclass to which the object
1505 * represented by the given 'rdn' will most likely belong to.
1506 * The return value is in static memory so it should not be
1507 * freed
1508 */
1509 const char *
getObjectClass(char * rdn)1510 getObjectClass(char *rdn) {
1511
1512 char *attrtype, *p;
1513 int len, i;
1514
1515 /* Skip leading whitespaces */
1516 for (p = rdn; *p == ' ' || *p == '\t'; p++);
1517 if (*p == '\0')
1518 return (0);
1519 attrtype = p;
1520
1521 /* Find '=' */
1522 if ((p = strchr(attrtype, '=')) == 0 || p == attrtype ||
1523 *(p - 1) == '\\')
1524 return (0);
1525
1526 /*
1527 * Skip trailing whitespaces in attrtype
1528 * Don't worry, p won't decrease beyond attrtype
1529 */
1530 for (--p; *p == ' ' || *p == '\t'; p--);
1531 len = p - attrtype + 1;
1532
1533 for (i = 0; oc_lookup[i].attrType; i++)
1534 if (!strncasecmp(oc_lookup[i].attrType, attrtype, len))
1535 /* Check length is right */
1536 if (len == strlen(oc_lookup[i].attrType))
1537 return (oc_lookup[i].objectClass);
1538
1539 return (0);
1540 }
1541
1542 /*
1543 * Split 'dn' into rdn and parentdn based on the first
1544 * occurrence of unescaped 'comma' or 'semicolon'. rdn
1545 * lies on the LHS while parentdn lies on the RHS of the
1546 * split. If none found, then an empty string ("") is
1547 * assigned to parentdn
1548 */
1549 int
splitDN(char * dn,char ** rdn,char ** parentdn)1550 splitDN(char *dn, char **rdn, char **parentdn) {
1551 char *value, *name;
1552 char *myself = "splitDN";
1553
1554 if ((name = sdup(myself, T, dn)) == 0)
1555 return (-1);
1556
1557 for (value = name; *value != '\0'; value++) {
1558 if (*value == ',' || *value == ';')
1559 if (value == name || *(value - 1) != '\\')
1560 break;
1561 }
1562
1563 if (*value != '\0') {
1564 *value = '\0';
1565 value++;
1566 } else
1567 value = 0;
1568
1569 if (parentdn) {
1570 if ((*parentdn = sdup(myself, T, value)) == 0) {
1571 sfree(name);
1572 return (-1);
1573 }
1574 }
1575 if (rdn)
1576 *rdn = name;
1577 else
1578 sfree(name);
1579
1580 return (1);
1581 }
1582
1583 /*
1584 * FUNCTION : makeNISObject()
1585 *
1586 * DESCRIPTION: Sets up a nis Object in the DIT.
1587 *
1588 * GIVEN :
1589 * Case 1: Both 'domain' and 'dn' are non-NULL
1590 * Create nisDomainObject with the given information
1591 * Case 2: Only 'domain' is non-NULL
1592 * Obtain the 'dn' from the nisLDAPdomainContext list
1593 * Create nisDomainObject with the above information
1594 * Case 3: Only 'dn' is non-NULL
1595 * Create an object with the 'dn'
1596 * Here we guess the objectclass attribute, based on
1597 * oc_lookup table
1598 * Case 4: Both 'domain' and 'dn' are NULL
1599 * Error
1600 *
1601 * RETURNS : SUCCESS = It worked
1602 * FAILURE = There was a problem.
1603 */
1604 suc_code
makeNISObject(char * domain,char * dn)1605 makeNISObject(char *domain, char *dn) {
1606 __nis_rule_value_t *rv;
1607 __nis_ldap_search_t *ls;
1608 int i, rc, nr, add_rc;
1609 char *val;
1610 char *myself = "makeNISObject";
1611
1612 if (!dn && !domain)
1613 return (FAILURE);
1614
1615 /*
1616 * If only 'domain' name is provided, then
1617 * try to find dn from the nisLDAPdomainContext
1618 * list generated by the parser
1619 */
1620 if (!dn) {
1621 for (i = 0; i < ypDomains.numDomains; i++) {
1622 if (ypDomains.domainLabels[i] == 0)
1623 continue;
1624 if (strcasecmp(domain, ypDomains.domainLabels[i])
1625 == 0) {
1626 dn = ypDomains.domains[i];
1627 break;
1628 }
1629 }
1630 if (!dn)
1631 return (FAILURE);
1632 }
1633
1634 /*
1635 * If only 'dn' is given, then it means that the
1636 * caller simply wants to a create an entry for
1637 * that 'dn'.
1638 *
1639 * If 'domain' is given, then check if the 'dn'
1640 * has already been set up as a nis domain object.
1641 * If not, see if we can make it become one.
1642 */
1643 if (domain) {
1644 /*
1645 * Check to see if the nis domain object has
1646 * already been set up
1647 */
1648 ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0,
1649 "objectclass=*", 0, 0, 0);
1650 if (ls == 0) {
1651 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1652 "%s: Unable to create ldapSearch "
1653 "request for dn: %s", myself, dn);
1654 return (FAILURE);
1655 }
1656 nr = -1;
1657 rv = ldapSearch(ls, &nr, 0, &rc);
1658 freeLdapSearch(ls);
1659 if (rc == LDAP_SUCCESS) {
1660 val = findVal("nisDomain", rv, mit_ldap);
1661 if (val != NULL) {
1662 /*
1663 * Yes, nis domain object found. Check
1664 * to see if the domain names match.
1665 * If so, we are done. If not, log
1666 * a warning message, and return SUCCESS.
1667 */
1668 if (strcasecmp(val, domain) == 0) {
1669 freeRuleValue(rv, nr);
1670 return (SUCCESS);
1671 } else {
1672 logmsg(MSG_NOTIMECHECK,
1673 LOG_WARNING,
1674 "%s: Entry (dn: %s) already "
1675 "contains a nis domain name "
1676 "(%s). The domain name (%s) "
1677 "is not added.",
1678 myself, dn, val, domain);
1679 freeRuleValue(rv, nr);
1680 return (SUCCESS);
1681 }
1682 } else {
1683 freeRuleValue(rv, nr);
1684 /*
1685 * Entry for the 'dn' exists, but it
1686 * is not a nis domain object yet.
1687 * Add the nisDoamin attribute and
1688 * the nisDomainObject objectclass to
1689 * the entry.
1690 */
1691 if ((rv = initRuleValue(1, 0)) == 0)
1692 return (FAILURE);
1693
1694 if (addSAttr2RuleValue("nisDomain",
1695 domain, rv) == -1) {
1696 freeRuleValue(rv, 1);
1697 return (FAILURE);
1698 }
1699 rc = ldapModify(dn, rv,
1700 "objectclass=nisDomainObject",
1701 0);
1702 freeRuleValue(rv, 1);
1703 if (rc == LDAP_SUCCESS) {
1704 logmsg(MSG_NOTIMECHECK,
1705 LOG_INFO,
1706 "%s: entry (dn: %s) "
1707 "modified to be an "
1708 "nis domain object",
1709 myself, dn);
1710 return (SUCCESS);
1711 } else {
1712 logmsg(MSG_NOTIMECHECK,
1713 LOG_ERR,
1714 "%s: unable to modify "
1715 "entry (dn: %s) to be "
1716 "a nis domain object: "
1717 "ldapModify error %d (%s)",
1718 myself, dn, rc,
1719 ldap_err2string(rc));
1720 return (FAILURE);
1721 }
1722 }
1723 } else { /* search for 'dn' failed */
1724 freeRuleValue(rv, nr);
1725
1726 /*
1727 * It is OK if no such object, otherwise
1728 * log an error.
1729 */
1730 if (rc != LDAP_NO_SUCH_OBJECT) {
1731 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1732 "%s: unable to retrieve "
1733 "entry (dn: %s): "
1734 "ldapSearch error %d (%s)",
1735 myself, dn, rc,
1736 ldap_err2string(rc));
1737 return (FAILURE);
1738 }
1739 }
1740
1741 /*
1742 * If the 'dn' is actually the naming context of
1743 * the DIT, we should be able to make it a nis domain
1744 * object without worrying about missing parent
1745 * entries. If unable to add the entry for the 'dn'
1746 * due to missing parent entries, fall through
1747 * to create them and then add the nis domain object.
1748 */
1749 if (addNISObject(domain, dn, &add_rc) == SUCCESS)
1750 return (SUCCESS);
1751 else if (add_rc != LDAP_NO_SUCH_OBJECT)
1752 return (FAILURE);
1753 }
1754
1755 /* Create parent */
1756 if (addParent(dn, NULL) == FAILURE)
1757 return (FAILURE);
1758
1759 if (addNISObject(domain, dn, NULL) == FAILURE)
1760 return (FAILURE);
1761
1762 return (SUCCESS);
1763 }
1764
1765 suc_code
addParent(char * dn,char ** attr)1766 addParent(char *dn, char **attr) {
1767 __nis_rule_value_t *rv;
1768 __nis_ldap_search_t *ls;
1769 int rc, nr;
1770 char *parentdn = 0, *rdn = 0;
1771 char *myself = "addParent";
1772
1773 /* Obtain parentdn */
1774 if (splitDN(dn, &rdn, &parentdn) == -1)
1775 return (FAILURE);
1776 if (!parentdn) {
1777 sfree(rdn);
1778 return (FAILURE);
1779 }
1780
1781 /* Check if parentdn exists */
1782 ls = buildLdapSearch(parentdn, LDAP_SCOPE_BASE, 0, 0,
1783 "objectclass=*", 0, 0, 0);
1784 if (ls == 0) {
1785 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1786 "%s: Unable to create ldapSearch request for "
1787 "parent (dn: %s) of (dn: %s)",
1788 myself, parentdn, dn);
1789 sfree(parentdn);
1790 sfree(rdn);
1791 return (FAILURE);
1792 }
1793 nr = -1;
1794 rv = ldapSearch(ls, &nr, 0, &rc);
1795 freeLdapSearch(ls);
1796 freeRuleValue(rv, nr);
1797
1798 /* Create parent if it doesn't exists */
1799 if (rc == LDAP_NO_SUCH_OBJECT) {
1800 if (makeNISObject(0, parentdn) == FAILURE) {
1801 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1802 "%s: Unable to create parent (dn: %s) of "
1803 "(dn: %s) in the DIT", myself, parentdn, dn);
1804 sfree(parentdn);
1805 sfree(rdn);
1806 return (FAILURE);
1807 }
1808 }
1809 sfree(parentdn);
1810
1811 if (attr && rdn)
1812 *attr = (char *)getObjectClass(rdn);
1813 sfree(rdn);
1814
1815 return (SUCCESS);
1816 }
1817
1818
1819
1820 /*
1821 * FUNCTION : is_fatal_error()
1822 *
1823 * DESCRIPTION: Works out if a failed mapping operation should be retried.
1824 *
1825 * INPUTS : Result code from operation
1826 *
1827 * OUTPUTS : TRUE = Fatal error, don't retry.
1828 * FALSE = Temporary error, retry.
1829 */
1830 bool_t
is_fatal_error(int res)1831 is_fatal_error(int res)
1832 {
1833
1834 if (0 > res)
1835 /* An internal mapping error. Not going to go away. */
1836 return (TRUE);
1837
1838 switch (res) {
1839 case (LDAP_PROTOCOL_ERROR):
1840 case (LDAP_TIMELIMIT_EXCEEDED):
1841 case (LDAP_PARTIAL_RESULTS):
1842 case (LDAP_BUSY):
1843 case (LDAP_UNAVAILABLE):
1844 case (LDAP_UNWILLING_TO_PERFORM):
1845 case (LDAP_OTHER):
1846 case (LDAP_SERVER_DOWN):
1847 case (LDAP_LOCAL_ERROR):
1848 case (LDAP_TIMEOUT):
1849 case (LDAP_NO_MEMORY):
1850 /* Probably worth a retry */
1851 return (FALSE);
1852
1853 default:
1854 return (TRUE);
1855 }
1856 }
1857
1858 /*
1859 * FUNCTION : addNISObject()
1860 *
1861 * DESCRIPTION: Add a nis Object in the DIT.
1862 *
1863 * GIVEN :
1864 * Case 1: 'dn' is NULL
1865 * Error
1866 * Case 2: 'domain' is non-NULL
1867 * Create nisDomainObject with the given information
1868 * Case 3: 'domain' is NULL
1869 * Create an object with the 'dn'
1870 * Here we guess the objectclass attribute, based on
1871 * oc_lookup table
1872 *
1873 * RETURNS : SUCCESS = It worked
1874 * FAILURE = There was a problem. If the ldap add
1875 * operation failed, ldap_rc will be set
1876 * to the ldap error code.
1877 */
1878 suc_code
addNISObject(char * domain,char * dn,int * ldap_rc)1879 addNISObject(char *domain, char *dn, int *ldap_rc) {
1880 __nis_rule_value_t *rv;
1881 int rc;
1882 char *objClassAttrs = NULL, *attrs;
1883 char *value, *svalue, *rdn = NULL;
1884 char *myself = "addNISObject";
1885
1886 if (!dn)
1887 return (FAILURE);
1888
1889 if ((rv = initRuleValue(1, 0)) == 0)
1890 return (FAILURE);
1891
1892 if (ldap_rc)
1893 *ldap_rc = -1;
1894
1895 /*
1896 * Add name=value pairs from RDN. Although this is not required
1897 * for SunOne Directory Server, during openldap interoperabilty
1898 * tests, it was found out that openldap server returned object
1899 * class violation errors if MUST attributes were not specified
1900 * explicitly.
1901 */
1902 if (splitDN(dn, &rdn, 0) == -1)
1903 return (FAILURE);
1904 if (rdn != NULL) {
1905 objClassAttrs = (char *)getObjectClass(rdn);
1906 if (objClassAttrs == NULL) {
1907 sfree(rdn);
1908 return (FAILURE);
1909 }
1910
1911 /*
1912 * RDN can be composed of multiple name=value pairs
1913 * concatenated by '+'. Hence, we need to determine each
1914 * pair and add it to 'rv'
1915 */
1916 for (value = rdn, svalue = NULL; *value != '\0'; value++) {
1917 if (*value == '+') {
1918 /* Make sure it's not escaped */
1919 if (value == rdn || *(value - 1) != '\\') {
1920 /*
1921 * We are at the start of the new
1922 * pair. 'svalue' now contains the
1923 * value for the previous pair. Add
1924 * the previous pair to 'rv'
1925 */
1926 *value = '\0';
1927 if (svalue &&
1928 addSAttr2RuleValue(rdn, svalue, rv)
1929 == -1) {
1930 sfree(rdn);
1931 freeRuleValue(rv, 1);
1932 return (FAILURE);
1933 }
1934 svalue = NULL;
1935 rdn = value + 1;
1936 continue;
1937 }
1938 }
1939
1940 if (*value == '=') {
1941 if (value == rdn || *(value - 1) != '\\') {
1942 /*
1943 * 'rdn' now contains the name.
1944 * Whatever follows till the next
1945 * unescaped '+' or '\0' is the
1946 * value for this pair.
1947 */
1948 *value = '\0';
1949 svalue = value + 1;
1950 continue;
1951 }
1952 }
1953 }
1954
1955 /*
1956 * End of String. Add the previous name=value pair to 'rv'
1957 */
1958 if (svalue && addSAttr2RuleValue(rdn, svalue, rv) == -1) {
1959 sfree(rdn);
1960 freeRuleValue(rv, 1);
1961 return (FAILURE);
1962 }
1963 sfree(rdn);
1964 } else /* rdn == NULL */
1965 return (FAILURE);
1966
1967 /* Create the entry */
1968 if (domain) {
1969 if (addSAttr2RuleValue("nisDomain", domain, rv) == -1) {
1970 freeRuleValue(rv, 1);
1971 return (FAILURE);
1972 }
1973 attrs = scat(myself, F, "objectclass=nisdomainobject,",
1974 objClassAttrs);
1975 if (!attrs) {
1976 freeRuleValue(rv, 1);
1977 return (FAILURE);
1978 }
1979 rc = ldapAdd(dn, rv, attrs, 0);
1980 sfree(attrs);
1981 } else {
1982 rc = ldapAdd(dn, rv, objClassAttrs, 0);
1983 }
1984
1985 if (rc == LDAP_SUCCESS)
1986 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1987 "%s: Entry (dn: %s) added to DIT",
1988 myself, dn);
1989 else if (rc == LDAP_ALREADY_EXISTS)
1990 /* Treat this as success */
1991 rc = LDAP_SUCCESS;
1992 else
1993 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1994 "%s: ldapAdd error %d (%s) for (dn: %s)",
1995 myself, rc, ldap_err2string(rc), dn);
1996
1997 freeRuleValue(rv, 1);
1998 if (ldap_rc)
1999 *ldap_rc = rc;
2000 return ((rc == LDAP_SUCCESS)?SUCCESS:FAILURE);
2001 }
2002