2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1992, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A *
2N/A * Common code and structures used by name-service-switch "compat" backends.
2N/A *
2N/A * Most of the code in the "compat" backend is a perverted form of code from
2N/A * the "files" backend; this file is no exception.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <ctype.h>
2N/A#include <bsm/libbsm.h>
2N/A#include <user_attr.h>
2N/A#include <pwd.h>
2N/A#include <shadow.h>
2N/A#include <grp.h>
2N/A#include <unistd.h> /* for GF_PATH */
2N/A#include <dlfcn.h>
2N/A#include <secdb.h>
2N/A#include "compat_common.h"
2N/A
2N/A/*
2N/A * This should be in a header.
2N/A */
2N/A
2N/Aextern int yp_get_default_domain(char **domain);
2N/A
2N/A/* from libc */
2N/Aextern int str2passwd(const char *instr, int lenstr, void *ent,
2N/A char *buffer, int buflen);
2N/Aextern int str2spwd(const char *instr, int lenstr, void *ent,
2N/A char *buffer, int buflen);
2N/Aextern int str2group(const char *instr, int lenstr, void *ent,
2N/A char *buffer, int buflen);
2N/A
2N/A/*
2N/A * str2auuser_s is very simple version of the str2auuser() that can be found in
2N/A * libc. They only copy the user name into the au_user_str_t structure
2N/A * (so check on user name can be performed).
2N/A */
2N/Astatic int
2N/Astr2auuser_s(
2N/A const char *instr,
2N/A int lenstr,
2N/A void *ent,
2N/A char *buffer,
2N/A int buflen)
2N/A{
2N/A char *last = NULL;
2N/A char *sep = KV_TOKEN_DELIMIT;
2N/A au_user_str_t *au_user = (au_user_str_t *)ent;
2N/A
2N/A if (lenstr >= buflen)
2N/A return (NSS_STR_PARSE_ERANGE);
2N/A (void) strncpy(buffer, instr, buflen);
2N/A au_user->au_name = _strtok_escape(buffer, sep, &last);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Routines to manage list of "-" users for get{pw, sp, gr}ent(). Current
2N/A * implementation is completely moronic; we use a linked list. But then
2N/A * that's what it's always done in 4.x...
2N/A */
2N/A
2N/Astruct setofstrings {
2N/A char *name;
2N/A struct setofstrings *next;
2N/A /*
2N/A * === Should get smart and malloc the string and pointer as one
2N/A * object rather than two.
2N/A */
2N/A};
2N/A
2N/Astatic void
2N/Astrset_free(ssp)
2N/A strset_t *ssp;
2N/A{
2N/A strset_t cur, nxt;
2N/A
2N/A for (cur = *ssp; cur != 0; cur = nxt) {
2N/A nxt = cur->next;
2N/A free(cur->name);
2N/A free(cur);
2N/A }
2N/A *ssp = 0;
2N/A}
2N/A
2N/Astatic boolean_t
2N/Astrset_add(ssp, nam)
2N/A strset_t *ssp;
2N/A const char *nam;
2N/A{
2N/A strset_t new;
2N/A
2N/A if (0 == (new = (strset_t)malloc(sizeof (*new)))) {
2N/A return (B_FALSE);
2N/A }
2N/A if (0 == (new->name = malloc(strlen(nam) + 1))) {
2N/A free(new);
2N/A return (B_FALSE);
2N/A }
2N/A (void) strcpy(new->name, nam);
2N/A new->next = *ssp;
2N/A *ssp = new;
2N/A return (B_TRUE);
2N/A}
2N/A
2N/Astatic boolean_t
2N/Astrset_in(ssp, nam)
2N/A const strset_t *ssp;
2N/A const char *nam;
2N/A{
2N/A strset_t cur;
2N/A
2N/A for (cur = *ssp; cur != 0; cur = cur->next) {
2N/A if (strcmp(cur->name, nam) == 0) {
2N/A return (B_TRUE);
2N/A }
2N/A }
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * Lookup and enumeration routines for +@group and -@group.
2N/A *
2N/A * This code knows a lot more about lib/libc/port/gen/getnetgrent.c than
2N/A * is really healthy. The set/get/end routines below duplicate code
2N/A * from that file, but keep the state information per-backend-instance
2N/A * instead of just per-process.
2N/A */
2N/A
2N/Aextern void _nss_initf_netgroup(nss_db_params_t *);
2N/A/*
2N/A * Should really share the db_root in getnetgrent.c in order to get the
2N/A * resource-management quotas right, but this will have to do.
2N/A */
2N/Astatic DEFINE_NSS_DB_ROOT(netgr_db_root);
2N/A
2N/Astatic boolean_t
2N/Anetgr_in(compat_backend_ptr_t be, const char *group, const char *user)
2N/A{
2N/A if (be->yp_domain == 0) {
2N/A if (yp_get_default_domain((char **)&be->yp_domain) != 0) {
2N/A return (B_FALSE);
2N/A }
2N/A }
2N/A return (innetgr(group, 0, user, be->yp_domain));
2N/A}
2N/A
2N/Astatic void
2N/Anetgr_set(be, netgroup)
2N/A compat_backend_ptr_t be;
2N/A const char *netgroup;
2N/A{
2N/A /*
2N/A * ===> Need comment to explain that this first "if" is optimizing
2N/A * for the same-netgroup-as-last-time case
2N/A */
2N/A if (be->getnetgrent_backend != 0 &&
2N/A NSS_INVOKE_DBOP(be->getnetgrent_backend,
2N/A NSS_DBOP_SETENT,
2N/A (void *) netgroup) != NSS_SUCCESS) {
2N/A NSS_INVOKE_DBOP(be->getnetgrent_backend, NSS_DBOP_DESTRUCTOR,
2N/A 0);
2N/A be->getnetgrent_backend = 0;
2N/A }
2N/A if (be->getnetgrent_backend == 0) {
2N/A struct nss_setnetgrent_args args;
2N/A
2N/A args.netgroup = netgroup;
2N/A args.iterator = 0;
2N/A (void) nss_search(&netgr_db_root, _nss_initf_netgroup,
2N/A NSS_DBOP_NETGROUP_SET, &args);
2N/A be->getnetgrent_backend = args.iterator;
2N/A }
2N/A}
2N/A
2N/Astatic boolean_t
2N/Anetgr_next_u(be, up)
2N/A compat_backend_ptr_t be;
2N/A char **up;
2N/A{
2N/A if (be->netgr_buffer == 0 &&
2N/A (be->netgr_buffer = malloc(NSS_BUFLEN_NETGROUP)) == 0) {
2N/A /* Out of memory */
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A do {
2N/A struct nss_getnetgrent_args args;
2N/A
2N/A args.buffer = be->netgr_buffer;
2N/A args.buflen = NSS_BUFLEN_NETGROUP;
2N/A args.status = NSS_NETGR_NO;
2N/A
2N/A if (be->getnetgrent_backend != 0) {
2N/A NSS_INVOKE_DBOP(be->getnetgrent_backend,
2N/A NSS_DBOP_GETENT, &args);
2N/A }
2N/A
2N/A if (args.status == NSS_NETGR_FOUND) {
2N/A *up = args.retp[NSS_NETGR_USER];
2N/A } else {
2N/A return (B_FALSE);
2N/A }
2N/A } while (*up == 0);
2N/A return (B_TRUE);
2N/A}
2N/A
2N/Astatic void
2N/Anetgr_end(be)
2N/A compat_backend_ptr_t be;
2N/A{
2N/A if (be->getnetgrent_backend != 0) {
2N/A NSS_INVOKE_DBOP(be->getnetgrent_backend,
2N/A NSS_DBOP_DESTRUCTOR, 0);
2N/A be->getnetgrent_backend = 0;
2N/A }
2N/A if (be->netgr_buffer != 0) {
2N/A free(be->netgr_buffer);
2N/A be->netgr_buffer = 0;
2N/A }
2N/A}
2N/A
2N/A
2N/A#define MAXFIELDS 9 /* Sufficient for passwd (7), shadow (9), group (4) */
2N/A
2N/Astatic nss_status_t
2N/Ado_merge(be, args, instr, linelen)
2N/A compat_backend_ptr_t be;
2N/A nss_XbyY_args_t *args;
2N/A const char *instr;
2N/A int linelen;
2N/A{
2N/A char *fields[MAXFIELDS];
2N/A int i;
2N/A int overrides;
2N/A const char *p;
2N/A const char *end = instr + linelen;
2N/A nss_status_t res = NSS_NOTFOUND;
2N/A
2N/A /*
2N/A * Potential optimization: only perform the field-splitting nonsense
2N/A * once per input line (at present, "+" and "+@netgroup" entries
2N/A * will cause us to do this multiple times in getent() requests).
2N/A */
2N/A
2N/A for (i = 0; i < MAXFIELDS; i++) {
2N/A fields[i] = 0;
2N/A }
2N/A for (p = instr, overrides = 0, i = 0; /* no test */; i++) {
2N/A const char *q = memchr(p, ':', end - p);
2N/A const char *r = (q == 0) ? end : q;
2N/A ssize_t len = r - p;
2N/A
2N/A if (len > 0) {
2N/A char *s = malloc(len + 1);
2N/A if (s == 0) {
2N/A overrides = -1; /* Indicates "you lose" */
2N/A break;
2N/A }
2N/A (void) memcpy(s, p, len);
2N/A s[len] = '\0';
2N/A fields[i] = s;
2N/A overrides++;
2N/A }
2N/A if (q == 0) {
2N/A /* End of line */
2N/A break;
2N/A } else {
2N/A /* Skip the colon at (*q) */
2N/A p = q + 1;
2N/A }
2N/A }
2N/A if (overrides == 1) {
2N/A /*
2N/A * return result here if /etc file format is requested
2N/A */
2N/A if (be->return_string_data != 1) {
2N/A /* No real overrides, return (*args) intact */
2N/A res = NSS_SUCCESS;
2N/A } else {
2N/A free(fields[0]);
2N/A fields[0] = NULL;
2N/A }
2N/A }
2N/A
2N/A if (overrides > 1 || be->return_string_data == 1) {
2N/A /*
2N/A * The zero'th field is always nonempty (+/-...), but at least
2N/A * one other field was also nonempty, i.e. wants to override
2N/A */
2N/A switch ((*be->mergef)(be, args, (const char **)fields)) {
2N/A case NSS_STR_PARSE_SUCCESS:
2N/A if (be->return_string_data != 1)
2N/A args->returnval = args->buf.result;
2N/A else
2N/A args->returnval = args->buf.buffer;
2N/A args->erange = 0;
2N/A res = NSS_SUCCESS;
2N/A break;
2N/A case NSS_STR_PARSE_ERANGE:
2N/A args->returnval = 0;
2N/A args->erange = 1;
2N/A res = NSS_NOTFOUND;
2N/A break;
2N/A case NSS_STR_PARSE_PARSE:
2N/A args->returnval = 0;
2N/A args->erange = 0;
2N/A/* ===> Very likely the wrong thing to do... */
2N/A res = NSS_NOTFOUND;
2N/A break;
2N/A }
2N/A } else if (res != NSS_SUCCESS) {
2N/A args->returnval = 0;
2N/A args->erange = 0;
2N/A res = NSS_UNAVAIL; /* ==> Right? */
2N/A }
2N/A
2N/A for (i = 0; i < MAXFIELDS; i++) {
2N/A if (fields[i] != 0) {
2N/A free(fields[i]);
2N/A }
2N/A }
2N/A
2N/A return (res);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Anss_status_t
2N/A_nss_compat_setent(be, dummy)
2N/A compat_backend_ptr_t be;
2N/A void *dummy;
2N/A{
2N/A if (be->f == 0) {
2N/A if (be->filename == 0) {
2N/A /* Backend isn't initialized properly? */
2N/A return (NSS_UNAVAIL);
2N/A }
2N/A if ((be->f = fopen(be->filename, "rF")) == 0) {
2N/A return (NSS_UNAVAIL);
2N/A }
2N/A } else {
2N/A rewind(be->f);
2N/A }
2N/A strset_free(&be->minuses);
2N/A /* ===> ??? nss_endent(be->db_rootp, be->db_initf, &be->db_context); */
2N/A
2N/A if (strcmp(be->filename, AUDITUSER_FILENAME) == 0)
2N/A be->state = GETENT_ATTRDB;
2N/A else
2N/A be->state = GETENT_FILE;
2N/A
2N/A be->return_string_data = 0;
2N/A
2N/A /* ===> ?? netgroup stuff? */
2N/A return (NSS_SUCCESS);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Anss_status_t
2N/A_nss_compat_endent(be, dummy)
2N/A compat_backend_ptr_t be;
2N/A void *dummy;
2N/A{
2N/A if (be->f != 0) {
2N/A (void) fclose(be->f);
2N/A be->f = 0;
2N/A }
2N/A if (be->buf != 0) {
2N/A free(be->buf);
2N/A be->buf = 0;
2N/A }
2N/A nss_endent(be->db_rootp, be->db_initf, &be->db_context);
2N/A
2N/A be->state = GETENT_FILE; /* Probably superfluous but comforting */
2N/A strset_free(&be->minuses);
2N/A netgr_end(be);
2N/A
2N/A /*
2N/A * Question: from the point of view of resource-freeing vs. time to
2N/A * start up again, how much should we do in endent() and how much
2N/A * in the destructor?
2N/A */
2N/A return (NSS_SUCCESS);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Anss_status_t
2N/A_nss_compat_destr(be, dummy)
2N/A compat_backend_ptr_t be;
2N/A void *dummy;
2N/A{
2N/A if (be != 0) {
2N/A if (be->f != 0) {
2N/A (void) _nss_compat_endent(be, 0);
2N/A }
2N/A nss_delete(be->db_rootp);
2N/A nss_delete(&netgr_db_root);
2N/A free(be->workarea);
2N/A free(be);
2N/A }
2N/A return (NSS_SUCCESS); /* In case anyone is dumb enough to check */
2N/A}
2N/A
2N/Astatic int
2N/Aread_line(f, buffer, buflen)
2N/A FILE *f;
2N/A char *buffer;
2N/A int buflen;
2N/A{
2N/A /*CONSTCOND*/
2N/A while (1) {
2N/A int linelen;
2N/A
2N/A if (fgets(buffer, buflen, f) == 0) {
2N/A /* End of file */
2N/A return (-1);
2N/A }
2N/A linelen = strlen(buffer);
2N/A /* linelen >= 1 (since fgets didn't return 0) */
2N/A
2N/A if (buffer[linelen - 1] == '\n') {
2N/A /*
2N/A * ===> The code below that calls read_line() doesn't
2N/A * play by the rules; it assumes in places that
2N/A * the line is null-terminated. For now we'll
2N/A * humour it.
2N/A */
2N/A buffer[--linelen] = '\0';
2N/A return (linelen);
2N/A }
2N/A if (feof(f)) {
2N/A /* Line is last line in file, and has no newline */
2N/A return (linelen);
2N/A }
2N/A /* Line too long for buffer; toss it and loop for next line */
2N/A /* ===== should syslog() in cases where previous code did */
2N/A while (fgets(buffer, buflen, f) != 0 &&
2N/A buffer[strlen(buffer) - 1] != '\n') {
2N/A ;
2N/A }
2N/A }
2N/A /*NOTREACHED*/
2N/A}
2N/A
2N/Astatic int
2N/Ais_nss_lookup_by_name(int attrdb, nss_dbop_t op)
2N/A{
2N/A int result = 0;
2N/A
2N/A if ((attrdb != 0) &&
2N/A (op == NSS_DBOP_AUDITUSER_BYNAME)) {
2N/A result = 1;
2N/A } else if ((attrdb == 0) &&
2N/A ((op == NSS_DBOP_GROUP_BYNAME) ||
2N/A (op == NSS_DBOP_PASSWD_BYNAME) ||
2N/A (op == NSS_DBOP_SHADOW_BYNAME))) {
2N/A result = 1;
2N/A }
2N/A
2N/A return (result);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Anss_status_t
2N/A_attrdb_compat_XY_all(be, argp, netdb, check, op_num)
2N/A compat_backend_ptr_t be;
2N/A nss_XbyY_args_t *argp;
2N/A int netdb;
2N/A compat_XY_check_func check;
2N/A nss_dbop_t op_num;
2N/A{
2N/A int parsestat;
2N/A int (*func)();
2N/A const char *filter = argp->key.name;
2N/A nss_status_t res;
2N/A
2N/A#ifdef DEBUG
2N/A (void) fprintf(stdout, "\n[compat_common.c: _attrdb_compat_XY_all]\n");
2N/A#endif /* DEBUG */
2N/A
2N/A if (be->buf == 0 &&
2N/A (be->buf = malloc(be->minbuf)) == 0) {
2N/A return (NSS_UNAVAIL);
2N/A }
2N/A if (check != NULL)
2N/A if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS)
2N/A return (res);
2N/A
2N/A res = NSS_NOTFOUND;
2N/A
2N/A /*
2N/A * assume a NULL buf.result pointer is an indication
2N/A * that the lookup result should be returned in /etc
2N/A * file format (if called from _nss_compat_getent(),
2N/A * be->return_string_data and argp->buf.result
2N/A * would be set already if argp->buf.result is NULL)
2N/A */
2N/A if (check != NULL) {
2N/A if (argp->buf.result == NULL) {
2N/A be->return_string_data = 1;
2N/A
2N/A /*
2N/A * the code executed later needs the result struct
2N/A * as working area
2N/A */
2N/A argp->buf.result = be->workarea;
2N/A } else
2N/A be->return_string_data = 0;
2N/A }
2N/A
2N/A /*
2N/A * use an alternate str2ent function if necessary
2N/A */
2N/A if (be->return_string_data == 1)
2N/A func = be->str2ent_alt;
2N/A else
2N/A func = argp->str2ent;
2N/A
2N/A /*CONSTCOND*/
2N/A while (1) {
2N/A int linelen;
2N/A char *instr = be->buf;
2N/A
2N/A if ((linelen = read_line(be->f, instr, be->minbuf)) < 0) {
2N/A /* End of file */
2N/A argp->returnval = 0;
2N/A argp->erange = 0;
2N/A break;
2N/A }
2N/A if (filter != 0 && strstr(instr, filter) == 0) {
2N/A /*
2N/A * Optimization: if the entry doesn't contain the
2N/A * filter string then it can't be the entry we want,
2N/A * so don't bother looking more closely at it.
2N/A */
2N/A continue;
2N/A }
2N/A if (netdb) {
2N/A char *first;
2N/A char *last;
2N/A
2N/A if ((last = strchr(instr, '#')) == 0) {
2N/A last = instr + linelen;
2N/A }
2N/A *last-- = '\0'; /* Nuke '\n' or #comment */
2N/A
2N/A /*
2N/A * Skip leading whitespace. Normally there isn't
2N/A * any, so it's not worth calling strspn().
2N/A */
2N/A for (first = instr; isspace(*first); first++) {
2N/A ;
2N/A }
2N/A if (*first == '\0') {
2N/A continue;
2N/A }
2N/A /*
2N/A * Found something non-blank on the line. Skip back
2N/A * over any trailing whitespace; since we know
2N/A * there's non-whitespace earlier in the line,
2N/A * checking for termination is easy.
2N/A */
2N/A while (isspace(*last)) {
2N/A --last;
2N/A }
2N/A linelen = last - first + 1;
2N/A if (first != instr) {
2N/A instr = first;
2N/A }
2N/A }
2N/A argp->returnval = 0;
2N/A parsestat = (*func)(instr, linelen, argp->buf.result,
2N/A argp->buf.buffer, argp->buf.buflen);
2N/A if (parsestat == NSS_STR_PARSE_SUCCESS) {
2N/A argp->returnval = argp->buf.result;
2N/A if (check == 0 || (*check)(argp)) {
2N/A int len;
2N/A
2N/A if (be->return_string_data != 1) {
2N/A res = NSS_SUCCESS;
2N/A break;
2N/A }
2N/A
2N/A /* copy string data to result buffer */
2N/A argp->buf.result = NULL;
2N/A argp->returnval = argp->buf.buffer;
2N/A if ((len = strlcpy(argp->buf.buffer, instr,
2N/A argp->buf.buflen)) >=
2N/A argp->buf.buflen) {
2N/A argp->returnval = NULL;
2N/A res = NSS_NOTFOUND;
2N/A argp->erange = 1;
2N/A break;
2N/A }
2N/A
2N/A argp->returnlen = len;
2N/A res = NSS_SUCCESS;
2N/A break;
2N/A }
2N/A } else if (parsestat == NSS_STR_PARSE_ERANGE) {
2N/A res = NSS_NOTFOUND;
2N/A argp->erange = 1;
2N/A break;
2N/A }
2N/A }
2N/A /*
2N/A * stayopen is set to 0 by default in order to close the opened
2N/A * file. Some applications may break if it is set to 1.
2N/A */
2N/A if (check != 0 && !argp->stayopen) {
2N/A (void) _nss_compat_endent(be, 0);
2N/A }
2N/A
2N/A if (res != NSS_SUCCESS) {
2N/A /*
2N/A * tell the nss_search() and nss_getent() below
2N/A * if the result should be returned in the /etc
2N/A * file format
2N/A */
2N/A if (be->return_string_data == 1)
2N/A argp->buf.result = NULL;
2N/A
2N/A if (op_num == NSS_DBOP_AUDITUSER_BYNAME) {
2N/A res = nss_search(be->db_rootp,
2N/A be->db_initf,
2N/A op_num,
2N/A argp);
2N/A } else {
2N/A res = nss_getent(be->db_rootp,
2N/A be->db_initf, &be->db_context, argp);
2N/A }
2N/A if (res != NSS_SUCCESS) {
2N/A argp->returnval = 0;
2N/A argp->erange = 0;
2N/A }
2N/A }
2N/A
2N/A return (res);
2N/A}
2N/A
2N/Astatic int
2N/Avalidate_ids(compat_backend_ptr_t be, nss_XbyY_args_t *argp,
2N/A char *line, int *linelenp, int buflen, int extra_chars)
2N/A{
2N/A if (be->return_string_data != 1) {
2N/A struct passwd *p;
2N/A struct group *g;
2N/A /*
2N/A * The data is already marshalled into
2N/A * struct passwd or group.
2N/A */
2N/A if (strcmp(be->filename, PASSWD) == 0) {
2N/A p = (struct passwd *)argp->returnval;
2N/A if (p->pw_uid > MAXUID)
2N/A p->pw_uid = UID_NOBODY;
2N/A if (p->pw_gid > MAXUID)
2N/A p->pw_gid = GID_NOBODY;
2N/A } else if (strcmp(be->filename, GF_PATH) == 0) {
2N/A g = (struct group *)argp->returnval;
2N/A if (g->gr_gid > MAXUID)
2N/A g->gr_gid = GID_NOBODY;
2N/A }
2N/A return (NSS_STR_PARSE_SUCCESS);
2N/A }
2N/A
2N/A /*
2N/A * The data needs to be returned in string format therefore
2N/A * validate the return string.
2N/A */
2N/A if (strcmp(be->filename, PASSWD) == 0)
2N/A return (validate_passwd_ids(line, linelenp, buflen,
2N/A extra_chars));
2N/A else if (strcmp(be->filename, GF_PATH) == 0)
2N/A return (validate_group_ids(line, linelenp, buflen,
2N/A extra_chars));
2N/A return (NSS_STR_PARSE_SUCCESS);
2N/A}
2N/A
2N/Anss_status_t
2N/A_nss_compat_XY_all(be, args, check, op_num)
2N/A compat_backend_ptr_t be;
2N/A nss_XbyY_args_t *args;
2N/A compat_XY_check_func check;
2N/A nss_dbop_t op_num;
2N/A{
2N/A nss_status_t res;
2N/A int parsestat;
2N/A
2N/A
2N/A if (be->buf == 0 &&
2N/A (be->buf = malloc(be->minbuf)) == 0) {
2N/A return (NSS_UNAVAIL); /* really panic, malloc failed */
2N/A }
2N/A
2N/A if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
2N/A return (res);
2N/A }
2N/A
2N/A res = NSS_NOTFOUND;
2N/A
2N/A /*
2N/A * assume a NULL buf.result pointer is an indication
2N/A * that the lookup result should be returned in /etc
2N/A * file format
2N/A */
2N/A if (args->buf.result == NULL) {
2N/A
2N/A be->return_string_data = 1;
2N/A
2N/A /*
2N/A * the code executed later needs the result struct
2N/A * as working area
2N/A */
2N/A args->buf.result = be->workarea;
2N/A
2N/A be->str2ent_save = args->str2ent;
2N/A args->str2ent = be->str2ent_alt;
2N/A } else
2N/A be->return_string_data = 0;
2N/A
2N/A /*CONSTCOND*/
2N/A while (1) {
2N/A int linelen;
2N/A char *instr = be->buf;
2N/A char *colon;
2N/A
2N/A linelen = read_line(be->f, instr, be->minbuf);
2N/A if (linelen < 0) {
2N/A /* End of file */
2N/A args->returnval = 0;
2N/A args->erange = 0;
2N/A break;
2N/A }
2N/A
2N/A args->returnval = 0; /* reset for both types of entries */
2N/A
2N/A if (instr[0] != '+' && instr[0] != '-') {
2N/A /* Simple, wholesome, God-fearing entry */
2N/A parsestat = (*args->str2ent)(instr, linelen,
2N/A args->buf.result,
2N/A args->buf.buffer,
2N/A args->buf.buflen);
2N/A if (parsestat == NSS_STR_PARSE_SUCCESS) {
2N/A args->returnval = args->buf.result;
2N/A if ((*check)(args) != 0) {
2N/A int len;
2N/A
2N/A parsestat = validate_ids(be, args,
2N/A instr, &linelen, be->minbuf, 1);
2N/A if (parsestat ==
2N/A NSS_STR_PARSE_ERANGE) {
2N/A args->erange = 1;
2N/A res = NSS_NOTFOUND;
2N/A break;
2N/A } else if (parsestat !=
2N/A NSS_STR_PARSE_SUCCESS) {
2N/A continue;
2N/A }
2N/A
2N/A if (be->return_string_data != 1) {
2N/A res = NSS_SUCCESS;
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * copy string data to
2N/A * result buffer
2N/A */
2N/A args->buf.result = NULL;
2N/A args->str2ent = be->str2ent_save;
2N/A if ((len = strlcpy(args->buf.buffer,
2N/A instr, args->buf.buflen)) >=
2N/A args->buf.buflen)
2N/A parsestat =
2N/A NSS_STR_PARSE_ERANGE;
2N/A else {
2N/A args->returnval =
2N/A args->buf.buffer;
2N/A args->returnlen = len;
2N/A res = NSS_SUCCESS;
2N/A break;
2N/A }
2N/A } else
2N/A continue;
2N/A }
2N/A
2N/A/* ===> Check the Dani logic here... */
2N/A
2N/A if (parsestat == NSS_STR_PARSE_ERANGE) {
2N/A args->erange = 1;
2N/A res = NSS_NOTFOUND;
2N/A break;
2N/A /* should we just skip this one long line ? */
2N/A } /* else if (parsestat == NSS_STR_PARSE_PARSE) */
2N/A /* don't care ! */
2N/A
2N/A/* ==> ?? */ continue;
2N/A }
2N/A
2N/A
2N/A /*
2N/A * Process "+", "+name", "+@netgroup", "-name" or "-@netgroup"
2N/A *
2N/A * This code is optimized for lookups by name.
2N/A *
2N/A * For lookups by identifier search key cannot be matched with
2N/A * the name of the "+" or "-" entry. So nss_search() is to be
2N/A * called before extracting the name i.e. via (*be->getnamef)().
2N/A *
2N/A * But for lookups by name, search key is compared with the name
2N/A * of the "+" or "-" entry to acquire a match and thus
2N/A * unnesessary calls to nss_search() is eliminated. Also for
2N/A * matching "-" entries, calls to nss_search() is eliminated.
2N/A */
2N/A
2N/A if ((colon = strchr(instr, ':')) != 0) {
2N/A *colon = '\0'; /* terminate field to extract name */
2N/A }
2N/A
2N/A if (instr[1] == '@') {
2N/A /*
2N/A * Case 1:
2N/A * The entry is of the form "+@netgroup" or
2N/A * "-@netgroup". If we're performing a lookup by name,
2N/A * we can simply extract the name from the search key
2N/A * (i.e. args->key.name). If not, then we must call
2N/A * nss_search() before extracting the name via the
2N/A * get_XXname() function. i.e. (*be->getnamef)(args).
2N/A */
2N/A if (is_nss_lookup_by_name(0, op_num) != 0) {
2N/A /* compare then search */
2N/A if (!be->permit_netgroups ||
2N/A !netgr_in(be, instr + 2, args->key.name))
2N/A continue;
2N/A if (instr[0] == '+') {
2N/A /* need to search for "+" entry */
2N/A (void) nss_search(be->db_rootp,
2N/A be->db_initf, op_num, args);
2N/A if (args->returnval == 0)
2N/A continue;
2N/A }
2N/A } else {
2N/A /* search then compare */
2N/A (void) nss_search(be->db_rootp,
2N/A be->db_initf, op_num, args);
2N/A if (args->returnval == 0)
2N/A continue;
2N/A if (!be->permit_netgroups ||
2N/A !netgr_in(be, instr + 2,
2N/A (*be->getnamef)(args)))
2N/A continue;
2N/A }
2N/A } else if (instr[1] == '\0') {
2N/A /*
2N/A * Case 2:
2N/A * The entry is of the form "+" or "-". The former
2N/A * allows all entries from name services. The latter
2N/A * is illegal and ought to be ignored.
2N/A */
2N/A if (instr[0] == '-')
2N/A continue;
2N/A /* need to search for "+" entry */
2N/A (void) nss_search(be->db_rootp, be->db_initf,
2N/A op_num, args);
2N/A if (args->returnval == 0)
2N/A continue;
2N/A } else {
2N/A /*
2N/A * Case 3:
2N/A * The entry is of the form "+name" or "-name".
2N/A * If we're performing a lookup by name, we can simply
2N/A * extract the name from the search key
2N/A * (i.e. args->key.name). If not, then we must call
2N/A * nss_search() before extracting the name via the
2N/A * get_XXname() function. i.e. (*be->getnamef)(args).
2N/A */
2N/A if (is_nss_lookup_by_name(0, op_num) != 0) {
2N/A /* compare then search */
2N/A if (strcmp(instr + 1, args->key.name) != 0)
2N/A continue;
2N/A if (instr[0] == '+') {
2N/A /* need to search for "+" entry */
2N/A (void) nss_search(be->db_rootp,
2N/A be->db_initf, op_num, args);
2N/A if (args->returnval == 0)
2N/A continue;
2N/A }
2N/A } else {
2N/A /* search then compare */
2N/A (void) nss_search(be->db_rootp,
2N/A be->db_initf, op_num, args);
2N/A if (args->returnval == 0)
2N/A continue;
2N/A if (strcmp(instr + 1, (*be->getnamef)(args))
2N/A != 0)
2N/A continue;
2N/A }
2N/A }
2N/A if (instr[0] == '-') {
2N/A /* no need to search for "-" entry */
2N/A args->returnval = 0;
2N/A args->erange = 0;
2N/A res = NSS_NOTFOUND;
2N/A } else {
2N/A if (colon != 0)
2N/A *colon = ':'; /* restoration */
2N/A res = do_merge(be, args, instr, linelen);
2N/A }
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * stayopen is set to 0 by default in order to close the opened
2N/A * file. Some applications may break if it is set to 1.
2N/A */
2N/A if (!args->stayopen) {
2N/A (void) _nss_compat_endent(be, 0);
2N/A }
2N/A
2N/A if (be->return_string_data == 1) {
2N/A args->str2ent = be->str2ent_save;
2N/A }
2N/A
2N/A return (res);
2N/A}
2N/A
2N/Anss_status_t
2N/A_nss_compat_getent(be, a)
2N/A compat_backend_ptr_t be;
2N/A void *a;
2N/A{
2N/A nss_XbyY_args_t *args = (nss_XbyY_args_t *)a;
2N/A nss_status_t res;
2N/A char *colon = 0; /* <=== need comment re lifetime */
2N/A
2N/A if (be->f == 0) {
2N/A if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
2N/A return (res);
2N/A }
2N/A }
2N/A
2N/A if (be->buf == 0 &&
2N/A (be->buf = malloc(be->minbuf)) == 0) {
2N/A return (NSS_UNAVAIL); /* really panic, malloc failed */
2N/A }
2N/A
2N/A /*
2N/A * assume a NULL buf.result pointer is an indication
2N/A * that the lookup result should be returned in /etc
2N/A * file format
2N/A */
2N/A if (args->buf.result == NULL) {
2N/A be->return_string_data = 1;
2N/A
2N/A /*
2N/A * the code executed later needs the result struct
2N/A * as working area
2N/A */
2N/A args->buf.result = be->workarea;
2N/A } else
2N/A be->return_string_data = 0;
2N/A
2N/A /*CONSTCOND*/
2N/A while (1) {
2N/A char *instr = be->buf;
2N/A int linelen;
2N/A char *name; /* === Need more distinctive label */
2N/A const char *savename;
2N/A
2N/A /*
2N/A * In the code below...
2N/A * break means "I found one, I think" (i.e. goto the
2N/A * code after the end of the switch statement),
2N/A * continue means "Next candidate"
2N/A * (i.e. loop around to the switch statement),
2N/A * return means "I'm quite sure" (either Yes or No).
2N/A */
2N/A switch (be->state) {
2N/A
2N/A case GETENT_DONE:
2N/A args->returnval = 0;
2N/A args->erange = 0;
2N/A return (NSS_NOTFOUND);
2N/A
2N/A case GETENT_ATTRDB:
2N/A args->key.name = NULL;
2N/A res = _attrdb_compat_XY_all(be,
2N/A args, 1, (compat_XY_check_func)NULL, 0);
2N/A return (res);
2N/A
2N/A case GETENT_FILE:
2N/A linelen = read_line(be->f, instr, be->minbuf);
2N/A if (linelen < 0) {
2N/A /* End of file */
2N/A be->state = GETENT_DONE;
2N/A continue;
2N/A }
2N/A if ((colon = strchr(instr, ':')) != 0) {
2N/A *colon = '\0';
2N/A }
2N/A if (instr[0] == '-') {
2N/A if (instr[1] != '@') {
2N/A (void) strset_add(&be->minuses,
2N/A instr + 1);
2N/A } else if (be->permit_netgroups) {
2N/A netgr_set(be, instr + 2);
2N/A while (netgr_next_u(be, &name)) {
2N/A (void) strset_add(&be->minuses,
2N/A name);
2N/A }
2N/A netgr_end(be);
2N/A } /* Else (silently) ignore the entry */
2N/A continue;
2N/A } else if (instr[0] != '+') {
2N/A int parsestat;
2N/A /*
2N/A * Normal entry, no +/- nonsense
2N/A */
2N/A if (colon != 0) {
2N/A *colon = ':';
2N/A }
2N/A args->returnval = 0;
2N/A parsestat = (*args->str2ent)(instr, linelen,
2N/A args->buf.result,
2N/A args->buf.buffer,
2N/A args->buf.buflen);
2N/A if (parsestat == NSS_STR_PARSE_SUCCESS) {
2N/A int len;
2N/A
2N/A if (be->return_string_data != 1) {
2N/A args->returnval =
2N/A args->buf.result;
2N/A return (NSS_SUCCESS);
2N/A }
2N/A
2N/A /*
2N/A * copy string data to
2N/A * result buffer
2N/A */
2N/A args->buf.result = NULL;
2N/A args->returnval =
2N/A args->buf.buffer;
2N/A if ((len = strlcpy(args->buf.buffer,
2N/A instr, args->buf.buflen)) >=
2N/A args->buf.buflen)
2N/A parsestat =
2N/A NSS_STR_PARSE_ERANGE;
2N/A else {
2N/A args->returnlen = len;
2N/A return (NSS_SUCCESS);
2N/A }
2N/A }
2N/A /* ==> ?? Treat ERANGE differently ?? */
2N/A if (parsestat == NSS_STR_PARSE_ERANGE) {
2N/A args->returnval = 0;
2N/A args->erange = 1;
2N/A return (NSS_NOTFOUND);
2N/A }
2N/A /* Skip the offending entry, get next */
2N/A continue;
2N/A } else if (instr[1] == '\0') {
2N/A /* Plain "+" */
2N/A nss_setent(be->db_rootp, be->db_initf,
2N/A &be->db_context);
2N/A be->state = GETENT_ALL;
2N/A be->linelen = linelen;
2N/A
2N/A continue;
2N/A } else if (instr[1] == '@') {
2N/A /* "+@netgroup" */
2N/A netgr_set(be, instr + 2);
2N/A be->state = GETENT_NETGROUP;
2N/A be->linelen = linelen;
2N/A continue;
2N/A } else {
2N/A /* "+name" */
2N/A name = instr + 1;
2N/A break;
2N/A }
2N/A /* NOTREACHED */
2N/A
2N/A case GETENT_ALL:
2N/A linelen = be->linelen;
2N/A args->returnval = 0;
2N/A if (be->return_string_data == 1) {
2N/A be->str2ent_save = args->str2ent;
2N/A args->str2ent = be->str2ent_alt;
2N/A }
2N/A
2N/A (void) nss_getent(be->db_rootp, be->db_initf,
2N/A &be->db_context, args);
2N/A if (args->returnval == 0) {
2N/A /* ==> ?? Treat ERANGE differently ?? */
2N/A nss_endent(be->db_rootp, be->db_initf,
2N/A &be->db_context);
2N/A be->state = GETENT_FILE;
2N/A if (be->return_string_data == 1)
2N/A args->str2ent = be->str2ent_save;
2N/A continue;
2N/A }
2N/A if (strset_in(&be->minuses, (*be->getnamef)(args)))
2N/A continue;
2N/A name = 0; /* tell code below we've done the lookup */
2N/A if (be->return_string_data == 1)
2N/A args->str2ent = be->str2ent_save;
2N/A break;
2N/A
2N/A case GETENT_NETGROUP:
2N/A linelen = be->linelen;
2N/A if (!netgr_next_u(be, &name)) {
2N/A netgr_end(be);
2N/A be->state = GETENT_FILE;
2N/A continue;
2N/A }
2N/A /* pass "name" variable to code below... */
2N/A break;
2N/A }
2N/A
2N/A if (name != 0) {
2N/A if (strset_in(&be->minuses, name)) {
2N/A continue;
2N/A }
2N/A /*
2N/A * Do a getXXXnam(name). If we were being pure,
2N/A * we'd introduce yet another function-pointer
2N/A * that the database-specific code had to supply
2N/A * to us. Instead we'll be grotty and hard-code
2N/A * the knowledge that
2N/A * (a) The username is always passwd in key.name,
2N/A * (b) NSS_DBOP_PASSWD_BYNAME ==
2N/A * NSS_DBOP_SHADOW_BYNAME ==
2N/A * NSS_DBOP_next_iter.
2N/A */
2N/A savename = args->key.name;
2N/A args->key.name = name;
2N/A args->returnval = 0;
2N/A if (be->return_string_data == 1) {
2N/A be->str2ent_save = args->str2ent;
2N/A args->str2ent = be->str2ent_alt;
2N/A }
2N/A
2N/A (void) nss_search(be->db_rootp, be->db_initf,
2N/A NSS_DBOP_next_iter, args);
2N/A
2N/A if (be->return_string_data == 1)
2N/A args->str2ent = be->str2ent_save;
2N/A args->key.name = savename; /* In case anyone cares */
2N/A }
2N/A /*
2N/A * Found one via "+", "+name" or "@netgroup".
2N/A * Override some fields if the /etc file says to do so.
2N/A */
2N/A if (args->returnval == 0) {
2N/A /* ==> ?? Should treat erange differently? */
2N/A continue;
2N/A }
2N/A /* 'colon' was set umpteen iterations ago in GETENT_FILE */
2N/A if (colon != 0) {
2N/A *colon = ':';
2N/A colon = 0;
2N/A }
2N/A return (do_merge(be, args, instr, linelen));
2N/A }
2N/A /*NOTREACHED*/
2N/A}
2N/A
2N/A/* We don't use this directly; we just copy the bits when we want to */
2N/A/* initialize the variable (in the compat_backend struct) that we do use */
2N/Astatic DEFINE_NSS_GETENT(context_initval);
2N/A
2N/Anss_backend_t *
2N/A_nss_compat_constr(ops, n_ops, filename, min_bufsize, rootp, initf, netgroups,
2N/A getname_func, merge_func)
2N/A compat_backend_op_t ops[];
2N/A int n_ops;
2N/A const char *filename;
2N/A int min_bufsize;
2N/A nss_db_root_t *rootp;
2N/A nss_db_initf_t initf;
2N/A int netgroups;
2N/A compat_get_name getname_func;
2N/A compat_merge_func merge_func;
2N/A{
2N/A compat_backend_ptr_t be;
2N/A
2N/A if ((be = (compat_backend_ptr_t)malloc(sizeof (*be))) == 0) {
2N/A return (0);
2N/A }
2N/A be->ops = ops;
2N/A be->n_ops = n_ops;
2N/A be->filename = filename;
2N/A be->f = 0;
2N/A be->minbuf = min_bufsize;
2N/A be->buf = 0;
2N/A
2N/A be->db_rootp = rootp;
2N/A be->db_initf = initf;
2N/A be->db_context = context_initval;
2N/A
2N/A be->getnamef = getname_func;
2N/A be->mergef = merge_func;
2N/A
2N/A be->state = GETENT_FILE; /* i.e. do Automatic setent(); */
2N/A if (strcmp(be->filename, AUDITUSER_FILENAME) == 0) {
2N/A be->state = GETENT_ATTRDB;
2N/A be->str2ent_alt = str2auuser_s;
2N/A be->workarea = calloc(1, sizeof (au_user_str_t));
2N/A } else if (strcmp(be->filename, PASSWD) == 0) {
2N/A be->str2ent_alt = str2passwd;
2N/A be->workarea = calloc(1, sizeof (struct passwd));
2N/A } else if (strcmp(be->filename, SHADOW) == 0) {
2N/A be->str2ent_alt = str2spwd;
2N/A be->workarea = calloc(1, sizeof (struct spwd));
2N/A } else { /* group */
2N/A be->str2ent_alt = str2group;
2N/A be->workarea = calloc(1, sizeof (struct group));
2N/A }
2N/A if (be->workarea == NULL)
2N/A return (NULL);
2N/A
2N/A be->minuses = 0;
2N/A
2N/A be->permit_netgroups = netgroups;
2N/A be->yp_domain = 0;
2N/A be->getnetgrent_backend = 0;
2N/A be->netgr_buffer = 0;
2N/A be->return_string_data = 0;
2N/A
2N/A return ((nss_backend_t *)be);
2N/A}