xref: /illumos-gate/usr/src/lib/nsswitch/ldap/common/ldap_common.c (revision a88046d1e68acaef04dc4175c8e09654bd94b8e9)
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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 #include "ldap_common.h"
27 #include <malloc.h>
28 #include <synch.h>
29 #include <syslog.h>
30 #include <rpcsvc/ypclnt.h>
31 #include <rpcsvc/yp_prot.h>
32 #include <thread.h>
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <signal.h>
36 #include <sys/stat.h>
37 
38 /* getent attributes filters */
39 #define	_F_GETALIASENT		"(objectClass=rfc822MailGroup)"
40 #define	_F_GETAUTHNAME		"(objectClass=SolarisAuthAttr)"
41 #define	_F_GETAUUSERNAME	"(objectClass=SolarisAuditUser)"
42 #define	_F_GETEXECNAME		"(objectClass=SolarisExecAttr)"
43 #define	_F_GETGRENT		"(objectClass=posixGroup)"
44 #define	_F_GETHOSTENT		"(objectClass=ipHost)"
45 #define	_F_GETNETENT		"(objectClass=ipNetwork)"
46 #define	_F_GETPROFNAME \
47 "(&(objectClass=SolarisProfAttr)(!(SolarisKernelSecurityPolicy=*)))"
48 #define	_F_GETPROTOENT		"(objectClass=ipProtocol)"
49 #define	_F_GETPWENT		"(objectClass=posixAccount)"
50 #define	_F_GETPRINTERENT	"(objectClass=sunPrinter)"
51 #define	_F_GETRPCENT		"(objectClass=oncRpc)"
52 #define	_F_GETSERVENT		"(objectClass=ipService)"
53 #define	_F_GETSPENT		"(objectclass=shadowAccount)"
54 #define	_F_GETUSERNAME		"(objectClass=SolarisUserAttr)"
55 #define	_F_GETPROJENT		"(objectClass=SolarisProject)"
56 #define	_F_GETTNRHDB		"(objectClass=ipTnetHost)"
57 #define	_F_GETTNRHTP		"(&(objectClass=ipTnetTemplate)"\
58 				"(SolarisAttrKeyValue=*))"
59 #define	_F_GETENT_SSD		"(%s)"
60 
61 /* getent sort attributes */
62 #define	_A_UID			"uid"
63 #define	_A_GIDNUMBER		"gidnumber"
64 #define	_A_CN			"cn"
65 #define	_A_IPNETWORKNUM		"ipnetworknumber"
66 #define	_A_PROJECTNAM		"SolarisProjectName"
67 #define	_A_IPTNETNUM		"ipTnetNumber"
68 #define	_A_IPTNETTMPLNAM	"ipTnetTemplateName"
69 
70 static struct gettablefilter {
71 	char *tablename;
72 	char *tablefilter;
73 	char *sortattr;
74 } gettablefilterent[] = {
75 	{(char *)_PASSWD,	(char *)_F_GETPWENT,	(char *)_A_UID},
76 	{(char *)_SHADOW,	(char *)_F_GETSPENT,	(char *)_A_UID},
77 	{(char *)_GROUP,	(char *)_F_GETGRENT,	(char *)_A_GIDNUMBER},
78 	{(char *)_HOSTS,	(char *)_F_GETHOSTENT,	(char *)_A_CN},
79 	{(char *)_NETWORKS,	(char *)_F_GETNETENT,
80 						(char *)_A_IPNETWORKNUM},
81 	{(char *)_PROTOCOLS,	(char *)_F_GETPROTOENT,	(char *)_A_CN},
82 	{(char *)_RPC,		(char *)_F_GETRPCENT,	(char *)_A_CN},
83 	{(char *)_ALIASES,	(char *)_F_GETALIASENT,	(char *)_A_CN},
84 	{(char *)_SERVICES,	(char *)_F_GETSERVENT,	(char *)_A_CN},
85 	{(char *)_AUUSER,	(char *)_F_GETAUUSERNAME,
86 							(char *)_A_UID},
87 	{(char *)_AUTHATTR,	(char *)_F_GETAUTHNAME,	(char *)_A_CN},
88 	{(char *)_EXECATTR,	(char *)_F_GETEXECNAME,	(char *)_A_CN},
89 	{(char *)_PROFATTR,	(char *)_F_GETPROFNAME,	(char *)_A_CN},
90 	{(char *)_USERATTR,	(char *)_F_GETUSERNAME,	(char *)_A_UID},
91 	{(char *)_PROJECT,	(char *)_F_GETPROJENT,	(char *)_A_PROJECTNAM},
92 	{(char *)_PRINTERS,	(char *)_F_GETPRINTERENT, (char *)_A_CN},
93 	{(char *)_TNRHDB,	(char *)_F_GETTNRHDB,	(char *)_A_IPTNETNUM},
94 	{(char *)_TNRHTP,	(char *)_F_GETTNRHTP,
95 						(char *)_A_IPTNETTMPLNAM},
96 	{(char *)NULL,		(char *)NULL,		(char *)NULL}
97 };
98 
99 
100 nss_status_t
101 switch_err(int rc, ns_ldap_error_t *error)
102 {
103 	switch (rc) {
104 	case NS_LDAP_SUCCESS:
105 		return (NSS_SUCCESS);
106 
107 	case NS_LDAP_NOTFOUND:
108 		return (NSS_NOTFOUND);
109 
110 	case NS_LDAP_PARTIAL:
111 		return (NSS_TRYAGAIN);
112 
113 	case NS_LDAP_INTERNAL:
114 		if (error && (error->status == LDAP_SERVER_DOWN ||
115 		    error->status == LDAP_TIMEOUT))
116 			return (NSS_TRYAGAIN);
117 		else
118 			return (NSS_UNAVAIL);
119 
120 	default:
121 		return (NSS_UNAVAIL);
122 	}
123 }
124 /* ARGSUSED */
125 nss_status_t
126 _nss_ldap_lookup(ldap_backend_ptr be, nss_XbyY_args_t *argp,
127 		char *database, char *searchfilter, char *domain,
128 		int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
129 		char **realfilter, const void *userdata),
130 		const void *userdata)
131 {
132 	int		callbackstat = 0;
133 	ns_ldap_error_t	*error = NULL;
134 	int		rc;
135 
136 #ifdef	DEBUG
137 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_lookup]\n");
138 	(void) fprintf(stdout, "\tsearchfilter: %s\n", searchfilter);
139 	(void) fprintf(stdout,
140 	    "\tuserdata: %s\n", userdata ? userdata : "NULL");
141 	(void) fprintf(stdout, "\tdatabase: %s\n", database);
142 #endif	/* DEBUG */
143 
144 	(void) __ns_ldap_freeResult(&be->result);
145 
146 	if ((rc = __ns_ldap_list(database, searchfilter, init_filter_cb,
147 	    be->attrs, NULL, 0, &be->result, &error, NULL,
148 	    userdata)) != NS_LDAP_SUCCESS) {
149 		argp->returnval = 0;
150 		rc = switch_err(rc, error);
151 		(void) __ns_ldap_freeError(&error);
152 
153 		return (rc);
154 	}
155 		(void) __ns_ldap_freeError(&error);
156 	/* callback function */
157 	if ((callbackstat =
158 	    be->ldapobj2str(be, argp)) != NSS_STR_PARSE_SUCCESS) {
159 		goto error_out;
160 	}
161 
162 	/*
163 	 * publickey does not have a front end marshaller and expects
164 	 * a string to be returned in NSS.
165 	 * No need to convert file format -> struct.
166 	 *
167 	 */
168 	if (be->db_type == NSS_LDAP_DB_PUBLICKEY) {
169 		argp->returnval = argp->buf.buffer;
170 		argp->returnlen = strlen(argp->buf.buffer);
171 		be->db_type = NSS_LDAP_DB_NONE;
172 		return (NSS_SUCCESS);
173 	}
174 	/*
175 	 *  Assume the switch engine wants the returned data in the file
176 	 *  format when argp->buf.result == NULL.
177 	 *  The front-end marshaller str2ether(ethers) uses
178 	 *  ent (argp->buf.result) and buffer (argp->buf.buffer)
179 	 *  for different purpose so ethers has to be treated differently.
180 	 */
181 	if (argp->buf.result != NULL ||
182 	    be->db_type == NSS_LDAP_DB_ETHERS) {
183 		/* file format -> struct */
184 		if (argp->str2ent == NULL) {
185 			callbackstat = NSS_STR_PARSE_PARSE;
186 			goto error_out;
187 		}
188 
189 		callbackstat = (*argp->str2ent)(be->buffer,
190 		    be->buflen,
191 		    argp->buf.result,
192 		    argp->buf.buffer,
193 		    argp->buf.buflen);
194 		if (callbackstat == NSS_STR_PARSE_SUCCESS) {
195 			if (be->db_type == NSS_LDAP_DB_ETHERS &&
196 			    argp->buf.buffer != NULL) {
197 				argp->returnval = argp->buf.buffer;
198 				argp->returnlen = strlen(argp->buf.buffer);
199 			} else {
200 				argp->returnval = argp->buf.result;
201 				argp->returnlen = 1; /* irrelevant */
202 			}
203 			if (be->buffer != NULL) {
204 				free(be->buffer);
205 				be->buffer = NULL;
206 				be->buflen = 0;
207 				be->db_type = NSS_LDAP_DB_NONE;
208 			}
209 			return ((nss_status_t)NSS_SUCCESS);
210 		}
211 	} else {
212 			/* return file format in argp->buf.buffer */
213 			argp->returnval = argp->buf.buffer;
214 			argp->returnlen = strlen(argp->buf.buffer);
215 			return ((nss_status_t)NSS_SUCCESS);
216 	}
217 
218 error_out:
219 	if (be->buffer != NULL) {
220 		free(be->buffer);
221 		be->buffer = NULL;
222 		be->buflen = 0;
223 		be->db_type = NSS_LDAP_DB_NONE;
224 	}
225 	/* error */
226 	if (callbackstat == NSS_STR_PARSE_PARSE) {
227 		argp->returnval = 0;
228 		return ((nss_status_t)NSS_NOTFOUND);
229 	}
230 	if (callbackstat == NSS_STR_PARSE_ERANGE) {
231 		argp->erange = 1;
232 		return ((nss_status_t)NSS_NOTFOUND);
233 	}
234 	if (callbackstat == NSS_STR_PARSE_NO_ADDR) {
235 		/* No IPV4 address is found */
236 		argp->h_errno = HOST_NOT_FOUND;
237 		return ((nss_status_t)NSS_NOTFOUND);
238 	}
239 	return ((nss_status_t)NSS_UNAVAIL);
240 }
241 
242 /*
243  *  This function is similar to _nss_ldap_lookup except it does not
244  *  do a callback.  It is only used by getnetgrent.c
245  */
246 
247 /* ARGSUSED */
248 nss_status_t
249 _nss_ldap_nocb_lookup(ldap_backend_ptr be, nss_XbyY_args_t *argp,
250 		char *database, char *searchfilter, const char * const *attrs,
251 		int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
252 		char **realfilter, const void *userdata),
253 		const void *userdata)
254 {
255 	ns_ldap_error_t	*error = NULL;
256 	int		rc;
257 
258 	if (attrs == NULL)
259 		attrs = be->attrs;
260 
261 #ifdef	DEBUG
262 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_nocb_lookup]\n");
263 	(void) fprintf(stdout, "\tsearchfilter: %s\n", searchfilter);
264 	(void) fprintf(stdout, "\tdatabase: %s\n", database);
265 	(void) fprintf(stdout,
266 	    "\tuserdata: %s\n", userdata ? userdata : "NULL");
267 #endif	/* DEBUG */
268 
269 	(void) __ns_ldap_freeResult(&be->result);
270 
271 	if ((rc = __ns_ldap_list(database, searchfilter, init_filter_cb,
272 	    attrs, NULL, 0, &be->result, &error, NULL,
273 	    userdata)) != NS_LDAP_SUCCESS) {
274 		if (argp != NULL)
275 			argp->returnval = 0;
276 		rc = switch_err(rc, error);
277 		(void) __ns_ldap_freeError(&error);
278 		return (rc);
279 	}
280 
281 	return ((nss_status_t)NSS_SUCCESS);
282 }
283 
284 
285 /*
286  *
287  */
288 
289 void
290 _clean_ldap_backend(ldap_backend_ptr be)
291 {
292 	ns_ldap_error_t *error;
293 
294 #ifdef	DEBUG
295 	(void) fprintf(stdout, "\n[ldap_common.c: _clean_ldap_backend]\n");
296 #endif	/* DEBUG */
297 
298 	if (be->tablename != NULL)
299 		free(be->tablename);
300 	if (be->result != NULL)
301 		(void) __ns_ldap_freeResult(&be->result);
302 	if (be->enumcookie != NULL)
303 		(void) __ns_ldap_endEntry(&be->enumcookie, &error);
304 	if (be->services_cookie != NULL)
305 		_nss_services_cookie_free((void **)&be->services_cookie);
306 	if (be->toglue != NULL) {
307 		free(be->toglue);
308 		be->toglue = NULL;
309 	}
310 	if (be->buffer != NULL) {
311 		free(be->buffer);
312 		be->buffer = NULL;
313 	}
314 	free(be);
315 }
316 
317 
318 /*
319  * _nss_ldap_destr will free all smalloc'ed variable strings and structures
320  * before exiting this nsswitch shared backend library. This function is
321  * called before returning control back to nsswitch.
322  */
323 
324 /*ARGSUSED1*/
325 nss_status_t
326 _nss_ldap_destr(ldap_backend_ptr be, void *a)
327 {
328 
329 #ifdef DEBUG
330 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_destr]\n");
331 #endif /* DEBUG */
332 
333 	(void) _clean_ldap_backend(be);
334 
335 	return ((nss_status_t)NSS_SUCCESS);
336 }
337 
338 
339 /*
340  * _nss_ldap_setent called before _nss_ldap_getent. This function is
341  * required by POSIX.
342  */
343 
344 nss_status_t
345 _nss_ldap_setent(ldap_backend_ptr be, void *a)
346 {
347 	struct gettablefilter	*gtf;
348 
349 #ifdef DEBUG
350 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_setent]\n");
351 #endif /* DEBUG */
352 
353 	if (be->setcalled == 1)
354 		(void) _nss_ldap_endent(be, a);
355 	be->filter = NULL;
356 	be->sortattr = NULL;
357 	for (gtf = gettablefilterent; gtf->tablename != (char *)NULL; gtf++) {
358 		if (strcmp(gtf->tablename, be->tablename))
359 			continue;
360 		be->filter = (char *)gtf->tablefilter;
361 		be->sortattr = (char *)gtf->sortattr;
362 		break;
363 	}
364 
365 	be->setcalled = 1;
366 	be->enumcookie = NULL;
367 	be->result = NULL;
368 	be->services_cookie = NULL;
369 	be->buffer = NULL;
370 	return ((nss_status_t)NSS_SUCCESS);
371 }
372 
373 
374 /*
375  * _nss_ldap_endent called after _nss_ldap_getent. This function is
376  * required by POSIX.
377  */
378 
379 /*ARGSUSED1*/
380 nss_status_t
381 _nss_ldap_endent(ldap_backend_ptr be, void *a)
382 {
383 	ns_ldap_error_t	*error = NULL;
384 
385 #ifdef DEBUG
386 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_endent]\n");
387 #endif /* DEBUG */
388 
389 	be->setcalled = 0;
390 	be->filter = NULL;
391 	be->sortattr = NULL;
392 	if (be->enumcookie != NULL) {
393 		(void) __ns_ldap_endEntry(&be->enumcookie, &error);
394 		(void) __ns_ldap_freeError(&error);
395 	}
396 	if (be->result != NULL) {
397 		(void) __ns_ldap_freeResult(&be->result);
398 	}
399 	if (be->services_cookie != NULL) {
400 		_nss_services_cookie_free((void **)&be->services_cookie);
401 	}
402 	if (be->buffer != NULL) {
403 		free(be->buffer);
404 		be->buffer = NULL;
405 	}
406 
407 	return ((nss_status_t)NSS_SUCCESS);
408 }
409 
410 
411 /*
412  *
413  */
414 
415 nss_status_t
416 _nss_ldap_getent(ldap_backend_ptr be, void *a)
417 {
418 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
419 	ns_ldap_error_t	*error = NULL;
420 	int		parsestat = 0;
421 	int		retcode = 0;
422 
423 #ifdef	DEBUG
424 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_getent]\n");
425 #endif	/* DEBUG */
426 
427 	if (be->setcalled == 0)
428 		(void) _nss_ldap_setent(be, a);
429 
430 next_entry:
431 	if (be->enumcookie == NULL) {
432 		retcode = __ns_ldap_firstEntry(be->tablename,
433 		    be->filter, be->sortattr, _merge_SSD_filter, be->attrs,
434 		    NULL, 0, &be->enumcookie,
435 		    &be->result, &error, _F_GETENT_SSD);
436 	} else {
437 		if (be->services_cookie == NULL) {
438 			retcode = __ns_ldap_nextEntry(be->enumcookie,
439 			    &be->result, &error);
440 		}
441 	}
442 	if (retcode != NS_LDAP_SUCCESS) {
443 		retcode = switch_err(retcode, error);
444 		(void) __ns_ldap_freeError(&error);
445 		(void) _nss_ldap_endent(be, a);
446 		return (retcode);
447 	}
448 
449 	if (be->result == NULL) {
450 		parsestat = NSS_STR_PARSE_NO_RESULT;
451 		goto error_out;
452 	}
453 	/* ns_ldap_entry_t -> file format */
454 	if ((parsestat = be->ldapobj2str(be, argp))
455 	    == NSS_STR_PARSE_SUCCESS) {
456 		if (argp->buf.result != NULL) {
457 			/* file format -> struct */
458 			if (argp->str2ent == NULL) {
459 				parsestat = NSS_STR_PARSE_NO_RESULT;
460 				goto error_out;
461 			}
462 			parsestat = (*argp->str2ent)(be->buffer,
463 			    be->buflen,
464 			    argp->buf.result,
465 			    argp->buf.buffer,
466 			    argp->buf.buflen);
467 			if (parsestat == NSS_STR_PARSE_SUCCESS) {
468 				if (be->buffer != NULL) {
469 					free(be->buffer);
470 					be->buffer = NULL;
471 					be->buflen = 0;
472 				}
473 				be->result = NULL;
474 				argp->returnval = argp->buf.result;
475 				argp->returnlen = 1; /* irrevelant */
476 				return ((nss_status_t)NSS_SUCCESS);
477 			}
478 		} else {
479 			/*
480 			 * nscd is not caching the enumerated
481 			 * entries. This code path would be dormant.
482 			 * Keep this path for the future references.
483 			 */
484 			argp->returnval = argp->buf.buffer;
485 			argp->returnlen =
486 			    strlen(argp->buf.buffer) + 1;
487 		}
488 	}
489 error_out:
490 	if (be->buffer != NULL) {
491 		free(be->buffer);
492 		be->buffer = NULL;
493 		be->buflen = 0;
494 	}
495 	be->result = NULL;
496 	if (parsestat == NSS_STR_PARSE_NO_RESULT) {
497 		argp->returnval = 0;
498 		(void) _nss_ldap_endent(be, a);
499 		return ((nss_status_t)NSS_NOTFOUND);
500 	}
501 
502 	if (parsestat == NSS_STR_PARSE_ERANGE) {
503 		argp->erange = 1;
504 		(void) _nss_ldap_endent(be, a);
505 		return ((nss_status_t)NSS_NOTFOUND);
506 	}
507 	if (parsestat == NSS_STR_PARSE_NO_ADDR)
508 		/*
509 		 * No IPV4 address is found in the current entry.
510 		 * It indicates that the entry contains IPV6 addresses
511 		 * only. Instead of calling _nss_ldap_endent to
512 		 * terminate, get next entry to continue enumeration.
513 		 * If it returned NSS_NOTFOUND here,
514 		 * gethostent() would return NULL
515 		 * and the enumeration would stop prematurely.
516 		 */
517 		goto next_entry;
518 
519 	if (parsestat == NSS_STR_PARSE_PARSE)
520 		/*
521 		 * There has been a parse error. Most likely some
522 		 * mandatory attributes are missing. Ignore the error
523 		 * and get the next entry. If we returned an error the
524 		 * enumeration would stop prematurely.
525 		 */
526 		goto next_entry;
527 
528 	return ((nss_status_t)NSS_SUCCESS);
529 }
530 
531 
532 /*
533  *
534  */
535 
536 nss_backend_t *
537 _nss_ldap_constr(ldap_backend_op_t ops[], int nops, char *tablename,
538 		const char **attrs, fnf ldapobj2str)
539 {
540 	ldap_backend_ptr	be;
541 
542 #ifdef	DEBUG
543 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_constr]\n");
544 #endif	/* DEBUG */
545 
546 	if ((be = (ldap_backend_ptr) calloc(1, sizeof (*be))) == 0)
547 		return (0);
548 	be->ops = ops;
549 	be->nops = (nss_dbop_t)nops;
550 	be->tablename = (char *)strdup(tablename);
551 	be->attrs = attrs;
552 	be->ldapobj2str = ldapobj2str;
553 
554 	return ((nss_backend_t *)be);
555 }
556 
557 
558 /*
559  *
560  */
561 int
562 chophostdomain(char *string, char *host, char *domain)
563 {
564 	char	*dot;
565 
566 	if (string == NULL)
567 		return (-1);
568 
569 	if ((dot = strchr(string, '.')) == NULL) {
570 		return (0);
571 	}
572 	*dot = '\0';
573 	(void) strcpy(host, string);
574 	(void) strcpy(domain, ++dot);
575 
576 	return (0);
577 }
578 
579 
580 /*
581  *
582  */
583 int
584 propersubdomain(char *domain, char *subdomain)
585 {
586 	int	domainlen, subdomainlen;
587 
588 	/* sanity check */
589 	if (domain == NULL || subdomain == NULL)
590 		return (-1);
591 
592 	domainlen = strlen(domain);
593 	subdomainlen = strlen(subdomain);
594 
595 	/* is afterdot a substring of domain? */
596 	if ((strncasecmp(domain, subdomain, subdomainlen)) != 0)
597 		return (-1);
598 
599 	if (domainlen == subdomainlen)
600 		return (1);
601 
602 	if (subdomainlen > domainlen)
603 		return (-1);
604 
605 	if (*(domain + subdomainlen) != '.')
606 		return (-1);
607 
608 	return (1);
609 }
610