/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Common code and structures used by name-service-switch "compat" backends.
*
* Most of the code in the "compat" backend is a perverted form of code from
* the "files" backend; this file is no exception.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <user_attr.h>
#include <pwd.h>
#include <shadow.h>
#include <grp.h>
#include <unistd.h> /* for GF_PATH */
#include <dlfcn.h>
#include "compat_common.h"
/*
* This should be in a header.
*/
extern int yp_get_default_domain(char **domain);
/* from libc */
/* from libnsl */
extern char *_strtok_escape(char *, char *, char **);
/*
* str2auuser_s and str2userattr_s are very simple version
* of the str2auuser() and str2userattr() that can be found in
* libnsl. They only copy the user name into the userstr_t
* or au_user_str_t structure (so check on user name can be
* performed).
*/
static int
const char *instr,
int lenstr,
void *ent,
char *buffer,
int buflen)
{
return (NSS_STR_PARSE_ERANGE);
return (0);
}
static int
const char *instr,
int lenstr,
void *ent,
char *buffer,
int buflen)
{
return (NSS_STR_PARSE_ERANGE);
return (0);
}
/*
* Routines to manage list of "-" users for get{pw, sp, gr}ent(). Current
* implementation is completely moronic; we use a linked list. But then
* that's what it's always done in 4.x...
*/
struct setofstrings {
char *name;
/*
* === Should get smart and malloc the string and pointer as one
* object rather than two.
*/
};
static void
{
}
*ssp = 0;
}
static boolean_t
const char *nam;
{
return (B_FALSE);
}
return (B_FALSE);
}
return (B_TRUE);
}
static boolean_t
const char *nam;
{
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Lookup and enumeration routines for +@group and -@group.
*
* This code knows a lot more about lib/libc/port/gen/getnetgrent.c than
* from that file, but keep the state information per-backend-instance
* instead of just per-process.
*/
extern void _nss_initf_netgroup(nss_db_params_t *);
/*
* Should really share the db_root in getnetgrent.c in order to get the
* resource-management quotas right, but this will have to do.
*/
static DEFINE_NSS_DB_ROOT(netgr_db_root);
static boolean_t
{
return (B_FALSE);
}
}
}
static void
const char *netgroup;
{
/*
* ===> Need comment to explain that this first "if" is optimizing
* for the same-netgroup-as-last-time case
*/
if (be->getnetgrent_backend != 0 &&
(void *) netgroup) != NSS_SUCCESS) {
0);
be->getnetgrent_backend = 0;
}
if (be->getnetgrent_backend == 0) {
}
}
static boolean_t
char **up;
{
if (be->netgr_buffer == 0 &&
/* Out of memory */
return (B_FALSE);
}
do {
if (be->getnetgrent_backend != 0) {
NSS_DBOP_GETENT, &args);
}
} else {
return (B_FALSE);
}
} while (*up == 0);
return (B_TRUE);
}
static void
{
if (be->getnetgrent_backend != 0) {
NSS_DBOP_DESTRUCTOR, 0);
be->getnetgrent_backend = 0;
}
if (be->netgr_buffer != 0) {
be->netgr_buffer = 0;
}
}
static nss_status_t
const char *instr;
int linelen;
{
int i;
int overrides;
const char *p;
/*
* Potential optimization: only perform the field-splitting nonsense
* once per input line (at present, "+" and "+@netgroup" entries
* will cause us to do this multiple times in getent() requests).
*/
for (i = 0; i < MAXFIELDS; i++) {
fields[i] = 0;
}
const char *r = (q == 0) ? end : q;
if (len > 0) {
if (s == 0) {
break;
}
s[len] = '\0';
fields[i] = s;
overrides++;
}
if (q == 0) {
/* End of line */
break;
} else {
/* Skip the colon at (*q) */
p = q + 1;
}
}
if (overrides == 1) {
/*
* return result here if /etc file format is requested
*/
/* No real overrides, return (*args) intact */
res = NSS_SUCCESS;
} else {
}
}
/*
* The zero'th field is always nonempty (+/-...), but at least
* one other field was also nonempty, i.e. wants to override
*/
case NSS_STR_PARSE_SUCCESS:
else
res = NSS_SUCCESS;
break;
case NSS_STR_PARSE_ERANGE:
res = NSS_NOTFOUND;
break;
case NSS_STR_PARSE_PARSE:
/* ===> Very likely the wrong thing to do... */
res = NSS_NOTFOUND;
break;
}
} else if (res != NSS_SUCCESS) {
}
for (i = 0; i < MAXFIELDS; i++) {
if (fields[i] != 0) {
}
}
return (res);
}
/*ARGSUSED*/
void *dummy;
{
if (be->f == 0) {
/* Backend isn't initialized properly? */
return (NSS_UNAVAIL);
}
return (NSS_UNAVAIL);
}
} else {
}
/* ===> ??? nss_endent(be->db_rootp, be->db_initf, &be->db_context); */
else
be->return_string_data = 0;
/* ===> ?? netgroup stuff? */
return (NSS_SUCCESS);
}
/*ARGSUSED*/
void *dummy;
{
if (be->f != 0) {
be->f = 0;
}
}
/*
* Question: from the point of view of resource-freeing vs. time to
* start up again, how much should we do in endent() and how much
* in the destructor?
*/
return (NSS_SUCCESS);
}
/*ARGSUSED*/
void *dummy;
{
if (be != 0) {
if (be->f != 0) {
(void) _nss_compat_endent(be, 0);
}
}
return (NSS_SUCCESS); /* In case anyone is dumb enough to check */
}
static int
FILE *f;
char *buffer;
int buflen;
{
/*CONSTCOND*/
while (1) {
int linelen;
/* End of file */
return (-1);
}
/* linelen >= 1 (since fgets didn't return 0) */
/*
* ===> The code below that calls read_line() doesn't
* play by the rules; it assumes in places that
* the line is null-terminated. For now we'll
* humour it.
*/
return (linelen);
}
if (feof(f)) {
/* Line is last line in file, and has no newline */
return (linelen);
}
/* Line too long for buffer; toss it and loop for next line */
/* ===== should syslog() in cases where previous code did */
;
}
}
/*NOTREACHED*/
}
static int
{
int result = 0;
if ((attrdb != 0) &&
((op == NSS_DBOP_AUDITUSER_BYNAME) ||
(op == NSS_DBOP_USERATTR_BYNAME))) {
result = 1;
} else if ((attrdb == 0) &&
((op == NSS_DBOP_GROUP_BYNAME) ||
(op == NSS_DBOP_PASSWD_BYNAME) ||
(op == NSS_DBOP_SHADOW_BYNAME))) {
result = 1;
}
return (result);
}
/*ARGSUSED*/
int netdb;
{
int parsestat;
int (*func)();
#ifdef DEBUG
#endif /* DEBUG */
return (NSS_UNAVAIL);
}
return (res);
res = NSS_NOTFOUND;
/*
* assume a NULL buf.result pointer is an indication
* that the lookup result should be returned in /etc
* file format (if called from _nss_compat_getent(),
* be->return_string_data and argp->buf.result
* would be set already if argp->buf.result is NULL)
*/
/*
* the code executed later needs the result struct
* as working area
*/
} else
be->return_string_data = 0;
}
/*
* use an alternate str2ent function if necessary
*/
else
/*CONSTCOND*/
while (1) {
int linelen;
/* End of file */
break;
}
/*
* Optimization: if the entry doesn't contain the
* filter string then it can't be the entry we want,
* so don't bother looking more closely at it.
*/
continue;
}
if (netdb) {
char *first;
char *last;
}
/*
* Skip leading whitespace. Normally there isn't
* any, so it's not worth calling strspn().
*/
;
}
if (*first == '\0') {
continue;
}
/*
* Found something non-blank on the line. Skip back
* over any trailing whitespace; since we know
* there's non-whitespace earlier in the line,
* checking for termination is easy.
*/
--last;
}
}
}
if (parsestat == NSS_STR_PARSE_SUCCESS) {
int len;
res = NSS_SUCCESS;
break;
}
/* copy string data to result buffer */
res = NSS_NOTFOUND;
break;
}
res = NSS_SUCCESS;
break;
}
} else if (parsestat == NSS_STR_PARSE_ERANGE) {
res = NSS_NOTFOUND;
break;
}
}
/*
* stayopen is set to 0 by default in order to close the opened
* file. Some applications may break if it is set to 1.
*/
(void) _nss_compat_endent(be, 0);
}
if (res != NSS_SUCCESS) {
/*
* tell the nss_search() and nss_getent() below
* if the result should be returned in the /etc
* file format
*/
if ((op_num == NSS_DBOP_USERATTR_BYNAME) ||
(op_num == NSS_DBOP_AUDITUSER_BYNAME)) {
argp);
} else {
}
if (res != NSS_SUCCESS) {
}
}
return (res);
}
static int
{
struct passwd *p;
struct group *g;
/*
* The data is already marshalled into
* struct passwd or group.
*/
p->pw_uid = UID_NOBODY;
p->pw_gid = GID_NOBODY;
g->gr_gid = GID_NOBODY;
}
return (NSS_STR_PARSE_SUCCESS);
}
/*
* The data needs to be returned in string format therefore
* validate the return string.
*/
extra_chars));
extra_chars));
return (NSS_STR_PARSE_SUCCESS);
}
{
int parsestat;
return (NSS_UNAVAIL); /* really panic, malloc failed */
}
return (res);
}
res = NSS_NOTFOUND;
/*
* assume a NULL buf.result pointer is an indication
* that the lookup result should be returned in /etc
* file format
*/
/*
* the code executed later needs the result struct
* as working area
*/
} else
be->return_string_data = 0;
/*CONSTCOND*/
while (1) {
int linelen;
char *colon;
if (linelen < 0) {
/* End of file */
break;
}
/* Simple, wholesome, God-fearing entry */
if (parsestat == NSS_STR_PARSE_SUCCESS) {
int len;
if (parsestat ==
res = NSS_NOTFOUND;
break;
} else if (parsestat !=
continue;
}
res = NSS_SUCCESS;
break;
}
/*
* copy string data to
* result buffer
*/
else {
res = NSS_SUCCESS;
break;
}
} else
continue;
}
/* ===> Check the Dani logic here... */
if (parsestat == NSS_STR_PARSE_ERANGE) {
res = NSS_NOTFOUND;
break;
/* should we just skip this one long line ? */
} /* else if (parsestat == NSS_STR_PARSE_PARSE) */
/* don't care ! */
/* ==> ?? */ continue;
}
/*
* Process "+", "+name", "+@netgroup", "-name" or "-@netgroup"
*
* This code is optimized for lookups by name.
*
* For lookups by identifier search key cannot be matched with
* the name of the "+" or "-" entry. So nss_search() is to be
* called before extracting the name i.e. via (*be->getnamef)().
*
* But for lookups by name, search key is compared with the name
* of the "+" or "-" entry to acquire a match and thus
* unnesessary calls to nss_search() is eliminated. Also for
* matching "-" entries, calls to nss_search() is eliminated.
*/
}
/*
* Case 1:
* The entry is of the form "+@netgroup" or
* "-@netgroup". If we're performing a lookup by name,
* we can simply extract the name from the search key
* (i.e. args->key.name). If not, then we must call
* nss_search() before extracting the name via the
* get_XXname() function. i.e. (*be->getnamef)(args).
*/
if (is_nss_lookup_by_name(0, op_num) != 0) {
/* compare then search */
if (!be->permit_netgroups ||
continue;
if (instr[0] == '+') {
/* need to search for "+" entry */
continue;
}
} else {
/* search then compare */
continue;
if (!be->permit_netgroups ||
continue;
}
/*
* Case 2:
* The entry is of the form "+" or "-". The former
* allows all entries from name services. The latter
* is illegal and ought to be ignored.
*/
if (instr[0] == '-')
continue;
/* need to search for "+" entry */
continue;
} else {
/*
* Case 3:
* The entry is of the form "+name" or "-name".
* If we're performing a lookup by name, we can simply
* extract the name from the search key
* (i.e. args->key.name). If not, then we must call
* nss_search() before extracting the name via the
* get_XXname() function. i.e. (*be->getnamef)(args).
*/
if (is_nss_lookup_by_name(0, op_num) != 0) {
/* compare then search */
continue;
if (instr[0] == '+') {
/* need to search for "+" entry */
continue;
}
} else {
/* search then compare */
continue;
!= 0)
continue;
}
}
if (instr[0] == '-') {
/* no need to search for "-" entry */
res = NSS_NOTFOUND;
} else {
if (colon != 0)
}
break;
}
/*
* stayopen is set to 0 by default in order to close the opened
* file. Some applications may break if it is set to 1.
*/
(void) _nss_compat_endent(be, 0);
}
}
return (res);
}
void *a;
{
if (be->f == 0) {
return (res);
}
}
return (NSS_UNAVAIL); /* really panic, malloc failed */
}
/*
* assume a NULL buf.result pointer is an indication
* that the lookup result should be returned in /etc
* file format
*/
/*
* the code executed later needs the result struct
* as working area
*/
} else
be->return_string_data = 0;
/*CONSTCOND*/
while (1) {
int linelen;
const char *savename;
/*
* In the code below...
* break means "I found one, I think" (i.e. goto the
* code after the end of the switch statement),
* continue means "Next candidate"
* (i.e. loop around to the switch statement),
* return means "I'm quite sure" (either Yes or No).
*/
case GETENT_DONE:
return (NSS_NOTFOUND);
case GETENT_ATTRDB:
return (res);
case GETENT_FILE:
if (linelen < 0) {
/* End of file */
continue;
}
*colon = '\0';
}
if (instr[0] == '-') {
instr + 1);
} else if (be->permit_netgroups) {
name);
}
} /* Else (silently) ignore the entry */
continue;
} else if (instr[0] != '+') {
int parsestat;
/*
* Normal entry, no +/- nonsense
*/
if (colon != 0) {
*colon = ':';
}
if (parsestat == NSS_STR_PARSE_SUCCESS) {
int len;
return (NSS_SUCCESS);
}
/*
* copy string data to
* result buffer
*/
else {
return (NSS_SUCCESS);
}
}
/* ==> ?? Treat ERANGE differently ?? */
if (parsestat == NSS_STR_PARSE_ERANGE) {
return (NSS_NOTFOUND);
}
/* Skip the offending entry, get next */
continue;
/* Plain "+" */
&be->db_context);
continue;
/* "+@netgroup" */
continue;
} else {
/* "+name" */
break;
}
/* NOTREACHED */
case GETENT_ALL:
}
/* ==> ?? Treat ERANGE differently ?? */
&be->db_context);
continue;
}
continue;
name = 0; /* tell code below we've done the lookup */
break;
case GETENT_NETGROUP:
continue;
}
/* pass "name" variable to code below... */
break;
}
if (name != 0) {
continue;
}
/*
* Do a getXXXnam(name). If we were being pure,
* we'd introduce yet another function-pointer
* that the database-specific code had to supply
* to us. Instead we'll be grotty and hard-code
* the knowledge that
* (a) The username is always passwd in key.name,
* (b) NSS_DBOP_PASSWD_BYNAME ==
* NSS_DBOP_SHADOW_BYNAME ==
* NSS_DBOP_next_iter.
*/
}
}
/*
* Found one via "+", "+name" or "@netgroup".
* Override some fields if the /etc file says to do so.
*/
/* ==> ?? Should treat erange differently? */
continue;
}
/* 'colon' was set umpteen iterations ago in GETENT_FILE */
if (colon != 0) {
*colon = ':';
colon = 0;
}
}
/*NOTREACHED*/
}
/* We don't use this directly; we just copy the bits when we want to */
/* initialize the variable (in the compat_backend struct) that we do use */
static DEFINE_NSS_GETENT(context_initval);
int n_ops;
const char *filename;
int min_bufsize;
int netgroups;
{
return (0);
}
be->f = 0;
} else { /* group */
}
return (NULL);
be->getnetgrent_backend = 0;
be->netgr_buffer = 0;
be->return_string_data = 0;
return ((nss_backend_t *)be);
}