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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include "ldap_common.h"
29 #include <malloc.h>
30 #include <synch.h>
31 #include <syslog.h>
32 #include <rpcsvc/ypclnt.h>
33 #include <rpcsvc/yp_prot.h>
34 #include <thread.h>
35 #include <ctype.h>
36 #include <stdlib.h>
37 #include <signal.h>
38 #include <sys/stat.h>
39 
40 /* getent attributes filters */
41 #define	_F_GETALIASENT		"(objectClass=rfc822MailGroup)"
42 #define	_F_GETAUTHNAME		"(objectClass=SolarisAuthAttr)"
43 #define	_F_GETAUUSERNAME	"(objectClass=SolarisAuditUser)"
44 #define	_F_GETEXECNAME		"(objectClass=SolarisExecAttr)"
45 #define	_F_GETGRENT		"(objectClass=posixGroup)"
46 #define	_F_GETHOSTENT		"(objectClass=ipHost)"
47 #define	_F_GETNETENT		"(objectClass=ipNetwork)"
48 #define	_F_GETPROFNAME \
49 "(&(objectClass=SolarisProfAttr)(!(SolarisKernelSecurityPolicy=*)))"
50 #define	_F_GETPROTOENT		"(objectClass=ipProtocol)"
51 #define	_F_GETPWENT		"(objectClass=posixAccount)"
52 #define	_F_GETPRINTERENT	"(objectClass=sunPrinter)"
53 #define	_F_GETRPCENT		"(objectClass=oncRpc)"
54 #define	_F_GETSERVENT		"(objectClass=ipService)"
55 #define	_F_GETSPENT		"(objectclass=shadowAccount)"
56 #define	_F_GETUSERNAME		"(objectClass=SolarisUserAttr)"
57 #define	_F_GETPROJENT		"(objectClass=SolarisProject)"
58 #define	_F_GETTNRHDB		"(objectClass=ipTnetHost)"
59 #define	_F_GETTNRHTP		"(&(objectClass=ipTnetTemplate)"\
60 				"(SolarisAttrKeyValue=*))"
61 #define	_F_GETENT_SSD		"(%s)"
62 
63 static struct gettablefilter {
64 	char *tablename;
65 	char *tablefilter;
66 } gettablefilterent[] = {
67 	{(char *)_PASSWD,	(char *)_F_GETPWENT},
68 	{(char *)_SHADOW,	(char *)_F_GETSPENT},
69 	{(char *)_GROUP,	(char *)_F_GETGRENT},
70 	{(char *)_HOSTS,	(char *)_F_GETHOSTENT},
71 	{(char *)_NETWORKS,	(char *)_F_GETNETENT},
72 	{(char *)_PROTOCOLS,	(char *)_F_GETPROTOENT},
73 	{(char *)_RPC,		(char *)_F_GETRPCENT},
74 	{(char *)_ALIASES,	(char *)_F_GETALIASENT},
75 	{(char *)_SERVICES,	(char *)_F_GETSERVENT},
76 	{(char *)_AUUSER,	(char *)_F_GETAUUSERNAME},
77 	{(char *)_AUTHATTR,	(char *)_F_GETAUTHNAME},
78 	{(char *)_EXECATTR,	(char *)_F_GETEXECNAME},
79 	{(char *)_PROFATTR,	(char *)_F_GETPROFNAME},
80 	{(char *)_USERATTR,	(char *)_F_GETUSERNAME},
81 	{(char *)_PROJECT,	(char *)_F_GETPROJENT},
82 	{(char *)_PRINTERS,	(char *)_F_GETPRINTERENT},
83 	{(char *)_TNRHDB,	(char *)_F_GETTNRHDB},
84 	{(char *)_TNRHTP,	(char *)_F_GETTNRHTP},
85 	{(char *)NULL,		(char *)NULL}
86 };
87 
88 
89 static nss_status_t
90 switch_err(int rc, ns_ldap_error_t *error)
91 {
92 	switch (rc) {
93 	    case NS_LDAP_SUCCESS:
94 		return (NSS_SUCCESS);
95 
96 	    case NS_LDAP_NOTFOUND:
97 		return (NSS_NOTFOUND);
98 
99 	    case NS_LDAP_PARTIAL:
100 		return (NSS_TRYAGAIN);
101 
102 	    case NS_LDAP_INTERNAL:
103 		    if (error && (error->status == LDAP_SERVER_DOWN ||
104 				error->status == LDAP_TIMEOUT))
105 			    return (NSS_TRYAGAIN);
106 		    else
107 			    return (NSS_UNAVAIL);
108 
109 	    default:
110 		return (NSS_UNAVAIL);
111 	}
112 }
113 /* ARGSUSED */
114 nss_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 
207 error_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 */
237 nss_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 
274 void
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*/
310 nss_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 
329 nss_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*/
363 nss_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 
397 nss_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 
412 next_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 		}
466 error_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 
505 nss_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  */
530 int
531 chophostdomain(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  */
552 int
553 propersubdomain(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