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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * getgrent.c 24 * 25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 * 28 * lib/nsswitch/compat/getgrent.c -- name-service-switch backend for getgrnam() 29 * et al that does 4.x compatibility. It looks in /etc/group; if it finds 30 * group entries there that begin with "+" or "-", it consults other 31 * services. By default it uses NIS (YP), but the user can override this 32 * with a "group_compat" entry in /etc/nsswitch.conf, e.g. 33 * group_compat: nisplus 34 * 35 * This code tries to produce the same results as the 4.x code, even when 36 * the latter seems ill thought-out. Bug-compatible, in other words. 37 * Though we do try to be more reasonable about the format of "+" and "-" 38 * entries here, i.e. you don't have to pad them with spurious colons and 39 * bogus uid/gid values. 40 * 41 * Caveats: 42 * - More than one source may be specified, with the usual switch semantics, 43 * but having multiple sources here is definitely odd. 44 * - People who recursively specify "compat" deserve what they get. 45 */ 46 47 #pragma ident "%Z%%M% %I% %E% SMI" 48 49 #include <grp.h> 50 #include <stdlib.h> 51 #include <unistd.h> /* for GF_PATH */ 52 #include <strings.h> 53 #include "compat_common.h" 54 55 static DEFINE_NSS_DB_ROOT(db_root); 56 57 static void 58 _nss_initf_group_compat(p) 59 nss_db_params_t *p; 60 { 61 p->name = NSS_DBNAM_GROUP; 62 p->config_name = NSS_DBNAM_GROUP_COMPAT; 63 p->default_config = NSS_DEFCONF_GROUP_COMPAT; 64 } 65 66 static const char * 67 get_grname(argp) 68 nss_XbyY_args_t *argp; 69 { 70 struct group *g = (struct group *)argp->returnval; 71 72 return (g->gr_name); 73 } 74 75 static int 76 check_grname(argp) 77 nss_XbyY_args_t *argp; 78 { 79 struct group *g = (struct group *)argp->returnval; 80 81 return (strcmp(g->gr_name, argp->key.name) == 0); 82 } 83 84 static nss_status_t 85 getbyname(be, a) 86 compat_backend_ptr_t be; 87 void *a; 88 { 89 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 90 91 return (_nss_compat_XY_all(be, argp, check_grname, 92 NSS_DBOP_GROUP_BYNAME)); 93 } 94 95 static int 96 check_grgid(argp) 97 nss_XbyY_args_t *argp; 98 { 99 struct group *g = (struct group *)argp->returnval; 100 101 return (g->gr_gid == argp->key.gid); 102 } 103 104 static nss_status_t 105 getbygid(be, a) 106 compat_backend_ptr_t be; 107 void *a; 108 { 109 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 110 111 return (_nss_compat_XY_all(be, argp, check_grgid, 112 NSS_DBOP_GROUP_BYGID)); 113 } 114 115 static nss_status_t 116 getbymember(be, a) 117 compat_backend_ptr_t be; 118 void *a; 119 { 120 struct nss_groupsbymem *argp = (struct nss_groupsbymem *)a; 121 int numgids = argp->numgids; 122 int maxgids = argp->maxgids; 123 gid_t *gid_array = argp->gid_array; 124 struct nss_XbyY_args grargs; 125 struct group *g; 126 nss_XbyY_buf_t *gb = NULL, *b = NULL; 127 128 /* 129 * Generic implementation: enumerate using getent(), then check each 130 * group returned by getent() to see whether it contains the user. 131 * There are much faster ways, but at least this one gets the right 132 * answer. 133 */ 134 if (numgids >= maxgids) { 135 /* full gid_array; nobody should have bothered to call us */ 136 return (NSS_SUCCESS); 137 } 138 139 b = NSS_XbyY_ALLOC(&gb, sizeof (struct group), NSS_BUFLEN_GROUP); 140 if (b == 0) 141 return (NSS_UNAVAIL); 142 143 NSS_XbyY_INIT(&grargs, gb->result, gb->buffer, gb->buflen, 144 argp->str2ent); 145 g = (struct group *)gb->result; 146 147 (void) _nss_compat_setent(be, 0); 148 while (_nss_compat_getent(be, &grargs) == NSS_SUCCESS) { 149 char **mem; 150 151 if (grargs.returnval == 0) { 152 continue; 153 } 154 for (mem = g->gr_mem; *mem != 0; mem++) { 155 if (strcmp(*mem, argp->username) == 0) { 156 int gid = g->gr_gid; 157 int i; 158 for (i = 0; i < numgids; i++) { 159 if (gid == gid_array[i]) { 160 break; 161 } 162 } 163 if (i == numgids) { 164 gid_array[numgids++] = gid; 165 argp->numgids = numgids; 166 if (numgids >= maxgids) { 167 /* filled the gid_array */ 168 (void) _nss_compat_endent(be, 169 0); 170 NSS_XbyY_FREE(&gb); 171 return (NSS_SUCCESS); 172 } 173 /* Done with this group, try next */ 174 break; 175 } 176 } 177 } 178 } 179 (void) _nss_compat_endent(be, 0); 180 NSS_XbyY_FREE(&gb); 181 return (NSS_NOTFOUND); /* Really means "gid_array not full yet" */ 182 } 183 184 /*ARGSUSED*/ 185 static int 186 merge_grents(be, argp, fields) 187 compat_backend_ptr_t be; 188 nss_XbyY_args_t *argp; 189 const char **fields; 190 { 191 struct group *g = (struct group *)argp->buf.result; 192 char *buf; 193 char *s; 194 int parsestat; 195 196 /* 197 * We're allowed to override the passwd (has anyone ever actually used 198 * the passwd in a group entry?) and the membership list, but not 199 * the groupname or the gid. 200 * That's what the SunOS 4.x code did; who are we to question it... 201 * 202 * Efficiency is heartlessly abandoned in the quest for simplicity. 203 */ 204 if (fields[1] == 0 && fields[3] == 0) { 205 /* No legal overrides, leave *argp unscathed */ 206 return (NSS_STR_PARSE_SUCCESS); 207 } 208 if ((buf = malloc(NSS_LINELEN_GROUP)) == 0) { 209 return (NSS_STR_PARSE_PARSE); 210 /* Really "out of memory", but PARSE_PARSE will have to do */ 211 } 212 s = buf; 213 (void) snprintf(s, NSS_LINELEN_GROUP, "%s:%s:%d:", 214 g->gr_name, 215 fields[1] != 0 ? fields[1] : g->gr_passwd, 216 g->gr_gid); 217 s += strlen(s); 218 if (fields[3] != 0) { 219 strcpy(s, fields[3]); 220 s += strlen(s); 221 } else { 222 char **memp; 223 224 for (memp = g->gr_mem; *memp != 0; memp++) { 225 size_t len = strlen(*memp); 226 if (s + len + 1 <= buf + NSS_LINELEN_GROUP) { 227 if (memp != g->gr_mem) { 228 *s++ = ','; 229 } 230 (void) memcpy(s, *memp, len); 231 s += len; 232 } else { 233 free(buf); 234 return (NSS_STR_PARSE_ERANGE); 235 } 236 } 237 } 238 parsestat = (*argp->str2ent)(buf, s - buf, 239 argp->buf.result, 240 argp->buf.buffer, 241 argp->buf.buflen); 242 free(buf); 243 return (parsestat); 244 } 245 246 static compat_backend_op_t group_ops[] = { 247 _nss_compat_destr, 248 _nss_compat_endent, 249 _nss_compat_setent, 250 _nss_compat_getent, 251 getbyname, 252 getbygid, 253 getbymember 254 }; 255 256 /*ARGSUSED*/ 257 nss_backend_t * 258 _nss_compat_group_constr(dummy1, dummy2, dummy3) 259 const char *dummy1, *dummy2, *dummy3; 260 { 261 return (_nss_compat_constr(group_ops, 262 sizeof (group_ops) / sizeof (group_ops[0]), 263 GF_PATH, 264 NSS_LINELEN_GROUP, 265 &db_root, 266 _nss_initf_group_compat, 267 0, 268 get_grname, 269 merge_grents)); 270 } 271