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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * nis/getgrent.c -- "nis" backend for nsswitch "group" database 28 */ 29 30 #include <grp.h> 31 #include <pwd.h> 32 #include "nis_common.h" 33 #include <ctype.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <rpc/auth.h> /* for MAXNETNAMELEN */ 37 38 static nss_status_t netid_lookup(struct nss_groupsbymem *argp); 39 40 static nss_status_t 41 getbyname(be, a) 42 nis_backend_ptr_t be; 43 void *a; 44 { 45 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 46 47 return (_nss_nis_lookup(be, argp, 0, 48 "group.byname", argp->key.name, 0)); 49 } 50 51 static nss_status_t 52 getbygid(be, a) 53 nis_backend_ptr_t be; 54 void *a; 55 { 56 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 57 char gidstr[12]; /* More than enough */ 58 59 if (argp->key.gid > MAXUID) 60 return (NSS_NOTFOUND); 61 (void) snprintf(gidstr, 12, "%d", argp->key.gid); 62 return (_nss_nis_lookup(be, argp, 0, "group.bygid", gidstr, 0)); 63 } 64 65 /* 66 * Validates group entry replacing gid > MAXUID by GID_NOBODY. 67 */ 68 int 69 validate_group_ids(char **linepp, int *linelenp, int allocbuf) 70 { 71 char *linep, *limit, *gidp, *newline; 72 ulong_t gid; 73 int oldgidlen, idlen; 74 int linelen = *linelenp, newlinelen; 75 76 linep = *linepp; 77 limit = linep + linelen; 78 79 /* +/- entries valid for compat source only */ 80 if (linelen == 0 || *linep == '+' || *linep == '-') 81 return (NSS_STR_PARSE_SUCCESS); 82 83 while (linep < limit && *linep++ != ':') /* skip groupname */ 84 continue; 85 while (linep < limit && *linep++ != ':') /* skip password */ 86 continue; 87 if (linep == limit) 88 return (NSS_STR_PARSE_PARSE); 89 90 gidp = linep; 91 gid = strtoul(gidp, (char **)&linep, 10); /* grab gid */ 92 oldgidlen = linep - gidp; 93 if (linep >= limit || oldgidlen == 0) 94 return (NSS_STR_PARSE_PARSE); 95 96 if (gid <= MAXUID) 97 return (NSS_STR_PARSE_SUCCESS); 98 99 idlen = snprintf(NULL, 0, "%u", GID_NOBODY); 100 newlinelen = linelen + idlen - oldgidlen; 101 if (newlinelen > linelen) { 102 /* need a larger buffer */ 103 if (!allocbuf || (newline = malloc(newlinelen + 1)) == NULL) 104 return (NSS_STR_PARSE_ERANGE); 105 /* Replace ephemeral ids by ID_NOBODY in the new buffer */ 106 *(gidp - 1) = '\0'; 107 (void) snprintf(newline, newlinelen + 1, "%s:%u%s", 108 *linepp, GID_NOBODY, linep); 109 free(*linepp); 110 *linepp = newline; 111 *linelenp = newlinelen; 112 return (NSS_STR_PARSE_SUCCESS); 113 } 114 115 /* Replace ephemeral gid by GID_NOBODY in the same buffer */ 116 (void) bcopy(linep, gidp + idlen, limit - linep + 1); 117 (void) snprintf(gidp, idlen + 1, "%u", GID_NOBODY); 118 *(gidp + idlen) = ':'; 119 *linelenp = newlinelen; 120 return (NSS_STR_PARSE_SUCCESS); 121 } 122 123 static nss_status_t 124 getbymember(be, a) 125 nis_backend_ptr_t be; 126 void *a; 127 { 128 struct nss_groupsbymem *argp = (struct nss_groupsbymem *)a; 129 130 if (strcmp(argp->username, "root") == 0) { 131 /* 132 * Assume that "root" can only sensibly be in /etc/group, 133 * not in NIS or NIS+ 134 * If we don't do this, a hung name-service may cause 135 * a root login or su to hang. 136 */ 137 return (NSS_NOTFOUND); 138 } 139 140 if (argp->force_slow_way != 1) { 141 switch (netid_lookup(argp)) { 142 case NSS_SUCCESS: 143 /* 144 * Return SUCESS only if array is full. Explained 145 * in <nss_dbdefs.h>. 146 */ 147 return ((argp->numgids == argp->maxgids) 148 ? NSS_SUCCESS 149 : NSS_NOTFOUND); 150 case NSS_NOTFOUND: 151 case NSS_UNAVAIL: 152 /* 153 * Failover to group map search if no luck with netid. 154 */ 155 break; 156 case NSS_TRYAGAIN: 157 return (NSS_TRYAGAIN); 158 } 159 } 160 161 return (_nss_nis_do_all(be, argp, argp->username, 162 (nis_do_all_func_t)argp->process_cstr)); 163 } 164 165 static nis_backend_op_t group_ops[] = { 166 _nss_nis_destr, 167 _nss_nis_endent, 168 _nss_nis_setent, 169 _nss_nis_getent_rigid, 170 getbyname, 171 getbygid, 172 getbymember 173 }; 174 175 /*ARGSUSED*/ 176 nss_backend_t * 177 _nss_nis_group_constr(dummy1, dummy2, dummy3) 178 const char *dummy1, *dummy2, *dummy3; 179 { 180 return (_nss_nis_constr(group_ops, 181 sizeof (group_ops) / sizeof (group_ops[0]), 182 "group.byname")); 183 } 184 185 /* 186 * Add gid to gid_array if it's not already there. gid_array must have room 187 * for one more entry. Return new size of array. 188 */ 189 static int 190 add_gid(gid_t gid_array[], int numgids, gid_t gid) 191 { 192 int i = 0; 193 194 for (i = 0; i < numgids; i++) { 195 if (gid_array[i] == gid) { 196 return (numgids); 197 } 198 } 199 gid_array[numgids++] = gid; 200 return (numgids); 201 } 202 203 /* 204 * Given buf, a null-terminated string containing the result of a successful 205 * netid lookup, add the gids to the gid_array. The string may contain extra 206 * whitesapce. On parse error, the valid portion of the gid_array is not 207 * modified. 208 */ 209 static int 210 parse_netid(const char *buf, gid_t gid_array[], int maxgids, int *numgids_ptr) 211 { 212 int numgids = *numgids_ptr; 213 char *buf_next; 214 gid_t gid; 215 long value; 216 217 /* Scan past "<uid>:" */ 218 while (isspace(*buf) || isdigit(*buf)) { 219 buf++; 220 } 221 222 if (*buf++ != ':') { 223 return (NSS_STR_PARSE_PARSE); 224 } 225 226 /* buf should now point to a comma-separated list of gids */ 227 while (*buf != '\0' && *buf != '\n') { 228 errno = 0; 229 value = strtol(buf, &buf_next, 10); 230 231 if (buf == buf_next) { 232 return (NSS_STR_PARSE_PARSE); 233 } else if ((value == LONG_MAX && errno == ERANGE) || 234 (ulong_t)value > INT_MAX) { 235 return (NSS_STR_PARSE_ERANGE); 236 } 237 238 gid = (gid_t)value; 239 if (numgids < maxgids) { 240 numgids = add_gid(gid_array, numgids, gid); 241 } 242 buf = buf_next; 243 if (*buf == ',') { 244 buf++; 245 } 246 } 247 *numgids_ptr = numgids; 248 return (NSS_STR_PARSE_SUCCESS); 249 } 250 251 252 /* 253 * Perform a lookup in the netid map. Fill in the gid_array if successful. 254 * Return values are like those for _nss_nis_lookup(). 255 */ 256 static nss_status_t 257 netid_lookup(struct nss_groupsbymem *argp) 258 { 259 const char *domain = _nss_nis_domain(); 260 struct passwd pw; 261 char pwbuf[NSS_BUFLEN_PASSWD]; 262 char netname[MAXNETNAMELEN + 1]; 263 nss_status_t res; 264 char *val; 265 int vallen; 266 int parse_res; 267 char *lasts; 268 269 /* 270 * Need to build up the netname for the user manually. Can't use 271 * user2netname() rpc library call, since that does all sorts of 272 * extra stuff based upon its own private name-service switch. 273 * 274 * Note that "root" has no user netname so return in error. 275 */ 276 if ((getpwnam_r(argp->username, &pw, pwbuf, sizeof (pwbuf)) == NULL) || 277 (pw.pw_uid == 0)) { 278 return (NSS_UNAVAIL); 279 } 280 if (snprintf(netname, MAXNETNAMELEN + 1, "unix.%d@%s", 281 pw.pw_uid, domain) < 0) { 282 return (NSS_UNAVAIL); 283 } 284 285 if ((res = _nss_nis_ypmatch(domain, "netid.byname", netname, 286 &val, &vallen, 0)) != NSS_SUCCESS) { 287 return (res); 288 } 289 290 (void) strtok_r(val, "#", &lasts); 291 292 parse_res = parse_netid(val, argp->gid_array, argp->maxgids, 293 &argp->numgids); 294 free(val); 295 return ((parse_res == NSS_STR_PARSE_SUCCESS) 296 ? NSS_SUCCESS : NSS_NOTFOUND); 297 } 298