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 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Common code and structures used by name-service-switch "compat" backends.
26 *
27 * Most of the code in the "compat" backend is a perverted form of code from
28 * the "files" backend; this file is no exception.
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <bsm/libbsm.h>
36 #include <user_attr.h>
37 #include <pwd.h>
38 #include <shadow.h>
39 #include <grp.h>
40 #include <unistd.h> /* for GF_PATH */
41 #include <dlfcn.h>
42 #include "compat_common.h"
43
44 /*
45 * This should be in a header.
46 */
47
48 extern int yp_get_default_domain(char **domain);
49
50 /* from libc */
51 extern int str2passwd(const char *instr, int lenstr, void *ent,
52 char *buffer, int buflen);
53 extern int str2spwd(const char *instr, int lenstr, void *ent,
54 char *buffer, int buflen);
55 extern int str2group(const char *instr, int lenstr, void *ent,
56 char *buffer, int buflen);
57
58 /* from libnsl */
59 extern char *_strtok_escape(char *, char *, char **);
60
61 /*
62 * str2auuser_s and str2userattr_s are very simple version
63 * of the str2auuser() and str2userattr() that can be found in
64 * libnsl. They only copy the user name into the userstr_t
65 * or au_user_str_t structure (so check on user name can be
66 * performed).
67 */
68 static int
str2auuser_s(const char * instr,int lenstr,void * ent,char * buffer,int buflen)69 str2auuser_s(
70 const char *instr,
71 int lenstr,
72 void *ent,
73 char *buffer,
74 int buflen)
75 {
76 char *last = NULL;
77 char *sep = KV_TOKEN_DELIMIT;
78 au_user_str_t *au_user = (au_user_str_t *)ent;
79
80 if (lenstr >= buflen)
81 return (NSS_STR_PARSE_ERANGE);
82 (void) strncpy(buffer, instr, buflen);
83 au_user->au_name = _strtok_escape(buffer, sep, &last);
84 return (0);
85 }
86
87 static int
str2userattr_s(const char * instr,int lenstr,void * ent,char * buffer,int buflen)88 str2userattr_s(
89 const char *instr,
90 int lenstr,
91 void *ent,
92 char *buffer,
93 int buflen)
94 {
95 char *last = NULL;
96 char *sep = KV_TOKEN_DELIMIT;
97 userstr_t *user = (userstr_t *)ent;
98
99 if (lenstr >= buflen)
100 return (NSS_STR_PARSE_ERANGE);
101 (void) strncpy(buffer, instr, buflen);
102 user->name = _strtok_escape(buffer, sep, &last);
103 return (0);
104 }
105
106 /*
107 * Routines to manage list of "-" users for get{pw, sp, gr}ent(). Current
108 * implementation is completely moronic; we use a linked list. But then
109 * that's what it's always done in 4.x...
110 */
111
112 struct setofstrings {
113 char *name;
114 struct setofstrings *next;
115 /*
116 * === Should get smart and malloc the string and pointer as one
117 * object rather than two.
118 */
119 };
120
121 static void
strset_free(ssp)122 strset_free(ssp)
123 strset_t *ssp;
124 {
125 strset_t cur, nxt;
126
127 for (cur = *ssp; cur != 0; cur = nxt) {
128 nxt = cur->next;
129 free(cur->name);
130 free(cur);
131 }
132 *ssp = 0;
133 }
134
135 static boolean_t
strset_add(ssp,nam)136 strset_add(ssp, nam)
137 strset_t *ssp;
138 const char *nam;
139 {
140 strset_t new;
141
142 if (0 == (new = (strset_t)malloc(sizeof (*new)))) {
143 return (B_FALSE);
144 }
145 if (0 == (new->name = malloc(strlen(nam) + 1))) {
146 free(new);
147 return (B_FALSE);
148 }
149 (void) strcpy(new->name, nam);
150 new->next = *ssp;
151 *ssp = new;
152 return (B_TRUE);
153 }
154
155 static boolean_t
strset_in(ssp,nam)156 strset_in(ssp, nam)
157 const strset_t *ssp;
158 const char *nam;
159 {
160 strset_t cur;
161
162 for (cur = *ssp; cur != 0; cur = cur->next) {
163 if (strcmp(cur->name, nam) == 0) {
164 return (B_TRUE);
165 }
166 }
167 return (B_FALSE);
168 }
169
170 /*
171 * Lookup and enumeration routines for +@group and -@group.
172 *
173 * This code knows a lot more about lib/libc/port/gen/getnetgrent.c than
174 * is really healthy. The set/get/end routines below duplicate code
175 * from that file, but keep the state information per-backend-instance
176 * instead of just per-process.
177 */
178
179 extern void _nss_initf_netgroup(nss_db_params_t *);
180 /*
181 * Should really share the db_root in getnetgrent.c in order to get the
182 * resource-management quotas right, but this will have to do.
183 */
184 static DEFINE_NSS_DB_ROOT(netgr_db_root);
185
186 static boolean_t
netgr_in(compat_backend_ptr_t be,const char * group,const char * user)187 netgr_in(compat_backend_ptr_t be, const char *group, const char *user)
188 {
189 if (be->yp_domain == 0) {
190 if (yp_get_default_domain((char **)&be->yp_domain) != 0) {
191 return (B_FALSE);
192 }
193 }
194 return (innetgr(group, 0, user, be->yp_domain));
195 }
196
197 static void
netgr_set(be,netgroup)198 netgr_set(be, netgroup)
199 compat_backend_ptr_t be;
200 const char *netgroup;
201 {
202 /*
203 * ===> Need comment to explain that this first "if" is optimizing
204 * for the same-netgroup-as-last-time case
205 */
206 if (be->getnetgrent_backend != 0 &&
207 NSS_INVOKE_DBOP(be->getnetgrent_backend,
208 NSS_DBOP_SETENT,
209 (void *) netgroup) != NSS_SUCCESS) {
210 NSS_INVOKE_DBOP(be->getnetgrent_backend, NSS_DBOP_DESTRUCTOR,
211 0);
212 be->getnetgrent_backend = 0;
213 }
214 if (be->getnetgrent_backend == 0) {
215 struct nss_setnetgrent_args args;
216
217 args.netgroup = netgroup;
218 args.iterator = 0;
219 (void) nss_search(&netgr_db_root, _nss_initf_netgroup,
220 NSS_DBOP_NETGROUP_SET, &args);
221 be->getnetgrent_backend = args.iterator;
222 }
223 }
224
225 static boolean_t
netgr_next_u(be,up)226 netgr_next_u(be, up)
227 compat_backend_ptr_t be;
228 char **up;
229 {
230 if (be->netgr_buffer == 0 &&
231 (be->netgr_buffer = malloc(NSS_BUFLEN_NETGROUP)) == 0) {
232 /* Out of memory */
233 return (B_FALSE);
234 }
235
236 do {
237 struct nss_getnetgrent_args args;
238
239 args.buffer = be->netgr_buffer;
240 args.buflen = NSS_BUFLEN_NETGROUP;
241 args.status = NSS_NETGR_NO;
242
243 if (be->getnetgrent_backend != 0) {
244 NSS_INVOKE_DBOP(be->getnetgrent_backend,
245 NSS_DBOP_GETENT, &args);
246 }
247
248 if (args.status == NSS_NETGR_FOUND) {
249 *up = args.retp[NSS_NETGR_USER];
250 } else {
251 return (B_FALSE);
252 }
253 } while (*up == 0);
254 return (B_TRUE);
255 }
256
257 static void
netgr_end(be)258 netgr_end(be)
259 compat_backend_ptr_t be;
260 {
261 if (be->getnetgrent_backend != 0) {
262 NSS_INVOKE_DBOP(be->getnetgrent_backend,
263 NSS_DBOP_DESTRUCTOR, 0);
264 be->getnetgrent_backend = 0;
265 }
266 if (be->netgr_buffer != 0) {
267 free(be->netgr_buffer);
268 be->netgr_buffer = 0;
269 }
270 }
271
272
273 #define MAXFIELDS 9 /* Sufficient for passwd (7), shadow (9), group (4) */
274
275 static nss_status_t
do_merge(be,args,instr,linelen)276 do_merge(be, args, instr, linelen)
277 compat_backend_ptr_t be;
278 nss_XbyY_args_t *args;
279 const char *instr;
280 int linelen;
281 {
282 char *fields[MAXFIELDS];
283 int i;
284 int overrides;
285 const char *p;
286 const char *end = instr + linelen;
287 nss_status_t res = NSS_NOTFOUND;
288
289 /*
290 * Potential optimization: only perform the field-splitting nonsense
291 * once per input line (at present, "+" and "+@netgroup" entries
292 * will cause us to do this multiple times in getent() requests).
293 */
294
295 for (i = 0; i < MAXFIELDS; i++) {
296 fields[i] = 0;
297 }
298 for (p = instr, overrides = 0, i = 0; /* no test */; i++) {
299 const char *q = memchr(p, ':', end - p);
300 const char *r = (q == 0) ? end : q;
301 ssize_t len = r - p;
302
303 if (len > 0) {
304 char *s = malloc(len + 1);
305 if (s == 0) {
306 overrides = -1; /* Indicates "you lose" */
307 break;
308 }
309 (void) memcpy(s, p, len);
310 s[len] = '\0';
311 fields[i] = s;
312 overrides++;
313 }
314 if (q == 0) {
315 /* End of line */
316 break;
317 } else {
318 /* Skip the colon at (*q) */
319 p = q + 1;
320 }
321 }
322 if (overrides == 1) {
323 /*
324 * return result here if /etc file format is requested
325 */
326 if (be->return_string_data != 1) {
327 /* No real overrides, return (*args) intact */
328 res = NSS_SUCCESS;
329 } else {
330 free(fields[0]);
331 fields[0] = NULL;
332 }
333 }
334
335 if (overrides > 1 || be->return_string_data == 1) {
336 /*
337 * The zero'th field is always nonempty (+/-...), but at least
338 * one other field was also nonempty, i.e. wants to override
339 */
340 switch ((*be->mergef)(be, args, (const char **)fields)) {
341 case NSS_STR_PARSE_SUCCESS:
342 if (be->return_string_data != 1)
343 args->returnval = args->buf.result;
344 else
345 args->returnval = args->buf.buffer;
346 args->erange = 0;
347 res = NSS_SUCCESS;
348 break;
349 case NSS_STR_PARSE_ERANGE:
350 args->returnval = 0;
351 args->erange = 1;
352 res = NSS_NOTFOUND;
353 break;
354 case NSS_STR_PARSE_PARSE:
355 args->returnval = 0;
356 args->erange = 0;
357 /* ===> Very likely the wrong thing to do... */
358 res = NSS_NOTFOUND;
359 break;
360 }
361 } else if (res != NSS_SUCCESS) {
362 args->returnval = 0;
363 args->erange = 0;
364 res = NSS_UNAVAIL; /* ==> Right? */
365 }
366
367 for (i = 0; i < MAXFIELDS; i++) {
368 if (fields[i] != 0) {
369 free(fields[i]);
370 }
371 }
372
373 return (res);
374 }
375
376 /*ARGSUSED*/
377 nss_status_t
_nss_compat_setent(be,dummy)378 _nss_compat_setent(be, dummy)
379 compat_backend_ptr_t be;
380 void *dummy;
381 {
382 if (be->f == 0) {
383 if (be->filename == 0) {
384 /* Backend isn't initialized properly? */
385 return (NSS_UNAVAIL);
386 }
387 if ((be->f = fopen(be->filename, "rF")) == 0) {
388 return (NSS_UNAVAIL);
389 }
390 } else {
391 rewind(be->f);
392 }
393 strset_free(&be->minuses);
394 /* ===> ??? nss_endent(be->db_rootp, be->db_initf, &be->db_context); */
395
396 if ((strcmp(be->filename, USERATTR_FILENAME) == 0) ||
397 (strcmp(be->filename, AUDITUSER_FILENAME) == 0))
398 be->state = GETENT_ATTRDB;
399 else
400 be->state = GETENT_FILE;
401
402 be->return_string_data = 0;
403
404 /* ===> ?? netgroup stuff? */
405 return (NSS_SUCCESS);
406 }
407
408 /*ARGSUSED*/
409 nss_status_t
_nss_compat_endent(be,dummy)410 _nss_compat_endent(be, dummy)
411 compat_backend_ptr_t be;
412 void *dummy;
413 {
414 if (be->f != 0) {
415 (void) fclose(be->f);
416 be->f = 0;
417 }
418 if (be->buf != 0) {
419 free(be->buf);
420 be->buf = 0;
421 }
422 nss_endent(be->db_rootp, be->db_initf, &be->db_context);
423
424 be->state = GETENT_FILE; /* Probably superfluous but comforting */
425 strset_free(&be->minuses);
426 netgr_end(be);
427
428 /*
429 * Question: from the point of view of resource-freeing vs. time to
430 * start up again, how much should we do in endent() and how much
431 * in the destructor?
432 */
433 return (NSS_SUCCESS);
434 }
435
436 /*ARGSUSED*/
437 nss_status_t
_nss_compat_destr(be,dummy)438 _nss_compat_destr(be, dummy)
439 compat_backend_ptr_t be;
440 void *dummy;
441 {
442 if (be != 0) {
443 if (be->f != 0) {
444 (void) _nss_compat_endent(be, 0);
445 }
446 nss_delete(be->db_rootp);
447 nss_delete(&netgr_db_root);
448 free(be->workarea);
449 free(be);
450 }
451 return (NSS_SUCCESS); /* In case anyone is dumb enough to check */
452 }
453
454 static int
read_line(f,buffer,buflen)455 read_line(f, buffer, buflen)
456 FILE *f;
457 char *buffer;
458 int buflen;
459 {
460 /*CONSTCOND*/
461 while (1) {
462 int linelen;
463
464 if (fgets(buffer, buflen, f) == 0) {
465 /* End of file */
466 return (-1);
467 }
468 linelen = strlen(buffer);
469 /* linelen >= 1 (since fgets didn't return 0) */
470
471 if (buffer[linelen - 1] == '\n') {
472 /*
473 * ===> The code below that calls read_line() doesn't
474 * play by the rules; it assumes in places that
475 * the line is null-terminated. For now we'll
476 * humour it.
477 */
478 buffer[--linelen] = '\0';
479 return (linelen);
480 }
481 if (feof(f)) {
482 /* Line is last line in file, and has no newline */
483 return (linelen);
484 }
485 /* Line too long for buffer; toss it and loop for next line */
486 /* ===== should syslog() in cases where previous code did */
487 while (fgets(buffer, buflen, f) != 0 &&
488 buffer[strlen(buffer) - 1] != '\n') {
489 ;
490 }
491 }
492 /*NOTREACHED*/
493 }
494
495 static int
is_nss_lookup_by_name(int attrdb,nss_dbop_t op)496 is_nss_lookup_by_name(int attrdb, nss_dbop_t op)
497 {
498 int result = 0;
499
500 if ((attrdb != 0) &&
501 ((op == NSS_DBOP_AUDITUSER_BYNAME) ||
502 (op == NSS_DBOP_USERATTR_BYNAME))) {
503 result = 1;
504 } else if ((attrdb == 0) &&
505 ((op == NSS_DBOP_GROUP_BYNAME) ||
506 (op == NSS_DBOP_PASSWD_BYNAME) ||
507 (op == NSS_DBOP_SHADOW_BYNAME))) {
508 result = 1;
509 }
510
511 return (result);
512 }
513
514 /*ARGSUSED*/
515 nss_status_t
_attrdb_compat_XY_all(be,argp,netdb,check,op_num)516 _attrdb_compat_XY_all(be, argp, netdb, check, op_num)
517 compat_backend_ptr_t be;
518 nss_XbyY_args_t *argp;
519 int netdb;
520 compat_XY_check_func check;
521 nss_dbop_t op_num;
522 {
523 int parsestat;
524 int (*func)();
525 const char *filter = argp->key.name;
526 nss_status_t res;
527
528 #ifdef DEBUG
529 (void) fprintf(stdout, "\n[compat_common.c: _attrdb_compat_XY_all]\n");
530 #endif /* DEBUG */
531
532 if (be->buf == 0 &&
533 (be->buf = malloc(be->minbuf)) == 0) {
534 return (NSS_UNAVAIL);
535 }
536 if (check != NULL)
537 if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS)
538 return (res);
539
540 res = NSS_NOTFOUND;
541
542 /*
543 * assume a NULL buf.result pointer is an indication
544 * that the lookup result should be returned in /etc
545 * file format (if called from _nss_compat_getent(),
546 * be->return_string_data and argp->buf.result
547 * would be set already if argp->buf.result is NULL)
548 */
549 if (check != NULL) {
550 if (argp->buf.result == NULL) {
551 be->return_string_data = 1;
552
553 /*
554 * the code executed later needs the result struct
555 * as working area
556 */
557 argp->buf.result = be->workarea;
558 } else
559 be->return_string_data = 0;
560 }
561
562 /*
563 * use an alternate str2ent function if necessary
564 */
565 if (be->return_string_data == 1)
566 func = be->str2ent_alt;
567 else
568 func = argp->str2ent;
569
570 /*CONSTCOND*/
571 while (1) {
572 int linelen;
573 char *instr = be->buf;
574
575 if ((linelen = read_line(be->f, instr, be->minbuf)) < 0) {
576 /* End of file */
577 argp->returnval = 0;
578 argp->erange = 0;
579 break;
580 }
581 if (filter != 0 && strstr(instr, filter) == 0) {
582 /*
583 * Optimization: if the entry doesn't contain the
584 * filter string then it can't be the entry we want,
585 * so don't bother looking more closely at it.
586 */
587 continue;
588 }
589 if (netdb) {
590 char *first;
591 char *last;
592
593 if ((last = strchr(instr, '#')) == 0) {
594 last = instr + linelen;
595 }
596 *last-- = '\0'; /* Nuke '\n' or #comment */
597
598 /*
599 * Skip leading whitespace. Normally there isn't
600 * any, so it's not worth calling strspn().
601 */
602 for (first = instr; isspace(*first); first++) {
603 ;
604 }
605 if (*first == '\0') {
606 continue;
607 }
608 /*
609 * Found something non-blank on the line. Skip back
610 * over any trailing whitespace; since we know
611 * there's non-whitespace earlier in the line,
612 * checking for termination is easy.
613 */
614 while (isspace(*last)) {
615 --last;
616 }
617 linelen = last - first + 1;
618 if (first != instr) {
619 instr = first;
620 }
621 }
622 argp->returnval = 0;
623 parsestat = (*func)(instr, linelen, argp->buf.result,
624 argp->buf.buffer, argp->buf.buflen);
625 if (parsestat == NSS_STR_PARSE_SUCCESS) {
626 argp->returnval = argp->buf.result;
627 if (check == 0 || (*check)(argp)) {
628 int len;
629
630 if (be->return_string_data != 1) {
631 res = NSS_SUCCESS;
632 break;
633 }
634
635 /* copy string data to result buffer */
636 argp->buf.result = NULL;
637 argp->returnval = argp->buf.buffer;
638 if ((len = strlcpy(argp->buf.buffer, instr,
639 argp->buf.buflen)) >=
640 argp->buf.buflen) {
641 argp->returnval = NULL;
642 res = NSS_NOTFOUND;
643 argp->erange = 1;
644 break;
645 }
646
647 argp->returnlen = len;
648 res = NSS_SUCCESS;
649 break;
650 }
651 } else if (parsestat == NSS_STR_PARSE_ERANGE) {
652 res = NSS_NOTFOUND;
653 argp->erange = 1;
654 break;
655 }
656 }
657 /*
658 * stayopen is set to 0 by default in order to close the opened
659 * file. Some applications may break if it is set to 1.
660 */
661 if (check != 0 && !argp->stayopen) {
662 (void) _nss_compat_endent(be, 0);
663 }
664
665 if (res != NSS_SUCCESS) {
666 /*
667 * tell the nss_search() and nss_getent() below
668 * if the result should be returned in the /etc
669 * file format
670 */
671 if (be->return_string_data == 1)
672 argp->buf.result = NULL;
673
674 if ((op_num == NSS_DBOP_USERATTR_BYNAME) ||
675 (op_num == NSS_DBOP_AUDITUSER_BYNAME)) {
676 res = nss_search(be->db_rootp,
677 be->db_initf,
678 op_num,
679 argp);
680 } else {
681 res = nss_getent(be->db_rootp,
682 be->db_initf, &be->db_context, argp);
683 }
684 if (res != NSS_SUCCESS) {
685 argp->returnval = 0;
686 argp->erange = 0;
687 }
688 }
689
690 return (res);
691 }
692
693 static int
validate_ids(compat_backend_ptr_t be,nss_XbyY_args_t * argp,char * line,int * linelenp,int buflen,int extra_chars)694 validate_ids(compat_backend_ptr_t be, nss_XbyY_args_t *argp,
695 char *line, int *linelenp, int buflen, int extra_chars)
696 {
697 if (be->return_string_data != 1) {
698 struct passwd *p;
699 struct group *g;
700 /*
701 * The data is already marshalled into
702 * struct passwd or group.
703 */
704 if (strcmp(be->filename, PASSWD) == 0) {
705 p = (struct passwd *)argp->returnval;
706 if (p->pw_uid > MAXUID)
707 p->pw_uid = UID_NOBODY;
708 if (p->pw_gid > MAXUID)
709 p->pw_gid = GID_NOBODY;
710 } else if (strcmp(be->filename, GF_PATH) == 0) {
711 g = (struct group *)argp->returnval;
712 if (g->gr_gid > MAXUID)
713 g->gr_gid = GID_NOBODY;
714 }
715 return (NSS_STR_PARSE_SUCCESS);
716 }
717
718 /*
719 * The data needs to be returned in string format therefore
720 * validate the return string.
721 */
722 if (strcmp(be->filename, PASSWD) == 0)
723 return (validate_passwd_ids(line, linelenp, buflen,
724 extra_chars));
725 else if (strcmp(be->filename, GF_PATH) == 0)
726 return (validate_group_ids(line, linelenp, buflen,
727 extra_chars));
728 return (NSS_STR_PARSE_SUCCESS);
729 }
730
731 nss_status_t
_nss_compat_XY_all(be,args,check,op_num)732 _nss_compat_XY_all(be, args, check, op_num)
733 compat_backend_ptr_t be;
734 nss_XbyY_args_t *args;
735 compat_XY_check_func check;
736 nss_dbop_t op_num;
737 {
738 nss_status_t res;
739 int parsestat;
740
741
742 if (be->buf == 0 &&
743 (be->buf = malloc(be->minbuf)) == 0) {
744 return (NSS_UNAVAIL); /* really panic, malloc failed */
745 }
746
747 if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
748 return (res);
749 }
750
751 res = NSS_NOTFOUND;
752
753 /*
754 * assume a NULL buf.result pointer is an indication
755 * that the lookup result should be returned in /etc
756 * file format
757 */
758 if (args->buf.result == NULL) {
759
760 be->return_string_data = 1;
761
762 /*
763 * the code executed later needs the result struct
764 * as working area
765 */
766 args->buf.result = be->workarea;
767
768 be->str2ent_save = args->str2ent;
769 args->str2ent = be->str2ent_alt;
770 } else
771 be->return_string_data = 0;
772
773 /*CONSTCOND*/
774 while (1) {
775 int linelen;
776 char *instr = be->buf;
777 char *colon;
778
779 linelen = read_line(be->f, instr, be->minbuf);
780 if (linelen < 0) {
781 /* End of file */
782 args->returnval = 0;
783 args->erange = 0;
784 break;
785 }
786
787 args->returnval = 0; /* reset for both types of entries */
788
789 if (instr[0] != '+' && instr[0] != '-') {
790 /* Simple, wholesome, God-fearing entry */
791 parsestat = (*args->str2ent)(instr, linelen,
792 args->buf.result,
793 args->buf.buffer,
794 args->buf.buflen);
795 if (parsestat == NSS_STR_PARSE_SUCCESS) {
796 args->returnval = args->buf.result;
797 if ((*check)(args) != 0) {
798 int len;
799
800 parsestat = validate_ids(be, args,
801 instr, &linelen, be->minbuf, 1);
802 if (parsestat ==
803 NSS_STR_PARSE_ERANGE) {
804 args->erange = 1;
805 res = NSS_NOTFOUND;
806 break;
807 } else if (parsestat !=
808 NSS_STR_PARSE_SUCCESS) {
809 continue;
810 }
811
812 if (be->return_string_data != 1) {
813 res = NSS_SUCCESS;
814 break;
815 }
816
817 /*
818 * copy string data to
819 * result buffer
820 */
821 args->buf.result = NULL;
822 args->str2ent = be->str2ent_save;
823 if ((len = strlcpy(args->buf.buffer,
824 instr, args->buf.buflen)) >=
825 args->buf.buflen)
826 parsestat =
827 NSS_STR_PARSE_ERANGE;
828 else {
829 args->returnval =
830 args->buf.buffer;
831 args->returnlen = len;
832 res = NSS_SUCCESS;
833 break;
834 }
835 } else
836 continue;
837 }
838
839 /* ===> Check the Dani logic here... */
840
841 if (parsestat == NSS_STR_PARSE_ERANGE) {
842 args->erange = 1;
843 res = NSS_NOTFOUND;
844 break;
845 /* should we just skip this one long line ? */
846 } /* else if (parsestat == NSS_STR_PARSE_PARSE) */
847 /* don't care ! */
848
849 /* ==> ?? */ continue;
850 }
851
852
853 /*
854 * Process "+", "+name", "+@netgroup", "-name" or "-@netgroup"
855 *
856 * This code is optimized for lookups by name.
857 *
858 * For lookups by identifier search key cannot be matched with
859 * the name of the "+" or "-" entry. So nss_search() is to be
860 * called before extracting the name i.e. via (*be->getnamef)().
861 *
862 * But for lookups by name, search key is compared with the name
863 * of the "+" or "-" entry to acquire a match and thus
864 * unnesessary calls to nss_search() is eliminated. Also for
865 * matching "-" entries, calls to nss_search() is eliminated.
866 */
867
868 if ((colon = strchr(instr, ':')) != 0) {
869 *colon = '\0'; /* terminate field to extract name */
870 }
871
872 if (instr[1] == '@') {
873 /*
874 * Case 1:
875 * The entry is of the form "+@netgroup" or
876 * "-@netgroup". If we're performing a lookup by name,
877 * we can simply extract the name from the search key
878 * (i.e. args->key.name). If not, then we must call
879 * nss_search() before extracting the name via the
880 * get_XXname() function. i.e. (*be->getnamef)(args).
881 */
882 if (is_nss_lookup_by_name(0, op_num) != 0) {
883 /* compare then search */
884 if (!be->permit_netgroups ||
885 !netgr_in(be, instr + 2, args->key.name))
886 continue;
887 if (instr[0] == '+') {
888 /* need to search for "+" entry */
889 (void) nss_search(be->db_rootp,
890 be->db_initf, op_num, args);
891 if (args->returnval == 0)
892 continue;
893 }
894 } else {
895 /* search then compare */
896 (void) nss_search(be->db_rootp,
897 be->db_initf, op_num, args);
898 if (args->returnval == 0)
899 continue;
900 if (!be->permit_netgroups ||
901 !netgr_in(be, instr + 2,
902 (*be->getnamef)(args)))
903 continue;
904 }
905 } else if (instr[1] == '\0') {
906 /*
907 * Case 2:
908 * The entry is of the form "+" or "-". The former
909 * allows all entries from name services. The latter
910 * is illegal and ought to be ignored.
911 */
912 if (instr[0] == '-')
913 continue;
914 /* need to search for "+" entry */
915 (void) nss_search(be->db_rootp, be->db_initf,
916 op_num, args);
917 if (args->returnval == 0)
918 continue;
919 } else {
920 /*
921 * Case 3:
922 * The entry is of the form "+name" or "-name".
923 * If we're performing a lookup by name, we can simply
924 * extract the name from the search key
925 * (i.e. args->key.name). If not, then we must call
926 * nss_search() before extracting the name via the
927 * get_XXname() function. i.e. (*be->getnamef)(args).
928 */
929 if (is_nss_lookup_by_name(0, op_num) != 0) {
930 /* compare then search */
931 if (strcmp(instr + 1, args->key.name) != 0)
932 continue;
933 if (instr[0] == '+') {
934 /* need to search for "+" entry */
935 (void) nss_search(be->db_rootp,
936 be->db_initf, op_num, args);
937 if (args->returnval == 0)
938 continue;
939 }
940 } else {
941 /* search then compare */
942 (void) nss_search(be->db_rootp,
943 be->db_initf, op_num, args);
944 if (args->returnval == 0)
945 continue;
946 if (strcmp(instr + 1, (*be->getnamef)(args))
947 != 0)
948 continue;
949 }
950 }
951 if (instr[0] == '-') {
952 /* no need to search for "-" entry */
953 args->returnval = 0;
954 args->erange = 0;
955 res = NSS_NOTFOUND;
956 } else {
957 if (colon != 0)
958 *colon = ':'; /* restoration */
959 res = do_merge(be, args, instr, linelen);
960 }
961 break;
962 }
963
964 /*
965 * stayopen is set to 0 by default in order to close the opened
966 * file. Some applications may break if it is set to 1.
967 */
968 if (!args->stayopen) {
969 (void) _nss_compat_endent(be, 0);
970 }
971
972 if (be->return_string_data == 1) {
973 args->str2ent = be->str2ent_save;
974 }
975
976 return (res);
977 }
978
979 nss_status_t
_nss_compat_getent(be,a)980 _nss_compat_getent(be, a)
981 compat_backend_ptr_t be;
982 void *a;
983 {
984 nss_XbyY_args_t *args = (nss_XbyY_args_t *)a;
985 nss_status_t res;
986 char *colon = 0; /* <=== need comment re lifetime */
987
988 if (be->f == 0) {
989 if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
990 return (res);
991 }
992 }
993
994 if (be->buf == 0 &&
995 (be->buf = malloc(be->minbuf)) == 0) {
996 return (NSS_UNAVAIL); /* really panic, malloc failed */
997 }
998
999 /*
1000 * assume a NULL buf.result pointer is an indication
1001 * that the lookup result should be returned in /etc
1002 * file format
1003 */
1004 if (args->buf.result == NULL) {
1005 be->return_string_data = 1;
1006
1007 /*
1008 * the code executed later needs the result struct
1009 * as working area
1010 */
1011 args->buf.result = be->workarea;
1012 } else
1013 be->return_string_data = 0;
1014
1015 /*CONSTCOND*/
1016 while (1) {
1017 char *instr = be->buf;
1018 int linelen;
1019 char *name; /* === Need more distinctive label */
1020 const char *savename;
1021
1022 /*
1023 * In the code below...
1024 * break means "I found one, I think" (i.e. goto the
1025 * code after the end of the switch statement),
1026 * continue means "Next candidate"
1027 * (i.e. loop around to the switch statement),
1028 * return means "I'm quite sure" (either Yes or No).
1029 */
1030 switch (be->state) {
1031
1032 case GETENT_DONE:
1033 args->returnval = 0;
1034 args->erange = 0;
1035 return (NSS_NOTFOUND);
1036
1037 case GETENT_ATTRDB:
1038 args->key.name = NULL;
1039 res = _attrdb_compat_XY_all(be,
1040 args, 1, (compat_XY_check_func)NULL, 0);
1041 return (res);
1042
1043 case GETENT_FILE:
1044 linelen = read_line(be->f, instr, be->minbuf);
1045 if (linelen < 0) {
1046 /* End of file */
1047 be->state = GETENT_DONE;
1048 continue;
1049 }
1050 if ((colon = strchr(instr, ':')) != 0) {
1051 *colon = '\0';
1052 }
1053 if (instr[0] == '-') {
1054 if (instr[1] != '@') {
1055 (void) strset_add(&be->minuses,
1056 instr + 1);
1057 } else if (be->permit_netgroups) {
1058 netgr_set(be, instr + 2);
1059 while (netgr_next_u(be, &name)) {
1060 (void) strset_add(&be->minuses,
1061 name);
1062 }
1063 netgr_end(be);
1064 } /* Else (silently) ignore the entry */
1065 continue;
1066 } else if (instr[0] != '+') {
1067 int parsestat;
1068 /*
1069 * Normal entry, no +/- nonsense
1070 */
1071 if (colon != 0) {
1072 *colon = ':';
1073 }
1074 args->returnval = 0;
1075 parsestat = (*args->str2ent)(instr, linelen,
1076 args->buf.result,
1077 args->buf.buffer,
1078 args->buf.buflen);
1079 if (parsestat == NSS_STR_PARSE_SUCCESS) {
1080 int len;
1081
1082 if (be->return_string_data != 1) {
1083 args->returnval =
1084 args->buf.result;
1085 return (NSS_SUCCESS);
1086 }
1087
1088 /*
1089 * copy string data to
1090 * result buffer
1091 */
1092 args->buf.result = NULL;
1093 args->returnval =
1094 args->buf.buffer;
1095 if ((len = strlcpy(args->buf.buffer,
1096 instr, args->buf.buflen)) >=
1097 args->buf.buflen)
1098 parsestat =
1099 NSS_STR_PARSE_ERANGE;
1100 else {
1101 args->returnlen = len;
1102 return (NSS_SUCCESS);
1103 }
1104 }
1105 /* ==> ?? Treat ERANGE differently ?? */
1106 if (parsestat == NSS_STR_PARSE_ERANGE) {
1107 args->returnval = 0;
1108 args->erange = 1;
1109 return (NSS_NOTFOUND);
1110 }
1111 /* Skip the offending entry, get next */
1112 continue;
1113 } else if (instr[1] == '\0') {
1114 /* Plain "+" */
1115 nss_setent(be->db_rootp, be->db_initf,
1116 &be->db_context);
1117 be->state = GETENT_ALL;
1118 be->linelen = linelen;
1119
1120 continue;
1121 } else if (instr[1] == '@') {
1122 /* "+@netgroup" */
1123 netgr_set(be, instr + 2);
1124 be->state = GETENT_NETGROUP;
1125 be->linelen = linelen;
1126 continue;
1127 } else {
1128 /* "+name" */
1129 name = instr + 1;
1130 break;
1131 }
1132 /* NOTREACHED */
1133
1134 case GETENT_ALL:
1135 linelen = be->linelen;
1136 args->returnval = 0;
1137 if (be->return_string_data == 1) {
1138 be->str2ent_save = args->str2ent;
1139 args->str2ent = be->str2ent_alt;
1140 }
1141
1142 (void) nss_getent(be->db_rootp, be->db_initf,
1143 &be->db_context, args);
1144 if (args->returnval == 0) {
1145 /* ==> ?? Treat ERANGE differently ?? */
1146 nss_endent(be->db_rootp, be->db_initf,
1147 &be->db_context);
1148 be->state = GETENT_FILE;
1149 if (be->return_string_data == 1)
1150 args->str2ent = be->str2ent_save;
1151 continue;
1152 }
1153 if (strset_in(&be->minuses, (*be->getnamef)(args)))
1154 continue;
1155 name = 0; /* tell code below we've done the lookup */
1156 if (be->return_string_data == 1)
1157 args->str2ent = be->str2ent_save;
1158 break;
1159
1160 case GETENT_NETGROUP:
1161 linelen = be->linelen;
1162 if (!netgr_next_u(be, &name)) {
1163 netgr_end(be);
1164 be->state = GETENT_FILE;
1165 continue;
1166 }
1167 /* pass "name" variable to code below... */
1168 break;
1169 }
1170
1171 if (name != 0) {
1172 if (strset_in(&be->minuses, name)) {
1173 continue;
1174 }
1175 /*
1176 * Do a getXXXnam(name). If we were being pure,
1177 * we'd introduce yet another function-pointer
1178 * that the database-specific code had to supply
1179 * to us. Instead we'll be grotty and hard-code
1180 * the knowledge that
1181 * (a) The username is always passwd in key.name,
1182 * (b) NSS_DBOP_PASSWD_BYNAME ==
1183 * NSS_DBOP_SHADOW_BYNAME ==
1184 * NSS_DBOP_next_iter.
1185 */
1186 savename = args->key.name;
1187 args->key.name = name;
1188 args->returnval = 0;
1189 if (be->return_string_data == 1) {
1190 be->str2ent_save = args->str2ent;
1191 args->str2ent = be->str2ent_alt;
1192 }
1193
1194 (void) nss_search(be->db_rootp, be->db_initf,
1195 NSS_DBOP_next_iter, args);
1196
1197 if (be->return_string_data == 1)
1198 args->str2ent = be->str2ent_save;
1199 args->key.name = savename; /* In case anyone cares */
1200 }
1201 /*
1202 * Found one via "+", "+name" or "@netgroup".
1203 * Override some fields if the /etc file says to do so.
1204 */
1205 if (args->returnval == 0) {
1206 /* ==> ?? Should treat erange differently? */
1207 continue;
1208 }
1209 /* 'colon' was set umpteen iterations ago in GETENT_FILE */
1210 if (colon != 0) {
1211 *colon = ':';
1212 colon = 0;
1213 }
1214 return (do_merge(be, args, instr, linelen));
1215 }
1216 /*NOTREACHED*/
1217 }
1218
1219 /* We don't use this directly; we just copy the bits when we want to */
1220 /* initialize the variable (in the compat_backend struct) that we do use */
1221 static DEFINE_NSS_GETENT(context_initval);
1222
1223 nss_backend_t *
_nss_compat_constr(ops,n_ops,filename,min_bufsize,rootp,initf,netgroups,getname_func,merge_func)1224 _nss_compat_constr(ops, n_ops, filename, min_bufsize, rootp, initf, netgroups,
1225 getname_func, merge_func)
1226 compat_backend_op_t ops[];
1227 int n_ops;
1228 const char *filename;
1229 int min_bufsize;
1230 nss_db_root_t *rootp;
1231 nss_db_initf_t initf;
1232 int netgroups;
1233 compat_get_name getname_func;
1234 compat_merge_func merge_func;
1235 {
1236 compat_backend_ptr_t be;
1237
1238 if ((be = (compat_backend_ptr_t)malloc(sizeof (*be))) == 0) {
1239 return (0);
1240 }
1241 be->ops = ops;
1242 be->n_ops = n_ops;
1243 be->filename = filename;
1244 be->f = 0;
1245 be->minbuf = min_bufsize;
1246 be->buf = 0;
1247
1248 be->db_rootp = rootp;
1249 be->db_initf = initf;
1250 be->db_context = context_initval;
1251
1252 be->getnamef = getname_func;
1253 be->mergef = merge_func;
1254
1255 be->state = GETENT_FILE; /* i.e. do Automatic setent(); */
1256 if (strcmp(be->filename, USERATTR_FILENAME) == 0) {
1257 be->state = GETENT_ATTRDB;
1258 be->str2ent_alt = str2userattr_s;
1259 be->workarea = calloc(1, sizeof (userstr_t));
1260 } else if (strcmp(be->filename, AUDITUSER_FILENAME) == 0) {
1261 be->state = GETENT_ATTRDB;
1262 be->str2ent_alt = str2auuser_s;
1263 be->workarea = calloc(1, sizeof (au_user_str_t));
1264 } else if (strcmp(be->filename, PASSWD) == 0) {
1265 be->str2ent_alt = str2passwd;
1266 be->workarea = calloc(1, sizeof (struct passwd));
1267 } else if (strcmp(be->filename, SHADOW) == 0) {
1268 be->str2ent_alt = str2spwd;
1269 be->workarea = calloc(1, sizeof (struct spwd));
1270 } else { /* group */
1271 be->str2ent_alt = str2group;
1272 be->workarea = calloc(1, sizeof (struct group));
1273 }
1274 if (be->workarea == NULL)
1275 return (NULL);
1276
1277 be->minuses = 0;
1278
1279 be->permit_netgroups = netgroups;
1280 be->yp_domain = 0;
1281 be->getnetgrent_backend = 0;
1282 be->netgr_buffer = 0;
1283 be->return_string_data = 0;
1284
1285 return ((nss_backend_t *)be);
1286 }
1287