ldap_common.c revision 2830:5228d1267a01
117706Sjulian/*
217706Sjulian * CDDL HEADER START
317706Sjulian *
417706Sjulian * The contents of this file are subject to the terms of the
517706Sjulian * Common Development and Distribution License (the "License").
617706Sjulian * You may not use this file except in compliance with the License.
717706Sjulian *
817706Sjulian * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
917706Sjulian * or http://www.opensolaris.org/os/licensing.
1017706Sjulian * See the License for the specific language governing permissions
1117706Sjulian * and limitations under the License.
1217706Sjulian *
13165967Simp * When distributing Covered Code, include this CDDL HEADER in each
1417706Sjulian * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1517706Sjulian * If applicable, add the following below this CDDL HEADER, with the
1617706Sjulian * fields enclosed by brackets "[]" replaced with your own identifying
1717706Sjulian * information: Portions Copyright [yyyy] [name of copyright owner]
1817706Sjulian *
1917706Sjulian * CDDL HEADER END
2049439Sdeischen */
2117706Sjulian/*
2217706Sjulian * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2317706Sjulian * Use is subject to license terms.
2417706Sjulian */
2517706Sjulian
2617706Sjulian#pragma ident	"%Z%%M%	%I%	%E% SMI"
2717706Sjulian
2817706Sjulian#include "ldap_common.h"
2950476Speter#include <malloc.h>
3017706Sjulian#include <synch.h>
31174112Sdeischen#include <syslog.h>
32174112Sdeischen#include <rpcsvc/ypclnt.h>
3317706Sjulian#include <rpcsvc/yp_prot.h>
3417706Sjulian#include <thread.h>
35174112Sdeischen#include <ctype.h>
36103388Smini#include <stdlib.h>
3717706Sjulian#include <signal.h>
38174112Sdeischen#include <sys/stat.h>
39174112Sdeischen
40174112Sdeischen/* getent attributes filters */
41117907Sdeischen#define	_F_GETALIASENT		"(objectClass=rfc822MailGroup)"
4297204Sdeischen#define	_F_GETAUTHNAME		"(objectClass=SolarisAuthAttr)"
4375369Sdeischen#define	_F_GETAUUSERNAME	"(objectClass=SolarisAuditUser)"
4497204Sdeischen#define	_F_GETEXECNAME		"(objectClass=SolarisExecAttr)"
4571581Sdeischen#define	_F_GETGRENT		"(objectClass=posixGroup)"
46113658Sdeischen#define	_F_GETHOSTENT		"(objectClass=ipHost)"
4735509Sjb#define	_F_GETNETENT		"(objectClass=ipNetwork)"
4817706Sjulian#define	_F_GETPROFNAME \
4971581Sdeischen"(&(objectClass=SolarisProfAttr)(!(SolarisKernelSecurityPolicy=*)))"
5017706Sjulian#define	_F_GETPROTOENT		"(objectClass=ipProtocol)"
51113658Sdeischen#define	_F_GETPWENT		"(objectClass=posixAccount)"
52117907Sdeischen#define	_F_GETPRINTERENT	"(objectClass=sunPrinter)"
5397204Sdeischen#define	_F_GETRPCENT		"(objectClass=oncRpc)"
5435509Sjb#define	_F_GETSERVENT		"(objectClass=ipService)"
55113658Sdeischen#define	_F_GETSPENT		"(objectclass=shadowAccount)"
56113658Sdeischen#define	_F_GETUSERNAME		"(objectClass=SolarisUserAttr)"
57114664Sdeischen#define	_F_GETPROJENT		"(objectClass=SolarisProject)"
58114664Sdeischen#define	_F_GETTNRHDB		"(objectClass=ipTnetHost)"
59117907Sdeischen#define	_F_GETTNRHTP		"(&(objectClass=ipTnetTemplate)"\
60114664Sdeischen				"(SolarisAttrKeyValue=*))"
61113658Sdeischen#define	_F_GETENT_SSD		"(%s)"
62117907Sdeischen
63117907Sdeischenstatic struct gettablefilter {
6497204Sdeischen	char *tablename;
6597204Sdeischen	char *tablefilter;
6697204Sdeischen} gettablefilterent[] = {
6744963Sjb	{(char *)_PASSWD,	(char *)_F_GETPWENT},
6897204Sdeischen	{(char *)_SHADOW,	(char *)_F_GETSPENT},
6997204Sdeischen	{(char *)_GROUP,	(char *)_F_GETGRENT},
7097204Sdeischen	{(char *)_HOSTS,	(char *)_F_GETHOSTENT},
71113658Sdeischen	{(char *)_NETWORKS,	(char *)_F_GETNETENT},
72113658Sdeischen	{(char *)_PROTOCOLS,	(char *)_F_GETPROTOENT},
73117907Sdeischen	{(char *)_RPC,		(char *)_F_GETRPCENT},
74113658Sdeischen	{(char *)_ALIASES,	(char *)_F_GETALIASENT},
7597204Sdeischen	{(char *)_SERVICES,	(char *)_F_GETSERVENT},
76113658Sdeischen	{(char *)_AUUSER,	(char *)_F_GETAUUSERNAME},
77113658Sdeischen	{(char *)_AUTHATTR,	(char *)_F_GETAUTHNAME},
78113658Sdeischen	{(char *)_EXECATTR,	(char *)_F_GETEXECNAME},
7997204Sdeischen	{(char *)_PROFATTR,	(char *)_F_GETPROFNAME},
8097204Sdeischen	{(char *)_USERATTR,	(char *)_F_GETUSERNAME},
81114664Sdeischen	{(char *)_PROJECT,	(char *)_F_GETPROJENT},
82113658Sdeischen	{(char *)_PRINTERS,	(char *)_F_GETPRINTERENT},
83117907Sdeischen	{(char *)_TNRHDB,	(char *)_F_GETTNRHDB},
84113658Sdeischen	{(char *)_TNRHTP,	(char *)_F_GETTNRHTP},
85117907Sdeischen	{(char *)NULL,		(char *)NULL}
86117907Sdeischen};
87113658Sdeischen
8817706Sjulian
8997204Sdeischenstatic nss_status_t
90113658Sdeischenswitch_err(int rc, ns_ldap_error_t *error)
91113658Sdeischen{
92113658Sdeischen	switch (rc) {
9317706Sjulian	    case NS_LDAP_SUCCESS:
9497204Sdeischen		return (NSS_SUCCESS);
95117907Sdeischen
9697204Sdeischen	    case NS_LDAP_NOTFOUND:
9797204Sdeischen		return (NSS_NOTFOUND);
9897204Sdeischen
99113658Sdeischen	    case NS_LDAP_PARTIAL:
10097204Sdeischen		return (NSS_TRYAGAIN);
10197204Sdeischen
10297204Sdeischen	    case NS_LDAP_INTERNAL:
10397204Sdeischen		    if (error && (error->status == LDAP_SERVER_DOWN ||
10497204Sdeischen				error->status == LDAP_TIMEOUT))
10597204Sdeischen			    return (NSS_TRYAGAIN);
106113658Sdeischen		    else
107117907Sdeischen			    return (NSS_UNAVAIL);
108117907Sdeischen
109117907Sdeischen	    default:
11097204Sdeischen		return (NSS_UNAVAIL);
111	}
112}
113/* ARGSUSED */
114nss_status_t
115_nss_ldap_lookup(ldap_backend_ptr be, nss_XbyY_args_t *argp,
116		char *database, char *searchfilter, char *domain,
117		int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
118		char **realfilter, const void *userdata),
119		const void *userdata)
120{
121	int		callbackstat = 0;
122	ns_ldap_error_t	*error = NULL;
123	int		rc;
124
125#ifdef	DEBUG
126	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_lookup]\n");
127	(void) fprintf(stdout, "\tsearchfilter: %s\n", searchfilter);
128	(void) fprintf(stdout,
129		"\tuserdata: %s\n", userdata ? userdata : "NULL");
130	(void) fprintf(stdout, "\tdatabase: %s\n", database);
131#endif	/* DEBUG */
132
133	(void) __ns_ldap_freeResult(&be->result);
134
135	if ((rc = __ns_ldap_list(database, searchfilter, init_filter_cb,
136		be->attrs, NULL, 0, &be->result, &error, NULL,
137		userdata)) != NS_LDAP_SUCCESS) {
138		argp->returnval = 0;
139		rc = switch_err(rc, error);
140		(void) __ns_ldap_freeError(&error);
141
142		return (rc);
143	}
144		(void) __ns_ldap_freeError(&error);
145	/* callback function */
146	if ((callbackstat =
147		    be->ldapobj2str(be, argp)) != NSS_STR_PARSE_SUCCESS) {
148		goto error_out;
149	}
150
151	/*
152	 * publickey does not have a front end marshaller and expects
153	 * a string to be returned in NSS.
154	 * No need to convert file format -> struct.
155	 *
156	 */
157	if (be->db_type == NSS_LDAP_DB_PUBLICKEY) {
158		argp->returnval = argp->buf.buffer;
159		argp->returnlen = strlen(argp->buf.buffer);
160		be->db_type = NSS_LDAP_DB_NONE;
161		return (NSS_SUCCESS);
162	}
163	/*
164	 *  Assume the switch engine wants the returned data in the file
165	 *  format when argp->buf.result == NULL.
166	 *  The front-end marshaller str2ether(ethers) uses
167	 *  ent (argp->buf.result) and buffer (argp->buf.buffer)
168	 *  for different purpose so ethers has to be treated differently.
169	 */
170	if (argp->buf.result != NULL ||
171			be->db_type == NSS_LDAP_DB_ETHERS) {
172		/* file format -> struct */
173		if (argp->str2ent == NULL) {
174			callbackstat = NSS_STR_PARSE_PARSE;
175			goto error_out;
176		}
177
178		callbackstat = (*argp->str2ent)(be->buffer,
179					be->buflen,
180					argp->buf.result,
181					argp->buf.buffer,
182					argp->buf.buflen);
183		if (callbackstat == NSS_STR_PARSE_SUCCESS) {
184			if (be->db_type == NSS_LDAP_DB_ETHERS &&
185					argp->buf.buffer != NULL) {
186				argp->returnval = argp->buf.buffer;
187				argp->returnlen = strlen(argp->buf.buffer);
188			} else {
189				argp->returnval = argp->buf.result;
190				argp->returnlen = 1; /* irrelevant */
191			}
192			if (be->buffer != NULL) {
193				free(be->buffer);
194				be->buffer = NULL;
195				be->buflen = 0;
196				be->db_type = NSS_LDAP_DB_NONE;
197			}
198			return ((nss_status_t)NSS_SUCCESS);
199		}
200	} else {
201			/* return file format in argp->buf.buffer */
202			argp->returnval = argp->buf.buffer;
203			argp->returnlen = strlen(argp->buf.buffer);
204			return ((nss_status_t)NSS_SUCCESS);
205	}
206
207error_out:
208	if (be->buffer != NULL) {
209		free(be->buffer);
210		be->buffer = NULL;
211		be->buflen = 0;
212		be->db_type = NSS_LDAP_DB_NONE;
213	}
214	/* error */
215	if (callbackstat == NSS_STR_PARSE_PARSE) {
216		argp->returnval = 0;
217		return ((nss_status_t)NSS_NOTFOUND);
218	}
219	if (callbackstat == NSS_STR_PARSE_ERANGE) {
220		argp->erange = 1;
221		return ((nss_status_t)NSS_NOTFOUND);
222	}
223	if (callbackstat == NSS_STR_PARSE_NO_ADDR) {
224		/* No IPV4 address is found */
225		argp->h_errno = HOST_NOT_FOUND;
226		return ((nss_status_t)NSS_NOTFOUND);
227	}
228	return ((nss_status_t)NSS_UNAVAIL);
229}
230
231/*
232 *  This function is similar to _nss_ldap_lookup except it does not
233 *  do a callback.  It is only used by getnetgrent.c
234 */
235
236/* ARGSUSED */
237nss_status_t
238_nss_ldap_nocb_lookup(ldap_backend_ptr be, nss_XbyY_args_t *argp,
239		char *database, char *searchfilter, char *domain,
240		int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
241		char **realfilter, const void *userdata),
242		const void *userdata)
243{
244	ns_ldap_error_t	*error = NULL;
245	int		rc;
246
247#ifdef	DEBUG
248	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_nocb_lookup]\n");
249	(void) fprintf(stdout, "\tsearchfilter: %s\n", searchfilter);
250	(void) fprintf(stdout, "\tdatabase: %s\n", database);
251	(void) fprintf(stdout,
252		"\tuserdata: %s\n", userdata ? userdata : "NULL");
253#endif	/* DEBUG */
254
255	(void) __ns_ldap_freeResult(&be->result);
256
257	if ((rc = __ns_ldap_list(database, searchfilter, init_filter_cb,
258		be->attrs, NULL, 0, &be->result, &error, NULL,
259		userdata)) != NS_LDAP_SUCCESS) {
260		argp->returnval = 0;
261		rc = switch_err(rc, error);
262		(void) __ns_ldap_freeError(&error);
263		return (rc);
264	}
265
266	return ((nss_status_t)NSS_SUCCESS);
267}
268
269
270/*
271 *
272 */
273
274void
275_clean_ldap_backend(ldap_backend_ptr be)
276{
277	ns_ldap_error_t *error;
278
279#ifdef	DEBUG
280	(void) fprintf(stdout, "\n[ldap_common.c: _clean_ldap_backend]\n");
281#endif	/* DEBUG */
282
283	if (be->tablename != NULL)
284		free(be->tablename);
285	if (be->result != NULL)
286		(void) __ns_ldap_freeResult(&be->result);
287	if (be->enumcookie != NULL)
288		(void) __ns_ldap_endEntry(&be->enumcookie, &error);
289	if (be->services_cookie != NULL)
290		_nss_services_cookie_free((void **)&be->services_cookie);
291	if (be->toglue != NULL) {
292		free(be->toglue);
293		be->toglue = NULL;
294	}
295	if (be->buffer != NULL) {
296		free(be->buffer);
297		be->buffer = NULL;
298	}
299	free(be);
300}
301
302
303/*
304 * _nss_ldap_destr will free all smalloc'ed variable strings and structures
305 * before exiting this nsswitch shared backend library. This function is
306 * called before returning control back to nsswitch.
307 */
308
309/*ARGSUSED1*/
310nss_status_t
311_nss_ldap_destr(ldap_backend_ptr be, void *a)
312{
313
314#ifdef DEBUG
315	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_destr]\n");
316#endif /* DEBUG */
317
318	(void) _clean_ldap_backend(be);
319
320	return ((nss_status_t)NSS_SUCCESS);
321}
322
323
324/*
325 * _nss_ldap_setent called before _nss_ldap_getent. This function is
326 * required by POSIX.
327 */
328
329nss_status_t
330_nss_ldap_setent(ldap_backend_ptr be, void *a)
331{
332	struct gettablefilter	*gtf;
333
334#ifdef DEBUG
335	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_setent]\n");
336#endif /* DEBUG */
337
338	if (be->setcalled == 1)
339		(void) _nss_ldap_endent(be, a);
340	be->filter = NULL;
341	for (gtf = gettablefilterent; gtf->tablename != (char *)NULL; gtf++) {
342		if (strcmp(gtf->tablename, be->tablename))
343			continue;
344		be->filter = (char *)gtf->tablefilter;
345		break;
346	}
347
348	be->setcalled = 1;
349	be->enumcookie = NULL;
350	be->result = NULL;
351	be->services_cookie = NULL;
352	be->buffer = NULL;
353	return ((nss_status_t)NSS_SUCCESS);
354}
355
356
357/*
358 * _nss_ldap_endent called after _nss_ldap_getent. This function is
359 * required by POSIX.
360 */
361
362/*ARGSUSED1*/
363nss_status_t
364_nss_ldap_endent(ldap_backend_ptr be, void *a)
365{
366	ns_ldap_error_t	*error = NULL;
367
368#ifdef DEBUG
369	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_endent]\n");
370#endif /* DEBUG */
371
372	be->setcalled = 0;
373	be->filter = NULL;
374	if (be->enumcookie != NULL) {
375		(void) __ns_ldap_endEntry(&be->enumcookie, &error);
376		(void) __ns_ldap_freeError(&error);
377	}
378	if (be->result != NULL) {
379		(void) __ns_ldap_freeResult(&be->result);
380	}
381	if (be->services_cookie != NULL) {
382		_nss_services_cookie_free((void **)&be->services_cookie);
383	}
384	if (be->buffer != NULL) {
385		free(be->buffer);
386		be->buffer = NULL;
387	}
388
389	return ((nss_status_t)NSS_SUCCESS);
390}
391
392
393/*
394 *
395 */
396
397nss_status_t
398_nss_ldap_getent(ldap_backend_ptr be, void *a)
399{
400	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
401	ns_ldap_error_t	*error = NULL;
402	int		parsestat = 0;
403	int		retcode = 0;
404
405#ifdef	DEBUG
406	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_getent]\n");
407#endif	/* DEBUG */
408
409	if (be->setcalled == 0)
410		(void) _nss_ldap_setent(be, a);
411
412next_entry:
413	if (be->enumcookie == NULL) {
414		retcode = __ns_ldap_firstEntry(be->tablename,
415		be->filter, _merge_SSD_filter, be->attrs, NULL,
416		0, &be->enumcookie,
417		&be->result, &error, _F_GETENT_SSD);
418	} else {
419		if (be->services_cookie == NULL) {
420			retcode = __ns_ldap_nextEntry(be->enumcookie,
421				&be->result, &error);
422		}
423	}
424	if (retcode != NS_LDAP_SUCCESS) {
425		retcode = switch_err(retcode, error);
426		(void) __ns_ldap_freeError(&error);
427		(void) _nss_ldap_endent(be, a);
428		return (retcode);
429	} else {
430		/* ns_ldap_entry_t -> file format */
431		if ((parsestat = be->ldapobj2str(be, argp))
432			== NSS_STR_PARSE_SUCCESS) {
433			if (argp->buf.result != NULL) {
434				/* file format -> struct */
435				if (argp->str2ent == NULL) {
436					parsestat = NSS_STR_PARSE_PARSE;
437					goto error_out;
438				}
439				parsestat = (*argp->str2ent)(be->buffer,
440						be->buflen,
441						argp->buf.result,
442						argp->buf.buffer,
443						argp->buf.buflen);
444				if (parsestat == NSS_STR_PARSE_SUCCESS) {
445					if (be->buffer != NULL) {
446						free(be->buffer);
447						be->buffer = NULL;
448						be->buflen = 0;
449					}
450					be->result = NULL;
451					argp->returnval = argp->buf.result;
452					argp->returnlen = 1; /* irrevelant */
453					return ((nss_status_t)NSS_SUCCESS);
454				}
455			} else {
456				/*
457				 * nscd is not caching the enumerated
458				 * entries. This code path would be dormant.
459				 * Keep this path for the future references.
460				 */
461				argp->returnval = argp->buf.buffer;
462				argp->returnlen =
463					strlen(argp->buf.buffer) + 1;
464			}
465		}
466error_out:
467		if (be->buffer != NULL) {
468			free(be->buffer);
469			be->buffer = NULL;
470			be->buflen = 0;
471		}
472		be->result = NULL;
473		if (parsestat == NSS_STR_PARSE_PARSE) {
474			argp->returnval = 0;
475			(void) _nss_ldap_endent(be, a);
476			return ((nss_status_t)NSS_NOTFOUND);
477		}
478
479		if (parsestat == NSS_STR_PARSE_ERANGE) {
480			argp->erange = 1;
481			(void) _nss_ldap_endent(be, a);
482			return ((nss_status_t)NSS_NOTFOUND);
483		}
484		if (parsestat == NSS_STR_PARSE_NO_ADDR)
485			/*
486			 * No IPV4 address is found in the current entry.
487			 * It indicates that the entry contains IPV6 addresses
488			 * only. Instead of calling _nss_ldap_endent to
489			 * terminate, get next entry to continue enumeration.
490			 * If it returned NSS_NOTFOUND here,
491			 * gethostent() would return NULL
492			 * and the enumeration would stop prematurely.
493			 */
494			goto next_entry;
495	}
496
497	return ((nss_status_t)NSS_SUCCESS);
498}
499
500
501/*
502 *
503 */
504
505nss_backend_t *
506_nss_ldap_constr(ldap_backend_op_t ops[], int nops, char *tablename,
507		const char **attrs, fnf ldapobj2str)
508{
509	ldap_backend_ptr	be;
510
511#ifdef	DEBUG
512	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_constr]\n");
513#endif	/* DEBUG */
514
515	if ((be = (ldap_backend_ptr) calloc(1, sizeof (*be))) == 0)
516		return (0);
517	be->ops = ops;
518	be->nops = (nss_dbop_t)nops;
519	be->tablename = (char *)strdup(tablename);
520	be->attrs = attrs;
521	be->ldapobj2str = ldapobj2str;
522
523	return ((nss_backend_t *)be);
524}
525
526
527/*
528 *
529 */
530int
531chophostdomain(char *string, char *host, char *domain)
532{
533	char	*dot;
534
535	if (string == NULL)
536		return (-1);
537
538	if ((dot = strchr(string, '.')) == NULL) {
539		return (0);
540	}
541	*dot = '\0';
542	(void) strcpy(host, string);
543	(void) strcpy(domain, ++dot);
544
545	return (0);
546}
547
548
549/*
550 *
551 */
552int
553propersubdomain(char *domain, char *subdomain)
554{
555	int	domainlen, subdomainlen;
556
557	/* sanity check */
558	if (domain == NULL || subdomain == NULL)
559		return (-1);
560
561	domainlen = strlen(domain);
562	subdomainlen = strlen(subdomain);
563
564	/* is afterdot a substring of domain? */
565	if ((strncasecmp(domain, subdomain, subdomainlen)) != 0)
566		return (-1);
567
568	if (domainlen == subdomainlen)
569		return (1);
570
571	if (subdomainlen > domainlen)
572		return (-1);
573
574	if (*(domain + subdomainlen) != '.')
575		return (-1);
576
577	return (1);
578}
579