xref: /illumos-gate/usr/src/lib/libnisdb/nis_parse_ldap_map.c (revision 2c5ec7a875dcd76853e6618614e990f1e8cdd56d)
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 2001-2003 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 <unistd.h>
34 #include <locale.h>
35 
36 #include "ldap_parse.h"
37 #include "nis_parse_ldap_conf.h"
38 #include "nis_parse_ldap_yp_util.h"
39 #include "nis_parse_ldap_util.h"
40 
41 /* other attribute functions */
42 static char *getIndex(const char **s_cur, const char *end_s);
43 static bool_t get_ttls(const char *s, const char *s_end,
44     __nis_table_mapping_t *t_mapping);
45 static __nis_object_dn_t *parse_object_dn(const char *s, const char *end);
46 static int	parse_name_fields(const char *name_s, const char *name_s_end,
47 	__nis_table_mapping_t *t_mapping);
48 static void get_mapping_rule(const char *s, int len,
49     __nis_table_mapping_t *tbl, bool_t to_ldap);
50 static bool_t get_deleteDisp(const char *s_begin, const char *s_end,
51     __nis_object_dn_t *obj_dn);
52 
53 /* mapping rule functions */
54 static const char *get_lhs(const char *s, const char *end_s,
55     __nis_mapping_rlhs_t *lhs, __nis_mapping_item_type_t item_type);
56 static const char *get_lhs_match(const char *s, const char *end_s,
57     __nis_mapping_rlhs_t *lhs, __nis_mapping_item_type_t item_type);
58 static const char *get_lhs_paren_item(const char *s, const char *end_s,
59     __nis_mapping_rlhs_t *lhs, __nis_mapping_item_type_t item_type);
60 static const char *get_rhs(const char *s, const char *end_s,
61     __nis_mapping_rlhs_t *lhs, __nis_mapping_item_type_t item_type);
62 static const char *get_mapping_item(const char *s, const char *end_s,
63     __nis_mapping_item_t *item, __nis_mapping_item_type_t type);
64 static const char *get_print_mapping_element(const char *s,
65     const char *end_s, char *fmt_string, __nis_mapping_element_t *e,
66     __nis_mapping_item_type_t item_type);
67 static const char *get_subElement(const char *s, const char *end_s,
68     __nis_mapping_sub_element_t *subelement,
69     __nis_mapping_item_type_t type);
70 static bool_t get_mapping_format(const char *fmt_string,
71     __nis_mapping_format_t **fmt, int *nfmt, int *numItems,
72     bool_t print_mapping);
73 extern __yp_domain_context_t ypDomains;
74 
75 /*
76  * FUNCTION:	add_mapping_attribute
77  *
78  *	Adds the attribute value to __nis_table_mapping_t
79  *	if the value is not yet set for the given database.
80  *
81  * RETURN VALUE:	0 on success, -1 on failure
82  *
83  * INPUT:		attribute number and value
84  */
85 
86 int
87 add_mapping_attribute(
88 	config_key		attrib_num,
89 	const char		*attrib_val,
90 	int			attrib_len,
91 	__nis_table_mapping_t	**table_mapping)
92 {
93 	const char		*s;
94 	const char		*attrib_end;
95 	const char		*db_id_end;
96 	const char		*begin_token;
97 	char			*index_string;
98 	__nis_object_dn_t	*objectDN;
99 	__nis_table_mapping_t	*t_mapping;
100 	__nis_table_mapping_t	*t;
101 
102 	bool_t			new_mapping	= FALSE;
103 	int				nm;
104 	char			*tmp_dbId;
105 
106 	attrib_end = attrib_val + attrib_len;
107 	for (s = attrib_val; s < attrib_end; s++)
108 		if (*s == COLON_CHAR)
109 			break;
110 
111 	if (s == attrib_end || *attrib_val == COLON_CHAR) {
112 		p_error = parse_unexpected_data_end_rule;
113 		return (-1);
114 	}
115 
116 	db_id_end = s;
117 	while (s > attrib_val && is_whitespace(s[-1]))
118 		s--;
119 
120 	if (s == attrib_val) {
121 		p_error = parse_unexpected_data_end_rule;
122 		return (-1);
123 	}
124 
125 	if (yp2ldap) {
126 		tmp_dbId = s_strndup(attrib_val, s - attrib_val);
127 		if (tmp_dbId == NULL) {
128 			p_error = parse_no_mem_error;
129 			return (-1);
130 		}
131 		if (strchr(tmp_dbId, COMMA_CHAR)) {
132 			/* domain explicitly specified */
133 			nm = check_domain_specific_order(tmp_dbId,
134 				attrib_num, *table_mapping, &ypDomains);
135 			/*
136 			 * No logging is needed here, as
137 			 * check_domain_specific_order
138 			 * will log any appropriate errors.
139 			 */
140 			if (nm != 0) {
141 				free(tmp_dbId);
142 				return (-1);
143 			}
144 		}
145 		free(tmp_dbId);
146 	}
147 
148 	if ((t_mapping = find_table_mapping(attrib_val,
149 			s - attrib_val, *table_mapping)) == NULL) {
150 		/* No mapping with this id, create one */
151 		t_mapping = (__nis_table_mapping_t *)
152 			s_calloc(1, sizeof (__nis_table_mapping_t));
153 
154 		if (t_mapping == NULL) {
155 			p_error = parse_no_mem_error;
156 			return (-1);
157 		}
158 		(void) initialize_table_mapping(t_mapping);
159 
160 		/* dbId is the label before the colon */
161 		t_mapping->dbId = s_strndup(attrib_val, s - attrib_val);
162 		if (t_mapping->dbId == NULL) {
163 			p_error = parse_no_mem_error;
164 			free(t_mapping);
165 			return (-1);
166 		}
167 		new_mapping = TRUE;
168 	} else {
169 		/* a table mapping already exists, use it */
170 		new_mapping = FALSE;
171 	}
172 
173 	s = db_id_end + 1;
174 	while (s < attrib_end && is_whitespace(*s))
175 		s++;
176 
177 	switch (attrib_num) {
178 		case key_yp_map_flags:
179 			if (t_mapping->usedns_flag != 0 ||
180 				t_mapping->securemap_flag != 0) {
181 				warn_duplicate_map(t_mapping->dbId,
182 					attrib_num);
183 				break;
184 			}
185 			while (is_whitespace(*s) && s < attrib_end)
186 				s++;
187 			while (s < attrib_end) {
188 				if (s < attrib_end && *s == 'b')
189 					t_mapping->usedns_flag = 1;
190 				if (s < attrib_end && *s == 's')
191 					t_mapping->securemap_flag = 1;
192 				s++;
193 			}
194 			break;
195 		case key_yp_comment_char:
196 			if (t_mapping->commentChar !=
197 				DEFAULT_COMMENT_CHAR) {
198 				warn_duplicate_map(t_mapping->dbId, attrib_num);
199 				break;
200 			}
201 			while (is_whitespace(*s) && s < attrib_end)
202 				s++;
203 			if (s < attrib_end && (s+1) < attrib_end &&
204 				(s+2) <= attrib_end) {
205 				while (is_whitespace(attrib_end[-1]))
206 					attrib_end--;
207 				while (*s != SINGLE_QUOTE_CHAR)
208 					s++;
209 				if (*s == SINGLE_QUOTE_CHAR &&
210 					*(s+2) == SINGLE_QUOTE_CHAR) {
211 					t_mapping->commentChar = *(s+1);
212 				} else if (*s == SINGLE_QUOTE_CHAR &&
213 					*(s+1) == SINGLE_QUOTE_CHAR) {
214 					t_mapping->commentChar = NULL;
215 				} else {
216 					/* anything else is an error */
217 					p_error = parse_bad_yp_comment_error;
218 				}
219 				break;
220 			} else {
221 				p_error = parse_bad_yp_comment_error;
222 				break;
223 			}
224 		case key_yp_repeated_field_separators:
225 			while (s < attrib_end && is_whitespace(*s))
226 				s++;
227 			if (s < attrib_end) {
228 				while (is_whitespace(attrib_end[-1]))
229 					attrib_end--;
230 				while (s < attrib_end &&
231 						*s != DOUBLE_QUOTE_CHAR)
232 					s++;
233 				s++;
234 				begin_token = s;
235 				while (s < attrib_end &&
236 						*s != DOUBLE_QUOTE_CHAR) {
237 					if (*s == ESCAPE_CHAR)
238 						s++;
239 					s++;
240 				}
241 				t_mapping->separatorStr =
242 					s_strndup(begin_token, s - begin_token);
243 				if (t_mapping->separatorStr == NULL)
244 					break;
245 			} else {
246 				p_error = parse_bad_field_separator_error;
247 			}
248 			break;
249 		case key_yp_name_fields:
250 		case key_yp_split_field:
251 			if (t_mapping->e || t_mapping->numSplits > 0) {
252 				warn_duplicate_map(t_mapping->dbId,
253 					attrib_num);
254 				break;
255 			}
256 			if (parse_name_fields(s, attrib_end, t_mapping)) {
257 				p_error = parse_bad_name_field;
258 			}
259 			break;
260 		case key_yp_db_id_map:
261 		case key_db_id_map:
262 			if (t_mapping->objName != NULL) {
263 				warn_duplicate_map(t_mapping->dbId, attrib_num);
264 				break;
265 			}
266 
267 			if (s < attrib_end && *s == OPEN_BRACKET) {
268 				index_string = getIndex(&s, attrib_end);
269 				if (index_string == NULL)
270 					break;
271 				(void) parse_index(index_string,
272 					index_string + strlen(index_string),
273 					&t_mapping->index);
274 				free(index_string);
275 				if (p_error != no_parse_error)
276 					break;
277 			}
278 			while (is_whitespace(*s) && s < attrib_end)
279 				s++;
280 			if (s < attrib_end) {
281 				while (is_whitespace(attrib_end[-1]))
282 					attrib_end--;
283 				t_mapping->objName =
284 					s_strndup_esc(s, attrib_end - s);
285 			} else {
286 				if (yp2ldap) {
287 					p_error = parse_bad_map_error;
288 				} else {
289 					t_mapping->objName = s_strndup(s, 0);
290 				}
291 			}
292 			break;
293 
294 		case key_yp_entry_ttl:
295 		case key_entry_ttl:
296 			if (t_mapping->initTtlLo != (time_t)NO_VALUE_SET) {
297 				warn_duplicate_map(t_mapping->dbId, attrib_num);
298 				break;
299 			}
300 
301 			if (!get_ttls(s, attrib_end, t_mapping))
302 				p_error = parse_bad_ttl_format_error;
303 			break;
304 
305 		case key_yp_ldap_object_dn:
306 		case key_ldap_object_dn:
307 			if (t_mapping->objectDN != NULL) {
308 				warn_duplicate_map(t_mapping->dbId, attrib_num);
309 				break;
310 			}
311 			objectDN = parse_object_dn(s, attrib_end);
312 			if (objectDN == NULL)
313 				break;
314 			t_mapping->objectDN = objectDN;
315 			t_mapping->seq_num = seq_num++;
316 			break;
317 
318 		case key_nis_to_ldap_map:
319 		case key_nisplus_to_ldap_map:
320 			if (t_mapping->ruleToLDAP != 0) {
321 				warn_duplicate_map(t_mapping->dbId, attrib_num);
322 				break;
323 			}
324 
325 			get_mapping_rule(s, attrib_end - s, t_mapping, TRUE);
326 			break;
327 
328 		case key_ldap_to_nis_map:
329 		case key_ldap_to_nisplus_map:
330 			if (t_mapping->ruleFromLDAP != NULL) {
331 				warn_duplicate_map(t_mapping->dbId, attrib_num);
332 				break;
333 			}
334 
335 			get_mapping_rule(s, attrib_end - s, t_mapping, FALSE);
336 			break;
337 
338 		default:
339 			p_error = parse_internal_error;
340 			break;
341 	}
342 	if (p_error == no_parse_error) {
343 		if (new_mapping) {
344 			if (*table_mapping == NULL)
345 				*table_mapping = t_mapping;
346 			else {
347 				for (t = *table_mapping; t->next != NULL;
348 				    t = t->next)
349 					;
350 				t->next = t_mapping;
351 			}
352 		}
353 	} else {
354 		if (new_mapping)
355 			free_table_mapping(t_mapping);
356 	}
357 	return (p_error == no_parse_error ? 0 : -1);
358 }
359 
360 /*
361  * FUNCTION:	add_ypdomains_attribute
362  *
363  * Adds the yp domains information to the __yp_domain_context_t
364  * structure.
365  *
366  * RETURN:		0 on success, -1 on failure
367  *
368  * INPUT:		attribute number and value
369  */
370 
371 int
372 add_ypdomains_attribute(
373 	config_key		attrib_num,
374 	const char		*attrib_val,
375 	int				attrib_len,
376 	__yp_domain_context_t	*ypDomains)
377 {
378 	const char 		*s;
379 	const char		*attrib_end;
380 	int				numDomains = 0;
381 
382 	attrib_end = attrib_val + attrib_len;
383 	for (s = attrib_val; s < attrib_end; s++) {
384 		if (*s == COLON_CHAR) {
385 			break;
386 		}
387 	}
388 	while (s > attrib_val && is_whitespace(s[-1]))
389 		s--;
390 
391 	if (s == attrib_val) {
392 		p_error = parse_unexpected_data_end_rule;
393 		return (-1);
394 	}
395 
396 	if (ypDomains == NULL) {
397 		/*
398 		 * No point allocating. We cant return the resulting structure,
399 		 * so just return failure. Should not ever happen because we
400 		 * are always called with a pointer to the global ypDomains
401 		 * structure.
402 		 */
403 		return (-1);
404 	}
405 
406 	switch (attrib_num) {
407 		case key_yp_domain_context:
408 			numDomains = ypDomains->numDomains;
409 			ypDomains->domainLabels =
410 				(char **)s_realloc(ypDomains->domainLabels,
411 				(numDomains + 1) *
412 				sizeof (ypDomains->domainLabels[0]));
413 			if (ypDomains->domainLabels == NULL) {
414 				p_error = parse_no_mem_error;
415 				free_yp_domain_context(ypDomains);
416 				break;
417 			}
418 			ypDomains->domainLabels[numDomains] =
419 				s_strndup(attrib_val, s - attrib_val);
420 			if (ypDomains->domainLabels[numDomains] == NULL) {
421 				p_error = parse_no_mem_error;
422 				free_yp_domain_context(ypDomains);
423 				break;
424 			}
425 			ypDomains->numDomains = numDomains + 1;
426 			while (s < attrib_end && is_whitespace(*s))
427 				s++;
428 			if (*s == COLON_CHAR)
429 				s++;
430 			while (s < attrib_end && is_whitespace(*s))
431 				s++;
432 			ypDomains->domains =
433 				(char **)s_realloc(ypDomains->domains,
434 				(numDomains + 1) *
435 				sizeof (ypDomains->domains[0]));
436 			if (ypDomains->domains == NULL) {
437 				p_error = parse_no_mem_error;
438 				free_yp_domain_context(ypDomains);
439 				break;
440 			}
441 
442 			if (s < attrib_end) {
443 				while (is_whitespace(attrib_end[-1]))
444 					attrib_end--;
445 				ypDomains->domains[numDomains] =
446 					s_strndup_esc(s, attrib_end - s);
447 				if (ypDomains->domains[numDomains] == NULL) {
448 					p_error = parse_no_mem_error;
449 					free_yp_domain_context(ypDomains);
450 					break;
451 				}
452 			} else {
453 				p_error = parse_unexpected_yp_domain_end_error;
454 				free(ypDomains->domainLabels[numDomains]);
455 				ypDomains->domainLabels[numDomains] = NULL;
456 				ypDomains->numDomains--;
457 				free_yp_domain_context(ypDomains);
458 			}
459 			break;
460 		case key_yppasswdd_domains:
461 			ypDomains->yppasswddDomainLabels =
462 				(char **)s_realloc(
463 				ypDomains->yppasswddDomainLabels,
464 				(ypDomains->numYppasswdd + 1) *
465 				sizeof (ypDomains->yppasswddDomainLabels[0]));
466 			if (ypDomains->yppasswddDomainLabels == NULL) {
467 				p_error = parse_no_mem_error;
468 				break;
469 			}
470 			ypDomains->yppasswddDomainLabels
471 				[ypDomains->numYppasswdd] =
472 				s_strndup(attrib_val, s - attrib_val);
473 			if (ypDomains->yppasswddDomainLabels
474 				[ypDomains->numYppasswdd] == NULL) {
475 				p_error = parse_no_mem_error;
476 			}
477 			ypDomains->numYppasswdd++;
478 			break;
479 	}
480 
481 	return (p_error == no_parse_error ? 0 : -1);
482 }
483 
484 /*
485  * FUNCTION:	get_ttls
486  *
487  *	Parse time to live attribute
488  *
489  * RETURN VALUE:	TRUE on success, FALSE on failure
490  *
491  * INPUT:		the attribute value
492  */
493 
494 static bool_t
495 get_ttls(
496 	const char		*s,
497 	const char		*s_end,
498 	__nis_table_mapping_t	*t_mapping)
499 {
500 	time_t		initTtlHi	= 0;
501 	time_t		initTtlLo	= 0;
502 	time_t		ttl		= 0;
503 	time_t		digit;
504 
505 	/*
506 	 * attribute should be of the form
507 	 * initialTTLlo ":" initialTTLhi ":" runningTTL
508 	 */
509 
510 	if (s == s_end) {
511 		p_error = parse_bad_ttl_format_error;
512 		return (FALSE);
513 	}
514 
515 	if (isdigit(*s)) {
516 		while (s < s_end && isdigit(*s)) {
517 			digit = (*s++) - '0';
518 			if (WILL_OVERFLOW_TIME(initTtlLo, digit))
519 				initTtlLo = TIME_MAX;
520 			else
521 				initTtlLo = initTtlLo * 10 + digit;
522 		}
523 	} else {
524 		initTtlLo = ONE_HOUR;
525 	}
526 
527 	while (s < s_end && is_whitespace(*s))
528 		s++;
529 	if (s + 1 >= s_end || *s++ != COLON_CHAR) {
530 		p_error = parse_bad_ttl_format_error;
531 		return (FALSE);
532 	}
533 
534 	while (s < s_end && is_whitespace(*s))
535 		s++;
536 	if (isdigit(*s)) {
537 		while (s < s_end && isdigit(*s)) {
538 			digit = (*s++) - '0';
539 			if (WILL_OVERFLOW_TIME(initTtlHi, digit))
540 				initTtlHi = TIME_MAX;
541 			else
542 				initTtlHi = initTtlHi * 10 + digit;
543 		}
544 	} else {
545 		initTtlHi = initTtlLo;
546 	}
547 
548 	while (s < s_end && is_whitespace(*s))
549 		s++;
550 	if (s >= s_end || *s++ != COLON_CHAR) {
551 		p_error = parse_bad_ttl_format_error;
552 		return (FALSE);
553 	}
554 
555 	while (s < s_end && is_whitespace(*s))
556 		s++;
557 	if (isdigit(*s)) {
558 		while (s < s_end && isdigit(*s)) {
559 			digit = (*s++) - '0';
560 			if (WILL_OVERFLOW_TIME(ttl, digit))
561 				ttl = TIME_MAX;
562 			else
563 				ttl = ttl * 10 + digit;
564 		}
565 	} else {
566 		ttl = ONE_HOUR;
567 	}
568 	while (s < s_end && is_whitespace(*s))
569 		s++;
570 	if (s != s_end) {
571 		p_error = parse_bad_ttl_format_error;
572 		return (FALSE);
573 	}
574 
575 	t_mapping->initTtlLo = initTtlLo;
576 	t_mapping->initTtlHi = initTtlHi;
577 	t_mapping->ttl = ttl;
578 	return (TRUE);
579 }
580 
581 /*
582  * FUNCTION:	parse_name_fields
583  *
584  * Parse yp name fields
585  *
586  * RETURN VALUE:	0 on success, non-zero on failure
587  *
588  * INPUTS:		attrib_value and attribute_end pointers.
589  */
590 
591 static int
592 parse_name_fields(const char *name_s,
593 	const char *name_s_end,
594 	__nis_table_mapping_t   *t_map)
595 {
596 	int	i, n = 0;
597 	int nElements = 0;
598 	int numSplits = 0;
599 	int parse_next_line = 1;
600 	int itm_count = 0;
601 	const char	*begin_fmt;
602 	const char	*end_fmt;
603 	const char	*begin_token;
604 	const char	*end_token;
605 	char	*fmt_string = NULL;
606 	__nis_mapping_format_t  *base = NULL;
607 	__nis_mapping_item_t    *item = NULL;
608 	__nis_mapping_element_t *elmnt = NULL;
609 	__nis_mapping_item_type_t   item_type = mit_nisplus;
610 	token_type	token;
611 
612 	t_map->numColumns = 0;
613 
614 	for (; parse_next_line > 0; parse_next_line--) {
615 		nElements = 0;
616 		item = NULL;
617 		base = NULL;
618 		while (name_s < name_s_end && *name_s != OPEN_PAREN_CHAR)
619 			name_s++;
620 		if (name_s == name_s_end) {
621 			p_error = parse_unexpected_data_end_rule;
622 			return (1);
623 		}
624 		while (name_s < name_s_end && *name_s != DOUBLE_QUOTE_CHAR)
625 			name_s++;
626 		if (name_s == name_s_end) {
627 			p_error = parse_unexpected_data_end_rule;
628 			return (1);
629 		}
630 		begin_fmt = ++name_s; /* start of format string */
631 		while (name_s < name_s_end && *name_s != DOUBLE_QUOTE_CHAR)
632 			name_s++;
633 		if (name_s == name_s_end) {
634 			p_error = parse_unexpected_data_end_rule;
635 			return (1);
636 		}
637 		end_fmt = name_s;
638 		fmt_string = s_strndup(begin_fmt, end_fmt - begin_fmt);
639 		if (fmt_string == NULL) {
640 			p_error = parse_no_mem_error;
641 			return (2);
642 		}
643 		if (!get_mapping_format(fmt_string, &base, &n, NULL, FALSE)) {
644 			p_error = parse_internal_error;
645 			free(fmt_string);
646 			fmt_string = NULL;
647 			return (3);
648 		}
649 		free(fmt_string);
650 		fmt_string = NULL;
651 		for (n = 0; base[n].type != mmt_end; n++) {
652 			if (base[n].type != mmt_item && base[n].type
653 				!= mmt_berstring) {
654 				if (base[n].type == mmt_berstring_null)
655 					base[n].type = mmt_berstring;
656 				continue;
657 			}
658 			while (name_s < name_s_end && *name_s != COMMA_CHAR)
659 				name_s++;
660 			name_s++;    /* now at comma char */
661 			while (name_s < name_s_end && is_whitespace(*name_s))
662 				name_s++;
663 			begin_token = name_s++;
664 			end_token = name_s_end;
665 			name_s = get_next_token(
666 				&begin_token, &end_token, &token);
667 			if (name_s == NULL) {
668 				p_error = parse_item_expected_error;
669 				return (4);
670 			}
671 			if (token != string_token) {
672 				p_error = parse_item_expected_error;
673 				return (5);
674 			}
675 			item = (__nis_mapping_item_t *)s_realloc(item,
676 				(nElements + 1) *
677 				sizeof (__nis_mapping_item_t));
678 			if (item == NULL) {
679 				p_error = parse_no_mem_error;
680 				return (2);
681 			}
682 			name_s = get_mapping_item(begin_token, name_s_end,
683 				&item[nElements], item_type);
684 			if (name_s == NULL) {
685 				p_error = parse_unmatched_escape;
686 				for (n = 0; n < (nElements + 1); n++)
687 					free_mapping_item(&item[n]);
688 				free_mapping_format(base);
689 				return (4);
690 			}
691 			nElements++;
692 		}
693 		if (p_error != no_parse_error) {
694 			for (n = 0; n < (nElements + 1); n++)
695 				free_mapping_item(&item[n]);
696 			free_mapping_format(base);
697 			return (6);
698 		}
699 		name_s = skip_token(name_s, name_s_end, close_paren_token);
700 		if (name_s == NULL) {
701 			p_error = parse_close_paren_expected_error;
702 			for (n = 0; n < (nElements + 1); n++)
703 				free_mapping_item(&item[n]);
704 			free_mapping_format(base);
705 			return (4);
706 		}
707 		while (name_s < name_s_end && is_whitespace(*name_s))
708 			name_s++;
709 		if (*name_s == COMMA_CHAR)
710 			parse_next_line++;
711 
712 		if (nElements == 0) {
713 			p_error = parse_no_match_item;
714 			for (n = 0; n < (nElements + 1); n++)
715 				free_mapping_item(&item[n]);
716 			free_mapping_format(base);
717 			return (7);
718 		}
719 		elmnt = (__nis_mapping_element_t *)s_realloc(elmnt,
720 			(numSplits + 1) *
721 			sizeof (__nis_mapping_element_t));
722 		if (elmnt == NULL) {
723 			for (n = 0; n < (nElements + 1); n++)
724 				free_mapping_item(&item[n]);
725 			free_mapping_format(base);
726 			p_error = parse_no_mem_error;
727 			return (2);
728 		}
729 		elmnt[numSplits].type = me_match;
730 		elmnt[numSplits].element.match.numItems = nElements;
731 		elmnt[numSplits].element.match.item = item;
732 		elmnt[numSplits].element.match.fmt = base;
733 		item = NULL;
734 		base = NULL;
735 
736 		t_map->e = elmnt;
737 		t_map->numSplits = numSplits;
738 		n = t_map->numColumns;
739 
740 		for (i = n, itm_count = 0; i < n + nElements; i++) {
741 			if (t_map->e[numSplits].element.
742 				match.item[itm_count].name) {
743 				if (!add_column(t_map,
744 					t_map->e[numSplits].element.
745 					match.item[itm_count].name))
746 					return (1);
747 				itm_count++;
748 			} else {
749 				p_error = parse_internal_error;
750 				for (n = 0; n < (nElements + 1); n++)
751 					free_mapping_item(&item[n]);
752 				free_mapping_format(base);
753 				free_mapping_element(elmnt);
754 				return (1);
755 			}
756 		}
757 		numSplits++;
758 	}
759 	elmnt = NULL;
760 
761 	if (item != NULL) {
762 		for (n = 0; n < t_map->numColumns; n++) {
763 			free_mapping_item(&item[n]);
764 		}
765 		free(item);
766 	}
767 	if (elmnt != NULL)
768 		free_mapping_element(elmnt);
769 	if (base != NULL)
770 		free_mapping_format(base);
771 
772 	return (p_error == no_parse_error ? 0 : -1);
773 }
774 
775 /*
776  * FUNCTION:	parse_object_dn
777  *
778  *	Parse object dn attribute
779  *
780  * RETURN VALUE:	__nis_object_dn_t on success
781  *			NULL on failure
782  *
783  * INPUT:		the attribute value
784  */
785 
786 static __nis_object_dn_t *
787 parse_object_dn(const char *s, const char *end)
788 {
789 	const char		*s_begin;
790 	const char		*s_end;
791 	object_dn_token		token;
792 	parse_object_dn_state	dn_state	= dn_begin_parse;
793 	__nis_object_dn_t	*obj_dn		= NULL;
794 	__nis_object_dn_t	*next		= NULL;
795 	__nis_object_dn_t	*last		= NULL;
796 
797 	/*
798 	 * The attribute should be of form
799 	 * objectDN *( ";" objectDN )
800 	 * objectDN = readObjectSpec [":"[writeObjectSpec]]
801 	 * readObjectSpec = [baseAndScope [filterAttrValList]]
802 	 * writeObjectSpec = [baseAndScope [attrValList [":" deleteDisp]]]
803 	 */
804 
805 	while (s < end) {
806 		s_begin = s;
807 		s_end = end;
808 		s = get_next_object_dn_token(&s_begin, &s_end, &token);
809 		if (s == NULL)
810 			break;
811 
812 		if (token == dn_no_token || token == dn_semi_token) {
813 			if (obj_dn == NULL)
814 				obj_dn = next;
815 			else
816 				last->next = next;
817 			last = next;
818 			next = NULL;
819 			if (token == dn_no_token)
820 				break;
821 			dn_state = dn_begin_parse;
822 		}
823 		if (next == NULL) {
824 			next = (__nis_object_dn_t *)
825 				s_calloc(1, sizeof (__nis_object_dn_t));
826 			if (next == NULL)
827 				break;
828 			next->read.scope = LDAP_SCOPE_ONELEVEL;
829 			next->write.scope = LDAP_SCOPE_UNKNOWN;
830 			next->delDisp = dd_always;
831 		}
832 		if (token == dn_semi_token)
833 			continue;
834 
835 		switch (dn_state) {
836 		    case dn_begin_parse:
837 			if (token == dn_ques_token)
838 				dn_state = dn_got_read_q_scope;
839 			else if (token == dn_colon_token) {
840 				dn_state = dn_got_write_colon;
841 				next->write.scope = LDAP_SCOPE_ONELEVEL;
842 			} else {
843 				if (!validate_dn(s_begin, s_end - s_begin))
844 					break;
845 				next->read.base =
846 					s_strndup_esc(s_begin, s_end - s_begin);
847 				dn_state = dn_got_read_dn;
848 			}
849 			break;
850 		    case dn_got_read_dn:
851 			if (token == dn_ques_token)
852 				dn_state = dn_got_read_q_scope;
853 			else if (token == dn_colon_token) {
854 				dn_state = dn_got_write_colon;
855 				next->write.scope = LDAP_SCOPE_ONELEVEL;
856 			} else
857 				p_error = parse_object_dn_syntax_error;
858 			break;
859 		    case dn_got_read_q_scope:
860 			if (token == dn_ques_token)
861 				dn_state = dn_got_read_q_filter;
862 			else if (token == dn_colon_token) {
863 				dn_state = dn_got_write_colon;
864 				next->write.scope = LDAP_SCOPE_ONELEVEL;
865 			} else if (token == dn_base_token) {
866 				next->read.scope = LDAP_SCOPE_BASE;
867 				dn_state = dn_got_read_scope;
868 			} else if (token == dn_one_token) {
869 				next->read.scope = LDAP_SCOPE_ONELEVEL;
870 				dn_state = dn_got_read_scope;
871 			} else if (token == dn_sub_token) {
872 				next->read.scope = LDAP_SCOPE_SUBTREE;
873 				dn_state = dn_got_read_scope;
874 			} else {
875 				p_error = parse_invalid_scope;
876 			}
877 			break;
878 		    case dn_got_read_scope:
879 			if (token == dn_ques_token)
880 				dn_state = dn_got_read_q_filter;
881 			else if (token == dn_colon_token) {
882 				dn_state = dn_got_write_colon;
883 				next->write.scope = LDAP_SCOPE_ONELEVEL;
884 			} else
885 				p_error = parse_object_dn_syntax_error;
886 			break;
887 		    case dn_got_read_q_filter:
888 			if (token == dn_ques_token) {
889 				p_error = parse_object_dn_syntax_error;
890 			} else if (token == dn_colon_token) {
891 				dn_state = dn_got_write_colon;
892 				next->write.scope = LDAP_SCOPE_ONELEVEL;
893 			} else {
894 				if (!validate_ldap_filter(s_begin, s_end))
895 					break;
896 				next->read.attrs =
897 					s_strndup_esc(s_begin, s_end - s_begin);
898 				dn_state = dn_got_read_filter;
899 			}
900 			break;
901 		    case dn_got_read_filter:
902 			if (token == dn_ques_token) {
903 				p_error = parse_object_dn_syntax_error;
904 			} else if (token == dn_colon_token) {
905 				dn_state = dn_got_write_colon;
906 				next->write.scope = LDAP_SCOPE_ONELEVEL;
907 			} else
908 				p_error = parse_object_dn_syntax_error;
909 			break;
910 		    case dn_got_write_colon:
911 			if (token == dn_ques_token)
912 				dn_state = dn_got_write_q_scope;
913 			else if (token == dn_colon_token) {
914 				dn_state = dn_got_delete_colon;
915 			} else {
916 				if (!validate_dn(s_begin, s_end - s_begin))
917 					break;
918 				next->write.base =
919 					s_strndup_esc(s_begin, s_end - s_begin);
920 				dn_state = dn_got_write_dn;
921 			}
922 			break;
923 		    case dn_got_write_dn:
924 			if (token == dn_ques_token)
925 				dn_state = dn_got_write_q_scope;
926 			else if (token == dn_colon_token) {
927 				dn_state = dn_got_delete_colon;
928 			} else
929 				p_error = parse_object_dn_syntax_error;
930 			break;
931 		    case dn_got_write_q_scope:
932 			if (token == dn_ques_token)
933 				dn_state = dn_got_write_q_filter;
934 			else if (token == dn_colon_token) {
935 				dn_state = dn_got_delete_colon;
936 			} else if (token == dn_base_token) {
937 				next->write.scope = LDAP_SCOPE_BASE;
938 				dn_state = dn_got_write_scope;
939 			} else if (token == dn_one_token) {
940 				next->write.scope = LDAP_SCOPE_ONELEVEL;
941 				dn_state = dn_got_write_scope;
942 			} else if (token == dn_sub_token) {
943 				next->write.scope = LDAP_SCOPE_SUBTREE;
944 				dn_state = dn_got_write_scope;
945 			} else {
946 				p_error = parse_invalid_scope;
947 			}
948 			break;
949 		    case dn_got_write_scope:
950 			if (token == dn_ques_token)
951 				dn_state = dn_got_write_q_filter;
952 			else if (token == dn_colon_token) {
953 				dn_state = dn_got_delete_colon;
954 			} else
955 				p_error = parse_object_dn_syntax_error;
956 			break;
957 		    case dn_got_write_q_filter:
958 			if (token == dn_ques_token) {
959 				p_error = parse_object_dn_syntax_error;
960 			} else if (token == dn_colon_token) {
961 				dn_state = dn_got_delete_colon;
962 			} else {
963 				if (!validate_ldap_filter(s_begin, s_end))
964 					break;
965 				next->write.attrs =
966 					s_strndup_esc(s_begin, s_end - s_begin);
967 				dn_state = dn_got_write_filter;
968 			}
969 			break;
970 		    case dn_got_write_filter:
971 			if (token == dn_ques_token) {
972 				p_error = parse_object_dn_syntax_error;
973 			} else if (token == dn_colon_token) {
974 				dn_state = dn_got_delete_colon;
975 
976 			} else
977 				p_error = parse_semi_expected_error;
978 			break;
979 		    case dn_got_delete_colon:
980 			if (token == dn_ques_token) {
981 				p_error = parse_object_dn_syntax_error;
982 			} else if (token == dn_colon_token) {
983 				p_error = parse_object_dn_syntax_error;
984 			} else {
985 				if (!get_deleteDisp(s_begin, s_end, next))
986 					break;
987 				dn_state = dn_got_delete_dsp;
988 			}
989 			break;
990 		    case dn_got_delete_dsp:
991 			p_error = parse_object_dn_syntax_error;
992 			break;
993 		}
994 
995 		if (p_error != no_parse_error)
996 			break;
997 	}
998 	if (p_error != no_parse_error) {
999 		if (obj_dn != NULL)
1000 			free_object_dn(obj_dn);
1001 		if (next != NULL)
1002 			free_object_dn(next);
1003 		obj_dn = NULL;
1004 	} else if (next != NULL) {
1005 		if (obj_dn == NULL)
1006 			obj_dn = next;
1007 		else
1008 			last->next = next;
1009 	} else if (obj_dn == NULL)
1010 		obj_dn = (__nis_object_dn_t *)
1011 			s_calloc(1, sizeof (__nis_object_dn_t));
1012 
1013 	return (obj_dn);
1014 }
1015 
1016 /*
1017  * FUNCTION:	get_mapping_rule
1018  *
1019  *	Parse mapping rule attributes
1020  *
1021  * RETURN VALUE:	None. Errors determined by p_error
1022  *
1023  * INPUT:		the attribute value and mapping rule type
1024  */
1025 
1026 static void
1027 get_mapping_rule(
1028 	const char		*s,
1029 	int			len,
1030 	__nis_table_mapping_t	*tbl,
1031 	bool_t			to_ldap)
1032 {
1033 	const char		*end_s			= s + len;
1034 	const char		*begin_token;
1035 	const char		*end_token;
1036 	__nis_mapping_rule_t	**rule			= NULL;
1037 	__nis_mapping_rule_t	*next			= NULL;
1038 	/* __nis_mapping_rule_t	**r; */
1039 	token_type		t;
1040 	int			nRules			= 0;
1041 	const char		*s1;
1042 	int			i;
1043 
1044 	/*
1045 	 * The attribute value is of the form
1046 	 * colattrspec *("," colattrspec)
1047 	 * colattrspec	= lhs "=" rhs
1048 	 * lhs		= lval | namespeclist
1049 	 * rhs		= rval | [namespec]
1050 	 */
1051 
1052 	for (;;) {
1053 		if ((next = (__nis_mapping_rule_t *)
1054 		    s_calloc(1, sizeof (__nis_mapping_rule_t))) == NULL)
1055 			break;
1056 
1057 		s = get_lhs(s, end_s, &next->lhs,
1058 			to_ldap ? mit_ldap : mit_nisplus);
1059 		if (s == NULL)
1060 			break;
1061 
1062 		begin_token = s;
1063 		end_token = end_s;
1064 		s1 = get_next_token(&begin_token, &end_token, &t);
1065 		if (s1 == NULL)
1066 			break;
1067 		if (!(to_ldap && (t == comma_token || t == no_token))) {
1068 			s = get_rhs(s, end_s, &next->rhs,
1069 				to_ldap ? mit_nisplus : mit_ldap);
1070 			if (s == NULL)
1071 				break;
1072 		}
1073 
1074 		if (next->lhs.numElements > 1 &&
1075 		    (next->rhs.numElements != 1 ||
1076 		    next->rhs.element[0].type != me_split)) {
1077 			p_error = parse_lhs_rhs_type_mismatch;
1078 			break;
1079 		}
1080 		if (rule == NULL) {
1081 			rule = (__nis_mapping_rule_t **)
1082 				malloc(sizeof (__nis_mapping_rule_t *));
1083 			if (rule == NULL)
1084 				break;
1085 		} else {
1086 			rule = (__nis_mapping_rule_t **)s_realloc(rule,
1087 				(nRules + 1) *
1088 				sizeof (__nis_mapping_rule_t *));
1089 			if (rule == NULL)
1090 				break;
1091 		}
1092 
1093 		rule[nRules++] = next;
1094 		next = NULL;
1095 
1096 		begin_token = s;
1097 		end_token = end_s;
1098 		s = get_next_token(&begin_token, &end_token, &t);
1099 		if (s == NULL)
1100 			break;
1101 		if (t == comma_token)
1102 			continue;
1103 		if (t != no_token) {
1104 			p_error = parse_unexpected_data_end_rule;
1105 			break;
1106 		}
1107 		if (to_ldap) {
1108 			tbl->numRulesToLDAP = nRules;
1109 			tbl->ruleToLDAP = rule;
1110 		} else {
1111 			tbl->numRulesFromLDAP = nRules;
1112 			tbl->ruleFromLDAP = rule;
1113 		}
1114 		return;
1115 	}
1116 
1117 	if (rule) {
1118 		for (i = 0; i < nRules; i++)
1119 			free_mapping_rule(rule[i]);
1120 		free(rule);
1121 	}
1122 	if (next)
1123 		free_mapping_rule(next);
1124 }
1125 
1126 /*
1127  * FUNCTION:	get_lhs
1128  *
1129  *	Parse left hand side of mapping rule attribute
1130  *
1131  * RETURN VALUE:	NULL if error
1132  *			position of beginning rhs
1133  *
1134  * INPUT:		the attribute value and mapping rule type
1135  */
1136 
1137 static const char *
1138 get_lhs(const char			*s,
1139 	const char			*end_s,
1140 	__nis_mapping_rlhs_t		*lhs,
1141 	__nis_mapping_item_type_t	item_type)
1142 {
1143 	token_type		t;
1144 	const char		*begin_token;
1145 	const char		*end_token;
1146 	const char		*sav_s;
1147 	__nis_mapping_element_t	*e		= NULL;
1148 
1149 	/*
1150 	 *	lhs can be expressed as:
1151 	 *		item
1152 	 *		(item)
1153 	 *		(item list)
1154 	 *		(fmt, item list)
1155 	 *
1156 	 * lhs = lval | namespeclist
1157 	 * lval = "(" formatspec "," namespec *("," namespec) ")"
1158 	 * namespeclist = namespec | "(" namespec *("," namespec) ")"
1159 	 */
1160 
1161 	for (; p_error == no_parse_error; ) {
1162 		begin_token = s;
1163 		end_token = end_s;
1164 		s = get_next_token(&begin_token, &end_token, &t);
1165 		if (s == NULL)
1166 			break;
1167 		if (t == no_token) {
1168 			p_error = parse_unexpected_data_end_rule;
1169 			break;
1170 		}
1171 
1172 		e = (__nis_mapping_element_t *)
1173 			s_calloc(1, sizeof (__nis_mapping_element_t));
1174 		if (e == NULL)
1175 			break;
1176 
1177 		if (t == open_paren_token) {
1178 			free(e);
1179 			e = NULL;
1180 
1181 			begin_token = s;
1182 			end_token = end_s;
1183 			sav_s = s;
1184 			s = get_next_token(&begin_token, &end_token, &t);
1185 			if (s == NULL)
1186 				break;
1187 
1188 			if (t == quoted_string_token) {
1189 				s = get_lhs_match(sav_s, end_s, lhs, item_type);
1190 				if (s == NULL)
1191 					break;
1192 			} else if (t == string_token) {
1193 				s = get_lhs_paren_item(sav_s, end_s, lhs,
1194 					item_type);
1195 				if (s == NULL)
1196 					break;
1197 			} else {
1198 				p_error = parse_bad_lhs_format_error;
1199 				break;
1200 			}
1201 		} else if (t == string_token) {
1202 			s = get_mapping_item(begin_token, end_s,
1203 				&e->element.item, item_type);
1204 			if (s == NULL)
1205 				break;
1206 			e->type = me_item;
1207 			if (!add_element(e, lhs))
1208 				break;
1209 			e = NULL;
1210 		} else {
1211 			p_error = parse_bad_lhs_format_error;
1212 			break;
1213 		}
1214 
1215 		s = skip_token(s, end_s, equal_token);
1216 		if (s == NULL)
1217 			break;
1218 		if (p_error == no_parse_error)
1219 			return (s);
1220 	}
1221 	if (e != NULL)
1222 		free_mapping_element(e);
1223 
1224 	return (NULL);
1225 }
1226 
1227 /*
1228  * FUNCTION:	get_lhs_match
1229  *
1230  *	Parse left hand side of mapping rule attribute in case of
1231  *	matching rule
1232  *
1233  * RETURN VALUE:	NULL if error
1234  *			position of beginning rhs
1235  *
1236  * INPUT:		the attribute value and mapping rule type
1237  */
1238 
1239 static const char *
1240 get_lhs_match(
1241 	const char			*s,
1242 	const char			*end_s,
1243 	__nis_mapping_rlhs_t		*lhs,
1244 	__nis_mapping_item_type_t	item_type)
1245 {
1246 	token_type			t;
1247 	const char			*begin_token;
1248 	const char			*end_token;
1249 	int				n		= 0;
1250 	int				nElements	= 0;
1251 	char				*fmt_string	= NULL;
1252 	__nis_mapping_format_t		*base		= NULL;
1253 	__nis_mapping_item_t		*item		= NULL;
1254 	__nis_mapping_item_t		*itm;
1255 	__nis_mapping_element_t		*e;
1256 
1257 	/*
1258 	 *  lval = "(" formatspec "," namespec *("," namespec) ")"
1259 	 */
1260 
1261 	for (; p_error == no_parse_error; ) {
1262 		begin_token = s;
1263 		end_token = end_s;
1264 		s = get_next_token(&begin_token, &end_token, &t);
1265 		if (s == NULL || t != quoted_string_token) {
1266 			p_error = parse_internal_error;
1267 			break;
1268 		}
1269 
1270 
1271 		fmt_string = s_strndup(begin_token, end_token - begin_token);
1272 		if (fmt_string == NULL)
1273 			break;
1274 
1275 		if (!get_mapping_format(fmt_string, &base, &n, NULL, FALSE))
1276 			break;
1277 
1278 		for (n = 0; base[n].type != mmt_end; n++) {
1279 			if (base[n].type != mmt_item &&
1280 			    base[n].type != mmt_berstring) {
1281 				if (base[n].type == mmt_berstring_null)
1282 					base[n].type = mmt_berstring;
1283 				continue;
1284 			}
1285 			s = skip_token(s, end_s, comma_token);
1286 			if (s == NULL) {
1287 				p_error = parse_not_enough_extract_items;
1288 				break;
1289 			}
1290 			begin_token = s;
1291 			end_token = end_s;
1292 			s = get_next_token(&begin_token, &end_token, &t);
1293 			if (s == NULL)
1294 				break;
1295 			if (t != string_token) {
1296 				p_error = parse_item_expected_error;
1297 				break;
1298 			}
1299 			itm = (__nis_mapping_item_t *)
1300 				s_realloc(item, (nElements + 1) *
1301 				sizeof (__nis_mapping_item_t));
1302 			if (itm == NULL)
1303 				break;
1304 			item = itm;
1305 
1306 			s = get_mapping_item(begin_token, end_s,
1307 				&item[nElements], item_type);
1308 			if (s == NULL)
1309 				break;
1310 			nElements++;
1311 		}
1312 		if (p_error != no_parse_error)
1313 			break;
1314 
1315 		s = skip_token(s, end_s, close_paren_token);
1316 		if (s == NULL)
1317 			break;
1318 		free(fmt_string);
1319 		fmt_string = NULL;
1320 
1321 		if (nElements == 0) {
1322 			p_error = parse_no_match_item;
1323 			break;
1324 		}
1325 		e = (__nis_mapping_element_t *)s_calloc(1,
1326 			sizeof (__nis_mapping_element_t));
1327 		if (e == NULL)
1328 			break;
1329 		e->type = me_match;
1330 		e->element.match.numItems = nElements;
1331 		e->element.match.item = item;
1332 		e->element.match.fmt = base;
1333 		lhs->numElements = 1;
1334 		lhs->element = e;
1335 
1336 		if (p_error == no_parse_error)
1337 			return (s);
1338 	}
1339 	if (item == NULL) {
1340 		for (n = 0; n < nElements; n++)
1341 			free_mapping_item(&item[n]);
1342 		free(item);
1343 	}
1344 	if (fmt_string != NULL)
1345 		free(fmt_string);
1346 	if (base != NULL)
1347 		free_mapping_format(base);
1348 
1349 	return (NULL);
1350 }
1351 
1352 /*
1353  * FUNCTION:	get_lhs_paren_item
1354  *
1355  *	Parse left hand side of mapping rule attribute in case of
1356  *	(item1, ..., item-n)
1357  *
1358  * RETURN VALUE:	NULL if error
1359  *			position of beginning rhs
1360  *
1361  * INPUT:		the attribute value and mapping rule type
1362  */
1363 
1364 static const char *
1365 get_lhs_paren_item(
1366 	const char			*s,
1367 	const char			*end_s,
1368 	__nis_mapping_rlhs_t		*lhs,
1369 	__nis_mapping_item_type_t	item_type)
1370 {
1371 	token_type		t;
1372 	const char		*begin_token;
1373 	const char		*end_token;
1374 	__nis_mapping_element_t	*e		= NULL;
1375 	int			n		= 0;
1376 	int			i;
1377 
1378 	/*
1379 	 * "(" namespec *("," namespec) ")"
1380 	 */
1381 
1382 	for (;;) {
1383 		e = (__nis_mapping_element_t *)s_realloc(e, (n + 1) *
1384 			sizeof (__nis_mapping_element_t));
1385 		if (e == NULL)
1386 			break;
1387 
1388 		s = get_mapping_item(s, end_s, &e[n].element.item,
1389 			item_type);
1390 		if (s == NULL)
1391 			break;
1392 		e[n].type = me_item;
1393 		n++;
1394 
1395 		begin_token = s;
1396 		end_token = end_s;
1397 		s = get_next_token(&begin_token, &end_token, &t);
1398 		if (s != NULL && t == close_paren_token) {
1399 			lhs->numElements = n;
1400 			if (n == 1)
1401 				e[0].element.item.repeat = TRUE;
1402 			lhs->element = e;
1403 			return (s);
1404 		}
1405 		if (s == NULL || t != comma_token) {
1406 			p_error = parse_comma_expected_error;
1407 			break;
1408 		}
1409 	}
1410 	for (i = 0; i < n; i++)
1411 		free_mapping_element(&e[i]);
1412 	if (e != NULL)
1413 		free(e);
1414 	return (NULL);
1415 }
1416 
1417 /*
1418  * FUNCTION:	get_rhs
1419  *
1420  *	Parse right hand side of mapping rule attribute
1421  *
1422  * RETURN VALUE:	NULL if error
1423  *			position of beginning next mapping rule
1424  *
1425  * INPUT:		the attribute value and mapping rule type
1426  */
1427 
1428 static const char *
1429 get_rhs(
1430 	const char			*s,
1431 	const char			*end_s,
1432 	__nis_mapping_rlhs_t		*rhs,
1433 	__nis_mapping_item_type_t	item_type)
1434 {
1435 	/*
1436 	 * This handles the following cases:
1437 	 *	name				me_item
1438 	 *	(name)				me_item
1439 	 *	(fmt, name-list)		me_print
1440 	 *	(item, fmt)			me_extract
1441 	 */
1442 
1443 	token_type		t;
1444 	const char		*begin_token;
1445 	const char		*end_token;
1446 	char			*str		= NULL;
1447 	__nis_mapping_format_t	*fmt		= NULL;
1448 	__nis_mapping_element_t	*e		= NULL;
1449 	__nis_mapping_item_t	item;
1450 	int			n;
1451 
1452 	(void) memset(&item, 0, sizeof (item));
1453 
1454 	for (; p_error == no_parse_error; ) {
1455 		begin_token = s;
1456 		end_token = end_s;
1457 		s = get_next_token(&begin_token, &end_token, &t);
1458 		if (s == NULL)
1459 			break;
1460 
1461 		e = (__nis_mapping_element_t *)
1462 			s_calloc(1, sizeof (__nis_mapping_element_t));
1463 		if (e == NULL)
1464 			break;
1465 
1466 		if (t == string_token) {
1467 			s = get_mapping_item(begin_token, end_s,
1468 				&e->element.item, item_type);
1469 		} else if (t == open_paren_token) {
1470 			begin_token = s;
1471 			end_token = end_s;
1472 			s = get_next_token(&begin_token, &end_token, &t);
1473 			if (s == NULL)
1474 				break;
1475 			if (t == string_token) {
1476 				/* (item, fmt) - me_extract */
1477 				/* (item, "c") - me_split */
1478 				s = get_mapping_item(begin_token, end_s,
1479 					&item, item_type);
1480 				if (s == NULL)
1481 					break;
1482 				begin_token = s;
1483 				end_token = end_s;
1484 				s = get_next_token(&begin_token, &end_token,
1485 					&t);
1486 				if (s == NULL)
1487 					break;
1488 				else if (t == close_paren_token) {
1489 					item.repeat = TRUE;
1490 					e->element.item = item;
1491 					e->type = me_item;
1492 					rhs->numElements = 1;
1493 					rhs->element = e;
1494 					return (s);
1495 				} else if (t != comma_token) {
1496 					p_error = parse_comma_expected_error;
1497 					break;
1498 				}
1499 
1500 				begin_token = s;
1501 				end_token = end_s;
1502 				s = get_next_token(&begin_token, &end_token,
1503 					&t);
1504 				if (s == NULL || t != quoted_string_token) {
1505 				    p_error =
1506 					parse_format_string_expected_error;
1507 				    break;
1508 				}
1509 
1510 				if (end_token == begin_token + 1 ||
1511 				    (*begin_token == ESCAPE_CHAR &&
1512 				    end_token == begin_token + 2)) {
1513 					e->type = me_split;
1514 					e->element.split.item = item;
1515 					e->element.split.delim = *begin_token;
1516 				} else {
1517 					str = s_strndup(begin_token,
1518 						end_token - begin_token);
1519 					if (str == NULL)
1520 						break;
1521 					if (!get_mapping_format(str, &fmt,
1522 					    NULL, &n, FALSE))
1523 						break;
1524 					free(str);
1525 					str = NULL;
1526 					if (n != 1) {
1527 					    p_error =
1528 						parse_bad_extract_format_spec;
1529 					    break;
1530 					}
1531 					e->type = me_extract;
1532 					e->element.extract.item = item;
1533 					e->element.extract.fmt = fmt;
1534 				}
1535 				s = skip_token(s, end_s, close_paren_token);
1536 			} else if (t == quoted_string_token) {
1537 				/* (fmt, name-list) - me_print */
1538 				str = s_strndup(begin_token,
1539 					end_token - begin_token);
1540 				if (str == NULL)
1541 					break;
1542 
1543 				s = get_print_mapping_element(s, end_s,
1544 					str, e, item_type);
1545 				free(str);
1546 				str = NULL;
1547 			} else {
1548 				p_error = parse_start_rhs_unrecognized;
1549 				break;
1550 			}
1551 		} else {
1552 			p_error = parse_start_rhs_unrecognized;
1553 			break;
1554 		}
1555 		if (s == NULL)
1556 			break;
1557 		rhs->numElements = 1;
1558 		rhs->element = e;
1559 		if (p_error == no_parse_error)
1560 			return (s);
1561 	}
1562 	if (str)
1563 		free(str);
1564 	if (fmt != NULL)
1565 		free_mapping_format(fmt);
1566 	if (e != NULL)
1567 		free_mapping_element(e);
1568 	free_mapping_item(&item);
1569 
1570 	return (NULL);
1571 }
1572 
1573 /*
1574  * FUNCTION:	get_print_mapping_element
1575  *
1576  *	Parse a print mapping rule attribute in case of the form
1577  *	(fmt, name-list)
1578  *
1579  * RETURN VALUE:	NULL if error
1580  *			position of beginning next mapping rule
1581  *
1582  * INPUT:		the attribute value and mapping rule type
1583  */
1584 
1585 static const char *
1586 get_print_mapping_element(
1587 	const char			*s,
1588 	const char			*end_s,
1589 	char				*fmt_string,
1590 	__nis_mapping_element_t		*e,
1591 	__nis_mapping_item_type_t	item_type)
1592 {
1593 	token_type			t;
1594 	const char			*begin_token;
1595 	const char			*end_token;
1596 	char				elide;
1597 	bool_t				doElide;
1598 	__nis_mapping_format_t		*base		= NULL;
1599 	__nis_mapping_sub_element_t	*subElement	= NULL;
1600 	int				n		= 0;
1601 	int				nSub		= 0;
1602 	int				numSubElements;
1603 
1604 	for (; p_error == no_parse_error; ) {
1605 		if (!get_mapping_format(fmt_string, &base, &n,
1606 		    &numSubElements, TRUE))
1607 			break;
1608 		subElement = (__nis_mapping_sub_element_t *)
1609 			s_calloc(numSubElements,
1610 			sizeof (__nis_mapping_sub_element_t));
1611 		if (subElement == NULL)
1612 			break;
1613 		for (n = 0; base[n].type != mmt_end; n++) {
1614 			if (base[n].type != mmt_item &&
1615 				base[n].type != mmt_berstring) {
1616 			    if (base[n].type == mmt_berstring_null)
1617 				base[n].type = mmt_berstring;
1618 			    continue;
1619 			}
1620 			if (nSub < numSubElements) {
1621 				s = skip_token(s, end_s, comma_token);
1622 				if (s == NULL) {
1623 					p_error = parse_bad_print_format;
1624 					break;
1625 				}
1626 			}
1627 
1628 			/* namelist may have parens around it */
1629 			s = get_subElement(s, end_s, &subElement[nSub],
1630 				item_type);
1631 			if (s == NULL)
1632 				break;
1633 			nSub++;
1634 		}
1635 		if (p_error != no_parse_error)
1636 			break;
1637 
1638 		begin_token = s;
1639 		end_token = end_s;
1640 		s = get_next_token(&begin_token, &end_token, &t);
1641 		if (s == NULL || t == no_token) {
1642 			p_error = parse_unexpected_data_end_rule;
1643 			break;
1644 		} else if (t == close_paren_token) {
1645 			doElide = FALSE;
1646 			elide = '\0';
1647 		} else if (t == comma_token) {
1648 			begin_token = s;
1649 			end_token = end_s;
1650 			s = get_next_token(&begin_token, &end_token, &t);
1651 			if (s != NULL && t == quoted_string_token &&
1652 			    (end_token == begin_token + 1 ||
1653 			    (*begin_token == ESCAPE_CHAR &&
1654 			    end_token == begin_token + 2))) {
1655 				if (numSubElements != 1 ||
1656 				    subElement->type == me_extract ||
1657 				    subElement->type == me_split) {
1658 					p_error = parse_cannot_elide;
1659 					break;
1660 				}
1661 				if (subElement->type == me_item &&
1662 				    !subElement->element.item.repeat) {
1663 					p_error = parse_cannot_elide;
1664 					break;
1665 				}
1666 				elide = *begin_token;
1667 				doElide = TRUE;
1668 
1669 			} else {
1670 				p_error = parse_bad_elide_char;
1671 				break;
1672 			}
1673 			s = skip_token(s, end_s, close_paren_token);
1674 			if (s == NULL)
1675 				break;
1676 		}
1677 
1678 		e->type = me_print;
1679 		e->element.print.fmt = base;
1680 		e->element.print.numSubElements = numSubElements;
1681 		e->element.print.subElement = subElement;
1682 		e->element.print.elide = elide;
1683 		e->element.print.doElide = doElide;
1684 
1685 		if (p_error == no_parse_error)
1686 			return (s);
1687 	}
1688 	if (base)
1689 		free_mapping_format(base);
1690 	if (subElement != NULL) {
1691 		for (n = 0; n < numSubElements; n++)
1692 			free_mapping_sub_element(&subElement[n]);
1693 		free(subElement);
1694 	}
1695 
1696 	return (NULL);
1697 }
1698 
1699 /*
1700  * FUNCTION:	get_mapping_item
1701  *
1702  *	Parse attribute string to get mapping item
1703  *
1704  * RETURN VALUE:	NULL if error
1705  *			position of beginning next token after item
1706  *
1707  * INPUT:		the attribute value and mapping rule type
1708  */
1709 
1710 static const char *
1711 get_mapping_item(
1712 	const char			*s,
1713 	const char			*end_s,
1714 	__nis_mapping_item_t		*item,
1715 	__nis_mapping_item_type_t	type)
1716 {
1717 	token_type			t;
1718 	const char			*begin_token;
1719 	const char			*end_token;
1720 	char				*name		= NULL;
1721 	char				*index_string;
1722 	const char			*s_sav;
1723 	int				len;
1724 
1725 	(void) memset(item, 0, sizeof (*item));
1726 
1727 	/*
1728 	 * A namepec is defined as follows:
1729 	 * namespec	= ["ldap:"] attrspec [searchTriple] |
1730 	 *		  ["nis+:"] colspec  [objectspec]
1731 	 *
1732 	 * The form of the item is assumed to be as follows:
1733 	 * ["ldap:"] attrspec [searchTriple]
1734 	 * attrspec = attribute | "(" attribute ")"
1735 	 * searchTriple	= ":" [baseDN] ["?" [scope] ["?" [filter]]]
1736 	 * baseDN = Base DN for search
1737 	 * scope = "base" | "one" | "sub"
1738 	 * filter = LDAP search filter
1739 	 *
1740 	 * The form of the objectspec is as follows:
1741 	 * ["nis+:"] colspec  [objectspec]
1742 	 * objectspec	= objectname | "[" indexlist "]" tablename
1743 	 * objectname	= The name of a NIS+ object
1744 	 * tablename	= The name of a NIS+ table
1745 	 * indexlist	= colspec ["," colspec]
1746 	 * colspec	= colname "=" colvalue
1747 	 * colname	= The name of a column in the table
1748 	 * colvalue	= colvaluestring | \" colvaluestring \"
1749 	 */
1750 
1751 	for (; p_error == no_parse_error; ) {
1752 		while (s < end_s && is_whitespace(*s))
1753 			s++;
1754 		len = end_s - s;
1755 		if (yp2ldap) {
1756 			if ((begin_token = skip_string("ldap:", s,
1757 				len)) != NULL) {
1758 				item->type = mit_ldap;
1759 			} else if ((begin_token = skip_string("yp:", s,
1760 				len)) != NULL) {
1761 				item->type = mit_nisplus;
1762 			} else {
1763 				item->type = type;
1764 				begin_token = s;
1765 			}
1766 		} else {
1767 			if ((begin_token = skip_string("ldap:", s,
1768 				len)) != NULL) {
1769 			item->type = mit_ldap;
1770 			} else if ((begin_token = skip_string("nis+:", s,
1771 				len)) != NULL) {
1772 				item->type = mit_nisplus;
1773 			} else if ((begin_token = skip_string("nisplus:", s,
1774 				len)) != NULL) {
1775 				item->type = mit_nisplus;
1776 			} else {
1777 				item->type = type;
1778 				begin_token = s;
1779 			}
1780 		}
1781 
1782 		end_token = end_s;
1783 		s = get_next_token(&begin_token, &end_token, &t);
1784 		if (s == NULL || t != string_token) {
1785 			p_error = parse_bad_item_format;
1786 			break;
1787 		}
1788 
1789 		item->name = s_strndup_esc(begin_token,
1790 			end_token - begin_token);
1791 		if (item->name == NULL)
1792 			break;
1793 		if (item->type == mit_ldap) {
1794 			item->searchSpec.triple.scope = LDAP_SCOPE_UNKNOWN;
1795 			begin_token = s;
1796 			end_token = end_s;
1797 			s_sav = s;
1798 			s = get_next_token(&begin_token, &end_token, &t);
1799 			if (s != NULL && t == colon_token) {
1800 				s = get_search_triple(s, end_s,
1801 					&item->searchSpec.triple);
1802 				if (s == NULL)
1803 					break;
1804 			} else
1805 				s = s_sav;
1806 		} else if (item->type == mit_nisplus) {
1807 			while (s < end_s && is_whitespace(*s))
1808 				s++;
1809 
1810 			if (s < end_s && *s == OPEN_BRACKET) {
1811 				index_string = getIndex(&s, end_s);
1812 				if (index_string == NULL)
1813 					break;
1814 				(void) parse_index(index_string,
1815 					index_string + strlen(index_string),
1816 					&item->searchSpec.obj.index);
1817 				free(index_string);
1818 				if (p_error != no_parse_error)
1819 					break;
1820 			}
1821 			s_sav = s;
1822 			begin_token = s;
1823 			end_token = end_s;
1824 			s = get_next_token(&begin_token, &end_token, &t);
1825 			if (s != NULL && t == string_token) {
1826 				name = s_strndup_esc(begin_token,
1827 					end_token - begin_token);
1828 				if (name == NULL)
1829 					break;
1830 				item->searchSpec.obj.name = name;
1831 			} else
1832 				s = s_sav;
1833 		}
1834 		if (p_error == no_parse_error)
1835 			return (s);
1836 	}
1837 	free_mapping_item(item);
1838 	(void) memset(item, 0, sizeof (*item));
1839 	if (name == NULL)
1840 		free(name);
1841 	return (NULL);
1842 }
1843 
1844 static const char *
1845 get_print_sub_element(const char		*s,
1846 		const char			*end_s,
1847 		__nis_mapping_item_type_t	type,
1848 		__nis_mapping_sub_element_t	*sub)
1849 {
1850 
1851 	int			k;
1852 	int			n;
1853 	const char		*begin_token;
1854 	const char		*end_token;
1855 	token_type		t;
1856 	__nis_mapping_format_t	*base;
1857 	__nis_mapping_item_t	*print_item;
1858 
1859 	k = 0;
1860 	base = sub->element.print.fmt;
1861 	print_item = sub->element.print.item;
1862 	sub->element.print.doElide = FALSE;
1863 	sub->element.print.elide = '\0';
1864 
1865 	for (n = 0; base[n].type != mmt_end; n++) {
1866 		if (base[n].type != mmt_item && base[n].type != mmt_berstring) {
1867 			if (base[n].type == mmt_berstring_null)
1868 					base[n].type = mmt_berstring;
1869 			continue;
1870 		}
1871 		s = skip_token(s, end_s, comma_token);
1872 		if (s == NULL) {
1873 			p_error = parse_bad_print_format;
1874 			break;
1875 		}
1876 
1877 		begin_token = s;
1878 		end_token = end_s;
1879 		s = get_next_token(&begin_token, &end_token, &t);
1880 		if (s == NULL)
1881 			break;
1882 		/*
1883 		 * Determine if of the form
1884 		 * ("fmt", (item), "delim") or
1885 		 * ("fmt", item1, item2, ..., item n)
1886 		 */
1887 		if (t == open_paren_token) {
1888 			if (sub->element.print.numItems != 1) {
1889 				p_error = parse_invalid_print_arg;
1890 				break;
1891 			}
1892 			s = get_mapping_item(s, end_s, &print_item[k++], type);
1893 			s = skip_token(s, end_s, close_paren_token);
1894 			s = skip_token(s, end_s, comma_token);
1895 			if (s == NULL) {
1896 				p_error = parse_bad_print_format;
1897 				break;
1898 			}
1899 			begin_token = s;
1900 			end_token = end_s;
1901 			s = get_next_token(&begin_token, &end_token, &t);
1902 			if (s == NULL)
1903 				break;
1904 			if (t != quoted_string_token ||
1905 				    begin_token + 1 != end_token) {
1906 				p_error = parse_bad_elide_char;
1907 				break;
1908 			}
1909 			sub->element.print.elide = *begin_token;
1910 			sub->element.print.doElide = TRUE;
1911 			print_item[0].repeat = TRUE;
1912 			break;
1913 		}
1914 		s = get_mapping_item(begin_token, end_s,
1915 			&print_item[k++], type);
1916 		if (s == NULL)
1917 			break;
1918 
1919 		if (p_error != no_parse_error)
1920 			break;
1921 	}
1922 
1923 	return (p_error == no_parse_error ? s : NULL);
1924 }
1925 
1926 /*
1927  * FUNCTION:	get_subElement
1928  *
1929  *	Parse attribute string to get sub element item
1930  *
1931  * RETURN VALUE:	NULL if error
1932  *			position of beginning next token after item
1933  *
1934  * INPUT:		the attribute value and mapping rule type
1935  */
1936 
1937 static const char *
1938 get_subElement(
1939 	const char			*s,
1940 	const char			*end_s,
1941 	__nis_mapping_sub_element_t	*subelement,
1942 	__nis_mapping_item_type_t	type)
1943 {
1944 	token_type			t;
1945 	const char			*begin_token;
1946 	const char			*end_token;
1947 	char				*fmt_string;
1948 	__nis_mapping_item_t		item;
1949 	__nis_mapping_element_type_t	e_type;
1950 	__nis_mapping_item_t		*print_item	= NULL;
1951 	__nis_mapping_format_t		*base		= NULL;
1952 	int				n		= 0;
1953 	int				numItems	= 0;
1954 	unsigned char			delim;
1955 	__nis_mapping_sub_element_t	sub;
1956 
1957 /*
1958  *	What is the form of we are expecting here
1959  *	item					me_item
1960  *	(item)					me_item
1961  *	("fmt", item1, item2, ..., item n)	me_print
1962  *	("fmt", (item), "elide")		me_print
1963  *	(name, "delim")				me_split
1964  *	(item, "fmt")				me_extract
1965  */
1966 	(void) memset(&item, 0, sizeof (item));
1967 
1968 	for (; p_error == no_parse_error; ) {
1969 		begin_token = s;
1970 		end_token = end_s;
1971 		s = get_next_token(&begin_token, &end_token, &t);
1972 		if (s == NULL)
1973 			break;
1974 		if (t == string_token) {	/* me_item */
1975 			s = get_mapping_item(begin_token, end_s,
1976 				&subelement->element.item, type);
1977 			if (s == NULL)
1978 				break;
1979 			subelement->type = me_item;
1980 			return (s);
1981 		} else if (t != open_paren_token) {
1982 			p_error = parse_item_expected_error;
1983 			break;
1984 		}
1985 
1986 		begin_token = s;
1987 		end_token = end_s;
1988 		s = get_next_token(&begin_token, &end_token, &t);
1989 		if (s == NULL)
1990 			break;
1991 
1992 		if (t != string_token && t != quoted_string_token) {
1993 			p_error = parse_item_expected_error;
1994 			break;
1995 		}
1996 		e_type = me_print;
1997 		if (t == string_token) {
1998 			/* me_item, me_extract or me_split */
1999 			s = get_mapping_item(begin_token, end_s, &item, type);
2000 			if (s == NULL)
2001 				break;
2002 
2003 			begin_token = s;
2004 			end_token = end_s;
2005 			s = get_next_token(&begin_token, &end_token, &t);
2006 			if (s == NULL) {
2007 				p_error = parse_unexpected_data_end_rule;
2008 				break;
2009 			} else if (t == close_paren_token) {
2010 				subelement->type = me_item;
2011 				item.repeat = TRUE;
2012 				subelement->element.item = item;
2013 				if (yp2ldap) {
2014 					while (s < end_s && is_whitespace(*s))
2015 						s++;
2016 					if (s == end_s) {
2017 						p_error =
2018 						parse_unexpected_data_end_rule;
2019 						break;
2020 					}
2021 					if (*s == DASH_CHAR && s < end_s) {
2022 						s++;
2023 						while (s < end_s &&
2024 							is_whitespace(*s))
2025 							s++;
2026 						begin_token = s;
2027 						end_token = end_s;
2028 
2029 						subelement->element.item.exItem
2030 							=
2031 							(__nis_mapping_item_t *)
2032 					s_malloc(sizeof (__nis_mapping_item_t));
2033 						if (!subelement->
2034 						element.item.exItem)
2035 							break;
2036 						s = get_mapping_item(s, end_s,
2037 							subelement->
2038 							element.item.exItem,
2039 							type);
2040 						if (s == NULL) {
2041 							p_error =
2042 							parse_internal_error;
2043 							free_mapping_item(
2044 							subelement->
2045 							element.item.exItem);
2046 							subelement->
2047 							element.item.exItem =
2048 								NULL;
2049 							break;
2050 						}
2051 					}
2052 				}
2053 				return (s);
2054 			} else if (t != comma_token) {
2055 				p_error = parse_comma_expected_error;
2056 				break;
2057 			}
2058 
2059 			begin_token = s;
2060 			end_token = end_s;
2061 			s = get_next_token(&begin_token, &end_token, &t);
2062 			if (s == NULL || t != quoted_string_token) {
2063 				p_error = parse_format_string_expected_error;
2064 				break;
2065 			}
2066 			if (end_token == begin_token + 1 ||
2067 			    (*begin_token == ESCAPE_CHAR &&
2068 			    end_token == begin_token + 2)) {
2069 					/* me_split */
2070 				delim = (unsigned char)end_token[-1];
2071 				s = skip_token(s, end_s, close_paren_token);
2072 				if (s == NULL)
2073 					break;
2074 				subelement->element.split.item = item;
2075 				subelement->element.split.delim = delim;
2076 				subelement->type = me_split;
2077 				return (s);
2078 			}
2079 			e_type = me_extract;
2080 		}
2081 		fmt_string = s_strndup(begin_token, end_token - begin_token);
2082 		if (fmt_string == NULL)
2083 			break;
2084 		if (!get_mapping_format(fmt_string, &base, &n, &numItems,
2085 		    e_type == me_print)) {
2086 			free(fmt_string);
2087 			break;
2088 		}
2089 		free(fmt_string);
2090 
2091 		if (numItems != 1 && e_type == me_extract) {
2092 			p_error = numItems == 0 ?
2093 				parse_not_enough_extract_items :
2094 				parse_too_many_extract_items;
2095 			break;
2096 		} else if (numItems > 0 && e_type == me_print) {
2097 			print_item = (__nis_mapping_item_t *)s_calloc(numItems,
2098 				sizeof (__nis_mapping_item_t));
2099 			if (print_item == NULL)
2100 				break;
2101 		}
2102 
2103 		if (e_type == me_print) {
2104 			sub.element.print.numItems = numItems;
2105 			sub.element.print.fmt = base;
2106 			sub.element.print.item = print_item;
2107 			s = get_print_sub_element(s, end_s, type, &sub);
2108 			if (s == NULL)
2109 				break;
2110 		}
2111 		s = skip_token(s, end_s, close_paren_token);
2112 		if (s == NULL)
2113 			break;
2114 
2115 		subelement->type = e_type;
2116 		if (e_type == me_extract) {
2117 			subelement->element.extract.fmt = base;
2118 			subelement->element.extract.item = item;
2119 		} else {
2120 			subelement->type = me_print;
2121 			subelement->element.print.fmt = base;
2122 			subelement->element.print.numItems = numItems;
2123 			subelement->element.print.item = print_item;
2124 			subelement->element.print.doElide =
2125 				sub.element.print.doElide;
2126 			subelement->element.print.elide =
2127 				sub.element.print.elide;
2128 		}
2129 		if (p_error == no_parse_error)
2130 			return (s);
2131 	}
2132 	free_mapping_item(&item);
2133 	if (base != NULL)
2134 		free_mapping_format(base);
2135 	if (print_item) {
2136 		for (n = 0; n < numItems; n++)
2137 			free_mapping_item(&print_item[n]);
2138 		free(print_item);
2139 	}
2140 
2141 	return (NULL);
2142 }
2143 
2144 /*
2145  * FUNCTION:	skip_get_dn
2146  *
2147  *	Get first token after dn
2148  *
2149  * RETURN VALUE:	NULL if error (not valid dn)
2150  *			position of beginning next token after dn
2151  *
2152  * INPUT:		the attribute value
2153  */
2154 
2155 const char *
2156 skip_get_dn(const char *dn, const char *end)
2157 {
2158 	size_t		len		= 0;
2159 	bool_t		in_quote	= FALSE;
2160 	bool_t		goteq		= FALSE;
2161 	bool_t		gotch		= FALSE;
2162 	bool_t		done		= FALSE;
2163 	bool_t		last_comma	= FALSE;
2164 	const char	*last_dn	= dn;
2165 
2166 	while (!done) {
2167 		dn += len;
2168 		if (last_comma) {
2169 			last_dn = dn;
2170 			last_comma = FALSE;
2171 		}
2172 		if (dn >= end)
2173 			break;
2174 		len = 1;
2175 		switch (*dn) {
2176 			case ESCAPE_CHAR:
2177 				len = 2;
2178 				gotch = TRUE;
2179 				break;
2180 			case DOUBLE_QUOTE_CHAR:
2181 				in_quote = !in_quote;
2182 				break;
2183 			case QUESTION_MARK:
2184 			case CLOSE_PAREN_CHAR:
2185 			case COLON_CHAR:
2186 				done = !in_quote;
2187 				/* FALLTHRU */
2188 			case SEMI_COLON_CHAR:
2189 			case PLUS_SIGN:
2190 			case COMMA_CHAR:
2191 				if (!in_quote) {
2192 					if (!goteq || !gotch)
2193 						return (last_dn);
2194 					goteq = FALSE;
2195 					gotch = FALSE;
2196 					if (*dn != PLUS_SIGN)
2197 						last_dn = dn;
2198 					last_comma = *dn == COMMA_CHAR;
2199 				} else {
2200 					gotch = TRUE;
2201 				}
2202 				break;
2203 			case EQUAL_CHAR:
2204 				if (!in_quote) {
2205 					if (!gotch || goteq)
2206 						return (NULL);
2207 					goteq = TRUE;
2208 					gotch = FALSE;
2209 				} else {
2210 					gotch = TRUE;
2211 				}
2212 				break;
2213 			default:
2214 				if (!is_whitespace(*dn))
2215 					gotch = TRUE;
2216 				break;
2217 		}
2218 	}
2219 
2220 	if (dn == end) {
2221 		if (!in_quote && goteq && gotch)
2222 			last_dn = dn;
2223 	}
2224 
2225 	return (last_dn);
2226 }
2227 
2228 /*
2229  * FUNCTION:	get_ldap_filter_element
2230  *
2231  *	Get an ldap filter element for a given string
2232  *
2233  * RETURN VALUE:	NULL if error
2234  *			__nis_mapping_element_t if success
2235  *
2236  * INPUT:		the string to parse
2237  */
2238 
2239 static __nis_mapping_element_t *
2240 get_ldap_filter_element(
2241 	const char			*s,
2242 	const char			*end_s
2243 )
2244 {
2245 	token_type			t;
2246 	const char			*begin_token;
2247 	const char			*end_token;
2248 	char				*format_str;
2249 	__nis_mapping_element_t		*e		= NULL;
2250 
2251 	begin_token = s;
2252 	end_token = end_s;
2253 	s = get_next_token(&begin_token, &end_token, &t);
2254 	if (s == NULL || t != open_paren_token)
2255 		return (NULL);
2256 
2257 	begin_token = s;
2258 	end_token = end_s;
2259 	s = get_next_token(&begin_token, &end_token, &t);
2260 	if (s == NULL || t != quoted_string_token)
2261 		return (NULL);
2262 
2263 	format_str = s_strndup(begin_token, end_token - begin_token);
2264 	if (format_str == NULL)
2265 		return (NULL);
2266 	e = (__nis_mapping_element_t *)
2267 		s_calloc(1, sizeof (__nis_mapping_element_t));
2268 	if (e != NULL) {
2269 		(void) get_print_mapping_element(s, end_s,
2270 				format_str, e, mit_nisplus);
2271 		if (p_error != no_parse_error) {
2272 			free_mapping_element(e);
2273 			e = NULL;
2274 		}
2275 	}
2276 	free(format_str);
2277 	return (e);
2278 }
2279 
2280 /*
2281  * FUNCTION:	get_search_triple
2282  *
2283  *	Get the search triple or if NULL determine if valid
2284  *
2285  * RETURN VALUE:	NULL if error
2286  *			position of beginning next token after
2287  *			search triple
2288  *
2289  * INPUT:		the attribute value
2290  */
2291 
2292 const char *
2293 get_search_triple(
2294 	const char			*s,
2295 	const char			*end_s,
2296 	__nis_search_triple_t		*triple
2297 )
2298 {
2299 	const char	*begin_token;
2300 	const char	*end_token;
2301 	char		*search_base	= NULL;
2302 	int		scope		= LDAP_SCOPE_ONELEVEL;
2303 	char		*filter		= NULL;
2304 	const char	*s1;
2305 	__nis_mapping_element_t
2306 			*element	= NULL;
2307 
2308 	/*
2309 	 * The form of the searchTriple is assumed to be as follows:
2310 	 * searchTriple	= [baseDN] ["?" [scope] ["?" [filter]]]
2311 	 * baseDN = Base DN for search
2312 	 * scope = "base" | "one" | "sub"
2313 	 * filter = LDAP search filter
2314 	 */
2315 	for (; p_error == no_parse_error; ) {
2316 		while (s < end_s && is_whitespace(*s))
2317 			s++;
2318 		if (s == end_s)
2319 			break;
2320 
2321 		if (!IS_TERMINAL_CHAR(*s)) {
2322 			begin_token = s;
2323 			s = skip_get_dn(begin_token, end_s);
2324 			if (s == NULL) {
2325 				p_error = parse_invalid_dn;
2326 				break;
2327 			}
2328 			if (triple != NULL) {
2329 				search_base = s_strndup(begin_token,
2330 					s - begin_token);
2331 				if (search_base == NULL)
2332 					break;
2333 			}
2334 			while (s < end_s && is_whitespace(*s))
2335 				s++;
2336 			if (s == end_s)
2337 				break;
2338 		}
2339 
2340 		if (!IS_TERMINAL_CHAR(*s)) {
2341 			p_error = parse_bad_ldap_item_format;
2342 			break;
2343 		}
2344 		if (*s != QUESTION_MARK)
2345 			break;
2346 
2347 		s++;
2348 		while (s < end_s && is_whitespace(*s))
2349 			s++;
2350 		if (s == end_s)
2351 			break;
2352 
2353 		/* base, one, or sub, or empty value */
2354 		if (!IS_TERMINAL_CHAR(*s)) {
2355 			if ((s1 = skip_string("base", s, end_s - s)) != NULL) {
2356 				scope = LDAP_SCOPE_BASE;
2357 			} else if ((s1 = skip_string("one", s, end_s - s)) !=
2358 					NULL) {
2359 				scope = LDAP_SCOPE_ONELEVEL;
2360 			} else if ((s1 = skip_string("sub", s, end_s - s)) !=
2361 					NULL) {
2362 				scope = LDAP_SCOPE_SUBTREE;
2363 			} else if (s + 1 < end_s && *s != QUESTION_MARK) {
2364 				p_error = parse_invalid_scope;
2365 				break;
2366 			}
2367 			if (s1 != NULL)
2368 				s = s1;
2369 			while (s < end_s && is_whitespace(*s))
2370 				s++;
2371 		}
2372 
2373 		if (s == end_s)
2374 			break;
2375 		if (*s != QUESTION_MARK)
2376 			break;
2377 		s++;
2378 		while (s < end_s && is_whitespace(*s))
2379 			s++;
2380 		if (s == end_s || IS_TERMINAL_CHAR(*s))
2381 			break;
2382 
2383 		/* LDAP search filter */
2384 		if (*s == OPEN_PAREN_CHAR) {
2385 		    begin_token = s;
2386 		    end_token = end_s;
2387 		    s = get_ldap_filter(&begin_token, &end_token);
2388 		    if (s == NULL)
2389 			break;
2390 		    s = end_token;
2391 		    element = get_ldap_filter_element(begin_token, end_token);
2392 		    if (element != NULL)
2393 			break;
2394 		} else {
2395 		    begin_token = s;
2396 		    end_token = end_s;
2397 		    s = get_ava_list(&begin_token, &end_token, TRUE);
2398 		    if (s == NULL)
2399 			break;
2400 		    s = end_token;
2401 		}
2402 		if (triple != NULL)
2403 			filter = s_strndup(begin_token, s - begin_token);
2404 		if (p_error == no_parse_error)
2405 			break;
2406 	}
2407 	if (p_error == no_parse_error && triple != NULL) {
2408 		triple->base = search_base;
2409 		triple->scope = scope;
2410 		triple->attrs = filter;
2411 		triple->element = element;
2412 		element = NULL;
2413 		filter = NULL;
2414 		search_base = NULL;
2415 	}
2416 
2417 	if (search_base != NULL)
2418 		free(search_base);
2419 	if (filter != NULL)
2420 		free(filter);
2421 	if (element != NULL) {
2422 		free_mapping_element(element);
2423 		free(element);
2424 	}
2425 	return (p_error == no_parse_error ? s : NULL);
2426 }
2427 
2428 /*
2429  * FUNCTION:	get_mapping_format
2430  *
2431  *	Get the __nis_mapping_format_t from the string
2432  *
2433  * RETURN VALUE:	FALSE if error
2434  *			TRUE if __nis_mapping_format_t returned
2435  *
2436  * INPUT:		the format string
2437  */
2438 
2439 static bool_t
2440 get_mapping_format(
2441 	const char		*fmt_string,
2442 	__nis_mapping_format_t	**fmt,
2443 	int			*nfmt,
2444 	int			*numItems,
2445 	bool_t			print_mapping)
2446 {
2447 	const char		*f	= fmt_string;
2448 	const char		*ef;
2449 	__nis_mapping_format_t	*b;
2450 	__nis_mapping_format_t	*base	= NULL;
2451 	int			n	= 0;
2452 	int			nItems	= 0;
2453 
2454 	f = fmt_string;
2455 	ef = f + strlen(f);
2456 	base = (__nis_mapping_format_t *)
2457 	    s_calloc(1, sizeof (__nis_mapping_format_t));
2458 
2459 	if (base == NULL)
2460 		return (FALSE);
2461 	base->type = mmt_begin;
2462 	n++;
2463 
2464 	for (;;) {
2465 		b = (__nis_mapping_format_t *)s_realloc(
2466 		    base, (n + 1) * sizeof (__nis_mapping_format_t));
2467 
2468 		if (b == NULL)
2469 			break;
2470 		base = b;
2471 		base[n].type = mmt_end;
2472 		if (f == ef) {
2473 			if (nfmt)
2474 				*nfmt = n + 1;
2475 			*fmt = base;
2476 			if (numItems)
2477 				*numItems = nItems;
2478 			return (TRUE);
2479 		}
2480 		if (print_mapping)
2481 		    f = get_next_print_format_item(f, ef, &base[n]);
2482 		else
2483 		    f = get_next_extract_format_item(f, ef, &base[n]);
2484 
2485 
2486 		if (f == NULL)
2487 			break;
2488 		if (base[n].type == mmt_item ||
2489 			base[n].type == mmt_berstring)
2490 			nItems++;
2491 		n++;
2492 	}
2493 	if (base != NULL)
2494 		free_mapping_format(base);
2495 	return (FALSE);
2496 }
2497 
2498 /*
2499  * FUNCTION:	getIndex
2500  *
2501  *	Returns a string containing the index
2502  *
2503  * RETURN VALUE:	NULL if error
2504  *			a string containing the index
2505  *
2506  * INPUT:		attribute containing the index
2507  */
2508 
2509 static char *
2510 getIndex(const char **s_cur, const char *s_end)
2511 {
2512 	const char	*s		= *s_cur + 1;
2513 	const char	*s1;
2514 	char		*s_index;
2515 	char		*s_index1;
2516 	char		*s_index_end;
2517 	int		n_brackets	= 1;
2518 	bool_t		in_quotes	= FALSE;
2519 	char		*index		= NULL;
2520 
2521 	while (s < s_end && is_whitespace(*s))
2522 		s++;
2523 	for (s1 = s; s1 < s_end; s1++) {
2524 		if (*s1 == ESCAPE_CHAR)
2525 			s1++;
2526 		else if (*s1 == DOUBLE_QUOTE_CHAR) {
2527 			in_quotes = !in_quotes;
2528 		} else if (in_quotes)
2529 			;
2530 		else if (*s1 == CLOSE_BRACKET) {
2531 			if (--n_brackets == 0)
2532 				break;
2533 		} else if (*s1 == OPEN_BRACKET)
2534 			n_brackets++;
2535 	}
2536 
2537 	if (n_brackets == 0) {
2538 		index = s_strndup(s, s1 - s);
2539 		if (index != NULL) {
2540 			s_index_end = index + (s1 - s);
2541 			s_index1 = index;
2542 			for (s_index = index; s_index < s_index_end;
2543 			    s_index++) {
2544 				if (*s_index == ESCAPE_CHAR) {
2545 					*s_index1++ = *s_index++;
2546 				} else if (*s_index == DOUBLE_QUOTE_CHAR) {
2547 					in_quotes = !in_quotes;
2548 				} else if (!in_quotes &&
2549 				    is_whitespace(*s_index)) {
2550 					continue;
2551 				}
2552 				*s_index1++ = *s_index;
2553 			}
2554 			*s_index1 = *s_index;
2555 
2556 			s = s1 + 1;
2557 
2558 			while (s < s_end && is_whitespace(*s))
2559 				s++;
2560 			*s_cur = s;
2561 		}
2562 	} else
2563 		p_error = parse_mismatched_brackets;
2564 
2565 	return (index);
2566 }
2567 
2568 /*
2569  * FUNCTION:	parse_index
2570  *
2571  *	Parse attribute string to get __nis_index_t
2572  *
2573  * RETURN VALUE:	FALSE if error
2574  *			TRUE if __nis_index_t returned
2575  *
2576  * INPUT:		the attribute value to parse
2577  */
2578 
2579 bool_t
2580 parse_index(const char *s, const char *end_s, __nis_index_t *index)
2581 {
2582 	const char		*begin_token;
2583 	const char		*end_token;
2584 	char			*name_str	= NULL;
2585 	char			**name;
2586 	char			*fmt_string	= NULL;
2587 	__nis_mapping_format_t	*v		= NULL;
2588 	__nis_mapping_format_t	**value;
2589 	token_type		t;
2590 	int			n		 = 0;
2591 
2592 	if (index != NULL)
2593 		(void) memset(index, 0, sizeof (*index));
2594 
2595 	while (s < end_s) {
2596 		if (n > 0) {
2597 			s = skip_token(s, end_s, comma_token);
2598 			if (s == NULL) {
2599 				p_error = parse_bad_index_format;
2600 				break;
2601 			}
2602 		}
2603 		begin_token = s;
2604 		end_token = end_s;
2605 		s = get_next_token(&begin_token, &end_token, &t);
2606 		if (s == NULL)
2607 			break;
2608 		if (t != string_token) {
2609 			p_error = parse_bad_index_format;
2610 			break;
2611 		}
2612 		s = skip_token(s, end_s, equal_token);
2613 		if (s == NULL) {
2614 			p_error = parse_bad_index_format;
2615 			break;
2616 		}
2617 		if (index != NULL) {
2618 			name_str = s_strndup_esc(begin_token,
2619 				end_token - begin_token);
2620 			if (name_str == NULL)
2621 				break;
2622 		}
2623 		begin_token = s;
2624 		end_token = end_s;
2625 		s = get_next_token(&begin_token, &end_token, &t);
2626 		if (s == NULL)
2627 			break;
2628 		if (t != string_token && t != quoted_string_token) {
2629 			p_error = parse_bad_index_format;
2630 			break;
2631 		}
2632 		fmt_string = s_strndup(begin_token, end_token - begin_token);
2633 		if (fmt_string == NULL)
2634 			break;
2635 		if (!get_mapping_format(fmt_string, &v, NULL, NULL, FALSE))
2636 			break;
2637 		free(fmt_string);
2638 		fmt_string = NULL;
2639 		if (index != NULL) {
2640 			name = s_realloc(index->name,
2641 				(n + 1) * sizeof (char *));
2642 			if (name == NULL)
2643 				break;
2644 			value = s_realloc(index->value,
2645 				(n + 1) * sizeof (__nis_mapping_format_t *));
2646 			if (value == NULL)
2647 				break;
2648 			name[n] = name_str;
2649 			name_str = NULL;
2650 			value[n] = v;
2651 			v = NULL;
2652 			index->numIndexes = ++n;
2653 			index->name = name;
2654 			index->value = value;
2655 		} else if (v != NULL) {
2656 			free_mapping_format(v);
2657 			v = NULL;
2658 		}
2659 	}
2660 	if (p_error != no_parse_error) {
2661 		if (name_str != NULL)
2662 			free(name_str);
2663 		if (v != NULL)
2664 			free_mapping_format(v);
2665 		if (fmt_string != NULL)
2666 			free(fmt_string);
2667 		if (index != NULL)
2668 			free_index(index);
2669 	}
2670 	return (p_error != no_parse_error);
2671 }
2672 
2673 /*
2674  * FUNCTION:	get_deleteDisp
2675  *
2676  *	Parse deleteDisp. Sets p_error if an error occurred.
2677  *
2678  * RETURN VALUE:	TRUE on success
2679  *			FAILURE on failure
2680  *
2681  * INPUT:		begin and end of string and __nis_object_dn_t
2682  */
2683 
2684 static bool_t
2685 get_deleteDisp(const char *s_begin, const char *s_end,
2686 		__nis_object_dn_t *obj_dn)
2687 {
2688 	/*
2689 	 * deleteDisp: "always" | perDbId | "never"
2690 	 * perDbId: "dbid" "=" delDatabaseId
2691 	 */
2692 
2693 	if (same_string("always", s_begin, s_end - s_begin)) {
2694 		obj_dn->delDisp = dd_always;
2695 	} else if (same_string("never", s_begin, s_end - s_begin)) {
2696 		obj_dn->delDisp = dd_never;
2697 	} else if ((s_begin = skip_string("dbid", s_begin, s_end - s_begin))
2698 			!= NULL) {
2699 		obj_dn->delDisp = dd_perDbId;
2700 		while (s_begin < s_end && is_whitespace(*s_begin))
2701 			s_begin++;
2702 		if (s_begin == s_end || *s_begin != EQUAL_CHAR) {
2703 			p_error = parse_object_dn_syntax_error;
2704 		} else {
2705 			s_begin++;
2706 			while (s_begin < s_end && is_whitespace(*s_begin))
2707 				s_begin++;
2708 			while (s_begin < s_end && is_whitespace(s_end[-1]))
2709 				s_end--;
2710 			if (s_begin == s_end) {
2711 				p_error = parse_object_dn_syntax_error;
2712 			} else {
2713 				obj_dn->dbIdName =
2714 					s_strndup(s_begin, s_end - s_begin);
2715 			}
2716 		}
2717 	} else {
2718 		p_error = parse_object_dn_syntax_error;
2719 	}
2720 	return (p_error == no_parse_error);
2721 }
2722