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