Print this page
12236 getmembers_DN doesn't properly handle errors from __ns_ldap_dn2uid
12240 nss_ldap does not properly look up group members by distinguished name
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/nsswitch/ldap/common/getgrent.c
+++ new/usr/src/lib/nsswitch/ldap/common/getgrent.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
↓ open down ↓ |
15 lines elided |
↑ open up ↑ |
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 *
25 25 * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
26 + * Copyright 2020 Joyent, Inc.
26 27 */
27 28
28 29 #include <grp.h>
29 30 #include "ldap_common.h"
30 31 #include <string.h>
31 32
32 33 /* String which may need to be removed from beginning of group password */
33 34 #define _CRYPT "{CRYPT}"
34 35 #define _NO_PASSWD_VAL ""
35 36
36 37 /* Group attributes filters */
37 38 #define _G_NAME "cn"
38 39 #define _G_GID "gidnumber"
39 40 #define _G_PASSWD "userpassword"
40 41 #define _G_MEMUID "memberuid"
41 42 #define _G_MEM_DN "member" /* DN */
42 43
43 44 #define _F_GETGRNAM "(&(objectClass=posixGroup)(cn=%s))"
44 45 #define _F_GETGRNAM_SSD "(&(%%s)(cn=%s))"
45 46 #define _F_GETGRGID "(&(objectClass=posixGroup)(gidNumber=%u))"
46 47 #define _F_GETGRGID_SSD "(&(%%s)(gidNumber=%u))"
47 48
48 49 /*
49 50 * When searching for groups in which a specified user is a member,
50 51 * there are a few different membership schema that might be in use.
51 52 * We'll use a filter that should work with an of the common ones:
52 53 * "memberUid=NAME", or "member=DN" (try uniquemember too?)
53 54 * The first parameter in the filter string is replaced by username,
54 55 * and the remaining ones by the full DN.
55 56 */
56 57 #define _F_GETGRMEM "(&(objectClass=posixGroup)" \
57 58 "(|(memberUid=%s)(member=%s)))"
58 59 #define _F_GETGRMEM_SSD "(&(%%s)" \
59 60 "(|(memberUid=%s)(member=%s)))"
60 61
61 62 static const char *gr_attrs[] = {
62 63 _G_NAME,
63 64 _G_GID,
64 65 _G_PASSWD,
65 66 _G_MEMUID,
66 67 _G_MEM_DN,
67 68 (char *)NULL
68 69 };
69 70
70 71 static int
71 72 getmembers_UID(char **bufpp, int *lenp, ns_ldap_attr_t *members);
72 73 static int
73 74 getmembers_DN(char **bufpp, int *lenp, ns_ldap_attr_t *members);
74 75
75 76
76 77 /*
77 78 * _nss_ldap_group2str is the data marshaling method for the group getXbyY
78 79 * (e.g., getgrnam(), getgrgid(), getgrent()) backend processes. This method
79 80 * is called after a successful ldap search has been performed. This method
80 81 * will parse the ldap search values into the file format.
81 82 * e.g.
82 83 *
83 84 * adm::4:root,adm,daemon
84 85 *
85 86 */
86 87
87 88 static int
88 89 _nss_ldap_group2str(ldap_backend_ptr be, nss_XbyY_args_t *argp)
89 90 {
90 91 int i;
91 92 int nss_result;
92 93 int buflen = 0, len;
93 94 char *buffer = NULL;
94 95 ns_ldap_result_t *result = be->result;
95 96 char **gname, **passwd, **gid, *password, *end;
96 97 char gid_nobody[NOBODY_STR_LEN];
97 98 char *gid_nobody_v[1];
98 99 ns_ldap_attr_t *members;
99 100
100 101 (void) snprintf(gid_nobody, sizeof (gid_nobody), "%u", GID_NOBODY);
101 102 gid_nobody_v[0] = gid_nobody;
102 103
103 104 if (result == NULL)
104 105 return (NSS_STR_PARSE_PARSE);
105 106 buflen = argp->buf.buflen;
106 107
107 108 if (argp->buf.result != NULL) {
108 109 if ((be->buffer = calloc(1, buflen)) == NULL) {
109 110 nss_result = NSS_STR_PARSE_PARSE;
110 111 goto result_grp2str;
111 112 }
112 113 buffer = be->buffer;
113 114 } else
114 115 buffer = argp->buf.buffer;
115 116
116 117 nss_result = NSS_STR_PARSE_SUCCESS;
117 118 (void) memset(buffer, 0, buflen);
118 119
119 120 gname = __ns_ldap_getAttr(result->entry, _G_NAME);
120 121 if (gname == NULL || gname[0] == NULL || (strlen(gname[0]) < 1)) {
121 122 nss_result = NSS_STR_PARSE_PARSE;
122 123 goto result_grp2str;
123 124 }
124 125 passwd = __ns_ldap_getAttr(result->entry, _G_PASSWD);
125 126 if (passwd == NULL || passwd[0] == NULL || (strlen(passwd[0]) == 0)) {
126 127 /* group password could be NULL, replace it with "" */
127 128 password = _NO_PASSWD_VAL;
128 129 } else {
129 130 /*
130 131 * Preen "{crypt}" if necessary.
131 132 * If the password does not include the {crypt} prefix
132 133 * then the password may be plain text. And thus
133 134 * perhaps crypt(3c) should be used to encrypt it.
134 135 * Currently the password is copied verbatim.
135 136 */
136 137 if (strncasecmp(passwd[0], _CRYPT, strlen(_CRYPT)) == 0)
137 138 password = passwd[0] + strlen(_CRYPT);
138 139 else
139 140 password = passwd[0];
140 141 }
141 142 gid = __ns_ldap_getAttr(result->entry, _G_GID);
142 143 if (gid == NULL || gid[0] == NULL || (strlen(gid[0]) < 1)) {
143 144 nss_result = NSS_STR_PARSE_PARSE;
144 145 goto result_grp2str;
145 146 }
146 147 /* Validate GID */
147 148 if (strtoul(gid[0], &end, 10) > MAXUID)
148 149 gid = gid_nobody_v;
149 150 len = snprintf(buffer, buflen, "%s:%s:%s:", gname[0], password, gid[0]);
150 151 TEST_AND_ADJUST(len, buffer, buflen, result_grp2str);
151 152
152 153 members = __ns_ldap_getAttrStruct(result->entry, _G_MEMUID);
153 154 if (members != NULL && members->attrvalue != NULL) {
154 155 nss_result = getmembers_UID(&buffer, &buflen, members);
155 156 if (nss_result != 0)
156 157 goto result_grp2str;
157 158 }
158 159
159 160 members = __ns_ldap_getAttrStruct(result->entry, _G_MEM_DN);
160 161 if (members != NULL && members->attrvalue != NULL) {
161 162 nss_result = getmembers_DN(&buffer, &buflen, members);
162 163 if (nss_result != 0)
163 164 goto result_grp2str;
164 165 }
165 166
166 167 /* The front end marshaller doesn't need the trailing nulls */
167 168 if (argp->buf.result != NULL)
168 169 be->buflen = strlen(be->buffer);
169 170 result_grp2str:
170 171 (void) __ns_ldap_freeResult(&be->result);
171 172 return (nss_result);
172 173 }
173 174
174 175 /*
175 176 * Process the list values from the "memberUid" attribute of the
176 177 * current group. Note that this list is often empty, and we
177 178 * get the real list of members via getmember_DN (see below).
178 179 */
179 180 static int
180 181 getmembers_UID(char **bufpp, int *lenp, ns_ldap_attr_t *members)
181 182 {
182 183 char *member_str, *strtok_state;
183 184 char *buffer;
184 185 int buflen;
185 186 int i, len;
186 187 int nss_result = 0;
187 188 int firsttime;
188 189
189 190 buffer = *bufpp;
190 191 buflen = *lenp;
191 192 firsttime = (buffer[-1] == ':');
192 193
193 194 for (i = 0; i < members->value_count; i++) {
194 195 member_str = members->attrvalue[i];
195 196 if (member_str == NULL)
196 197 goto out;
197 198
198 199 #ifdef DEBUG
199 200 (void) fprintf(stdout, "getmembers_UID: uid=<%s>\n",
200 201 member_str);
201 202 #endif
202 203 /*
203 204 * If not a valid Unix user name, or
204 205 * not valid in ldap, just skip.
205 206 */
206 207 if (member_str[0] == '\0' ||
207 208 strpbrk(member_str, " ,:=") != NULL)
208 209 continue;
209 210
210 211 if (firsttime) {
211 212 len = snprintf(buffer, buflen, "%s", member_str);
212 213 firsttime = 0;
213 214 } else {
214 215 len = snprintf(buffer, buflen, ",%s", member_str);
215 216 }
216 217 TEST_AND_ADJUST(len, buffer, buflen, out);
217 218 }
218 219
219 220 out:
220 221 *bufpp = buffer;
221 222 *lenp = buflen;
222 223 return (nss_result);
223 224 }
224 225
225 226 /*
226 227 * Process the list values from the "member" attribute of the
227 228 * current group. Note that this list is ONLY one that can be
228 229 * assumed to be non-empty. The problem here is that this list
229 230 * contains the list of members as "distinguished names" (DN),
230 231 * and we want the Unix names (known here as "uid"). We must
231 232 * lookup the "uid" for each DN in the member list. Example:
↓ open down ↓ |
196 lines elided |
↑ open up ↑ |
232 233 * CN=Doe\, John,OU=Users,DC=contoso,DC=com => john.doe
233 234 */
234 235 static int
235 236 getmembers_DN(char **bufpp, int *lenp, ns_ldap_attr_t *members)
236 237 {
237 238 ns_ldap_error_t *error = NULL;
238 239 char *member_dn, *member_uid;
239 240 char *buffer;
240 241 int buflen;
241 242 int i, len;
242 - int nss_result = 0;
243 + int nss_result = 0; /* used by TEST_AND_ADJUST macro */
243 244 int firsttime;
244 245
245 246 buffer = *bufpp;
246 247 buflen = *lenp;
247 248 firsttime = (buffer[-1] == ':');
248 249
249 250 for (i = 0; i < members->value_count; i++) {
250 251 member_dn = members->attrvalue[i];
251 252 if (member_dn == NULL)
252 253 goto out;
253 254
254 255 /*
255 256 * The attribute name was "member", so these should be
↓ open down ↓ |
3 lines elided |
↑ open up ↑ |
256 257 * full distinguished names (DNs). We need to loookup
257 258 * the Unix UID (name) for each.
258 259 */
259 260 #ifdef DEBUG
260 261 (void) fprintf(stdout, "getmembers_DN: dn=%s\n",
261 262 member_dn);
262 263 #endif
263 264 if (member_dn[0] == '\0')
264 265 continue;
265 266
266 - nss_result = __ns_ldap_dn2uid(member_dn,
267 - &member_uid, NULL, &error);
268 - if (nss_result != NS_LDAP_SUCCESS) {
267 + if (__ns_ldap_dn2uid(member_dn,
268 + &member_uid, NULL, &error) != NS_LDAP_SUCCESS) {
269 269 (void) __ns_ldap_freeError(&error);
270 270 error = NULL;
271 271 continue;
272 272 }
273 273 #ifdef DEBUG
274 274 (void) fprintf(stdout, "getmembers_DN: uid=<%s>\n",
275 275 member_uid);
276 276 #endif
277 277 /* Skip invalid names. */
278 278 if (member_uid[0] == '\0' ||
279 279 strpbrk(member_uid, " ,:=") != NULL) {
280 280 free(member_uid);
281 281 continue;
282 282 }
283 283
284 284 if (firsttime) {
285 285 len = snprintf(buffer, buflen, "%s", member_uid);
286 286 firsttime = 0;
287 287 } else {
288 288 len = snprintf(buffer, buflen, ",%s", member_uid);
289 289 }
290 290 free(member_uid);
291 291 TEST_AND_ADJUST(len, buffer, buflen, out);
292 292 }
293 293
294 294 out:
295 295 *bufpp = buffer;
296 296 *lenp = buflen;
297 297 return (nss_result);
298 298 }
299 299
300 300 /*
301 301 * getbynam gets a group entry by name. This function constructs an ldap
302 302 * search filter using the name invocation parameter and the getgrnam search
303 303 * filter defined. Once the filter is constructed, we searche for a matching
304 304 * entry and marshal the data results into struct group for the frontend
305 305 * process. The function _nss_ldap_group2ent performs the data marshaling.
306 306 */
307 307
308 308 static nss_status_t
309 309 getbynam(ldap_backend_ptr be, void *a)
310 310 {
311 311 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
312 312 char searchfilter[SEARCHFILTERLEN];
313 313 char userdata[SEARCHFILTERLEN];
314 314 char groupname[SEARCHFILTERLEN];
315 315 int ret;
316 316
317 317 if (_ldap_filter_name(groupname, argp->key.name, sizeof (groupname)) !=
318 318 0)
319 319 return ((nss_status_t)NSS_NOTFOUND);
320 320
321 321 ret = snprintf(searchfilter, sizeof (searchfilter),
322 322 _F_GETGRNAM, groupname);
323 323 if (ret >= sizeof (searchfilter) || ret < 0)
324 324 return ((nss_status_t)NSS_NOTFOUND);
325 325
326 326 ret = snprintf(userdata, sizeof (userdata), _F_GETGRNAM_SSD, groupname);
327 327 if (ret >= sizeof (userdata) || ret < 0)
328 328 return ((nss_status_t)NSS_NOTFOUND);
329 329
330 330 return ((nss_status_t)_nss_ldap_lookup(be, argp,
331 331 _GROUP, searchfilter, NULL, _merge_SSD_filter, userdata));
332 332 }
333 333
334 334
335 335 /*
336 336 * getbygid gets a group entry by number. This function constructs an ldap
337 337 * search filter using the name invocation parameter and the getgrgid search
338 338 * filter defined. Once the filter is constructed, we searche for a matching
339 339 * entry and marshal the data results into struct group for the frontend
340 340 * process. The function _nss_ldap_group2ent performs the data marshaling.
341 341 */
342 342
343 343 static nss_status_t
344 344 getbygid(ldap_backend_ptr be, void *a)
345 345 {
346 346 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
347 347 char searchfilter[SEARCHFILTERLEN];
348 348 char userdata[SEARCHFILTERLEN];
349 349 int ret;
350 350
351 351 if (argp->key.uid > MAXUID)
352 352 return ((nss_status_t)NSS_NOTFOUND);
353 353
354 354 ret = snprintf(searchfilter, sizeof (searchfilter),
355 355 _F_GETGRGID, argp->key.uid);
356 356 if (ret >= sizeof (searchfilter) || ret < 0)
357 357 return ((nss_status_t)NSS_NOTFOUND);
358 358
359 359 ret = snprintf(userdata, sizeof (userdata),
360 360 _F_GETGRGID_SSD, argp->key.uid);
361 361 if (ret >= sizeof (userdata) || ret < 0)
362 362 return ((nss_status_t)NSS_NOTFOUND);
363 363
364 364 return ((nss_status_t)_nss_ldap_lookup(be, argp,
365 365 _GROUP, searchfilter, NULL, _merge_SSD_filter, userdata));
366 366
367 367 }
368 368
369 369
370 370 /*
371 371 * Use a custom attributes list for getbymember, because the LDAP
372 372 * query for this requests a list of groups, and the result can be
373 373 * very large if it includes the list of members with each group.
374 374 * We don't need or want the list of members in this case.
375 375 */
376 376 static const char *grbymem_attrs[] = {
377 377 _G_NAME, /* cn */
378 378 _G_GID, /* gidnumber */
379 379 (char *)NULL
380 380 };
381 381
382 382 /*
383 383 * getbymember returns all groups a user is defined in. This function
384 384 * uses different architectural procedures than the other group backend
385 385 * system calls because it's a private interface. This function constructs
386 386 * an ldap search filter using the name invocation parameter. Once the
387 387 * filter is constructed, we search for all matching groups counting
388 388 * and storing each group name, gid, etc. Data marshaling is used for
389 389 * group processing. The function _nss_ldap_group2ent() performs the
390 390 * data marshaling.
391 391 *
392 392 * (const char *)argp->username; (size_t)strlen(argp->username);
393 393 * (gid_t)argp->gid_array; (int)argp->maxgids;
394 394 * (int)argp->numgids;
395 395 */
396 396
397 397 static nss_status_t
398 398 getbymember(ldap_backend_ptr be, void *a)
399 399 {
400 400 ns_ldap_error_t *error = NULL;
401 401 int i, j, k;
402 402 int gcnt = (int)0;
403 403 char **groupvalue;
404 404 nss_status_t lstat;
405 405 struct nss_groupsbymem *argp = (struct nss_groupsbymem *)a;
406 406 char searchfilter[SEARCHFILTERLEN];
407 407 char userdata[SEARCHFILTERLEN];
408 408 char name[SEARCHFILTERLEN];
409 409 char escdn[SEARCHFILTERLEN];
410 410 ns_ldap_result_t *result;
411 411 ns_ldap_entry_t *curEntry;
412 412 char *dn;
413 413 gid_t gid;
414 414 int ret1, ret2;
415 415
416 416 if (strcmp(argp->username, "") == 0 ||
417 417 strcmp(argp->username, "root") == 0)
418 418 return ((nss_status_t)NSS_NOTFOUND);
419 419
420 420 if (_ldap_filter_name(name, argp->username, sizeof (name)) != 0)
421 421 return ((nss_status_t)NSS_NOTFOUND);
422 422
423 423 /*
424 424 * Look up the user DN in ldap. If it's not found, search solely by
425 425 * username.
426 426 */
427 427 lstat = __ns_ldap_uid2dn(name, &dn, NULL, &error);
428 428 if (lstat != (nss_status_t)NS_LDAP_SUCCESS) {
429 429 /* Can't get DN. Use bare name */
430 430 (void) __ns_ldap_freeError(&error);
431 431 dn = name;
432 432 }
433 433 /* Note: must free dn if != name */
434 434
435 435 /*
436 436 * Compose filter patterns
437 437 */
438 438 ret1 = snprintf(searchfilter, sizeof (searchfilter),
439 439 _F_GETGRMEM, name, dn);
440 440 ret2 = snprintf(userdata, sizeof (userdata),
441 441 _F_GETGRMEM_SSD, name, dn);
442 442 if (dn != name)
443 443 free(dn);
444 444 if (ret1 >= sizeof (searchfilter) || ret1 < 0)
445 445 return ((nss_status_t)NSS_NOTFOUND);
446 446 if (ret2 >= sizeof (userdata) || ret2 < 0)
447 447 return ((nss_status_t)NSS_NOTFOUND);
448 448
449 449 /*
450 450 * Query for groups matching the filter.
451 451 */
452 452 lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, NULL,
453 453 _GROUP, searchfilter, grbymem_attrs,
454 454 _merge_SSD_filter, userdata);
455 455 if (lstat != (nss_status_t)NS_LDAP_SUCCESS)
456 456 return ((nss_status_t)lstat);
457 457 if (be->result == NULL)
458 458 return (NSS_NOTFOUND);
459 459
460 460 /*
461 461 * Walk the query result, collecting GIDs.
462 462 */
463 463 result = (ns_ldap_result_t *)be->result;
464 464 curEntry = (ns_ldap_entry_t *)result->entry;
465 465 gcnt = (int)argp->numgids;
466 466 for (i = 0; i < result->entries_count; i++) {
467 467
468 468 /*
469 469 * Does this group have a gidNumber attr?
470 470 */
471 471 groupvalue = __ns_ldap_getAttr(curEntry, _G_GID);
472 472 if (groupvalue == NULL || groupvalue[0] == NULL) {
473 473 /* Drop this group from the list */
474 474 goto next_group;
475 475 }
476 476
477 477 /*
478 478 * Convert it to a numeric GID
479 479 */
480 480 errno = 0;
481 481 gid = (gid_t)strtol(groupvalue[0], (char **)NULL, 10);
482 482 if (errno != 0)
483 483 goto next_group;
484 484
485 485 /*
486 486 * If we don't already have this GID, add it.
487 487 */
488 488 if (argp->numgids < argp->maxgids) {
489 489 for (k = 0; k < argp->numgids; k++) {
490 490 if (argp->gid_array[k] == gid) {
491 491 /* already have it */
492 492 goto next_group;
493 493 }
494 494 }
495 495 argp->gid_array[argp->numgids++] = gid;
496 496 }
497 497
498 498 next_group:
499 499 curEntry = curEntry->next;
500 500 }
501 501
502 502 (void) __ns_ldap_freeResult((ns_ldap_result_t **)&be->result);
503 503 if (gcnt == argp->numgids)
504 504 return ((nss_status_t)NSS_NOTFOUND);
505 505
506 506 /*
507 507 * Return NSS_SUCCESS only if array is full.
508 508 * Explained in <nss_dbdefs.h>.
509 509 */
510 510 return ((nss_status_t)((argp->numgids == argp->maxgids)
511 511 ? NSS_SUCCESS
512 512 : NSS_NOTFOUND));
513 513 }
514 514
515 515 static ldap_backend_op_t gr_ops[] = {
516 516 _nss_ldap_destr,
517 517 _nss_ldap_endent,
518 518 _nss_ldap_setent,
519 519 _nss_ldap_getent,
520 520 getbynam,
521 521 getbygid,
522 522 getbymember
523 523 };
524 524
525 525
526 526 /*ARGSUSED0*/
527 527 nss_backend_t *
528 528 _nss_ldap_group_constr(const char *dummy1, const char *dummy2,
529 529 const char *dummy3)
530 530 {
531 531
532 532 return ((nss_backend_t *)_nss_ldap_constr(gr_ops,
533 533 sizeof (gr_ops)/sizeof (gr_ops[0]), _GROUP, gr_attrs,
534 534 _nss_ldap_group2str));
535 535 }
↓ open down ↓ |
257 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX