xref: /illumos-gate/usr/src/lib/libnisdb/nis_parse_ldap_yp_util.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <syslog.h>
34 
35 #include "ldap_parse.h"
36 #include "nis_parse_ldap_conf.h"
37 #include "nis_parse_ldap_err.h"
38 #include "ldap_util.h"
39 
40 extern __nis_mapping_rule_t **dup_mapping_rules(
41 	__nis_mapping_rule_t **rules, int n_rules);
42 extern __nis_mapping_rule_t *dup_mapping_rule(
43 	__nis_mapping_rule_t *in);
44 
45 static int	merge_table_mapping(__nis_table_mapping_t *in,
46 	__nis_table_mapping_t *out);
47 __nis_table_mapping_t *new_merged_mapping(const char *,
48 	__nis_table_mapping_t *intbl);
49 static int append_mapping_rule(__nis_mapping_rule_t *src_rule,
50 	__nis_table_mapping_t *tbl, int flag);
51 
52 
53 static int copy_object_dn(__nis_object_dn_t	*in,
54 		__nis_object_dn_t	*newdn);
55 
56 /*
57  * FUNCTION:	initialize_table_mapping
58  *
59  * Initialize the __nis_table_mapping_t structure.
60  *
61  * INPUT:	__nis_table_mapping_t
62  *
63  */
64 void
65 initialize_table_mapping(
66 	__nis_table_mapping_t *mapping)
67 {
68 	if (mapping != NULL) {
69 		mapping->dbId = NULL;
70 
71 		mapping->index.numIndexes = 0;
72 		mapping->index.name = NULL;
73 		mapping->index.value = NULL;
74 
75 		mapping->numColumns = 0;
76 		mapping->column = NULL;
77 
78 		mapping->initTtlLo = (time_t)NO_VALUE_SET;
79 		mapping->initTtlHi = (time_t)NO_VALUE_SET;
80 		mapping->ttl = (time_t)NO_VALUE_SET;
81 
82 		mapping->usedns_flag = 0;
83 		mapping->securemap_flag = 0;
84 		mapping->commentChar = DEFAULT_COMMENT_CHAR;
85 		mapping->numSplits = 0;
86 
87 		mapping->objectDN = NULL;
88 
89 		mapping->separatorStr = DEFAULT_SEP_STRING;
90 
91 		mapping->numRulesFromLDAP = 0;
92 		mapping->numRulesToLDAP = 0;
93 
94 		mapping->ruleFromLDAP = NULL;
95 		mapping->ruleToLDAP = NULL;
96 
97 		mapping->e = NULL;
98 		mapping->objName = NULL;
99 		mapping->objPath = NULL;
100 		mapping->obj = NULL;
101 		mapping->isMaster = 0;
102 		mapping->seq_num = NO_VALUE_SET;
103 	}
104 }
105 
106 /*
107  * FUNCTION:	initialize_yp_parse_structs
108  *
109  * Initialize the __yp_domain_context_t structure.
110  *
111  * INPUT:		__yp_domain_context_t
112  *
113  */
114 void
115 initialize_yp_parse_structs(
116 	__yp_domain_context_t	*ypDomains)
117 {
118 	ypDomains->numDomains = 0;
119 	ypDomains->domainLabels = NULL;
120 	ypDomains->domains = NULL;
121 	ypDomains->numYppasswdd = 0;
122 	ypDomains->yppasswddDomainLabels = NULL;
123 }
124 
125 /*
126  * FUNCTION: 	merge_table_mapping
127  *
128  * Merges information from one table_mapping struct
129  * into another
130  *
131  * INPUT: Source and Destination table_mapping structs.
132  * RETURN: 0 on success and > 0 on error.
133  */
134 
135 static int
136 merge_table_mapping(
137 	__nis_table_mapping_t *in,
138 	__nis_table_mapping_t *out)
139 {
140 	int i;
141 	int len;
142 	int orig_num_rules;
143 	int append;
144 
145 	if (in == NULL)
146 		return (1);
147 
148 	if (in->dbId == NULL)
149 		return (1);
150 
151 	/*
152 	 * If 'in' is generic (non-expanded) and 'out' is domain-specific,
153 	 * then rules from 'in' should not be appended to those in 'out'.
154 	 */
155 	if (!strchr(in->dbId, COMMA_CHAR) && strchr(out->dbId, COMMA_CHAR))
156 		append = 0;
157 	else
158 		append = 1;
159 
160 
161 	if (!out->index.numIndexes && in->index.numIndexes > 0) {
162 		if (!dup_index(&in->index, &out->index))
163 			return (1);
164 	}
165 
166 	/* add_column() increments numColumns, so we don't */
167 	if (!out->numColumns && in->numColumns > 0) {
168 		for (i = 0; i < in->numColumns; i++) {
169 			if (!add_column(out, in->column[i]))
170 				return (1);
171 		}
172 	}
173 
174 	if (out->commentChar == DEFAULT_COMMENT_CHAR &&
175 	    in->commentChar != DEFAULT_COMMENT_CHAR)
176 		out->commentChar = in->commentChar;
177 
178 	if (out->usedns_flag == 0)
179 		out->usedns_flag = in->usedns_flag;
180 
181 	if (out->securemap_flag == 0)
182 		out->securemap_flag = in->securemap_flag;
183 
184 	if ((strcmp(out->separatorStr, DEFAULT_SEP_STRING) == 0) &&
185 	    (strcmp(in->separatorStr, DEFAULT_SEP_STRING) != 0)) {
186 		out->separatorStr = s_strdup(in->separatorStr);
187 		if (!out->separatorStr)
188 			return (2);
189 	}
190 
191 	if (!out->numSplits && !out->e && in->e) {
192 		out->numSplits = in->numSplits;
193 		out->e = (__nis_mapping_element_t *)
194 		    s_calloc(1, (in->numSplits+1) *
195 		    sizeof (__nis_mapping_element_t));
196 		if (!out->e)
197 			return (2);
198 		for (i = 0; i <= in->numSplits; i++) {
199 			if (!dup_mapping_element(&in->e[i], &out->e[i])) {
200 				for (; i > 0; i--) {
201 					free_mapping_element(&out->e[i - 1]);
202 				}
203 				out->e = NULL;
204 				return (1);
205 			}
206 		}
207 	}
208 
209 	if (out->initTtlLo == (time_t)NO_VALUE_SET &&
210 	    in->initTtlLo != (time_t)NO_VALUE_SET)
211 		out->initTtlLo = in->initTtlLo;
212 
213 	if (out->initTtlHi == (time_t)NO_VALUE_SET &&
214 	    in->initTtlHi != (time_t)NO_VALUE_SET)
215 		out->initTtlHi = in->initTtlHi;
216 
217 	if (out->ttl == (time_t)NO_VALUE_SET &&
218 	    in->ttl != (time_t)NO_VALUE_SET)
219 		out->ttl = in->ttl;
220 
221 	if (!out->numRulesFromLDAP && in->numRulesFromLDAP) {
222 		out->ruleFromLDAP = dup_mapping_rules(in->ruleFromLDAP,
223 		    in->numRulesFromLDAP);
224 		if (!out->ruleFromLDAP)
225 			return (1);
226 		out->numRulesFromLDAP = in->numRulesFromLDAP;
227 	} else if (append && out->numRulesFromLDAP && in->numRulesFromLDAP) {
228 		orig_num_rules = out->numRulesFromLDAP;
229 		for (i = 0; i < in->numRulesFromLDAP; i++) {
230 			if (append_mapping_rule(in->ruleFromLDAP[i], out, 0)) {
231 				for (i = out->numRulesFromLDAP;
232 				    i > orig_num_rules; i--) {
233 					free_mapping_rule(out->ruleFromLDAP[i]);
234 					out->ruleFromLDAP[i] = NULL;
235 				}
236 				return (1);
237 
238 			}
239 		}
240 	}
241 
242 	if (!out->numRulesToLDAP && in->numRulesToLDAP) {
243 		out->ruleToLDAP = dup_mapping_rules(in->ruleToLDAP,
244 		    in->numRulesToLDAP);
245 		if (!out->ruleToLDAP)
246 			return (1);
247 		out->numRulesToLDAP = in->numRulesToLDAP;
248 	} else if (append && out->numRulesToLDAP && in->numRulesToLDAP) {
249 		orig_num_rules = out->numRulesToLDAP;
250 		for (i = 0; i < in->numRulesToLDAP; i++) {
251 			if (append_mapping_rule(in->ruleToLDAP[i], out, 1)) {
252 				for (i = out->numRulesToLDAP;
253 				    i > orig_num_rules; i--) {
254 					free_mapping_rule(out->ruleToLDAP[i]);
255 					out->ruleToLDAP[i] = NULL;
256 				}
257 				return (1);
258 			}
259 		}
260 	}
261 	if (!out->objectDN && in->objectDN) {
262 		out->objectDN = (__nis_object_dn_t *)
263 		    s_calloc(1, sizeof (__nis_object_dn_t));
264 		if (!out->objectDN)
265 			return (2);
266 		if (copy_object_dn(in->objectDN, out->objectDN)) {
267 			free_object_dn(out->objectDN);
268 			out->objectDN = NULL;
269 			return (1);
270 		}
271 	}
272 
273 	if (!out->objName && in->objName) {
274 		if (!strchr(in->objName, SPACE_CHAR)) {
275 			/* objName has no space- a single map dbIdMapping */
276 			out->objName = s_strndup(in->objName,
277 			    strlen(in->objName));
278 			if (!out->objName)
279 				return (2);
280 		}
281 	}
282 
283 	if (!out->objName && out->dbId) {
284 		out->objName = s_strndup(out->dbId, strlen(out->dbId));
285 		if (!out->objName)
286 			return (2);
287 	}
288 
289 	if (out->seq_num == NO_VALUE_SET && in->seq_num >= 0)
290 		out->seq_num = in->seq_num;
291 
292 	return (p_error == no_parse_error ? 0 : 1);
293 }
294 
295 /*
296  * FUNCTION:	copy_object_dn
297  *
298  * Copies a __nis_object_dn_t structure.
299  *
300  * RETURN:	0 on success, > 0 on failure.
301  *
302  * NOTE:	The caller MUST free newdn using
303  *		free_object_dn() if return value != 0 (error condition)
304  */
305 
306 static int
307 copy_object_dn(__nis_object_dn_t *in, __nis_object_dn_t *newdn)
308 {
309 	if (in == NULL) {
310 		p_error = parse_no_object_dn;
311 		return (1);
312 	}
313 	while (in != NULL) {
314 		if (in->read.base == NULL) {
315 			newdn->read.base = NULL;
316 		} else {
317 			newdn->read.base = s_strndup(
318 			    in->read.base, strlen(in->read.base));
319 			if (newdn->read.base == NULL)
320 				return (2);
321 		}
322 		newdn->read.scope = in->read.scope;
323 		if (in->read.attrs) {
324 			newdn->read.attrs = s_strndup(
325 			    in->read.attrs, strlen(in->read.attrs));
326 			if (newdn->read.attrs == NULL) {
327 				return (2);
328 			}
329 		} else {
330 			newdn->read.attrs = NULL;
331 		}
332 		newdn->read.element = in->read.element;
333 		if (in->write.base != NULL) {
334 			newdn->write.base = s_strndup(
335 			    in->write.base, strlen(in->write.base));
336 			if (newdn->write.base == NULL)
337 				return (2);
338 		} else {
339 			newdn->write.base = NULL;
340 		}
341 		newdn->write.scope = in->write.scope;
342 		if (in->write.attrs != NULL) {
343 			newdn->write.attrs = s_strndup(
344 			    in->write.attrs, strlen(in->write.attrs));
345 			if (newdn->write.attrs == NULL) {
346 				return (2);
347 			}
348 		} else {
349 			newdn->write.attrs = NULL;
350 		}
351 		newdn->write.element = in->write.element;
352 		if (in->dbIdName) {
353 			newdn->dbIdName = s_strndup(in->dbIdName,
354 			    strlen(in->dbIdName));
355 			if (newdn->dbIdName == NULL)
356 				return (2);
357 		}
358 
359 		if (in->delDisp)
360 			newdn->delDisp = in->delDisp;
361 
362 		if (in->dbId && in->numDbIds > 0) {
363 			newdn->dbId = dup_mapping_rules(in->dbId,
364 			    in->numDbIds);
365 			if (!newdn->dbId)
366 				return (1);
367 			newdn->numDbIds = in->numDbIds;
368 		}
369 		if (in->next != NULL) {
370 			newdn->next = (__nis_object_dn_t *)s_calloc(1,
371 			    sizeof (__nis_object_dn_t));
372 			if (newdn->next == NULL)
373 				return (1);
374 			newdn = newdn->next;
375 			in = in->next;
376 		} else {
377 			return (0);
378 		}
379 	} /* End of while on in */
380 
381 	return (0);
382 }
383 
384 /*
385  * FUNCTION:	free_yp_domain_context
386  *
387  * Frees __yp_domain_context_t
388  *
389  * INPUT:		__yp_domain_context_t
390  */
391 void
392 free_yp_domain_context(__yp_domain_context_t *domains)
393 {
394 	int i;
395 
396 	if (domains != NULL) {
397 		for (i = 0; i < domains->numDomains; i++) {
398 			if (domains->domains[i] != NULL) {
399 				free(domains->domains[i]);
400 				domains->domains[i] = NULL;
401 			}
402 			if (domains->domainLabels[i] != NULL) {
403 				free(domains->domainLabels[i]);
404 				domains->domainLabels[i] = NULL;
405 			}
406 		}
407 		domains->domains = NULL;
408 		domains->domainLabels = NULL;
409 		for (i = 0; i < domains->numYppasswdd; i++) {
410 			if (domains->yppasswddDomainLabels[i] != NULL) {
411 				free(domains->yppasswddDomainLabels[i]);
412 				domains->yppasswddDomainLabels[i] =
413 				    NULL;
414 			}
415 		}
416 		domains->yppasswddDomainLabels = NULL;
417 		domains->numDomains = 0;
418 		domains = NULL;
419 	}
420 }
421 
422 /*
423  * FUNCTION:	second_parser_pass
424  *
425  * Prepares the linked list of table_mappings for processing
426  * by finish_parse(), adding, merging and deleting structures
427  * as necessary. Also adds dummy objectDN info. for splitField's.
428  *
429  * RETURN VALUE: 0 on success, > 0 on failure.
430  */
431 int
432 second_parser_pass(__nis_table_mapping_t **table_mapping)
433 {
434 	__nis_table_mapping_t   *t, *t2;
435 	__nis_table_mapping_t   *t_new = NULL, *tg;
436 	__nis_table_mapping_t	*prev = NULL;
437 	__nis_object_dn_t   *objectDN;
438 	char	*objs, *dom;
439 	char	*objName = NULL;
440 	char	*lasts;
441 	char	*tobj, *alias, *dupalias, *tmp;
442 	char	*myself = "second_parser_pass";
443 	int	i = 0, len;
444 	int	remove_t = 0;
445 	int	add_t = 0;
446 
447 	prev = NULL;
448 	for (t = *table_mapping; t != NULL; ) {
449 		/*
450 		 * Temporarily using this field to flag deletion.
451 		 * 0 : don't delete
452 		 * 1 : delete
453 		 * The mapping structure will be deleted in final_parser_pass
454 		 */
455 		t->isMaster = 0;
456 
457 		if (!t->dbId) {
458 			p_error = parse_bad_map_error;
459 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
460 			    "%s: no dbId field", myself);
461 			return (1);
462 		}
463 		tg = NULL;
464 		dom = strchr(t->dbId, COMMA_CHAR);
465 		if (t->objName != NULL) {
466 			objName = strdup(t->objName);
467 			if (objName == NULL) {
468 				p_error = parse_no_mem_error;
469 				logmsg(MSG_NOMEM, LOG_ERR,
470 				    "%s: Cannot allocate memory for objName",
471 				    myself);
472 				return (1);
473 			}
474 			objs = (char *)strtok_r(objName, " ", &lasts);
475 			/* Get the generic mapping */
476 			if (dom != NULL) {
477 				tg = find_table_mapping(t->dbId, dom - t->dbId,
478 				    *table_mapping);
479 			}
480 		} else {
481 			objs = NULL;
482 			if (dom == NULL) {
483 				t->objName = s_strndup(t->dbId,
484 				    strlen(t->dbId));
485 				if (!t->objName) {
486 					logmsg(MSG_NOMEM, LOG_ERR,
487 					    "%s: Cannot allocate memory for "
488 					    "t->objName", myself);
489 					objs = NULL;
490 					return (2);
491 				}
492 			} else {
493 				/* Force relationship for domain specific */
494 
495 				/* Get the generic mapping */
496 				tg = find_table_mapping(t->dbId, dom - t->dbId,
497 				    *table_mapping);
498 				if (tg == NULL || tg->objName == NULL) {
499 					/* If not found, use dbId for objName */
500 					t->objName = s_strndup(t->dbId,
501 					    strlen(t->dbId));
502 					if (t->objName == NULL) {
503 						logmsg(MSG_NOMEM, LOG_ERR,
504 				    "%s: Cannot allocate memory for t->objName",
505 						    myself);
506 						return (2);
507 					}
508 				} else {
509 					dom++;
510 					tobj = s_strndup(tg->objName,
511 					    strlen(tg->objName));
512 					if (tobj == NULL) {
513 						logmsg(MSG_NOMEM, LOG_ERR,
514 				    "%s: Cannot allocate memory for t->objName",
515 						    myself);
516 						return (2);
517 					}
518 					alias = (char *)strtok_r(tobj, " ",
519 					    &lasts);
520 
521 					/* Loop 'breaks' on errors */
522 					while (alias) {
523 						tmp = NULL;
524 						dupalias = s_strndup(alias,
525 						    strlen(alias));
526 						if (!dupalias)
527 							break;
528 						if (getfullmapname(&dupalias,
529 						    dom)) {
530 							i = 1;
531 							break;
532 						}
533 						if (t->objName == NULL)
534 							t->objName = dupalias;
535 						else {
536 							len = strlen(t->objName)
537 							    + strlen(dupalias) +
538 							    2;
539 							tmp = s_calloc(1, len);
540 							if (tmp == NULL)
541 								break;
542 							snprintf(tmp, len,
543 							    "%s %s",
544 							    t->objName,
545 							    dupalias);
546 							free(dupalias);
547 							dupalias = NULL;
548 							free(t->objName);
549 							t->objName = tmp;
550 						}
551 						alias = (char *)strtok_r(NULL,
552 						    " ", &lasts);
553 					}
554 
555 					if (tobj)
556 						free(tobj);
557 
558 					if (alias ||
559 					    (objName = s_strdup(t->objName))
560 					    == NULL) {
561 						if (i)
562 							logmsg(MSG_NOTIMECHECK,
563 							    LOG_ERR,
564 		    "%s: getfullmapname failed for %s for domain \"%s\"",
565 							    myself, dupalias,
566 							    dom);
567 						else {
568 							p_error =
569 							    parse_no_mem_error;
570 							logmsg(MSG_NOMEM,
571 							    LOG_ERR,
572 					    "%s: Cannot allocate memory",
573 							    myself);
574 						}
575 						if (dupalias)
576 							free(dupalias);
577 						if (t->objName)
578 							free(t->objName);
579 						return (2);
580 
581 					}
582 					objs = (char *)strtok_r(objName, " ",
583 					    &lasts);
584 				}
585 			}
586 		}
587 
588 		if (tg != NULL) {
589 			if (merge_table_mapping(tg, t)) {
590 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
591 	    "Error merging information from the %s to the %s mapping structure",
592 				    tg->dbId, t->dbId);
593 				objs = NULL;
594 				if (objName)
595 					free(objName);
596 				return (1);
597 			}
598 		}
599 
600 		/*
601 		 * If objName is "map1 map2" then do the second pass.
602 		 * If it is just "map1" however skip the expansion.
603 		 * Also skip it if t->objName is null.
604 		 */
605 		if (objs && strncasecmp(objs, t->objName,
606 		    strlen(t->objName))) {
607 			t2 = find_table_mapping(objs, strlen(objs),
608 			    *table_mapping);
609 			if (t2) {
610 				if (merge_table_mapping(t, t2)) {
611 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
612 	    "Error merging information from the %s to the %s mapping structure",
613 					    t->dbId, t2->dbId);
614 					objs = NULL;
615 					if (objName)
616 						free(objName);
617 					return (1);
618 				}
619 				t->isMaster = 1;
620 			} else {
621 				t_new = new_merged_mapping(objs, t);
622 				if (t_new) {
623 					t->isMaster = 1;
624 					if (prev != NULL)
625 						prev->next = t_new;
626 					else
627 						*table_mapping = t_new;
628 					prev = t_new;
629 					prev->next = t;
630 				} else {
631 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
632 				    "Error creating a new mapping structure %s",
633 					    objs);
634 					objs = NULL;
635 					if (objName)
636 						free(objName);
637 					return (1);
638 				}
639 			}
640 			while ((objs = (char *)strtok_r(NULL, " ", &lasts))
641 			    != NULL) {
642 				t2 = find_table_mapping(objs, strlen(objs),
643 				    *table_mapping);
644 				if (t2) {
645 					if (merge_table_mapping(t, t2)) {
646 						logmsg(MSG_NOTIMECHECK, LOG_ERR,
647 	    "Error merging information from the %s to the %s mapping structure",
648 						    t->dbId, t2->dbId);
649 						objs = NULL;
650 						if (objName)
651 							free(objName);
652 						return (1);
653 					}
654 					t->isMaster = 1;
655 				} else {
656 					/*
657 					 * create a new t_map with dbId = objs
658 					 * and copy t->* into new t_map
659 					 */
660 					t_new = new_merged_mapping(objs, t);
661 					if (t_new) {
662 						t->isMaster = 1;
663 						if (prev != NULL)
664 							prev->next = t_new;
665 						else
666 							*table_mapping = t_new;
667 						prev = t_new;
668 						prev->next = t;
669 					} else {
670 						logmsg(MSG_NOTIMECHECK, LOG_ERR,
671 				    "Error creating a new mapping structure %s",
672 						    objs);
673 						objs = NULL;
674 						if (objName)
675 							free(objName);
676 						return (1);
677 					}
678 				}
679 			}
680 		} /* if objs!= NULL */
681 
682 		prev = t;
683 		t = t->next;
684 
685 		if (objName) {
686 			free(objName);
687 			objName = NULL;
688 			objs = NULL;
689 		}
690 	} /* for t = table_mapping loop */
691 	return (0);
692 }
693 
694 __nis_table_mapping_t *
695 new_merged_mapping(const char *match,
696 	__nis_table_mapping_t	*intbl)
697 {
698 
699 	__nis_table_mapping_t	*outtable = NULL;
700 
701 	outtable = (__nis_table_mapping_t *)
702 	    s_calloc(1, sizeof (__nis_table_mapping_t));
703 	if (outtable == NULL)
704 		return (NULL);
705 	initialize_table_mapping(outtable);
706 	outtable->dbId = s_strndup(match, strlen(match));
707 	if (outtable->dbId == NULL) {
708 		free_table_mapping(outtable);
709 		outtable = NULL;
710 		return (NULL);
711 	}
712 	if (merge_table_mapping(intbl, outtable)) {
713 		free_table_mapping(outtable);
714 		outtable = NULL;
715 	}
716 	return (outtable);
717 }
718 
719 /*
720  * FUNCTION:	final_parser_pass
721  *
722  * completes the final expansion of t_map structures linked list.
723  * all structures will have a non-null objPath as well as a objName
724  * in the form of "mapname . domainname ." or "splitfieldname .
725  * domainname .".
726  *
727  * RETURN VALUE:	0 on success, -1 on failure, -2 on fatal error.
728  */
729 int
730 final_parser_pass(
731 	__nis_table_mapping_t   **table_mapping,
732 	__yp_domain_context_t   *ypDomains)
733 {
734 	__nis_table_mapping_t   *t;
735 	__nis_table_mapping_t	*t1, *returned_map;
736 	__nis_table_mapping_t   *prev = NULL;
737 	int			i;
738 	char			*myself = "final_parser_pass";
739 	int			nm;
740 	bool_t			r;
741 	int			del_tbl_flag = 0;
742 
743 	if (ypDomains) {
744 		if (!ypDomains->numDomains) {
745 			p_error = parse_internal_error;
746 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
747 			    "%s:No domains specified.", myself);
748 			return (-1);
749 		}
750 	} else {
751 		p_error = parse_internal_error;
752 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
753 		    "%s:No domain structure supplied.", myself);
754 		return (-1);
755 	}
756 	prev = NULL;
757 
758 	for (t = *table_mapping; t != NULL; ) {
759 
760 		/* Delete if marked for deletion by second_parser_pass */
761 		if (t->isMaster == 1) {
762 			if (prev != NULL)
763 				prev->next = t->next;
764 			else
765 				*table_mapping = t->next;
766 			t1 = t;
767 			t = t->next;
768 			free_table_mapping(t1);
769 			continue;
770 		}
771 
772 		if (!t->objName && t->dbId) {
773 			t->objName = s_strndup(t->dbId, strlen(t->dbId));
774 			if (!t->objName) {
775 				logmsg(MSG_NOMEM, LOG_ERR,
776 				    "%s:Could not allocate.", myself);
777 				return (-1);
778 			}
779 		}
780 		i = ypDomains->numDomains;
781 		while (i > 0) {
782 			if (i == 1) {
783 			/* modify existing table_mapping's */
784 				nm = checkfullmapname(t->dbId,
785 				    ypDomains->domainLabels[0],
786 				    table_mapping, &returned_map);
787 				if (nm == 1) {
788 					/* delete this mapping structure */
789 					logmsg(MSG_NOTIMECHECK,
790 					    LOG_WARNING,
791 					    "Mapping structure %s,%s "
792 					    "already exists.",
793 					    t->dbId,
794 					    ypDomains->domainLabels[0]);
795 					if (merge_table_mapping(t,
796 					    returned_map)) {
797 						logmsg(MSG_NOTIMECHECK, LOG_ERR,
798 						    "Error merging information "
799 						    "from the %s to the %s "
800 						    "mapping structure.",
801 						    t->dbId,
802 						    returned_map->dbId);
803 						return (-1);
804 					}
805 					if (del_tbl_flag == 0)
806 						del_tbl_flag = 1;
807 				} else if (nm == -1) {
808 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
809 			"Error searching for %s,%s structure",
810 					    t->dbId,
811 					    ypDomains->domainLabels[0]);
812 					return (-1);
813 				} else if (nm == 0 || nm == 2) {
814 					if ((append_domainContext(&t,
815 					    ypDomains->domainLabels[0],
816 					    ypDomains->domains[0])) != 0) {
817 						logmsg(MSG_NOTIMECHECK, LOG_ERR,
818 					"Error appending domainContext %s",
819 						    ypDomains->domainLabels[0]);
820 						return (-1);
821 					}
822 					del_tbl_flag = 0;
823 				}
824 			} else { /* if (i > 1) */
825 				/* need to create new table_mapping's */
826 				nm = checkfullmapname(t->dbId,
827 				    ypDomains->domainLabels[i - 1],
828 				    table_mapping, &returned_map);
829 				if (nm == -1) {
830 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
831 				"Error searching for %s,%s structure",
832 					    t->dbId,
833 					    ypDomains->domainLabels[i - 1]);
834 					return (-1);
835 				} else if (nm == 0) {
836 					t1 = new_merged_mapping(t->dbId, t);
837 					/* we clone ourselves */
838 					if (t1) {
839 						if ((append_domainContext(&t1,
840 					ypDomains->domainLabels[i - 1],
841 					ypDomains->domains[i - 1])) != 0) {
842 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
843 					"Error appending domainContext %s",
844 					ypDomains->domainLabels[i - 1]);
845 							free(t1);
846 							return (-1);
847 						}
848 						if (prev != NULL) {
849 							t1->next = prev->next;
850 							prev->next = t1;
851 							prev = prev->next;
852 						} else {
853 							t1->next =
854 							    *table_mapping;
855 							*table_mapping = t1;
856 							prev = t1;
857 						}
858 					} else { /* if !t1 */
859 						p_error = parse_internal_error;
860 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
861 					"%s:Could not create new table -"
862 					" check all instances of %s for errors",
863 					    myself, t->dbId);
864 						return (-1);
865 					}
866 				} else if (nm == 1) {
867 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
868 				"Mapping structure %s,%s already exists.",
869 					    t->dbId,
870 					    ypDomains->domainLabels[i - 1]);
871 					/*
872 					 * We should be deleting this, but can't
873 					 * really do it here, because we need to
874 					 * match with the domainLabels[0] case
875 					 * too. So we will just flag it for now.
876 					 */
877 					if (merge_table_mapping(t,
878 					    returned_map)) {
879 						logmsg(MSG_NOTIMECHECK, LOG_ERR,
880 	"Error merging information from the %s to the %s mapping structure.",
881 						    t->dbId,
882 						    returned_map->dbId);
883 						return (-1);
884 					}
885 					del_tbl_flag = 1;
886 				} else if (nm == 2) {
887 					if ((append_domainContext(&t,
888 					    ypDomains->domainLabels[i - 1],
889 					    ypDomains->domains[i - 1])) != 0) {
890 						logmsg(MSG_NOTIMECHECK, LOG_ERR,
891 					"Error appending domainContext %s",
892 						ypDomains->domainLabels[i - 1]);
893 						return (-1);
894 					}
895 				} /* end of "if (nm == 0)" */
896 			} /* end of else if (i > 1) */
897 
898 
899 			/*
900 			 * 'merge_table_mapping' only copies unexpanded
901 			 * objectDN values into returned_map. Hence,
902 			 * read.base and write.base in returned_map
903 			 * needs to be expanded.
904 			 */
905 			if (nm == 1 && returned_map && returned_map->objectDN) {
906 				r = make_fqdn(
907 				    returned_map->objectDN,
908 				    ypDomains->domains[i - 1]);
909 				if (r == TRUE &&
910 				    returned_map->objectDN->write.base) {
911 					r = make_full_dn(
912 					    &returned_map->objectDN->write.base,
913 					    ypDomains->domains[i - 1]);
914 				}
915 
916 				if (r == FALSE) {
917 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
918 					    "Error appending domainContext "
919 					    "%s to %s",
920 					    ypDomains->domainLabels[i - 1],
921 					    returned_map->dbId);
922 					return (-2);
923 				}
924 			}
925 			i--;
926 		} /* end of while i > 0 loop */
927 
928 		if (del_tbl_flag == 1) {
929 			if (prev != NULL) {
930 				prev->next = t->next;
931 				free_table_mapping(t);
932 				t = prev->next;
933 			} else {
934 				*table_mapping = t->next;
935 				free_table_mapping(t);
936 				t = *table_mapping;
937 			}
938 			del_tbl_flag = 0;
939 		} else {
940 			prev = t;
941 			t = t->next;
942 		}
943 	} /* end of table mapping loop */
944 
945 	for (t = *table_mapping; t != NULL; t = t->next) {
946 		if (!t->dbId) {
947 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
948 			    "%s:Fatal error: structure with no dbId found.",
949 			    myself);
950 			return (-2);
951 		}
952 		append_dot(&t->dbId);
953 		if (!t->objectDN) {
954 			p_error = parse_internal_error;
955 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
956 			    "%s:No objectDN for %s.", myself, t->dbId);
957 			return (-1);
958 		}
959 	}
960 
961 	return (0);
962 }
963 
964 /*
965  * FUNCTION: append_mapping_rule
966  *
967  * Appends mapping rules to a table_mapping structure
968  * with previously existing rules. flag controls whether
969  * the functions works on the rules From or To LDAP.
970  *
971  * RETURN VALUE: 0 on success, >= 1 on failure.
972  */
973 
974 static int
975 append_mapping_rule(__nis_mapping_rule_t *src_rule,
976 	__nis_table_mapping_t *dst, int flag)
977 {
978 	__nis_mapping_rule_t **rules = NULL;
979 
980 	if (flag == 0) {
981 		if (dst->ruleFromLDAP == NULL) {
982 			p_error = parse_internal_error;
983 			return (1);
984 		}
985 		rules = (__nis_mapping_rule_t **)
986 		    s_realloc(dst->ruleFromLDAP,
987 		    (dst->numRulesFromLDAP + 1) *
988 		    sizeof (__nis_mapping_rule_t *));
989 		if (rules == NULL)
990 			return (2);
991 		dst->ruleFromLDAP = rules;
992 		rules[dst->numRulesFromLDAP] = dup_mapping_rule(src_rule);
993 		if (rules[dst->numRulesFromLDAP] == NULL) {
994 			p_error = parse_no_mem_error;
995 			return (2);
996 		}
997 		dst->numRulesFromLDAP++;
998 	} else if (flag == 1) {
999 		if (dst->ruleToLDAP == NULL) {
1000 			p_error = parse_internal_error;
1001 			return (1);
1002 		}
1003 		rules = (__nis_mapping_rule_t **)
1004 		    s_realloc(dst->ruleToLDAP,
1005 		    (dst->numRulesToLDAP + 1) *
1006 		    sizeof (__nis_mapping_rule_t *));
1007 		if (rules == NULL)
1008 			return (2);
1009 		dst->ruleToLDAP = rules;
1010 		rules[dst->numRulesToLDAP] = dup_mapping_rule(src_rule);
1011 		if (rules[dst->numRulesToLDAP] == NULL) {
1012 			p_error = parse_no_mem_error;
1013 			return (2);
1014 		}
1015 		dst->numRulesToLDAP++;
1016 	} else
1017 		return (1);
1018 
1019 	return (0);
1020 }
1021 
1022 /*
1023  * FUNCTION: check_domain_specific_order
1024  *
1025  * Makes sure that an attribute with explicitly specified
1026  * nisLDAPdomainContext is found before its non-domain
1027  * specific counterpart.
1028  *
1029  * RETURN VALUE: 0 normal exit
1030  *               1 if domain specific attribute found
1031  *                 after non-domain specific one.
1032  *				 -1 some error condition
1033  */
1034 
1035 int
1036 check_domain_specific_order(const char *sd,
1037 	config_key	attrib_num,
1038 	__nis_table_mapping_t *table_mapping,
1039 	__yp_domain_context_t   *ypDomains)
1040 {
1041 	__nis_table_mapping_t *t;
1042 	char    *myself = "check_domain_specific_order";
1043 	char	*type;
1044 	char	*dbId = 0;
1045 	int 	i, len;
1046 	int		match = 0;
1047 
1048 	if (ypDomains) {
1049 		if (!ypDomains->numDomains) {
1050 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1051 			    "%s:No domains specified.", myself);
1052 			return (-1);
1053 		}
1054 	} else {
1055 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1056 		    "%s:No domain structure supplied.", myself);
1057 		return (-1);
1058 	}
1059 
1060 	for (i = 0; i < ypDomains->numDomains; i++) {
1061 		for (t = table_mapping; t != NULL; t = t->next) {
1062 			len = strlen(sd);
1063 			if ((strcasecmp(t->dbId, sd) == 0) && (len ==
1064 			    strlen(t->dbId)))
1065 				/* prevent from matching against itself */
1066 				continue;
1067 			dbId = s_strndup(t->dbId, strlen(t->dbId));
1068 			if (dbId == NULL) {
1069 				logmsg(MSG_NOMEM, LOG_ERR,
1070 				    "%s:Memory allocation error.", myself);
1071 				return (-1);
1072 			}
1073 
1074 			if (getfullmapname(&dbId,
1075 			    ypDomains->domainLabels[i])) {
1076 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1077 				"Error getting fully qualified name for %s",
1078 				    dbId);
1079 				free(dbId);
1080 				return (-1);
1081 			}
1082 			if ((strcasecmp(dbId, sd) == 0) && (len ==
1083 			    strlen(dbId))) {
1084 				match = 0;
1085 				switch (attrib_num) {
1086 					case key_yp_map_flags:
1087 						if (t->usedns_flag != 0 ||
1088 						    t->securemap_flag != 0)
1089 							match = 1;
1090 						type = YP_MAP_FLAGS;
1091 						break;
1092 					case key_yp_comment_char:
1093 						if (t->commentChar !=
1094 						    DEFAULT_COMMENT_CHAR)
1095 							match = 1;
1096 						type = YP_COMMENT_CHAR;
1097 						break;
1098 					case key_yp_repeated_field_separators:
1099 						if (strcmp(t->separatorStr,
1100 						    DEFAULT_SEP_STRING) != 0)
1101 							match = 1;
1102 						type =
1103 					YP_REPEATED_FIELD_SEPARATORS;
1104 						break;
1105 					case key_yp_name_fields:
1106 						if (t->e && t->numColumns)
1107 							match = 1;
1108 						type = YP_NAME_FIELDS;
1109 					case key_yp_split_field:
1110 						if (t->e && t->numColumns)
1111 							match = 1;
1112 						type = YP_SPLIT_FIELD;
1113 						break;
1114 					case key_yp_db_id_map:
1115 						if (t->objName)
1116 							match = 1;
1117 						type = YP_DB_ID_MAP;
1118 						break;
1119 					case key_yp_entry_ttl:
1120 						if (t->initTtlLo !=
1121 						    (time_t)NO_VALUE_SET)
1122 							match = 1;
1123 						type = YP_ENTRY_TTL;
1124 						break;
1125 					case key_yp_ldap_object_dn:
1126 						if (t->objectDN)
1127 							match = 1;
1128 						type = YP_LDAP_OBJECT_DN;
1129 						break;
1130 					case key_nis_to_ldap_map:
1131 						if (t->ruleToLDAP)
1132 							match = 1;
1133 						type = NIS_TO_LDAP_MAP;
1134 						break;
1135 					case key_ldap_to_nis_map:
1136 						if (t->ruleFromLDAP)
1137 							match = 1;
1138 						type = LDAP_TO_NIS_MAP;
1139 						break;
1140 					default:
1141 						type = "unknown";
1142 						match = 0;
1143 						break;
1144 				}	/* end of switch */
1145 				if (match) {
1146 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
1147 "Relative attribute '%s' of type '%s' found before fully qualified one '%s'",
1148 					    t->dbId, type, sd);
1149 					free(dbId);
1150 					dbId = NULL;
1151 					return (1);
1152 				}
1153 			} /* end of strncasecmp */
1154 			free(dbId);
1155 			dbId = NULL;
1156 		} /* end of t loop */
1157 	} /* end of i loop */
1158 	if (dbId)
1159 		free(dbId);
1160 	dbId = NULL;
1161 	return (0);
1162 }
1163 
1164 int
1165 getfullmapname(char **mapname, const char *domainname)
1166 {
1167 	char *maps = *mapname;
1168 	int maplen = strlen(maps);
1169 	int domainlen = strlen(domainname);
1170 
1171 	if (!maplen || !domainlen ||
1172 	    maps[maplen - 1] == PERIOD_CHAR)
1173 		return (1);
1174 	else if (strchr(maps, COMMA_CHAR)) {
1175 		/* map already has a domain part, do nothing */
1176 		return (0);
1177 	} else {
1178 		append_comma(&maps);
1179 		maplen = strlen(maps);
1180 		maps = realloc(maps, (maplen + domainlen + 1));
1181 		if (maps != NULL) {
1182 			if (strlcat(maps, domainname, (maplen + domainlen + 1))
1183 			    >= (maplen + domainlen + 1))
1184 				return (1);
1185 			*mapname = maps;
1186 			return (0);
1187 		} else
1188 			return (1);
1189 	}
1190 }
1191 
1192 /*
1193  * FUNCTION: checkfullmapname
1194  *
1195  * Tries to find out if by appending the table mapping structures
1196  * with each of the provided nisLDAPdomainContexts, an already
1197  * existing fqdn table mapping structure results. That would be the
1198  * case when a full qualified domain specific attribute was present.
1199  *
1200  * Note that per NISLDAPmapping(4) such an attribute MUST be listed
1201  * in the mapping file BEFORE its non-fqdn counterpart.
1202  *
1203  * RETURNS:	0 normal exit, 1 if an existing structure found, -1 for all
1204  * errors, 2 if already fqdn. If returning 1 the existing structure is
1205  * in found_map.
1206  */
1207 
1208 int
1209 checkfullmapname(const char *mapname, const char *domainname,
1210 __nis_table_mapping_t **table_mapping,
1211 __nis_table_mapping_t **found_map)
1212 {
1213 	char *map;
1214 
1215 	*found_map = NULL;
1216 
1217 	/* This function does not alter mapname */
1218 
1219 	if (!mapname || !domainname || *table_mapping == NULL)
1220 		return (-1);
1221 
1222 	if (strchr(mapname, COMMA_CHAR))
1223 		return (2);
1224 
1225 	if ((map = s_strndup(mapname, strlen(mapname))) == 0)
1226 		return (-1);
1227 
1228 	if (getfullmapname(&map, domainname)) {
1229 		free(map);
1230 		return (-1);
1231 	}
1232 
1233 	*found_map = find_table_mapping(map, strlen(map), *table_mapping);
1234 	if (*found_map) {
1235 		free(map);
1236 		return (1);
1237 	}
1238 
1239 	free(map);
1240 	return (0);
1241 }
1242 
1243 /*
1244  * FUNCTION:	append_domainContext
1245  *
1246  * Higher level function to append the domains to the appropriate
1247  * fields in a table mapping structure. Calls either getfullmapname()
1248  * or make_full_dn() to do the actual append.
1249  *
1250  * RETURNS: 0 on success, -1 on any error.
1251  */
1252 
1253 int
1254 append_domainContext(__nis_table_mapping_t **table_map,
1255 char   *DomainLabel, char *Domain)
1256 {
1257 	__nis_table_mapping_t *tmp_map = *table_map;
1258 	char *lasts;
1259 	char *tmp_dbId = NULL;
1260 	char *id = NULL;
1261 	int  domain_specific = 0;
1262 	char *myself = "append_domainContext";
1263 
1264 	if (!DomainLabel || !Domain || !tmp_map)
1265 		return (-1);
1266 	if (tmp_map->dbId == NULL || tmp_map->objName == NULL) {
1267 		p_error = parse_bad_map_error;
1268 		return (-1);
1269 	}
1270 	tmp_dbId = s_strndup(tmp_map->dbId, strlen(tmp_map->dbId));
1271 	if (!tmp_dbId)
1272 		return (-1);
1273 	if (strchr(tmp_map->dbId, COMMA_CHAR)) {
1274 		domain_specific = 1;
1275 		id = (char *)strtok_r(tmp_dbId, COMMA_STRING, &lasts);
1276 		if (id)
1277 			id = (char *)strtok_r(NULL, COMMA_STRING, &lasts);
1278 		else {
1279 			free(tmp_dbId);
1280 			return (-1);
1281 		}
1282 		if (!id) {
1283 			free(tmp_dbId);
1284 			return (-1);
1285 		}
1286 		if (strcasecmp(id, DomainLabel)) {
1287 			free(tmp_dbId);
1288 			return (0);
1289 		}
1290 	} else {
1291 		if (getfullmapname(&tmp_map->dbId, DomainLabel)) {
1292 			free(tmp_dbId);
1293 			return (-1);
1294 		}
1295 		append_dot(&tmp_map->dbId);
1296 	}
1297 	if (tmp_dbId)
1298 		free(tmp_dbId);
1299 	tmp_dbId = NULL;
1300 
1301 	if (getfullmapname(&tmp_map->objName, DomainLabel))
1302 		return (-1);
1303 	append_dot(&tmp_map->objName);
1304 
1305 	/*
1306 	 * If domain specific mapping doesn't have objectDN,
1307 	 * then don't touch. Most probably, pass for the generic mapping
1308 	 * will handle this by coping over it's own objectDN
1309 	 */
1310 	if (domain_specific && tmp_map->objectDN == NULL)
1311 		return (0);
1312 
1313 	if (tmp_map->objectDN == NULL) {
1314 		/* Allocate memory to objectDN */
1315 		tmp_map->objectDN = (__nis_object_dn_t *)
1316 		    s_calloc(1, sizeof (__nis_object_dn_t));
1317 		if (tmp_map->objectDN == NULL) {
1318 			logmsg(MSG_NOMEM, LOG_ERR,
1319 "%s: Cannot allocate memory for objectDN",
1320 			    myself);
1321 			return (2);
1322 		}
1323 		tmp_map->objectDN->read.base = NULL;
1324 		tmp_map->objectDN->write.base = NULL;
1325 		tmp_map->objectDN->read.attrs = NULL;
1326 		tmp_map->objectDN->write.attrs = NULL;
1327 		tmp_map->objectDN->read.scope = LDAP_SCOPE_ONELEVEL;
1328 		tmp_map->objectDN->write.scope = LDAP_SCOPE_UNKNOWN;
1329 	}
1330 
1331 	if (!make_fqdn(tmp_map->objectDN, Domain))
1332 		return (-1);
1333 	if (tmp_map->objectDN->write.base) {
1334 		if (!make_full_dn(&tmp_map->objectDN->write.base, Domain))
1335 			return (-1);
1336 	}
1337 
1338 	return (0);
1339 }
1340