xref: /illumos-gate/usr/src/lib/nsswitch/ldap/common/ldap_common.c (revision 6e375c8351497b82ffa4f33cbf61d712999b4605)
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 2007 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 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 		if (argp != NULL)
261 			argp->returnval = 0;
262 		rc = switch_err(rc, error);
263 		(void) __ns_ldap_freeError(&error);
264 		return (rc);
265 	}
266 
267 	return ((nss_status_t)NSS_SUCCESS);
268 }
269 
270 
271 /*
272  *
273  */
274 
275 void
276 _clean_ldap_backend(ldap_backend_ptr be)
277 {
278 	ns_ldap_error_t *error;
279 
280 #ifdef	DEBUG
281 	(void) fprintf(stdout, "\n[ldap_common.c: _clean_ldap_backend]\n");
282 #endif	/* DEBUG */
283 
284 	if (be->tablename != NULL)
285 		free(be->tablename);
286 	if (be->result != NULL)
287 		(void) __ns_ldap_freeResult(&be->result);
288 	if (be->enumcookie != NULL)
289 		(void) __ns_ldap_endEntry(&be->enumcookie, &error);
290 	if (be->services_cookie != NULL)
291 		_nss_services_cookie_free((void **)&be->services_cookie);
292 	if (be->toglue != NULL) {
293 		free(be->toglue);
294 		be->toglue = NULL;
295 	}
296 	if (be->buffer != NULL) {
297 		free(be->buffer);
298 		be->buffer = NULL;
299 	}
300 	free(be);
301 }
302 
303 
304 /*
305  * _nss_ldap_destr will free all smalloc'ed variable strings and structures
306  * before exiting this nsswitch shared backend library. This function is
307  * called before returning control back to nsswitch.
308  */
309 
310 /*ARGSUSED1*/
311 nss_status_t
312 _nss_ldap_destr(ldap_backend_ptr be, void *a)
313 {
314 
315 #ifdef DEBUG
316 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_destr]\n");
317 #endif /* DEBUG */
318 
319 	(void) _clean_ldap_backend(be);
320 
321 	return ((nss_status_t)NSS_SUCCESS);
322 }
323 
324 
325 /*
326  * _nss_ldap_setent called before _nss_ldap_getent. This function is
327  * required by POSIX.
328  */
329 
330 nss_status_t
331 _nss_ldap_setent(ldap_backend_ptr be, void *a)
332 {
333 	struct gettablefilter	*gtf;
334 
335 #ifdef DEBUG
336 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_setent]\n");
337 #endif /* DEBUG */
338 
339 	if (be->setcalled == 1)
340 		(void) _nss_ldap_endent(be, a);
341 	be->filter = NULL;
342 	for (gtf = gettablefilterent; gtf->tablename != (char *)NULL; gtf++) {
343 		if (strcmp(gtf->tablename, be->tablename))
344 			continue;
345 		be->filter = (char *)gtf->tablefilter;
346 		break;
347 	}
348 
349 	be->setcalled = 1;
350 	be->enumcookie = NULL;
351 	be->result = NULL;
352 	be->services_cookie = NULL;
353 	be->buffer = NULL;
354 	return ((nss_status_t)NSS_SUCCESS);
355 }
356 
357 
358 /*
359  * _nss_ldap_endent called after _nss_ldap_getent. This function is
360  * required by POSIX.
361  */
362 
363 /*ARGSUSED1*/
364 nss_status_t
365 _nss_ldap_endent(ldap_backend_ptr be, void *a)
366 {
367 	ns_ldap_error_t	*error = NULL;
368 
369 #ifdef DEBUG
370 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_endent]\n");
371 #endif /* DEBUG */
372 
373 	be->setcalled = 0;
374 	be->filter = NULL;
375 	if (be->enumcookie != NULL) {
376 		(void) __ns_ldap_endEntry(&be->enumcookie, &error);
377 		(void) __ns_ldap_freeError(&error);
378 	}
379 	if (be->result != NULL) {
380 		(void) __ns_ldap_freeResult(&be->result);
381 	}
382 	if (be->services_cookie != NULL) {
383 		_nss_services_cookie_free((void **)&be->services_cookie);
384 	}
385 	if (be->buffer != NULL) {
386 		free(be->buffer);
387 		be->buffer = NULL;
388 	}
389 
390 	return ((nss_status_t)NSS_SUCCESS);
391 }
392 
393 
394 /*
395  *
396  */
397 
398 nss_status_t
399 _nss_ldap_getent(ldap_backend_ptr be, void *a)
400 {
401 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
402 	ns_ldap_error_t	*error = NULL;
403 	int		parsestat = 0;
404 	int		retcode = 0;
405 
406 #ifdef	DEBUG
407 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_getent]\n");
408 #endif	/* DEBUG */
409 
410 	if (be->setcalled == 0)
411 		(void) _nss_ldap_setent(be, a);
412 
413 next_entry:
414 	if (be->enumcookie == NULL) {
415 		retcode = __ns_ldap_firstEntry(be->tablename,
416 		    be->filter, _merge_SSD_filter, be->attrs, NULL,
417 		    0, &be->enumcookie,
418 		    &be->result, &error, _F_GETENT_SSD);
419 	} else {
420 		if (be->services_cookie == NULL) {
421 			retcode = __ns_ldap_nextEntry(be->enumcookie,
422 			    &be->result, &error);
423 		}
424 	}
425 	if (retcode != NS_LDAP_SUCCESS) {
426 		retcode = switch_err(retcode, error);
427 		(void) __ns_ldap_freeError(&error);
428 		(void) _nss_ldap_endent(be, a);
429 		return (retcode);
430 	} else {
431 		/* ns_ldap_entry_t -> file format */
432 		if ((parsestat = be->ldapobj2str(be, argp))
433 		    == NSS_STR_PARSE_SUCCESS) {
434 			if (argp->buf.result != NULL) {
435 				/* file format -> struct */
436 				if (argp->str2ent == NULL) {
437 					parsestat = NSS_STR_PARSE_PARSE;
438 					goto error_out;
439 				}
440 				parsestat = (*argp->str2ent)(be->buffer,
441 				    be->buflen,
442 				    argp->buf.result,
443 				    argp->buf.buffer,
444 				    argp->buf.buflen);
445 				if (parsestat == NSS_STR_PARSE_SUCCESS) {
446 					if (be->buffer != NULL) {
447 						free(be->buffer);
448 						be->buffer = NULL;
449 						be->buflen = 0;
450 					}
451 					be->result = NULL;
452 					argp->returnval = argp->buf.result;
453 					argp->returnlen = 1; /* irrevelant */
454 					return ((nss_status_t)NSS_SUCCESS);
455 				}
456 			} else {
457 				/*
458 				 * nscd is not caching the enumerated
459 				 * entries. This code path would be dormant.
460 				 * Keep this path for the future references.
461 				 */
462 				argp->returnval = argp->buf.buffer;
463 				argp->returnlen =
464 				    strlen(argp->buf.buffer) + 1;
465 			}
466 		}
467 error_out:
468 		if (be->buffer != NULL) {
469 			free(be->buffer);
470 			be->buffer = NULL;
471 			be->buflen = 0;
472 		}
473 		be->result = NULL;
474 		if (parsestat == NSS_STR_PARSE_PARSE) {
475 			argp->returnval = 0;
476 			(void) _nss_ldap_endent(be, a);
477 			return ((nss_status_t)NSS_NOTFOUND);
478 		}
479 
480 		if (parsestat == NSS_STR_PARSE_ERANGE) {
481 			argp->erange = 1;
482 			(void) _nss_ldap_endent(be, a);
483 			return ((nss_status_t)NSS_NOTFOUND);
484 		}
485 		if (parsestat == NSS_STR_PARSE_NO_ADDR)
486 			/*
487 			 * No IPV4 address is found in the current entry.
488 			 * It indicates that the entry contains IPV6 addresses
489 			 * only. Instead of calling _nss_ldap_endent to
490 			 * terminate, get next entry to continue enumeration.
491 			 * If it returned NSS_NOTFOUND here,
492 			 * gethostent() would return NULL
493 			 * and the enumeration would stop prematurely.
494 			 */
495 			goto next_entry;
496 	}
497 
498 	return ((nss_status_t)NSS_SUCCESS);
499 }
500 
501 
502 /*
503  *
504  */
505 
506 nss_backend_t *
507 _nss_ldap_constr(ldap_backend_op_t ops[], int nops, char *tablename,
508 		const char **attrs, fnf ldapobj2str)
509 {
510 	ldap_backend_ptr	be;
511 
512 #ifdef	DEBUG
513 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_constr]\n");
514 #endif	/* DEBUG */
515 
516 	if ((be = (ldap_backend_ptr) calloc(1, sizeof (*be))) == 0)
517 		return (0);
518 	be->ops = ops;
519 	be->nops = (nss_dbop_t)nops;
520 	be->tablename = (char *)strdup(tablename);
521 	be->attrs = attrs;
522 	be->ldapobj2str = ldapobj2str;
523 
524 	return ((nss_backend_t *)be);
525 }
526 
527 
528 /*
529  *
530  */
531 int
532 chophostdomain(char *string, char *host, char *domain)
533 {
534 	char	*dot;
535 
536 	if (string == NULL)
537 		return (-1);
538 
539 	if ((dot = strchr(string, '.')) == NULL) {
540 		return (0);
541 	}
542 	*dot = '\0';
543 	(void) strcpy(host, string);
544 	(void) strcpy(domain, ++dot);
545 
546 	return (0);
547 }
548 
549 
550 /*
551  *
552  */
553 int
554 propersubdomain(char *domain, char *subdomain)
555 {
556 	int	domainlen, subdomainlen;
557 
558 	/* sanity check */
559 	if (domain == NULL || subdomain == NULL)
560 		return (-1);
561 
562 	domainlen = strlen(domain);
563 	subdomainlen = strlen(subdomain);
564 
565 	/* is afterdot a substring of domain? */
566 	if ((strncasecmp(domain, subdomain, subdomainlen)) != 0)
567 		return (-1);
568 
569 	if (domainlen == subdomainlen)
570 		return (1);
571 
572 	if (subdomainlen > domainlen)
573 		return (-1);
574 
575 	if (*(domain + subdomainlen) != '.')
576 		return (-1);
577 
578 	return (1);
579 }
580