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