/* * 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 * or http://www.opensolaris.org/os/licensing. * 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 */ /* * getgrent.c * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * lib/nsswitch/compat/getgrent.c -- name-service-switch backend for getgrnam() * et al that does 4.x compatibility. It looks in /etc/group; if it finds * group entries there that begin with "+" or "-", it consults other * services. By default it uses NIS (YP), but the user can override this * with a "group_compat" entry in /etc/nsswitch.conf, e.g. * group_compat: ldap * * This code tries to produce the same results as the 4.x code, even when * the latter seems ill thought-out. Bug-compatible, in other words. * Though we do try to be more reasonable about the format of "+" and "-" * entries here, i.e. you don't have to pad them with spurious colons and * bogus uid/gid values. * * Caveats: * - More than one source may be specified, with the usual switch semantics, * but having multiple sources here is definitely odd. * - People who recursively specify "compat" deserve what they get. */ #include #include #include /* for GF_PATH */ #include #include "compat_common.h" static DEFINE_NSS_DB_ROOT(db_root); static void _nss_initf_group_compat(p) nss_db_params_t *p; { p->name = NSS_DBNAM_GROUP; p->config_name = NSS_DBNAM_GROUP_COMPAT; p->default_config = NSS_DEFCONF_GROUP_COMPAT; } /* * Validates group entry replacing gid > MAXUID by GID_NOBODY. */ int validate_group_ids(char *line, int *linelenp, int buflen, int extra_chars) { char *linep, *limit, *gidp; ulong_t gid; int oldgidlen, idlen; int linelen = *linelenp, newlinelen; if (linelen == 0 || *line == '+' || *line == '-') return (NSS_STR_PARSE_SUCCESS); linep = line; limit = line + linelen; while (linep < limit && *linep++ != ':') /* skip groupname */ continue; while (linep < limit && *linep++ != ':') /* skip password */ continue; if (linep == limit) return (NSS_STR_PARSE_PARSE); gidp = linep; gid = strtoul(gidp, (char **)&linep, 10); /* grab gid */ oldgidlen = linep - gidp; if (linep >= limit || oldgidlen == 0) return (NSS_STR_PARSE_PARSE); if (gid <= MAXUID) return (NSS_STR_PARSE_SUCCESS); idlen = snprintf(NULL, 0, "%u", GID_NOBODY); newlinelen = linelen + idlen - oldgidlen; if (newlinelen + extra_chars > buflen) return (NSS_STR_PARSE_ERANGE); (void) bcopy(linep, gidp + idlen, limit - linep + extra_chars); (void) snprintf(gidp, idlen + 1, "%u", GID_NOBODY); *(gidp + idlen) = ':'; *linelenp = newlinelen; return (NSS_STR_PARSE_SUCCESS); } static const char * get_grname(argp) nss_XbyY_args_t *argp; { struct group *g = (struct group *)argp->returnval; return (g->gr_name); } static int check_grname(argp) nss_XbyY_args_t *argp; { struct group *g = (struct group *)argp->returnval; return (strcmp(g->gr_name, argp->key.name) == 0); } static nss_status_t getbyname(be, a) compat_backend_ptr_t be; void *a; { nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; return (_nss_compat_XY_all(be, argp, check_grname, NSS_DBOP_GROUP_BYNAME)); } static int check_grgid(argp) nss_XbyY_args_t *argp; { struct group *g = (struct group *)argp->returnval; return (g->gr_gid == argp->key.gid); } static nss_status_t getbygid(be, a) compat_backend_ptr_t be; void *a; { nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; if (argp->key.gid > MAXUID) return (NSS_NOTFOUND); return (_nss_compat_XY_all(be, argp, check_grgid, NSS_DBOP_GROUP_BYGID)); } static nss_status_t getbymember(be, a) compat_backend_ptr_t be; void *a; { struct nss_groupsbymem *argp = (struct nss_groupsbymem *)a; int numgids = argp->numgids; int maxgids = argp->maxgids; gid_t *gid_array = argp->gid_array; struct nss_XbyY_args grargs; struct group *g; nss_XbyY_buf_t *gb = NULL, *b = NULL; /* * Generic implementation: enumerate using getent(), then check each * group returned by getent() to see whether it contains the user. * There are much faster ways, but at least this one gets the right * answer. */ if (numgids >= maxgids) { /* full gid_array; nobody should have bothered to call us */ return (NSS_SUCCESS); } b = NSS_XbyY_ALLOC(&gb, sizeof (struct group), NSS_BUFLEN_GROUP); if (b == 0) return (NSS_UNAVAIL); NSS_XbyY_INIT(&grargs, gb->result, gb->buffer, gb->buflen, argp->str2ent); g = (struct group *)gb->result; (void) _nss_compat_setent(be, 0); while (_nss_compat_getent(be, &grargs) == NSS_SUCCESS) { char **mem; if (grargs.returnval == 0) { continue; } for (mem = g->gr_mem; *mem != 0; mem++) { if (strcmp(*mem, argp->username) == 0) { int gid = g->gr_gid; int i; for (i = 0; i < numgids; i++) { if (gid == gid_array[i]) { break; } } if (i == numgids) { gid_array[numgids++] = gid; argp->numgids = numgids; if (numgids >= maxgids) { /* filled the gid_array */ (void) _nss_compat_endent(be, 0); NSS_XbyY_FREE(&gb); return (NSS_SUCCESS); } /* Done with this group, try next */ break; } } } } (void) _nss_compat_endent(be, 0); NSS_XbyY_FREE(&gb); return (NSS_NOTFOUND); /* Really means "gid_array not full yet" */ } /*ARGSUSED*/ static int merge_grents(be, argp, fields) compat_backend_ptr_t be; nss_XbyY_args_t *argp; const char **fields; { struct group *g = (struct group *)argp->buf.result; char *buf; char *s; int parsestat; int dlen; /* * We're allowed to override the passwd (has anyone ever actually used * the passwd in a group entry?) and the membership list, but not * the groupname or the gid. * That's what the SunOS 4.x code did; who are we to question it... * * Efficiency is heartlessly abandoned in the quest for simplicity. */ if (fields[1] == 0 && fields[3] == 0 && be->return_string_data != 1) { /* No legal overrides, leave *argp unscathed */ return (NSS_STR_PARSE_SUCCESS); } if ((buf = malloc(NSS_LINELEN_GROUP)) == 0) { return (NSS_STR_PARSE_PARSE); /* Really "out of memory", but PARSE_PARSE will have to do */ } s = buf; (void) snprintf(s, NSS_LINELEN_GROUP, "%s:%s:%u:", g->gr_name, fields[1] != 0 ? fields[1] : g->gr_passwd, g->gr_gid); s += strlen(s); if (fields[3] != 0) { (void) strcpy(s, fields[3]); s += strlen(s); } else { char **memp; for (memp = g->gr_mem; *memp != 0; memp++) { size_t len = strlen(*memp); if (s + len + 1 <= buf + NSS_LINELEN_GROUP) { if (memp != g->gr_mem) { *s++ = ','; } (void) memcpy(s, *memp, len); s += len; } else { free(buf); return (NSS_STR_PARSE_ERANGE); } } } dlen = s - buf; /* * if asked, return the data in /etc file format */ if (be->return_string_data == 1) { /* reset the result ptr to the original value */ argp->buf.result = NULL; if (dlen > argp->buf.buflen) { parsestat = NSS_STR_PARSE_ERANGE; } else { (void) strncpy(argp->buf.buffer, buf, dlen); argp->returnval = argp->buf.buffer; argp->returnlen = dlen; parsestat = NSS_SUCCESS; } } else { parsestat = (*argp->str2ent)(buf, dlen, argp->buf.result, argp->buf.buffer, argp->buf.buflen); } free(buf); return (parsestat); } static compat_backend_op_t group_ops[] = { _nss_compat_destr, _nss_compat_endent, _nss_compat_setent, _nss_compat_getent, getbyname, getbygid, getbymember }; /*ARGSUSED*/ nss_backend_t * _nss_compat_group_constr(dummy1, dummy2, dummy3) const char *dummy1, *dummy2, *dummy3; { return (_nss_compat_constr(group_ops, sizeof (group_ops) / sizeof (group_ops[0]), GF_PATH, NSS_LINELEN_GROUP, &db_root, _nss_initf_group_compat, 0, get_grname, merge_grents)); }