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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * nis_common.c
29  *
30  * Common code and structures used by name-service-switch "nis" backends.
31  */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include "nis_common.h"
36 #include <string.h>
37 #include <synch.h>
38 #include <rpcsvc/ypclnt.h>
39 #include <rpcsvc/yp_prot.h>
40 #include <thread.h>
41 #include <ctype.h>
42 #include <stdlib.h>
43 #include <signal.h>
44 
45 #ifndef	MT_UNSAFE_YP		/* Is the libnsl YP client code MT-unsafe? */
46 #define	MT_UNSAFE_YP	0	/* No, not any longer */
47 #endif
48 
49 #if	MT_UNSAFE_YP
50 static mutex_t	one_lane = DEFAULTMUTEX;
51 #endif
52 
53 /* <rpcsvc/ypclnt.h> uses (char *) where it should use (const char *) */
54 typedef char *grrr;
55 
56 /*
57  * The YP client code thinks it's being helpful by appending '\n' and '\0'
58  *   to the values returned by yp_match() et al.  In order to do this it
59  *   ends up doing more malloc()ing and data copying than would otherwise
60  *   be necessary.  If we're interested in performance we should provide
61  *   alternative library interfaces that skip the helpfulness and instead
62  *   let the XDR routines dump the value directly into the buffer where
63  *   we really want it.  For now, though, we just use the vanilla interface.
64  */
65 
66 static nss_status_t
67 switch_err(ypstatus, ismatch)
68 	int			ypstatus;
69 	int			ismatch;
70 {
71 	switch (ypstatus) {
72 	case 0:
73 		errno = 0;
74 		return (NSS_SUCCESS);
75 
76 	case YPERR_BADARGS:
77 	case YPERR_KEY:
78 		errno = 0;
79 		return (NSS_NOTFOUND);
80 
81 		/*
82 		 *  When the YP server is running in DNS forwarding mode,
83 		 *  the forwarder will return YPERR_NOMORE to us if it
84 		 *  is unable to contact a server (i.e., it has timed out).
85 		 *  The NSS_NISSERVDNS_TRYAGAIN is returned for timeout errors.
86 		 */
87 	case YPERR_NOMORE:
88 		if (ismatch)
89 			return (NSS_NISSERVDNS_TRYAGAIN);
90 		else
91 			return (NSS_NOTFOUND);
92 
93 	case YPERR_DOMAIN:
94 	case YPERR_YPSERV:
95 	case YPERR_BUSY:
96 		return (NSS_TRYAGAIN);
97 
98 	default:
99 		return (NSS_UNAVAIL);
100 	}
101 }
102 
103 /*ARGSUSED*/
104 nss_status_t
105 _nss_nis_setent(be, dummy)
106 	nis_backend_ptr_t	be;
107 	void			*dummy;
108 {
109 	if (be->enum_key != 0) {
110 		free(be->enum_key);
111 		be->enum_key = 0;
112 	}
113 	be->enum_keylen = 0;
114 	return (NSS_SUCCESS);
115 }
116 
117 nss_status_t
118 _nss_nis_endent(be, dummy)
119 	nis_backend_ptr_t	be;
120 	void			*dummy;
121 {
122 	return (_nss_nis_setent(be, dummy));
123 	/* Nothing else we can clean up, is there? */
124 }
125 
126 void
127 massage_netdb(const char **valp, int *vallenp)
128 {
129 	const char		*first;
130 	const char		*last;
131 	const char		*val	= *valp;
132 	int			vallen	= *vallenp;
133 
134 	if ((last = memchr(val, '#', vallen)) == 0) {
135 		last = val + vallen;
136 	}
137 	for (first = val;  first < last && isspace(*first);  first++) {
138 		;
139 	}
140 	for (/* cstyle */;  first < last && isspace(last[-1]);  last--) {
141 		;
142 	}
143 	/*
144 	 * Don't check for an empty line because it shouldn't ever
145 	 *   have made it into the YP map.
146 	 */
147 	*valp = first;
148 	*vallenp = (int)(last - first);
149 }
150 
151 nss_status_t
152 _nss_nis_ypmatch(domain, map, key, valp, vallenp, ypstatusp)
153 	const char		*domain;
154 	const char		*map;
155 	const char		*key;
156 	char			**valp;
157 	int			*vallenp;
158 	int			*ypstatusp;
159 {
160 	int			ypstatus;
161 
162 #if	MT_UNSAFE_YP
163 	sigset_t		oldmask, newmask;
164 
165 	(void) sigfillset(&newmask);
166 	(void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
167 	(void) mutex_lock(&one_lane);
168 #endif
169 	ypstatus = __yp_match_cflookup((grrr)domain, (grrr)map,
170 			    (grrr)key, (int)strlen(key), valp, vallenp, 0);
171 #if	MT_UNSAFE_YP
172 	(void) mutex_unlock(&one_lane);
173 	(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
174 #endif
175 
176 	if (ypstatusp != 0) {
177 		*ypstatusp = ypstatus;
178 	}
179 	return (switch_err(ypstatus, 1));
180 }
181 
182 /*
183  * XXX special version of _nss_nis_ypmatch() for handling C2 (passwd.adjunct)
184  * lookups when we need a reserved port.
185  */
186 
187 static nss_status_t
188 _nss_nis_ypmatch_rsvdport(domain, map, key, valp, vallenp, ypstatusp)
189 	const char		*domain;
190 	const char		*map;
191 	const char		*key;
192 	char			**valp;
193 	int			*vallenp;
194 	int			*ypstatusp;
195 {
196 	int			ypstatus;
197 
198 #if	MT_UNSAFE_YP
199 	sigset_t		oldmask, newmask;
200 
201 	(void) sigfillset(&newmask);
202 	(void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
203 	(void) mutex_lock(&one_lane);
204 #endif
205 	ypstatus = __yp_match_rsvdport_cflookup((grrr)domain, (grrr)map,
206 			    (grrr)key, strlen(key), valp, vallenp, 0);
207 #if	MT_UNSAFE_YP
208 	(void) mutex_unlock(&one_lane);
209 	(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
210 #endif
211 
212 	if (ypstatusp != 0) {
213 		*ypstatusp = ypstatus;
214 	}
215 	return (switch_err(ypstatus, 1));
216 }
217 
218 nss_status_t
219 _nss_nis_lookup(be, args, netdb, map, key, ypstatusp)
220 	nis_backend_ptr_t	be;
221 	nss_XbyY_args_t		*args;
222 	int			netdb;
223 	const char		*map;
224 	const char		*key;
225 	int			*ypstatusp;
226 {
227 	nss_status_t		res;
228 	int			vallen;
229 	char			*val;
230 	char			*free_ptr;
231 	int			parsestat;
232 
233 	if ((res = _nss_nis_ypmatch(be->domain, map, key, &val, &vallen,
234 				    ypstatusp)) != NSS_SUCCESS) {
235 		return (res);
236 	}
237 
238 	free_ptr = val;
239 
240 	if (netdb) {
241 		massage_netdb((const char **)&val, &vallen);
242 	}
243 
244 	args->returnval = NULL;
245 	args->returnlen = 0;
246 	parsestat = (*args->str2ent)(val, vallen,
247 			args->buf.result, args->buf.buffer, args->buf.buflen);
248 	if (parsestat == NSS_STR_PARSE_SUCCESS) {
249 		args->returnval = args->buf.result;
250 		args->returnlen = vallen;
251 		res = NSS_SUCCESS;
252 	} else if (parsestat == NSS_STR_PARSE_ERANGE) {
253 		args->erange = 1;
254 		/* We won't find this otherwise, anyway */
255 		res = NSS_NOTFOUND;
256 	} /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */
257 
258 	free(free_ptr);
259 
260 	return (res);
261 }
262 
263 nss_status_t
264 _nss_nis_lookup_rsvdport(be, args, netdb, map, key, ypstatusp)
265 	nis_backend_ptr_t	be;
266 	nss_XbyY_args_t		*args;
267 	int			netdb;
268 	const char		*map;
269 	const char		*key;
270 	int			*ypstatusp;
271 {
272 	nss_status_t		res;
273 	int			vallen;
274 	char			*val;
275 	char			*free_ptr;
276 	int			parsestat;
277 
278 	if ((res = _nss_nis_ypmatch_rsvdport(be->domain, map, key, &val,
279 				    &vallen, ypstatusp)) != NSS_SUCCESS) {
280 		return (res);
281 	}
282 
283 	free_ptr = val;
284 
285 	if (netdb) {
286 		massage_netdb((const char **)&val, &vallen);
287 	}
288 
289 	args->returnval = NULL;
290 	args->returnlen = 0;
291 	parsestat = (*args->str2ent)(val, vallen,
292 			args->buf.result, args->buf.buffer, args->buf.buflen);
293 	if (parsestat == NSS_STR_PARSE_SUCCESS) {
294 		args->returnval = args->buf.result;
295 		args->returnlen = vallen;
296 		res = NSS_SUCCESS;
297 	} else if (parsestat == NSS_STR_PARSE_ERANGE) {
298 		args->erange = 1;
299 		/* We won't find this otherwise, anyway */
300 		res = NSS_NOTFOUND;
301 	} /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */
302 
303 	free(free_ptr);
304 
305 	return (res);
306 }
307 
308 static nss_status_t
309 do_getent(be, args, netdb)
310 	nis_backend_ptr_t	be;
311 	nss_XbyY_args_t		*args;
312 	int			netdb;
313 {
314 	nss_status_t		res;
315 	int			ypstatus;
316 	int			outkeylen, outvallen;
317 	char			*outkey, *outval;
318 	char			*free_ptr;
319 	int			parsestat;
320 
321 #if	MT_UNSAFE_YP
322 	sigset_t		oldmask, newmask;
323 
324 	(void) sigfillset(&newmask);
325 	(void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
326 	(void) mutex_lock(&one_lane);
327 #endif
328 	if (be->enum_key == 0) {
329 		ypstatus = __yp_first_cflookup((grrr)be->domain,
330 					    (grrr)be->enum_map, &outkey,
331 					    &outkeylen, &outval,
332 					    &outvallen, 0);
333 	} else {
334 		ypstatus = __yp_next_cflookup((grrr)be->domain,
335 					    (grrr)be->enum_map, be->enum_key,
336 					    be->enum_keylen, &outkey,
337 					    &outkeylen, &outval,
338 					    &outvallen, 0);
339 	}
340 #if	MT_UNSAFE_YP
341 	(void) mutex_unlock(&one_lane);
342 	(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
343 #endif
344 
345 	if ((res = switch_err(ypstatus, 0)) != NSS_SUCCESS) {
346 		return (res);
347 	}
348 
349 	free_ptr = outval;
350 
351 	if (netdb) {
352 		massage_netdb((const char **)&outval, &outvallen);
353 	}
354 
355 	args->returnval = NULL;
356 	args->returnlen = 0;
357 	parsestat = (*args->str2ent)(outval, outvallen,
358 			args->buf.result, args->buf.buffer, args->buf.buflen);
359 	if (parsestat == NSS_STR_PARSE_SUCCESS) {
360 		args->returnval = args->buf.result;
361 		args->returnlen = outvallen;
362 		res = NSS_SUCCESS;
363 	} else if (parsestat == NSS_STR_PARSE_ERANGE) {
364 		args->erange = 1;
365 		/* We won't find this otherwise, anyway */
366 		res = NSS_NOTFOUND;
367 	} /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */
368 
369 	free(free_ptr);
370 
371 	if (be->enum_key != 0) {
372 		free(be->enum_key);
373 	}
374 	be->enum_key = outkey;
375 	be->enum_keylen = outkeylen;
376 
377 	return (res);
378 }
379 
380 nss_status_t
381 _nss_nis_getent_rigid(be, args)
382 	nis_backend_ptr_t	be;
383 	void			*args;
384 {
385 	return (do_getent(be, (nss_XbyY_args_t *)args, 0));
386 }
387 
388 nss_status_t
389 _nss_nis_getent_netdb(be, args)
390 	nis_backend_ptr_t	be;
391 	void			*args;
392 {
393 	return (do_getent(be, (nss_XbyY_args_t *)args, 1));
394 }
395 
396 
397 struct cb_data {
398 	void			*args;
399 	const char		*filter;
400 	nis_do_all_func_t	func;
401 	nss_status_t		result;
402 };
403 
404 enum { ITER_NEXT = 0, ITER_STOP = 1 };	/* Should be in <rpcsvc/ypclnt.h> */
405 
406 /*ARGSUSED*/
407 static int
408 do_cback(instatus, inkey, inkeylen, inval, invallen, indata)
409 	int			instatus;
410 	const char		*inkey;
411 	int			inkeylen;
412 	const char		*inval;
413 	int			invallen;
414 	struct cb_data		*indata;
415 {
416 	nss_status_t		res;
417 
418 	if (instatus != YP_TRUE) {
419 		return (ITER_NEXT);	/* yp_all may decide otherwise... */
420 	}
421 
422 	if (indata->filter != 0 && strstr(inval, indata->filter) == 0) {
423 		/*
424 		 * Optimization:  if the entry doesn't contain the filter
425 		 *   string then it can't be the entry we want, so don't
426 		 *   bother looking more closely at it.
427 		 */
428 		return (ITER_NEXT);
429 	}
430 
431 	res = (*indata->func)(inval, invallen, indata->args);
432 
433 	if (res == NSS_NOTFOUND) {
434 		return (ITER_NEXT);
435 	} else {
436 		indata->result = res;
437 		return (ITER_STOP);
438 	}
439 }
440 
441 nss_status_t
442 _nss_nis_do_all(be, args, filter, func)
443 	nis_backend_ptr_t	be;
444 	void			*args;
445 	const char		*filter;
446 	nis_do_all_func_t	func;
447 {
448 	int			ypall_status;
449 	struct cb_data		data;
450 	struct ypall_callback	cback;
451 
452 	data.args	= args;
453 	data.filter	= filter;
454 	data.func	= func;
455 	data.result	= NSS_NOTFOUND;
456 
457 	cback.foreach	= do_cback;
458 	cback.data	= (char *)&data;
459 
460 #if	MT_UNSAFE_YP
461 	sigset_t		oldmask, newmask;
462 
463 	(void) sigfillset(&newmask);
464 	(void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
465 	(void) mutex_lock(&one_lane);
466 #endif
467 	ypall_status = __yp_all_cflookup((grrr)be->domain,
468 			(grrr) be->enum_map, &cback, 0);
469 #if	MT_UNSAFE_YP
470 	(void) mutex_unlock(&one_lane);
471 	(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
472 #endif
473 
474 	switch (ypall_status) {
475 	    case 0:
476 		return (data.result);
477 	    case YPERR_DOMAIN:
478 	    case YPERR_YPSERV:
479 	    case YPERR_BUSY:		/* Probably never get this, but... */
480 		return (NSS_TRYAGAIN);
481 	    default:
482 		return (NSS_UNAVAIL);
483 	}
484 }
485 
486 struct XbyY_data {
487 	nss_XbyY_args_t		*args;
488 	nis_XY_check_func	func;
489 	int			netdb;
490 };
491 
492 static nss_status_t
493 XbyY_iterator(instr, instr_len, a)
494 	const char		*instr;
495 	int			instr_len;
496 	void			*a;
497 {
498 	struct XbyY_data	*xydata	= (struct XbyY_data *)a;
499 	nss_XbyY_args_t		*args	= xydata->args;
500 	nss_status_t		res;
501 	int			parsestat;
502 
503 	if (xydata->netdb) {
504 		massage_netdb(&instr, &instr_len);
505 	}
506 
507 	args->returnval = NULL;
508 	args->returnlen = 0;
509 	parsestat = (*args->str2ent)(instr, instr_len,
510 			args->buf.result, args->buf.buffer, args->buf.buflen);
511 	if (parsestat == NSS_STR_PARSE_SUCCESS) {
512 		args->returnval = args->buf.result;
513 		if ((*xydata->func)(args)) {
514 			res = NSS_SUCCESS;
515 			args->returnlen = instr_len;
516 		} else {
517 			res = NSS_NOTFOUND;
518 			args->returnval = 0;
519 		}
520 	} else if (parsestat == NSS_STR_PARSE_ERANGE) {
521 		/*
522 		 * If we got here because (*str2ent)() found that the buffer
523 		 * wasn't big enough, maybe we should quit and return erange.
524 		 * Instead we'll keep looking and eventually return "not
525 		 * found" -- it's a bug, but not an earth-shattering one.
526 		 */
527 		args->erange = 1;	/* <== Is this a good idea? */
528 		res = NSS_NOTFOUND;
529 	} /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */
530 
531 	return (res);
532 }
533 
534 nss_status_t
535 _nss_nis_XY_all(be, args, netdb, filter, func)
536 	nis_backend_ptr_t	be;
537 	nss_XbyY_args_t		*args;
538 	int			netdb;
539 	const char		*filter;
540 	nis_XY_check_func	func;
541 {
542 	struct XbyY_data	data;
543 
544 	data.args = args;
545 	data.func = func;
546 	data.netdb = netdb;
547 
548 	return (_nss_nis_do_all(be, &data, filter, XbyY_iterator));
549 	/* Now how many levels of callbacks was that? */
550 }
551 
552 
553 /*ARGSUSED*/
554 nss_status_t
555 _nss_nis_destr(be, dummy)
556 	nis_backend_ptr_t	be;
557 	void			*dummy;
558 {
559 	if (be != 0) {
560 		/* === Should change to invoke ops[ENDENT] ? */
561 		(void) _nss_nis_endent(be, 0);
562 		free(be);
563 	}
564 	return (NSS_SUCCESS);	/* In case anyone is dumb enough to check */
565 }
566 
567 /* We want to lock this even if the YP routines are MT-safe */
568 static mutex_t	yp_domain_lock = DEFAULTMUTEX;
569 static char	*yp_domain;
570 
571 const char *
572 _nss_nis_domain()
573 {
574 	char			*domain;
575 
576 	/*
577 	 * This much locking is probably more "by the book" than necessary...
578 	 */
579 	sigset_t		oldmask, newmask;
580 
581 	(void) sigfillset(&newmask);
582 	(void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
583 	(void) mutex_lock(&yp_domain_lock);
584 
585 	if ((domain = yp_domain) == 0) {
586 #if	MT_UNSAFE_YP
587 		(void) mutex_lock(&one_lane);
588 #endif
589 		if (yp_get_default_domain(&yp_domain) == 0) {
590 			domain = yp_domain;
591 		}
592 #if	MT_UNSAFE_YP
593 		(void) mutex_unlock(&one_lane);
594 #endif
595 	}
596 
597 	(void) mutex_unlock(&yp_domain_lock);
598 	(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
599 
600 	return (domain);
601 }
602 
603 nss_backend_t *
604 _nss_nis_constr(ops, n_ops, enum_map)
605 	nis_backend_op_t	ops[];
606 	int			n_ops;
607 	const char		*enum_map;
608 {
609 	const char		*domain;
610 	nis_backend_ptr_t	be;
611 
612 	if ((domain = _nss_nis_domain()) == 0 ||
613 	    (be = (nis_backend_ptr_t)malloc(sizeof (*be))) == 0) {
614 		return (0);
615 	}
616 	be->ops		= ops;
617 	be->n_ops	= n_ops;
618 	be->domain	= domain;
619 	be->enum_map	= enum_map;   /* Don't strdup, assume valid forever */
620 	be->enum_key	= 0;
621 	be->enum_keylen	= 0;
622 
623 	return ((nss_backend_t *)be);
624 }
625 
626 /*
627  * This routine is used to parse lines of the form:
628  * 	name number aliases
629  * It returns 1 if the key in argp matches any one of the
630  * names in the line, otherwise 0
631  * Used by rpc
632  */
633 int
634 _nss_nis_check_name_aliases(nss_XbyY_args_t *argp, const char *line,
635 	int linelen)
636 {
637 	const char	*limit, *linep, *keyp;
638 
639 	linep = line;
640 	limit = line + linelen;
641 	keyp = argp->key.name;
642 
643 	/* compare name */
644 	while (*keyp && linep < limit && !isspace(*linep) && *keyp == *linep) {
645 		keyp++;
646 		linep++;
647 	}
648 	if (*keyp == '\0' && linep < limit && isspace(*linep))
649 		return (1);
650 	/* skip remainder of the name, if any */
651 	while (linep < limit && !isspace(*linep))
652 		linep++;
653 	/* skip the delimiting spaces */
654 	while (linep < limit && isspace(*linep))
655 		linep++;
656 	/* compare with the aliases */
657 	while (linep < limit) {
658 		/*
659 		 * 1st pass: skip number
660 		 * Other passes: skip remainder of the alias name, if any
661 		 */
662 		while (linep < limit && !isspace(*linep))
663 			linep++;
664 		/* skip the delimiting spaces */
665 		while (linep < limit && isspace(*linep))
666 			linep++;
667 		/* compare with the alias name */
668 		keyp = argp->key.name;
669 		while (*keyp && linep < limit && !isspace(*linep) &&
670 		    *keyp == *linep) {
671 			keyp++;
672 			linep++;
673 		}
674 		if (*keyp == '\0' && (linep == limit || isspace(*linep)))
675 			return (1);
676 	}
677 	return (0);
678 }
679