summaryrefslogtreecommitdiff
path: root/usr/src/lib/libnisdb/ldap_parse.c
blob: 9650ee53923e9caf1814e85aa2907df051e1b1e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <malloc.h>
#include <stdlib.h>
#include <deflt.h>
#include <limits.h>

#include "ldap_parse.h"
#include "ldap_glob.h"
#include "ldap_attr.h"
#include "ldap_util.h"
#include "ldap_map.h"
#include "ldap_ruleval.h"
#include "nis_parse_ldap_conf.h"

int yp2ldap = 0;
/*
 * List of mapping structures in original (i.e., as in config file) order.
 * Lined on the 'seqNext' field.
 */
__nis_table_mapping_t	*ldapMappingSeq = 0;

/*
 * Call the parser for the config file 'ldapConfFile', and command line
 * attribute settings per 'ldapCLA'.
 *
 * Returns
 *	0	Success
 *	-1	Config file stat/open or parse error
 *	1	No mapping should be used.
 */
int
parseConfig(char **ldapCLA, char *ldapConfFile) {
	int		ret;

	/*
	 * Establish defaults for ldapDBTableMapping, so that we have
	 * valid values even if there's no mapping config to parse.
	 */
	ldapDBTableMapping.initTtlLo = (3600-1800);
	ldapDBTableMapping.initTtlHi = (3600+1800);
	ldapDBTableMapping.ttl = 3600;
	ldapDBTableMapping.enumExpire = 0;
	ldapDBTableMapping.fromLDAP = FALSE;
	ldapDBTableMapping.toLDAP = FALSE;
	ldapDBTableMapping.expire = 0;

	ret = parse_ldap_migration((const char **)ldapCLA, ldapConfFile);

	return (ret);
}

/*
 * Convert the linked list of __nis_table_mapping_t's (produced by the
 * attribute parser) to the 'ldapMappingList', keyed on the objPath.
 *
 * Once this function has returned, the 'tlist' is invalid, and must
 * not be used in any way.
 */
int
linked2hash(__nis_table_mapping_t *tlist) {
	__nis_hash_table_mt	dbids;
	__nis_table_mapping_t	*t, *told, *x, **seqNext;
	__nis_object_dn_t	*o, *to;
	char			*myself = "linked2hash";
#ifdef	NISDB_LDAP_DEBUG
	char			*selectDbid = getenv("NISLDAPSELECTDBID");
	char			**sdi, *s;
	int			i, nsdi;
#endif	/* NISDB_LDAP_DEBUG */


	if (tlist == 0)
		return (0);

	/* proxyInfo.default_nis_domain must end in a dot */
	{
		int	len = slen(proxyInfo.default_nis_domain);

		if (len > 0 && proxyInfo.default_nis_domain[len-1] != '.') {
			char	*domain = am(myself, len+2);

			(void) memcpy(domain, proxyInfo.default_nis_domain,
					len);
			domain[len] = '.';
			domain[len+1] = '\0';
			sfree(proxyInfo.default_nis_domain);
			proxyInfo.default_nis_domain = domain;
		}
	}

#ifdef	NISDB_LDAP_DEBUG
	for (nsdi = 0, s = selectDbid; s != 0 && *s != '\0'; s++) {
		if (*s != ' ') {
			nsdi++;
			while (*s != ' ' && *s != '\0')
				s++;
			if (*s == '\0')
				break;
		}
	}
	if (nsdi > 0) {
		sdi = am(myself, nsdi * sizeof (sdi[0]));
		if (sdi == 0)
			logmsg(MSG_NOTIMECHECK, LOG_WARNING,
				"%s: Memory alloc failure for dbId selection",
				myself);
		else {
			for (i = 0, s = selectDbid; *s != '\0'; s++) {
				if (*s != ' ') {
					sdi[i++] = selectDbid;
					while (*s != ' ' && *s != '\0')
						s++;
					if (*s != '\0') {
						*s = '\0';
						s++;
					} else
						break;
					selectDbid = s;
				}
			}
		}
	}
#endif	/* NISDB_LDAP_DEBUG */

	__nis_init_hash_table(&dbids, 0);

	seqNext = &ldapMappingSeq;
	for (t = tlist; t != 0; t = told) {
		int	len;

#ifdef	NISDB_LDAP_DEBUG
		/*
		 * If the dbId doesn't match 'selectDbid', skip this
		 * mapping. Re-insert on 'tlist', in order to keep memory
		 * leak checking happy. Note that 'tlist' may end up pointing
		 * into the real mapping list, so it shouldn't be used once
		 * this routine has been called.
		 */
		if (nsdi > 0) {
			for (i = 0; i < nsdi; i++) {
				if (strcmp(sdi[i], t->dbId) == 0)
					break;
			}
			if (i >= nsdi) {
				told = t->next;
				if (tlist != t)
					t->next = tlist;
				else
					t->next = 0;
				tlist = t;
				continue;
			}
		}
#endif	/* NISDB_LDAP_DEBUG */

		told = t->next;
		t->next = 0;

		/* Make sure t->item.name is set correctly */
		if (t->item.name == 0)
			t->item.name = t->dbId;

		/* Remove leading dot in object name, if any */
		len = slen(t->objName);
		while (len > 0 && t->objName[0] == '.') {
			(void) memmove(t->objName, &t->objName[1], len);
			len -= 1;
		}

		/*
		 * Initialize the object path, which is what we'll
		 * rehash on.
		 */
		if (yp2ldap) {
			t->objPath = internal_table_name(t->objName,
				t->objPath);
			if (!t->objPath) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
			"%s: Failed to obtain internal table name for \"%s\"",
					myself, t->objName);
				return (-1);
			}
		} else {
			t->objPath = am(myself, len + MAXPATHLEN + 1);
			if (t->objPath == 0)
				return (-1);
			if (internal_table_name(t->objName,
				t->objPath) == 0) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
			"%s: Failed to obtain internal table name for \"%s\"",
					myself, t->objName);
				return (-1);
			}
		}

		/*
		 * Initialize the column name array.
		 */
		if (!yp2ldap) {
			if (setColumnsDuringConfig && setColumnNames(t)) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
				"%s: Unable to find column names for \"%s\"",
					myself, NIL(t->objName));
				return (-1);
			}
		}

		/*
		 * If there are multiple mapping target containers, make
		 * each one into it's own mapping structure. They can all
		 * be minimal copies (i.e., share pointers to sub-structures
		 * other than the objectDN).
		 *
		 * If objectDN is NULL, we will never use this structure.
		 * In order to allow the rest of the mapping code to assume
		 * objectDN != NULL, skip the mapping (even if x == t).
		 */
		for (o = to = t->objectDN; o != 0; o = o->next) {
			__nis_table_mapping_t	*p;

			if (o == to) {
				x = t;
				/*
				 * Only insert the first mapping for an
				 * object on the sequential list.
				 */
				*seqNext = t;
				t->seqNext = 0;
				seqNext = (__nis_table_mapping_t **)&t->seqNext;
			} else {
				x = am(myself, sizeof (*x));
				if (x == 0) {
					/*
					 * This happens during rpc.nisd
					 * initialization, and it's an
					 * unrecoverable disaster, so don't
					 * bother cleaning up.
					 */
					return (-1);
				}
				memcpy(x, t, sizeof (*x));
				x->objectDN = o;
				x->next = 0;
			}

			/*
			 * If x->objectDN->write.base is NULL, clone it from
			 * x->objectDN->read.base.
			 */
			if (x->objectDN->write.scope != LDAP_SCOPE_UNKNOWN) {
				if (x->objectDN->write.base == 0 &&
						x->objectDN->read.base != 0) {
					x->objectDN->write.base =
						sdup(myself, T,
						x->objectDN->read.base);
					if (x->objectDN->write.base == 0)
						return (-1);
				}
				if (x->objectDN->write.attrs == 0 &&
						x->objectDN->read.attrs != 0) {
					x->objectDN->write.attrs =
						sdup(myself, T,
						x->objectDN->read.attrs);
					if (x->objectDN->write.attrs == 0)
						return (-1);
				}
			}

			if (o != to) {
				/* Insert last on the 't->next' list */
				for (p = t; p->next != 0; p = p->next);
				p->next = x;
			}
		}

		/* Insert on dbid hash list */
		if (t->objectDN != 0 && !__nis_insert_item_mt(t, &dbids, 0)) {
			logmsg(MSG_NOTIMECHECK, LOG_ERR,
			"%s: Error inserting mapping for \"%s\" on hash list",
				myself, NIL(t->objName));
#ifdef	NISDB_LDAP_DEBUG
			abort();
#endif	/* NISDB_LDAP_DEBUG */
			return (-1);
		}
	}

	/*
	 * dbids2objs() will remove the entries on 'dbids', so no need
	 * to clean up that list from this function.
	 */
	return (dbids2objs(&dbids, &ldapMappingList));
}

int
dbids2objs(__nis_hash_table_mt *dbids, __nis_hash_table_mt *objs) {
	__nis_table_mapping_t	*t, *o;
	char			*myself = "dbids2objs";


	while ((t = __nis_pop_item_mt(dbids)) != 0) {
		/* Previous entry for this object ? */
		o = __nis_find_item_mt(t->objPath, objs, -1, 0);
		if (o != 0) {
			__nis_table_mapping_t	*p = o;
			/*
			 * Mapping already exists, so this is an alternate.
			 * Find the end of the list of any previous alt's,
			 * and insert there.
			 */
			while (p->next != 0) {
				p = p->next;
			}
			p->next = t;
			if (!__nis_release_item(o, objs, -1)) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
					"%s: __nis_release_item error",
					myself);
				return (-1);
			}
		} else {
			t->item.name = t->objPath;
			if (!__nis_insert_item_mt(t, objs, 0)) {
				logmsg(MSG_NOTIMECHECK, LOG_ERR,
					"%s: __nis_insert_item error",
					myself);
				return (-1);
			}
		}
	}

	return (0);
}

/*
 * internal_table_name()
 *
 * Removes the local domain part from a fully qualified name
 * to create the internal table name for an object. These tables are
 * stored in /var/nis/<hostname>
 *
 * Imported from rpc.nisd/nisdb.c.
 */
char *
internal_table_name(nis_name name, char *res)
{
	char		*s, *t;
	int		i, j;

	if (yp2ldap) {
		if (name == NULL)
			return (NULL);
		res = s_strndup(name, strlen(name));
		if (res == NULL)
			return (NULL);
		return (res);
	}

	if (res == NULL)
		return (NULL);
	/* pointer at the first character of the table name */
	s = relative_name(name);

	/*
	 * If s == NULL then either this is a request for a lookup
	 * in our parents namespace (ILLEGAL), or we're the root
	 * server and this is a lookup in our namespace.
	 */
	if (s) {
		return (NULL);
	}

	t = strrchr(res, '/');
	if (t)
		t++; /* Point past the slash */
	/* Strip off the quotes if they were used here. */
	if (t[0] == '"') {
		/* Check for simply a quoted quote. */
		if (t[1] != '"') {
			j = strlen(t);
			/* shift string left by one */
			for (i = 0; i < j; i++)
				t[i] = t[i+1];
			t[j-2] = '\0'; /* Trounce trailing dquote */
		}
	}
	/*
	 * OK so now we have the unique name for the table.
	 * At this point we can fix it up to match local
	 * file system conventions if we so desire. Since it
	 * is only used in this form by _this_ server we can
	 * mangle it any way we want, as long as we are consistent
	 * about it. :-)
	 */
	__make_legal(res);
	return (res);
}

/*
 * SYSTEM DEPENDENT
 *
 * This function makes the table name "legal" for the underlying file system.
 *
 * Imported from rpc.nisd/nisdb.c.
 */
void
__make_legal(char *s)
{
	while (*s) {
		if (isupper(*s))
			*s = tolower(*s);
		s++;
	}
}

/*
 * relative_name()
 * This internal function will remove from the NIS name, the domain
 * name of the current server, this will leave the unique part in
 * the name this becomes the "internal" version of the name. If this
 * function returns NULL then the name we were given to resolve is
 * bad somehow.
 *
 * A dynamically-allocated string is returned.
 *
 * Imported from rpc.nisd/nis_log_common.c
 */

nis_name
relative_name(s)
	char	*s;	/* string with the name in it. */
{
	char			*d;
	char			*buf;
	int			dl, sl;
	name_pos		p;

	if (s == NULL)
		return (NULL);

	d = __nis_rpc_domain();
	if (d == NULL)
		return (NULL);
	dl = strlen(d); 	/* _always dot terminated_   */

	buf = strdup(s);
	if (buf == NULL)
		return (NULL);
	strcpy(buf, s);		/* Make a private copy of 's'   */
	sl = strlen(buf);

	if (dl == 1) {			/* We're the '.' directory   */
		buf[sl-1] = '\0';	/* Lose the 'dot'	  */
		return (buf);
	}

	p = nis_dir_cmp(buf, d);

	/* 's' is above 'd' in the tree */
	if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == SAME_NAME)) {
		free(buf);
		return (NULL);
	}

	/* Insert a NUL where the domain name starts in the string */
	buf[(sl - dl) - 1] = '\0';

	/* Don't return a zero length name */
	if (buf[0] == '\0') {
		free((void *)buf);
		return (NULL);
	}

	return (buf);
}

/*
 * Wrapper for internal_table_name() that allocates a large enough
 * buffer for the internal name. Return value must be freed by caller.
 * If the input 'name' is NULL, the name of the root directory table
 * is returned.
 */
char *
internalTableName(char *name) {
	char	*buf, *res;
	char	*myself = "internalTableName";

	buf = (char *)am(myself, MAXPATHLEN + NIS_MAXNAMELEN + 1);
	if (buf == 0)
		return (0);

	if (name == 0) {
		(void) memcpy(buf, ROOTDIRFILE, slen(ROOTDIRFILE));
		return (buf);
	}

	res = internal_table_name(name, buf);
	if (res != buf) {
		sfree(buf);
		buf = 0;
	}

	return (buf);
}

/*
 * Return the object mapping for the object indicated either by the
 * internal DB name ('intNameArg'; preferred), or the FQ object name
 * 'name'. If 'asObj' is non-zero, the caller is interested in the
 * object mapping proper, not a mapping of table entries. Optionally,
 * also indicate if the object is mapped from (read) or to (write) LDAP.
 *
 * Note that there may be more than one mapping of the appropriate type.
 * Use the selectTableMapping() function in ldap_map.c to get all
 * alternatives. However, the function below works as a short-cut if:
 *
 *	You only want an indication that _a_ mapping of the desired
 *	type exists, or
 *
 *	You want the non-objectDN information for an object-mapping
 *	proper (i.e., _not_ the mapping for entries in a table).
 */
__nis_table_mapping_t *
getObjMapping(char *name, char *intNameArg, int asObj,
		int *doRead, int *doWrite) {
	__nis_table_mapping_t	*t, *x;
	char			*intName;
	int			freeIntName = 0, rd, wr;

	if (doRead != 0)
		*doRead = 0;
	if (doWrite != 0)
		*doWrite = 0;

	if (intNameArg == 0) {
		if (name == 0)
			return (0);
		intName = internalTableName(name);
		if (intName == 0)
			return (0);
		freeIntName = 1;
	} else {
		intName = intNameArg;
	}

	t = __nis_find_item_mt(intName, &ldapMappingList, 0, 0);
	if (t == 0) {
		if (freeIntName)
			sfree(intName);
		return (0);
	}

	rd = wr = 0;
	for (x = t; x != 0; x = x->next) {
		/*
		 * If we're looking for an object mapping, and this
		 * one's for entries in a table, skip it.
		 */
		if (asObj && x->objType == NIS_TABLE_OBJ &&
				x->numColumns > 0)
			continue;
		/* Check if we should read/write */
		if (x->objectDN->read.scope != LDAP_SCOPE_UNKNOWN)
			rd++;
		if (x->objectDN->write.scope != LDAP_SCOPE_UNKNOWN)
			wr++;
	}

	if (doRead != 0)
		*doRead = (rd > 0) ? 1 : 0;
	if (doWrite != 0)
		*doWrite = (wr > 0) ? 1 : 0;

	if (freeIntName)
		sfree(intName);

	return (x);
}