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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
26 */
27
28 #include "lint.h"
29 #include <mtlib.h>
30 #include <sys/types.h>
31 #include <grp.h>
32 #include <memory.h>
33 #include <deflt.h>
34 #include <nsswitch.h>
35 #include <nss_dbdefs.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <synch.h>
40 #include <sys/param.h>
41 #include <sys/mman.h>
42 #include <errno.h>
43
44 extern int _getgroupsbymember(const char *, gid_t[], int, int);
45 int str2group(const char *, int, void *, char *, int);
46
47 static DEFINE_NSS_DB_ROOT(db_root);
48 static DEFINE_NSS_GETENT(context);
49
50 #define USE_NETID_STR "NETID_AUTHORITATIVE=TRUE"
51
52 void
_nss_initf_group(nss_db_params_t * p)53 _nss_initf_group(nss_db_params_t *p)
54 {
55 p->name = NSS_DBNAM_GROUP;
56 p->default_config = NSS_DEFCONF_GROUP;
57 }
58
59 #include <getxby_door.h>
60 #include <sys/door.h>
61
62 struct group *
63 _uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
64 int buflen);
65
66 struct group *
67 _uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen);
68
69 /*
70 * POSIX.1c Draft-6 version of the function getgrnam_r.
71 * It was implemented by Solaris 2.3.
72 */
73 struct group *
getgrnam_r(const char * name,struct group * result,char * buffer,int buflen)74 getgrnam_r(const char *name, struct group *result, char *buffer, int buflen)
75 {
76 nss_XbyY_args_t arg;
77
78 if (name == (const char *)NULL) {
79 errno = ERANGE;
80 return (NULL);
81 }
82 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
83 arg.key.name = name;
84 (void) nss_search(&db_root, _nss_initf_group,
85 NSS_DBOP_GROUP_BYNAME, &arg);
86 return ((struct group *)NSS_XbyY_FINI(&arg));
87 }
88
89 /*
90 * POSIX.1c Draft-6 version of the function getgrgid_r.
91 * It was implemented by Solaris 2.3.
92 */
93 struct group *
getgrgid_r(gid_t gid,struct group * result,char * buffer,int buflen)94 getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen)
95 {
96 nss_XbyY_args_t arg;
97
98 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
99 arg.key.gid = gid;
100 (void) nss_search(&db_root, _nss_initf_group,
101 NSS_DBOP_GROUP_BYGID, &arg);
102 return ((struct group *)NSS_XbyY_FINI(&arg));
103 }
104
105 struct group *
_uncached_getgrgid_r(gid_t gid,struct group * result,char * buffer,int buflen)106 _uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer,
107 int buflen)
108 {
109 nss_XbyY_args_t arg;
110
111 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
112 arg.key.gid = gid;
113 (void) nss_search(&db_root, _nss_initf_group,
114 NSS_DBOP_GROUP_BYGID, &arg);
115 return ((struct group *)NSS_XbyY_FINI(&arg));
116 }
117
118 /*
119 * POSIX.1c standard version of the function getgrgid_r.
120 * User gets it via static getgrgid_r from the header file.
121 */
122 int
__posix_getgrgid_r(gid_t gid,struct group * grp,char * buffer,size_t bufsize,struct group ** result)123 __posix_getgrgid_r(gid_t gid, struct group *grp, char *buffer,
124 size_t bufsize, struct group **result)
125 {
126 int nerrno = 0;
127 int oerrno = errno;
128
129 errno = 0;
130 if ((*result = getgrgid_r(gid, grp, buffer, (uintptr_t)bufsize))
131 == NULL) {
132 nerrno = errno;
133 }
134 errno = oerrno;
135 return (nerrno);
136 }
137
138 struct group *
_uncached_getgrnam_r(const char * name,struct group * result,char * buffer,int buflen)139 _uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
140 int buflen)
141 {
142 nss_XbyY_args_t arg;
143
144 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
145 arg.key.name = name;
146 (void) nss_search(&db_root, _nss_initf_group,
147 NSS_DBOP_GROUP_BYNAME, &arg);
148 return ((struct group *)NSS_XbyY_FINI(&arg));
149 }
150
151 /*
152 * POSIX.1c standard version of the function getgrnam_r.
153 * User gets it via static getgrnam_r from the header file.
154 */
155 int
__posix_getgrnam_r(const char * name,struct group * grp,char * buffer,size_t bufsize,struct group ** result)156 __posix_getgrnam_r(const char *name, struct group *grp, char *buffer,
157 size_t bufsize, struct group **result)
158 {
159 int nerrno = 0;
160 int oerrno = errno;
161
162 if ((*result = getgrnam_r(name, grp, buffer, (uintptr_t)bufsize))
163 == NULL) {
164 nerrno = errno;
165 }
166 errno = oerrno;
167 return (nerrno);
168 }
169
170 void
setgrent(void)171 setgrent(void)
172 {
173 nss_setent(&db_root, _nss_initf_group, &context);
174 }
175
176 void
endgrent(void)177 endgrent(void)
178 {
179 nss_endent(&db_root, _nss_initf_group, &context);
180 nss_delete(&db_root);
181 }
182
183 struct group *
getgrent_r(struct group * result,char * buffer,int buflen)184 getgrent_r(struct group *result, char *buffer, int buflen)
185 {
186 nss_XbyY_args_t arg;
187 char *nam;
188
189 /* In getXXent_r(), protect the unsuspecting caller from +/- entries */
190
191 do {
192 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
193 /* No key to fill in */
194 (void) nss_getent(&db_root, _nss_initf_group, &context, &arg);
195 } while (arg.returnval != 0 &&
196 (nam = ((struct group *)arg.returnval)->gr_name) != 0 &&
197 (*nam == '+' || *nam == '-'));
198
199 return ((struct group *)NSS_XbyY_FINI(&arg));
200 }
201
202 struct group *
fgetgrent_r(FILE * f,struct group * result,char * buffer,int buflen)203 fgetgrent_r(FILE *f, struct group *result, char *buffer, int buflen)
204 {
205 extern void _nss_XbyY_fgets(FILE *, nss_XbyY_args_t *);
206 nss_XbyY_args_t arg;
207
208 /* ... but in fgetXXent_r, the caller deserves any +/- entry it gets */
209
210 /* No key to fill in */
211 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
212 _nss_XbyY_fgets(f, &arg);
213 return ((struct group *)NSS_XbyY_FINI(&arg));
214 }
215
216 /*
217 * _getgroupsbymember(uname, gid_array, maxgids, numgids):
218 * Private interface for initgroups(). It returns the group ids of
219 * groups of which the specified user is a member.
220 *
221 * Arguments:
222 * username Username of the putative member
223 * gid_array Space in which to return the gids. The first [numgids]
224 * elements are assumed to already contain valid gids.
225 * maxgids Maximum number of elements in gid_array.
226 * numgids Number of elements (normally 0 or 1) that already contain
227 * valid gids.
228 * Return value:
229 * number of valid gids in gid_array (may be zero)
230 * or
231 * -1 (and errno set appropriately) on errors (none currently defined)
232 *
233 * NSS2 Consistency enhancements:
234 * The "files normal" format between an application and nscd for the
235 * NSS_DBOP_GROUP_BYMEMBER nss_search operation is defined to be a
236 * processed array of numgids [up to maxgids] gid_t values. gid_t
237 * values in the array are unique.
238 */
239
240 extern nss_status_t process_cstr(const char *, int, struct nss_groupsbymem *);
241
242 int
_getgroupsbymember(const char * username,gid_t gid_array[],int maxgids,int numgids)243 _getgroupsbymember(const char *username, gid_t gid_array[],
244 int maxgids, int numgids)
245 {
246 struct nss_groupsbymem arg;
247 void *defp;
248
249 arg.username = username;
250 arg.gid_array = gid_array;
251 arg.maxgids = maxgids;
252 arg.numgids = numgids;
253 /*
254 * In backwards compatibility mode, use the old str2group &
255 * process_cstr interfaces. Ditto within nscd processing.
256 */
257 arg.str2ent = str2group;
258 arg.process_cstr = process_cstr;
259
260 /*
261 * The old value being provided here was 0, ie do the quick
262 * way. Given that this was never actually used under NIS
263 * and had the wrong (now corrected) meaning for NIS+ we need
264 * to change the default to be 1 (TRUE) as we now need the
265 * admin to decided to use netid, setting NETID_AUTHORITATIVE
266 * in /etc/default/nss to TRUE gets us a value of 0 for
267 * force_slow_way - don't you just love double negatives ;-)
268 *
269 * We need to do this to preserve the behaviour seen when the
270 * admin makes no changes.
271 */
272 arg.force_slow_way = 1;
273
274 if ((defp = defopen_r(__NSW_DEFAULT_FILE)) != NULL) {
275 if (defread_r(USE_NETID_STR, defp) != NULL)
276 arg.force_slow_way = 0;
277 defclose_r(defp);
278 }
279
280 (void) nss_search(&db_root, _nss_initf_group,
281 NSS_DBOP_GROUP_BYMEMBER, &arg);
282
283 return (arg.numgids);
284 }
285
286
287 static char *
gettok(char ** nextpp,char sep)288 gettok(char **nextpp, char sep)
289 {
290 char *p = *nextpp;
291 char *q = p;
292 char c;
293
294 if (p == 0)
295 return (0);
296
297 while ((c = *q) != '\0' && c != sep)
298 q++;
299
300 if (c == '\0')
301 *nextpp = 0;
302 else {
303 *q++ = '\0';
304 *nextpp = q;
305 }
306 return (p);
307 }
308
309 /*
310 * Return values: 0 = success, 1 = parse error, 2 = erange ...
311 * The structure pointer passed in is a structure in the caller's space
312 * wherein the field pointers would be set to areas in the buffer if
313 * need be. instring and buffer should be separate areas.
314 */
315 int
str2group(const char * instr,int lenstr,void * ent,char * buffer,int buflen)316 str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
317 {
318 struct group *group = (struct group *)ent;
319 char *p, *next;
320 int black_magic; /* "+" or "-" entry */
321 char **memlist, **limit;
322 ulong_t tmp;
323
324 if (lenstr + 1 > buflen)
325 return (NSS_STR_PARSE_ERANGE);
326
327 /*
328 * We copy the input string into the output buffer and
329 * operate on it in place.
330 */
331 if (instr != buffer) {
332 /* Overlapping buffer copies are OK */
333 (void) memmove(buffer, instr, lenstr);
334 buffer[lenstr] = '\0';
335 }
336
337 /* quick exit do not entry fill if not needed */
338 if (ent == (void *)NULL)
339 return (NSS_STR_PARSE_SUCCESS);
340
341 next = buffer;
342
343 /*
344 * Parsers for passwd and group have always been pretty rigid;
345 * we wouldn't want to buck a Unix tradition
346 */
347
348 group->gr_name = p = gettok(&next, ':');
349 if (*p == '\0') {
350 /* Empty group-name; not allowed */
351 return (NSS_STR_PARSE_PARSE);
352 }
353
354 /* Always return at least an empty gr_mem list */
355 memlist = (char **)ROUND_UP(buffer + lenstr + 1, sizeof (char *));
356 limit = (char **)ROUND_DOWN(buffer + buflen, sizeof (char *));
357 *memlist = 0;
358 group->gr_mem = memlist;
359
360 black_magic = (*p == '+' || *p == '-');
361 if (black_magic) {
362 /* Then the rest of the group entry is optional */
363 group->gr_passwd = 0;
364 group->gr_gid = 0;
365 }
366
367 group->gr_passwd = p = gettok(&next, ':');
368 if (p == 0) {
369 if (black_magic)
370 return (NSS_STR_PARSE_SUCCESS);
371 else
372 return (NSS_STR_PARSE_PARSE);
373 }
374
375 p = next; /* gid */
376 if (p == 0 || *p == '\0') {
377 if (black_magic)
378 return (NSS_STR_PARSE_SUCCESS);
379 else
380 return (NSS_STR_PARSE_PARSE);
381 }
382 if (!black_magic) {
383 errno = 0;
384 tmp = strtoul(p, &next, 10);
385 if (next == p || errno != 0) {
386 /* gid field should be nonempty */
387 /* also check errno from strtoul */
388 return (NSS_STR_PARSE_PARSE);
389 }
390 if (tmp >= UINT32_MAX)
391 group->gr_gid = GID_NOBODY;
392 else
393 group->gr_gid = (gid_t)tmp;
394 }
395 if (*next++ != ':') {
396 /* Parse error, even for a '+' entry (which should have */
397 /* an empty gid field, since it's always overridden) */
398 return (NSS_STR_PARSE_PARSE);
399 }
400
401 /* === Could check and complain if there are any extra colons */
402 while (memlist < limit) {
403 p = gettok(&next, ',');
404 if (p == 0 || *p == '\0') {
405 *memlist = 0;
406 /* Successfully parsed and stored */
407 return (NSS_STR_PARSE_SUCCESS);
408 }
409 *memlist++ = p;
410 }
411 /* Out of space; error even for black_magic */
412 return (NSS_STR_PARSE_ERANGE);
413 }
414
415 nss_status_t
process_cstr(const char * instr,int instr_len,struct nss_groupsbymem * gbm)416 process_cstr(const char *instr, int instr_len, struct nss_groupsbymem *gbm)
417 {
418 /*
419 * It's possible to do a much less inefficient version of this by
420 * selectively duplicating code from str2group(). For now,
421 * however, we'll take the easy way out and implement this on
422 * top of str2group().
423 */
424
425 const char *username = gbm->username;
426 nss_XbyY_buf_t *buf;
427 struct group *grp;
428 char **memp;
429 char *mem;
430 int parsestat;
431
432 buf = _nss_XbyY_buf_alloc(sizeof (struct group), NSS_BUFLEN_GROUP);
433 if (buf == 0)
434 return (NSS_UNAVAIL);
435
436 grp = (struct group *)buf->result;
437
438 parsestat = (*gbm->str2ent)(instr, instr_len,
439 grp, buf->buffer, buf->buflen);
440
441 if (parsestat != NSS_STR_PARSE_SUCCESS) {
442 _nss_XbyY_buf_free(buf);
443 return (NSS_NOTFOUND); /* === ? */
444 }
445
446 if (grp->gr_mem) {
447 for (memp = grp->gr_mem; (memp) && ((mem = *memp) != 0);
448 memp++) {
449 if (strcmp(mem, username) == 0) {
450 gid_t gid = grp->gr_gid;
451 gid_t *gidp = gbm->gid_array;
452 int numgids = gbm->numgids;
453 int i;
454
455 _nss_XbyY_buf_free(buf);
456
457 for (i = 0; i < numgids && *gidp != gid; i++,
458 gidp++) {
459 ;
460 }
461 if (i >= numgids) {
462 if (i >= gbm->maxgids) {
463 /* Filled the array; stop searching */
464 return (NSS_SUCCESS);
465 }
466 *gidp = gid;
467 gbm->numgids = numgids + 1;
468 }
469 return (NSS_NOTFOUND); /* Explained in */
470 /* <nss_dbdefs.h> */
471 }
472 }
473 }
474 _nss_XbyY_buf_free(buf);
475 return (NSS_NOTFOUND);
476 }
477