#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <bsm/libbsm.h>
#include <user_attr.h>
#include <pwd.h>
#include <shadow.h>
#include <grp.h>
#include <unistd.h>
#include <dlfcn.h>
#include "compat_common.h"
extern int yp_get_default_domain(char **domain);
extern int str2passwd(const char *instr, int lenstr, void *ent,
char *buffer, int buflen);
extern int str2spwd(const char *instr, int lenstr, void *ent,
char *buffer, int buflen);
extern int str2group(const char *instr, int lenstr, void *ent,
char *buffer, int buflen);
extern char *_strtok_escape(char *, char *, char **);
static int
str2auuser_s(
const char *instr,
int lenstr,
void *ent,
char *buffer,
int buflen)
{
char *last = NULL;
char *sep = KV_TOKEN_DELIMIT;
au_user_str_t *au_user = (au_user_str_t *)ent;
if (lenstr >= buflen)
return (NSS_STR_PARSE_ERANGE);
(void) strncpy(buffer, instr, buflen);
au_user->au_name = _strtok_escape(buffer, sep, &last);
return (0);
}
static int
str2userattr_s(
const char *instr,
int lenstr,
void *ent,
char *buffer,
int buflen)
{
char *last = NULL;
char *sep = KV_TOKEN_DELIMIT;
userstr_t *user = (userstr_t *)ent;
if (lenstr >= buflen)
return (NSS_STR_PARSE_ERANGE);
(void) strncpy(buffer, instr, buflen);
user->name = _strtok_escape(buffer, sep, &last);
return (0);
}
struct setofstrings {
char *name;
struct setofstrings *next;
};
static void
strset_free(ssp)
strset_t *ssp;
{
strset_t cur, nxt;
for (cur = *ssp; cur != 0; cur = nxt) {
nxt = cur->next;
free(cur->name);
free(cur);
}
*ssp = 0;
}
static boolean_t
strset_add(ssp, nam)
strset_t *ssp;
const char *nam;
{
strset_t new;
if (0 == (new = (strset_t)malloc(sizeof (*new)))) {
return (B_FALSE);
}
if (0 == (new->name = malloc(strlen(nam) + 1))) {
free(new);
return (B_FALSE);
}
(void) strcpy(new->name, nam);
new->next = *ssp;
*ssp = new;
return (B_TRUE);
}
static boolean_t
strset_in(ssp, nam)
const strset_t *ssp;
const char *nam;
{
strset_t cur;
for (cur = *ssp; cur != 0; cur = cur->next) {
if (strcmp(cur->name, nam) == 0) {
return (B_TRUE);
}
}
return (B_FALSE);
}
extern void _nss_initf_netgroup(nss_db_params_t *);
static DEFINE_NSS_DB_ROOT(netgr_db_root);
static boolean_t
netgr_in(compat_backend_ptr_t be, const char *group, const char *user)
{
if (be->yp_domain == 0) {
if (yp_get_default_domain((char **)&be->yp_domain) != 0) {
return (B_FALSE);
}
}
return (innetgr(group, 0, user, be->yp_domain));
}
static void
netgr_set(be, netgroup)
compat_backend_ptr_t be;
const char *netgroup;
{
if (be->getnetgrent_backend != 0 &&
NSS_INVOKE_DBOP(be->getnetgrent_backend,
NSS_DBOP_SETENT,
(void *) netgroup) != NSS_SUCCESS) {
NSS_INVOKE_DBOP(be->getnetgrent_backend, NSS_DBOP_DESTRUCTOR,
0);
be->getnetgrent_backend = 0;
}
if (be->getnetgrent_backend == 0) {
struct nss_setnetgrent_args args;
args.netgroup = netgroup;
args.iterator = 0;
(void) nss_search(&netgr_db_root, _nss_initf_netgroup,
NSS_DBOP_NETGROUP_SET, &args);
be->getnetgrent_backend = args.iterator;
}
}
static boolean_t
netgr_next_u(be, up)
compat_backend_ptr_t be;
char **up;
{
if (be->netgr_buffer == 0 &&
(be->netgr_buffer = malloc(NSS_BUFLEN_NETGROUP)) == 0) {
return (B_FALSE);
}
do {
struct nss_getnetgrent_args args;
args.buffer = be->netgr_buffer;
args.buflen = NSS_BUFLEN_NETGROUP;
args.status = NSS_NETGR_NO;
if (be->getnetgrent_backend != 0) {
NSS_INVOKE_DBOP(be->getnetgrent_backend,
NSS_DBOP_GETENT, &args);
}
if (args.status == NSS_NETGR_FOUND) {
*up = args.retp[NSS_NETGR_USER];
} else {
return (B_FALSE);
}
} while (*up == 0);
return (B_TRUE);
}
static void
netgr_end(be)
compat_backend_ptr_t be;
{
if (be->getnetgrent_backend != 0) {
NSS_INVOKE_DBOP(be->getnetgrent_backend,
NSS_DBOP_DESTRUCTOR, 0);
be->getnetgrent_backend = 0;
}
if (be->netgr_buffer != 0) {
free(be->netgr_buffer);
be->netgr_buffer = 0;
}
}
#define MAXFIELDS 9
static nss_status_t
do_merge(be, args, instr, linelen)
compat_backend_ptr_t be;
nss_XbyY_args_t *args;
const char *instr;
int linelen;
{
char *fields[MAXFIELDS];
int i;
int overrides;
const char *p;
const char *end = instr + linelen;
nss_status_t res = NSS_NOTFOUND;
for (i = 0; i < MAXFIELDS; i++) {
fields[i] = 0;
}
for (p = instr, overrides = 0, i = 0; ; i++) {
const char *q = memchr(p, ':', end - p);
const char *r = (q == 0) ? end : q;
ssize_t len = r - p;
if (len > 0) {
char *s = malloc(len + 1);
if (s == 0) {
overrides = -1;
break;
}
(void) memcpy(s, p, len);
s[len] = '\0';
fields[i] = s;
overrides++;
}
if (q == 0) {
break;
} else {
p = q + 1;
}
}
if (overrides == 1) {
if (be->return_string_data != 1) {
res = NSS_SUCCESS;
} else {
free(fields[0]);
fields[0] = NULL;
}
}
if (overrides > 1 || be->return_string_data == 1) {
switch ((*be->mergef)(be, args, (const char **)fields)) {
case NSS_STR_PARSE_SUCCESS:
if (be->return_string_data != 1)
args->returnval = args->buf.result;
else
args->returnval = args->buf.buffer;
args->erange = 0;
res = NSS_SUCCESS;
break;
case NSS_STR_PARSE_ERANGE:
args->returnval = 0;
args->erange = 1;
res = NSS_NOTFOUND;
break;
case NSS_STR_PARSE_PARSE:
args->returnval = 0;
args->erange = 0;
res = NSS_NOTFOUND;
break;
}
} else if (res != NSS_SUCCESS) {
args->returnval = 0;
args->erange = 0;
res = NSS_UNAVAIL;
}
for (i = 0; i < MAXFIELDS; i++) {
if (fields[i] != 0) {
free(fields[i]);
}
}
return (res);
}
nss_status_t
_nss_compat_setent(be, dummy)
compat_backend_ptr_t be;
void *dummy;
{
if (be->f == 0) {
if (be->filename == 0) {
return (NSS_UNAVAIL);
}
if ((be->f = fopen(be->filename, "rF")) == 0) {
return (NSS_UNAVAIL);
}
} else {
rewind(be->f);
}
strset_free(&be->minuses);
if ((strcmp(be->filename, USERATTR_FILENAME) == 0) ||
(strcmp(be->filename, AUDITUSER_FILENAME) == 0))
be->state = GETENT_ATTRDB;
else
be->state = GETENT_FILE;
be->return_string_data = 0;
return (NSS_SUCCESS);
}
nss_status_t
_nss_compat_endent(be, dummy)
compat_backend_ptr_t be;
void *dummy;
{
if (be->f != 0) {
(void) fclose(be->f);
be->f = 0;
}
if (be->buf != 0) {
free(be->buf);
be->buf = 0;
}
nss_endent(be->db_rootp, be->db_initf, &be->db_context);
be->state = GETENT_FILE;
strset_free(&be->minuses);
netgr_end(be);
return (NSS_SUCCESS);
}
nss_status_t
_nss_compat_destr(be, dummy)
compat_backend_ptr_t be;
void *dummy;
{
if (be != 0) {
if (be->f != 0) {
(void) _nss_compat_endent(be, 0);
}
nss_delete(be->db_rootp);
nss_delete(&netgr_db_root);
free(be->workarea);
free(be);
}
return (NSS_SUCCESS);
}
static int
read_line(f, buffer, buflen)
FILE *f;
char *buffer;
int buflen;
{
while (1) {
int linelen;
if (fgets(buffer, buflen, f) == 0) {
return (-1);
}
linelen = strlen(buffer);
if (buffer[linelen - 1] == '\n') {
buffer[--linelen] = '\0';
return (linelen);
}
if (feof(f)) {
return (linelen);
}
while (fgets(buffer, buflen, f) != 0 &&
buffer[strlen(buffer) - 1] != '\n') {
;
}
}
}
static int
is_nss_lookup_by_name(int attrdb, nss_dbop_t op)
{
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);
}
nss_status_t
_attrdb_compat_XY_all(be, argp, netdb, check, op_num)
compat_backend_ptr_t be;
nss_XbyY_args_t *argp;
int netdb;
compat_XY_check_func check;
nss_dbop_t op_num;
{
int parsestat;
int (*func)();
const char *filter = argp->key.name;
nss_status_t res;
#ifdef DEBUG
(void) fprintf(stdout, "\n[compat_common.c: _attrdb_compat_XY_all]\n");
#endif
if (be->buf == 0 &&
(be->buf = malloc(be->minbuf)) == 0) {
return (NSS_UNAVAIL);
}
if (check != NULL)
if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS)
return (res);
res = NSS_NOTFOUND;
if (check != NULL) {
if (argp->buf.result == NULL) {
be->return_string_data = 1;
argp->buf.result = be->workarea;
} else
be->return_string_data = 0;
}
if (be->return_string_data == 1)
func = be->str2ent_alt;
else
func = argp->str2ent;
while (1) {
int linelen;
char *instr = be->buf;
if ((linelen = read_line(be->f, instr, be->minbuf)) < 0) {
argp->returnval = 0;
argp->erange = 0;
break;
}
if (filter != 0 && strstr(instr, filter) == 0) {
continue;
}
if (netdb) {
char *first;
char *last;
if ((last = strchr(instr, '#')) == 0) {
last = instr + linelen;
}
*last-- = '\0';
for (first = instr; isspace(*first); first++) {
;
}
if (*first == '\0') {
continue;
}
while (isspace(*last)) {
--last;
}
linelen = last - first + 1;
if (first != instr) {
instr = first;
}
}
argp->returnval = 0;
parsestat = (*func)(instr, linelen, argp->buf.result,
argp->buf.buffer, argp->buf.buflen);
if (parsestat == NSS_STR_PARSE_SUCCESS) {
argp->returnval = argp->buf.result;
if (check == 0 || (*check)(argp)) {
int len;
if (be->return_string_data != 1) {
res = NSS_SUCCESS;
break;
}
argp->buf.result = NULL;
argp->returnval = argp->buf.buffer;
if ((len = strlcpy(argp->buf.buffer, instr,
argp->buf.buflen)) >=
argp->buf.buflen) {
argp->returnval = NULL;
res = NSS_NOTFOUND;
argp->erange = 1;
break;
}
argp->returnlen = len;
res = NSS_SUCCESS;
break;
}
} else if (parsestat == NSS_STR_PARSE_ERANGE) {
res = NSS_NOTFOUND;
argp->erange = 1;
break;
}
}
if (check != 0 && !argp->stayopen) {
(void) _nss_compat_endent(be, 0);
}
if (res != NSS_SUCCESS) {
if (be->return_string_data == 1)
argp->buf.result = NULL;
if ((op_num == NSS_DBOP_USERATTR_BYNAME) ||
(op_num == NSS_DBOP_AUDITUSER_BYNAME)) {
res = nss_search(be->db_rootp,
be->db_initf,
op_num,
argp);
} else {
res = nss_getent(be->db_rootp,
be->db_initf, &be->db_context, argp);
}
if (res != NSS_SUCCESS) {
argp->returnval = 0;
argp->erange = 0;
}
}
return (res);
}
static int
validate_ids(compat_backend_ptr_t be, nss_XbyY_args_t *argp,
char *line, int *linelenp, int buflen, int extra_chars)
{
if (be->return_string_data != 1) {
struct passwd *p;
struct group *g;
if (strcmp(be->filename, PASSWD) == 0) {
p = (struct passwd *)argp->returnval;
if (p->pw_uid > MAXUID)
p->pw_uid = UID_NOBODY;
if (p->pw_gid > MAXUID)
p->pw_gid = GID_NOBODY;
} else if (strcmp(be->filename, GF_PATH) == 0) {
g = (struct group *)argp->returnval;
if (g->gr_gid > MAXUID)
g->gr_gid = GID_NOBODY;
}
return (NSS_STR_PARSE_SUCCESS);
}
if (strcmp(be->filename, PASSWD) == 0)
return (validate_passwd_ids(line, linelenp, buflen,
extra_chars));
else if (strcmp(be->filename, GF_PATH) == 0)
return (validate_group_ids(line, linelenp, buflen,
extra_chars));
return (NSS_STR_PARSE_SUCCESS);
}
nss_status_t
_nss_compat_XY_all(be, args, check, op_num)
compat_backend_ptr_t be;
nss_XbyY_args_t *args;
compat_XY_check_func check;
nss_dbop_t op_num;
{
nss_status_t res;
int parsestat;
if (be->buf == 0 &&
(be->buf = malloc(be->minbuf)) == 0) {
return (NSS_UNAVAIL);
}
if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
return (res);
}
res = NSS_NOTFOUND;
if (args->buf.result == NULL) {
be->return_string_data = 1;
args->buf.result = be->workarea;
be->str2ent_save = args->str2ent;
args->str2ent = be->str2ent_alt;
} else
be->return_string_data = 0;
while (1) {
int linelen;
char *instr = be->buf;
char *colon;
linelen = read_line(be->f, instr, be->minbuf);
if (linelen < 0) {
args->returnval = 0;
args->erange = 0;
break;
}
args->returnval = 0;
if (instr[0] != '+' && instr[0] != '-') {
parsestat = (*args->str2ent)(instr, linelen,
args->buf.result,
args->buf.buffer,
args->buf.buflen);
if (parsestat == NSS_STR_PARSE_SUCCESS) {
args->returnval = args->buf.result;
if ((*check)(args) != 0) {
int len;
parsestat = validate_ids(be, args,
instr, &linelen, be->minbuf, 1);
if (parsestat ==
NSS_STR_PARSE_ERANGE) {
args->erange = 1;
res = NSS_NOTFOUND;
break;
} else if (parsestat !=
NSS_STR_PARSE_SUCCESS) {
continue;
}
if (be->return_string_data != 1) {
res = NSS_SUCCESS;
break;
}
args->buf.result = NULL;
args->str2ent = be->str2ent_save;
if ((len = strlcpy(args->buf.buffer,
instr, args->buf.buflen)) >=
args->buf.buflen)
parsestat =
NSS_STR_PARSE_ERANGE;
else {
args->returnval =
args->buf.buffer;
args->returnlen = len;
res = NSS_SUCCESS;
break;
}
} else
continue;
}
if (parsestat == NSS_STR_PARSE_ERANGE) {
args->erange = 1;
res = NSS_NOTFOUND;
break;
}
continue;
}
if ((colon = strchr(instr, ':')) != 0) {
*colon = '\0';
}
if (instr[1] == '@') {
if (is_nss_lookup_by_name(0, op_num) != 0) {
if (!be->permit_netgroups ||
!netgr_in(be, instr + 2, args->key.name))
continue;
if (instr[0] == '+') {
(void) nss_search(be->db_rootp,
be->db_initf, op_num, args);
if (args->returnval == 0)
continue;
}
} else {
(void) nss_search(be->db_rootp,
be->db_initf, op_num, args);
if (args->returnval == 0)
continue;
if (!be->permit_netgroups ||
!netgr_in(be, instr + 2,
(*be->getnamef)(args)))
continue;
}
} else if (instr[1] == '\0') {
if (instr[0] == '-')
continue;
(void) nss_search(be->db_rootp, be->db_initf,
op_num, args);
if (args->returnval == 0)
continue;
} else {
if (is_nss_lookup_by_name(0, op_num) != 0) {
if (strcmp(instr + 1, args->key.name) != 0)
continue;
if (instr[0] == '+') {
(void) nss_search(be->db_rootp,
be->db_initf, op_num, args);
if (args->returnval == 0)
continue;
}
} else {
(void) nss_search(be->db_rootp,
be->db_initf, op_num, args);
if (args->returnval == 0)
continue;
if (strcmp(instr + 1, (*be->getnamef)(args))
!= 0)
continue;
}
}
if (instr[0] == '-') {
args->returnval = 0;
args->erange = 0;
res = NSS_NOTFOUND;
} else {
if (colon != 0)
*colon = ':';
res = do_merge(be, args, instr, linelen);
}
break;
}
if (!args->stayopen) {
(void) _nss_compat_endent(be, 0);
}
if (be->return_string_data == 1) {
args->str2ent = be->str2ent_save;
}
return (res);
}
nss_status_t
_nss_compat_getent(be, a)
compat_backend_ptr_t be;
void *a;
{
nss_XbyY_args_t *args = (nss_XbyY_args_t *)a;
nss_status_t res;
char *colon = 0;
if (be->f == 0) {
if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
return (res);
}
}
if (be->buf == 0 &&
(be->buf = malloc(be->minbuf)) == 0) {
return (NSS_UNAVAIL);
}
if (args->buf.result == NULL) {
be->return_string_data = 1;
args->buf.result = be->workarea;
} else
be->return_string_data = 0;
while (1) {
char *instr = be->buf;
int linelen;
char *name;
const char *savename;
switch (be->state) {
case GETENT_DONE:
args->returnval = 0;
args->erange = 0;
return (NSS_NOTFOUND);
case GETENT_ATTRDB:
args->key.name = NULL;
res = _attrdb_compat_XY_all(be,
args, 1, (compat_XY_check_func)NULL, 0);
return (res);
case GETENT_FILE:
linelen = read_line(be->f, instr, be->minbuf);
if (linelen < 0) {
be->state = GETENT_DONE;
continue;
}
if ((colon = strchr(instr, ':')) != 0) {
*colon = '\0';
}
if (instr[0] == '-') {
if (instr[1] != '@') {
(void) strset_add(&be->minuses,
instr + 1);
} else if (be->permit_netgroups) {
netgr_set(be, instr + 2);
while (netgr_next_u(be, &name)) {
(void) strset_add(&be->minuses,
name);
}
netgr_end(be);
}
continue;
} else if (instr[0] != '+') {
int parsestat;
if (colon != 0) {
*colon = ':';
}
args->returnval = 0;
parsestat = (*args->str2ent)(instr, linelen,
args->buf.result,
args->buf.buffer,
args->buf.buflen);
if (parsestat == NSS_STR_PARSE_SUCCESS) {
int len;
if (be->return_string_data != 1) {
args->returnval =
args->buf.result;
return (NSS_SUCCESS);
}
args->buf.result = NULL;
args->returnval =
args->buf.buffer;
if ((len = strlcpy(args->buf.buffer,
instr, args->buf.buflen)) >=
args->buf.buflen)
parsestat =
NSS_STR_PARSE_ERANGE;
else {
args->returnlen = len;
return (NSS_SUCCESS);
}
}
if (parsestat == NSS_STR_PARSE_ERANGE) {
args->returnval = 0;
args->erange = 1;
return (NSS_NOTFOUND);
}
continue;
} else if (instr[1] == '\0') {
nss_setent(be->db_rootp, be->db_initf,
&be->db_context);
be->state = GETENT_ALL;
be->linelen = linelen;
continue;
} else if (instr[1] == '@') {
netgr_set(be, instr + 2);
be->state = GETENT_NETGROUP;
be->linelen = linelen;
continue;
} else {
name = instr + 1;
break;
}
case GETENT_ALL:
linelen = be->linelen;
args->returnval = 0;
if (be->return_string_data == 1) {
be->str2ent_save = args->str2ent;
args->str2ent = be->str2ent_alt;
}
(void) nss_getent(be->db_rootp, be->db_initf,
&be->db_context, args);
if (args->returnval == 0) {
nss_endent(be->db_rootp, be->db_initf,
&be->db_context);
be->state = GETENT_FILE;
if (be->return_string_data == 1)
args->str2ent = be->str2ent_save;
continue;
}
if (strset_in(&be->minuses, (*be->getnamef)(args)))
continue;
name = 0;
if (be->return_string_data == 1)
args->str2ent = be->str2ent_save;
break;
case GETENT_NETGROUP:
linelen = be->linelen;
if (!netgr_next_u(be, &name)) {
netgr_end(be);
be->state = GETENT_FILE;
continue;
}
break;
}
if (name != 0) {
if (strset_in(&be->minuses, name)) {
continue;
}
savename = args->key.name;
args->key.name = name;
args->returnval = 0;
if (be->return_string_data == 1) {
be->str2ent_save = args->str2ent;
args->str2ent = be->str2ent_alt;
}
(void) nss_search(be->db_rootp, be->db_initf,
NSS_DBOP_next_iter, args);
if (be->return_string_data == 1)
args->str2ent = be->str2ent_save;
args->key.name = savename;
}
if (args->returnval == 0) {
continue;
}
if (colon != 0) {
*colon = ':';
colon = 0;
}
return (do_merge(be, args, instr, linelen));
}
}
static DEFINE_NSS_GETENT(context_initval);
nss_backend_t *
_nss_compat_constr(ops, n_ops, filename, min_bufsize, rootp, initf, netgroups,
getname_func, merge_func)
compat_backend_op_t ops[];
int n_ops;
const char *filename;
int min_bufsize;
nss_db_root_t *rootp;
nss_db_initf_t initf;
int netgroups;
compat_get_name getname_func;
compat_merge_func merge_func;
{
compat_backend_ptr_t be;
if ((be = (compat_backend_ptr_t)malloc(sizeof (*be))) == 0) {
return (0);
}
be->ops = ops;
be->n_ops = n_ops;
be->filename = filename;
be->f = 0;
be->minbuf = min_bufsize;
be->buf = 0;
be->db_rootp = rootp;
be->db_initf = initf;
be->db_context = context_initval;
be->getnamef = getname_func;
be->mergef = merge_func;
be->state = GETENT_FILE;
if (strcmp(be->filename, USERATTR_FILENAME) == 0) {
be->state = GETENT_ATTRDB;
be->str2ent_alt = str2userattr_s;
be->workarea = calloc(1, sizeof (userstr_t));
} else if (strcmp(be->filename, AUDITUSER_FILENAME) == 0) {
be->state = GETENT_ATTRDB;
be->str2ent_alt = str2auuser_s;
be->workarea = calloc(1, sizeof (au_user_str_t));
} else if (strcmp(be->filename, PASSWD) == 0) {
be->str2ent_alt = str2passwd;
be->workarea = calloc(1, sizeof (struct passwd));
} else if (strcmp(be->filename, SHADOW) == 0) {
be->str2ent_alt = str2spwd;
be->workarea = calloc(1, sizeof (struct spwd));
} else {
be->str2ent_alt = str2group;
be->workarea = calloc(1, sizeof (struct group));
}
if (be->workarea == NULL)
return (NULL);
be->minuses = 0;
be->permit_netgroups = netgroups;
be->yp_domain = 0;
be->getnetgrent_backend = 0;
be->netgr_buffer = 0;
be->return_string_data = 0;
return ((nss_backend_t *)be);
}