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