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 2009 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
getbyname(be,a)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
getbygid(be,a)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
validate_group_ids(char ** linepp,int * linelenp,int allocbuf)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
getbymember(be,a)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
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 *
_nss_nis_group_constr(dummy1,dummy2,dummy3)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
add_gid(gid_t gid_array[],int numgids,gid_t gid)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
parse_netid(const char * buf,gid_t gid_array[],int maxgids,int * numgids_ptr)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
netid_lookup(struct nss_groupsbymem * argp)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