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
48 static mutex_t one_lane = DEFAULTMUTEX;
49 #endif
50
51 /* <rpcsvc/ypclnt.h> uses (char *) where it should use (const char *) */
52 typedef 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
64 static nss_status_t
switch_err(ypstatus,ismatch)65 switch_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*/
102 nss_status_t
_nss_nis_setent(be,dummy)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
115 nss_status_t
_nss_nis_endent(be,dummy)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
124 void
massage_netdb(const char ** valp,int * vallenp)125 massage_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
149 nss_status_t
_nss_nis_ypmatch(domain,map,key,valp,vallenp,ypstatusp)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
185 static nss_status_t
_nss_nis_ypmatch_rsvdport(domain,map,key,valp,vallenp,ypstatusp)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
216 nss_status_t
_nss_nis_lookup(be,args,netdb,map,key,ypstatusp)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
272 nss_status_t
_nss_nis_lookup_rsvdport(be,args,netdb,map,key,ypstatusp)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
317 static nss_status_t
do_getent(be,args,netdb)318 do_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
389 nss_status_t
_nss_nis_getent_rigid(be,args)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
397 nss_status_t
_nss_nis_getent_netdb(be,args)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
406 struct cb_data {
407 void *args;
408 const char *filter;
409 nis_do_all_func_t func;
410 nss_status_t result;
411 };
412
413 enum { ITER_NEXT = 0, ITER_STOP = 1 }; /* Should be in <rpcsvc/ypclnt.h> */
414
415 /*ARGSUSED*/
416 static int
do_cback(instatus,inkey,inkeylen,inval,invallen,indata)417 do_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
450 nss_status_t
_nss_nis_do_all(be,args,filter,func)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
495 struct XbyY_data {
496 nss_XbyY_args_t *args;
497 nis_XY_check_func func;
498 int netdb;
499 };
500
501 static nss_status_t
XbyY_iterator(instr,instr_len,a)502 XbyY_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
543 nss_status_t
_nss_nis_XY_all(be,args,netdb,filter,func)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*/
563 nss_status_t
_nss_nis_destr(be,dummy)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 */
577 static mutex_t yp_domain_lock = DEFAULTMUTEX;
578 static char *yp_domain;
579
580 const char *
_nss_nis_domain()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
612 nss_backend_t *
_nss_nis_constr(ops,n_ops,enum_map)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 */
642 int
_nss_nis_check_name_aliases(nss_XbyY_args_t * argp,const char * line,int linelen)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