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