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 2008 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 #include <grp.h> 47 #include <stdlib.h> 48 #include <unistd.h> /* for GF_PATH */ 49 #include <strings.h> 50 #include "compat_common.h" 51 52 static DEFINE_NSS_DB_ROOT(db_root); 53 54 static void 55 _nss_initf_group_compat(p) 56 nss_db_params_t *p; 57 { 58 p->name = NSS_DBNAM_GROUP; 59 p->config_name = NSS_DBNAM_GROUP_COMPAT; 60 p->default_config = NSS_DEFCONF_GROUP_COMPAT; 61 } 62 63 /* 64 * Validates group entry replacing gid > MAXUID by GID_NOBODY. 65 */ 66 int 67 validate_group_ids(char *line, int *linelenp, int buflen, int extra_chars) 68 { 69 char *linep, *limit, *gidp; 70 ulong_t gid; 71 int oldgidlen, idlen; 72 int linelen = *linelenp, newlinelen; 73 74 if (linelen == 0 || *line == '+' || *line == '-') 75 return (NSS_STR_PARSE_SUCCESS); 76 77 linep = line; 78 limit = line + linelen; 79 80 while (linep < limit && *linep++ != ':') /* skip groupname */ 81 continue; 82 while (linep < limit && *linep++ != ':') /* skip password */ 83 continue; 84 if (linep == limit) 85 return (NSS_STR_PARSE_PARSE); 86 87 gidp = linep; 88 gid = strtoul(gidp, (char **)&linep, 10); /* grab gid */ 89 oldgidlen = linep - gidp; 90 if (linep >= limit || oldgidlen == 0) 91 return (NSS_STR_PARSE_PARSE); 92 93 if (gid <= MAXUID) 94 return (NSS_STR_PARSE_SUCCESS); 95 96 idlen = snprintf(NULL, 0, "%u", GID_NOBODY); 97 newlinelen = linelen + idlen - oldgidlen; 98 if (newlinelen + extra_chars > buflen) 99 return (NSS_STR_PARSE_ERANGE); 100 101 (void) bcopy(linep, gidp + idlen, limit - linep + extra_chars); 102 (void) snprintf(gidp, idlen + 1, "%u", GID_NOBODY); 103 *(gidp + idlen) = ':'; 104 *linelenp = newlinelen; 105 return (NSS_STR_PARSE_SUCCESS); 106 } 107 108 static const char * 109 get_grname(argp) 110 nss_XbyY_args_t *argp; 111 { 112 struct group *g = (struct group *)argp->returnval; 113 114 return (g->gr_name); 115 } 116 117 static int 118 check_grname(argp) 119 nss_XbyY_args_t *argp; 120 { 121 struct group *g = (struct group *)argp->returnval; 122 123 return (strcmp(g->gr_name, argp->key.name) == 0); 124 } 125 126 static nss_status_t 127 getbyname(be, a) 128 compat_backend_ptr_t be; 129 void *a; 130 { 131 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 132 133 return (_nss_compat_XY_all(be, argp, check_grname, 134 NSS_DBOP_GROUP_BYNAME)); 135 } 136 137 static int 138 check_grgid(argp) 139 nss_XbyY_args_t *argp; 140 { 141 struct group *g = (struct group *)argp->returnval; 142 143 return (g->gr_gid == argp->key.gid); 144 } 145 146 static nss_status_t 147 getbygid(be, a) 148 compat_backend_ptr_t be; 149 void *a; 150 { 151 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 152 153 if (argp->key.gid > MAXUID) 154 return (NSS_NOTFOUND); 155 return (_nss_compat_XY_all(be, argp, check_grgid, 156 NSS_DBOP_GROUP_BYGID)); 157 } 158 159 static nss_status_t 160 getbymember(be, a) 161 compat_backend_ptr_t be; 162 void *a; 163 { 164 struct nss_groupsbymem *argp = (struct nss_groupsbymem *)a; 165 int numgids = argp->numgids; 166 int maxgids = argp->maxgids; 167 gid_t *gid_array = argp->gid_array; 168 struct nss_XbyY_args grargs; 169 struct group *g; 170 nss_XbyY_buf_t *gb = NULL, *b = NULL; 171 172 /* 173 * Generic implementation: enumerate using getent(), then check each 174 * group returned by getent() to see whether it contains the user. 175 * There are much faster ways, but at least this one gets the right 176 * answer. 177 */ 178 if (numgids >= maxgids) { 179 /* full gid_array; nobody should have bothered to call us */ 180 return (NSS_SUCCESS); 181 } 182 183 b = NSS_XbyY_ALLOC(&gb, sizeof (struct group), NSS_BUFLEN_GROUP); 184 if (b == 0) 185 return (NSS_UNAVAIL); 186 187 NSS_XbyY_INIT(&grargs, gb->result, gb->buffer, gb->buflen, 188 argp->str2ent); 189 g = (struct group *)gb->result; 190 191 (void) _nss_compat_setent(be, 0); 192 while (_nss_compat_getent(be, &grargs) == NSS_SUCCESS) { 193 char **mem; 194 195 if (grargs.returnval == 0) { 196 continue; 197 } 198 for (mem = g->gr_mem; *mem != 0; mem++) { 199 if (strcmp(*mem, argp->username) == 0) { 200 int gid = g->gr_gid; 201 int i; 202 for (i = 0; i < numgids; i++) { 203 if (gid == gid_array[i]) { 204 break; 205 } 206 } 207 if (i == numgids) { 208 gid_array[numgids++] = gid; 209 argp->numgids = numgids; 210 if (numgids >= maxgids) { 211 /* filled the gid_array */ 212 (void) _nss_compat_endent(be, 213 0); 214 NSS_XbyY_FREE(&gb); 215 return (NSS_SUCCESS); 216 } 217 /* Done with this group, try next */ 218 break; 219 } 220 } 221 } 222 } 223 (void) _nss_compat_endent(be, 0); 224 NSS_XbyY_FREE(&gb); 225 return (NSS_NOTFOUND); /* Really means "gid_array not full yet" */ 226 } 227 228 /*ARGSUSED*/ 229 static int 230 merge_grents(be, argp, fields) 231 compat_backend_ptr_t be; 232 nss_XbyY_args_t *argp; 233 const char **fields; 234 { 235 struct group *g = (struct group *)argp->buf.result; 236 char *buf; 237 char *s; 238 int parsestat; 239 int dlen; 240 241 /* 242 * We're allowed to override the passwd (has anyone ever actually used 243 * the passwd in a group entry?) and the membership list, but not 244 * the groupname or the gid. 245 * That's what the SunOS 4.x code did; who are we to question it... 246 * 247 * Efficiency is heartlessly abandoned in the quest for simplicity. 248 */ 249 if (fields[1] == 0 && fields[3] == 0 && 250 be->return_string_data != 1) { 251 /* No legal overrides, leave *argp unscathed */ 252 return (NSS_STR_PARSE_SUCCESS); 253 } 254 if ((buf = malloc(NSS_LINELEN_GROUP)) == 0) { 255 return (NSS_STR_PARSE_PARSE); 256 /* Really "out of memory", but PARSE_PARSE will have to do */ 257 } 258 s = buf; 259 (void) snprintf(s, NSS_LINELEN_GROUP, "%s:%s:%u:", 260 g->gr_name, 261 fields[1] != 0 ? fields[1] : g->gr_passwd, 262 g->gr_gid); 263 s += strlen(s); 264 if (fields[3] != 0) { 265 (void) strcpy(s, fields[3]); 266 s += strlen(s); 267 } else { 268 char **memp; 269 270 for (memp = g->gr_mem; *memp != 0; memp++) { 271 size_t len = strlen(*memp); 272 if (s + len + 1 <= buf + NSS_LINELEN_GROUP) { 273 if (memp != g->gr_mem) { 274 *s++ = ','; 275 } 276 (void) memcpy(s, *memp, len); 277 s += len; 278 } else { 279 free(buf); 280 return (NSS_STR_PARSE_ERANGE); 281 } 282 } 283 } 284 285 dlen = s - buf; 286 287 /* 288 * if asked, return the data in /etc file format 289 */ 290 if (be->return_string_data == 1) { 291 /* reset the result ptr to the original value */ 292 argp->buf.result = NULL; 293 294 if (dlen > argp->buf.buflen) { 295 parsestat = NSS_STR_PARSE_ERANGE; 296 } else { 297 (void) strncpy(argp->buf.buffer, buf, dlen); 298 argp->returnval = argp->buf.buffer; 299 argp->returnlen = dlen; 300 parsestat = NSS_SUCCESS; 301 } 302 } else { 303 parsestat = (*argp->str2ent)(buf, dlen, 304 argp->buf.result, 305 argp->buf.buffer, 306 argp->buf.buflen); 307 } 308 309 free(buf); 310 return (parsestat); 311 } 312 313 static compat_backend_op_t group_ops[] = { 314 _nss_compat_destr, 315 _nss_compat_endent, 316 _nss_compat_setent, 317 _nss_compat_getent, 318 getbyname, 319 getbygid, 320 getbymember 321 }; 322 323 /*ARGSUSED*/ 324 nss_backend_t * 325 _nss_compat_group_constr(dummy1, dummy2, dummy3) 326 const char *dummy1, *dummy2, *dummy3; 327 { 328 return (_nss_compat_constr(group_ops, 329 sizeof (group_ops) / sizeof (group_ops[0]), 330 GF_PATH, 331 NSS_LINELEN_GROUP, 332 &db_root, 333 _nss_initf_group_compat, 334 0, 335 get_grname, 336 merge_grents)); 337 } 338