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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
25 */
26
27 /*
28 * native LDAP related utility routines
29 */
30
31 #include "idmapd.h"
32 #include "idmap_priv.h"
33 #include "ns_sldap.h"
34 #include "nldaputils.h"
35 #include <assert.h>
36
37 /*
38 * The following are format strings used to construct LDAP search filters
39 * when looking up Native LDAP directory service. The _F_XXX_SSD format
40 * is used by the libsldap API if a corresponding SSD is defined in
41 * Native LDAP configuration. The SSD contains a string that replaces
42 * the first %s in _F_XXX_SSD. If no SSD is defined then the regular
43 * _F_XXX format is used.
44 *
45 * Note that '\\' needs to be represented as "\\5c" in LDAP filters.
46 */
47
48 /* Native LDAP lookup using UNIX username */
49 #define _F_GETPWNAM "(&(objectClass=posixAccount)(uid=%s))"
50 #define _F_GETPWNAM_SSD "(&(%%s)(uid=%s))"
51
52 /*
53 * Native LDAP user lookup using names of well-known SIDs
54 * Note the use of 1$, 2$ in the format string which basically
55 * allows snprintf to re-use its first two arguments.
56 */
57 #define _F_GETPWWNAMWK \
58 "(&(objectClass=posixAccount)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
59 #define _F_GETPWWNAMWK_SSD "(&(%%s)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
60
61 /* Native LDAP user lookup using winname@windomain OR windomain\winname */
62 #define _F_GETPWWNAMDOM \
63 "(&(objectClass=posixAccount)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
64 #define _F_GETPWWNAMDOM_SSD "(&(%%s)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
65
66 /* Native LDAP lookup using UID */
67 #define _F_GETPWUID "(&(objectClass=posixAccount)(uidNumber=%u))"
68 #define _F_GETPWUID_SSD "(&(%%s)(uidNumber=%u))"
69
70 /* Native LDAP lookup using UNIX groupname */
71 #define _F_GETGRNAM "(&(objectClass=posixGroup)(cn=%s))"
72 #define _F_GETGRNAM_SSD "(&(%%s)(cn=%s))"
73
74 /* Native LDAP group lookup using names of well-known SIDs */
75 #define _F_GETGRWNAMWK \
76 "(&(objectClass=posixGroup)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
77 #define _F_GETGRWNAMWK_SSD "(&(%%s)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
78
79 /* Native LDAP group lookup using winname@windomain OR windomain\winname */
80 #define _F_GETGRWNAMDOM \
81 "(&(objectClass=posixGroup)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
82 #define _F_GETGRWNAMDOM_SSD "(&(%%s)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
83
84 /* Native LDAP lookup using GID */
85 #define _F_GETGRGID "(&(objectClass=posixGroup)(gidNumber=%u))"
86 #define _F_GETGRGID_SSD "(&(%%s)(gidNumber=%u))"
87
88 /* Native LDAP attribute names */
89 #define UID "uid"
90 #define CN "cn"
91 #define UIDNUMBER "uidnumber"
92 #define GIDNUMBER "gidnumber"
93 #define DN "dn"
94
95 #define IS_NLDAP_RC_FATAL(x) ((x == NS_LDAP_MEMORY) ? 1 : 0)
96
97 typedef struct idmap_nldap_q {
98 char **winname;
99 char **windomain;
100 char **unixname;
101 uid_t *pid;
102 char **dn;
103 char **attr;
104 char **value;
105 int is_user;
106 idmap_retcode *rc;
107 int lrc;
108 ns_ldap_result_t *result;
109 ns_ldap_error_t *errorp;
110 char *filter;
111 char *udata;
112 } idmap_nldap_q_t;
113
114 typedef struct idmap_nldap_query_state {
115 const char *nldap_winname_attr;
116 const char *defdom;
117 int nqueries;
118 int qid;
119 int flag;
120 ns_ldap_list_batch_t *batch;
121 idmap_nldap_q_t queries[1];
122 } idmap_nldap_query_state_t;
123
124 /*
125 * This routine has been copied from lib/nsswitch/ldap/common/ldap_utils.c
126 * after removing the debug statements.
127 *
128 * This is a generic filter callback function for merging the filter
129 * from service search descriptor with an existing search filter. This
130 * routine expects userdata to contain a format string with a single %s
131 * in it, and will use the format string with sprintf() to insert the
132 * SSD filter.
133 *
134 * This routine and userdata are passed to the __ns_ldap_list_batch_add()
135 * API.
136 *
137 * Consider an example that uses __ns_ldap_list_batch_add() to lookup
138 * native LDAP directory using a given userid 'xy12345'. In this
139 * example the userdata will contain the filter "(&(%s)(cn=xy1234))".
140 * If a SSD is defined to replace the rfc2307bis specified filter
141 * i.e. (objectClass=posixAccount) by a site-specific filter
142 * say (department=sds) then this routine when called will produce
143 * "(&(department=sds)(uid=xy1234))" as the real search filter.
144 */
145 static
146 int
merge_SSD_filter(const ns_ldap_search_desc_t * desc,char ** realfilter,const void * userdata)147 merge_SSD_filter(const ns_ldap_search_desc_t *desc,
148 char **realfilter, const void *userdata)
149 {
150 int len;
151 char *checker;
152
153 if (realfilter == NULL)
154 return (NS_LDAP_INVALID_PARAM);
155 *realfilter = NULL;
156 if (desc == NULL || desc->filter == NULL || userdata == NULL)
157 return (NS_LDAP_INVALID_PARAM);
158
159 /* Parameter check. We only want one %s here, otherwise bail. */
160 len = 0; /* Reuse 'len' as "Number of %s hits"... */
161 checker = (char *)userdata;
162 do {
163 checker = strchr(checker, '%');
164 if (checker != NULL) {
165 if (len > 0 || *(checker + 1) != 's')
166 return (NS_LDAP_INVALID_PARAM);
167 len++; /* Got our %s. */
168 checker += 2;
169 } else if (len != 1)
170 return (NS_LDAP_INVALID_PARAM);
171 } while (checker != NULL);
172
173 len = strlen(userdata) + strlen(desc->filter) + 1;
174 *realfilter = (char *)malloc(len);
175 if (*realfilter == NULL)
176 return (NS_LDAP_MEMORY);
177 (void) sprintf(*realfilter, (char *)userdata, desc->filter);
178 return (NS_LDAP_SUCCESS);
179 }
180
181 static
182 char
hex_char(int n)183 hex_char(int n)
184 {
185 return ("0123456789abcdef"[n & 0xf]);
186 }
187
188 /*
189 * If the input string contains special characters that needs to be
190 * escaped before the string can be used in a LDAP filter then this
191 * function will return a new sanitized string. Otherwise this function
192 * returns the input string (This saves us un-necessary memory allocations
193 * especially when processing a batch of requests). The caller must free
194 * the returned string if it isn't the input string.
195 *
196 * The escape mechanism for LDAP filter is described in RFC2254 basically
197 * it's \hh where hh are the two hexadecimal digits representing the ASCII
198 * value of the encoded character (case of hh is not significant).
199 * Example: * -> \2a, ( -> \28, ) -> \29, \ -> \5c,
200 *
201 * outstring = sanitize_for_ldap_filter(instring);
202 * if (outstring == NULL)
203 * Out of memory
204 * else
205 * Use outstring
206 * if (outstring != instring)
207 * free(outstring);
208 * done
209 */
210 char *
sanitize_for_ldap_filter(const char * str)211 sanitize_for_ldap_filter(const char *str)
212 {
213 const char *p;
214 char *q, *s_str = NULL;
215 int n;
216
217 /* Get a count of special characters */
218 for (p = str, n = 0; *p; p++)
219 if (*p == '*' || *p == '(' || *p == ')' ||
220 *p == '\\' || *p == '%')
221 n++;
222 /* If count is zero then no need to sanitize */
223 if (n == 0)
224 return ((char *)str);
225 /* Create output buffer that will contain the sanitized value */
226 s_str = calloc(1, n * 2 + strlen(str) + 1);
227 if (s_str == NULL)
228 return (NULL);
229 for (p = str, q = s_str; *p; p++) {
230 if (*p == '*' || *p == '(' || *p == ')' ||
231 *p == '\\' || *p == '%') {
232 *q++ = '\\';
233 *q++ = hex_char(*p >> 4);
234 *q++ = hex_char(*p & 0xf);
235 } else
236 *q++ = *p;
237 }
238 return (s_str);
239 }
240
241 /*
242 * Map libsldap status to idmap status
243 */
244 static
245 idmap_retcode
nldaprc2retcode(int rc)246 nldaprc2retcode(int rc)
247 {
248 switch (rc) {
249 case NS_LDAP_SUCCESS:
250 case NS_LDAP_SUCCESS_WITH_INFO:
251 return (IDMAP_SUCCESS);
252 case NS_LDAP_NOTFOUND:
253 return (IDMAP_ERR_NOTFOUND);
254 case NS_LDAP_MEMORY:
255 return (IDMAP_ERR_MEMORY);
256 case NS_LDAP_CONFIG:
257 return (IDMAP_ERR_NS_LDAP_CFG);
258 case NS_LDAP_OP_FAILED:
259 return (IDMAP_ERR_NS_LDAP_OP_FAILED);
260 case NS_LDAP_PARTIAL:
261 return (IDMAP_ERR_NS_LDAP_PARTIAL);
262 case NS_LDAP_INTERNAL:
263 return (IDMAP_ERR_INTERNAL);
264 case NS_LDAP_INVALID_PARAM:
265 return (IDMAP_ERR_ARG);
266 default:
267 return (IDMAP_ERR_OTHER);
268 }
269 /*NOTREACHED*/
270 }
271
272 /*
273 * Create a batch for native LDAP lookup.
274 */
275 static
276 idmap_retcode
idmap_nldap_lookup_batch_start(int nqueries,idmap_nldap_query_state_t ** qs)277 idmap_nldap_lookup_batch_start(int nqueries, idmap_nldap_query_state_t **qs)
278 {
279 idmap_nldap_query_state_t *s;
280
281 s = calloc(1, sizeof (*s) +
282 (nqueries - 1) * sizeof (idmap_nldap_q_t));
283 if (s == NULL)
284 return (IDMAP_ERR_MEMORY);
285 if (__ns_ldap_list_batch_start(&s->batch) != NS_LDAP_SUCCESS) {
286 free(s);
287 return (IDMAP_ERR_MEMORY);
288 }
289 s->nqueries = nqueries;
290 s->flag = NS_LDAP_KEEP_CONN;
291 *qs = s;
292 return (IDMAP_SUCCESS);
293 }
294
295 /*
296 * Add a lookup by winname request to the batch.
297 */
298 static
299 idmap_retcode
idmap_nldap_bywinname_batch_add(idmap_nldap_query_state_t * qs,const char * winname,const char * windomain,int is_user,char ** dn,char ** attr,char ** value,char ** unixname,uid_t * pid,idmap_retcode * rc)300 idmap_nldap_bywinname_batch_add(idmap_nldap_query_state_t *qs,
301 const char *winname, const char *windomain, int is_user,
302 char **dn, char **attr, char **value,
303 char **unixname, uid_t *pid, idmap_retcode *rc)
304 {
305 idmap_nldap_q_t *q;
306 const char *db, *filter, *udata;
307 int flen, ulen, wksid = 0;
308 char *s_winname, *s_windomain;
309 const char **attrs;
310 const char *pwd_attrs[] = {UID, UIDNUMBER, NULL, NULL};
311 const char *grp_attrs[] = {CN, GIDNUMBER, NULL, NULL};
312
313 s_winname = s_windomain = NULL;
314 q = &(qs->queries[qs->qid++]);
315 q->unixname = unixname;
316 q->pid = pid;
317 q->rc = rc;
318 q->is_user = is_user;
319 q->dn = dn;
320 q->attr = attr;
321 q->value = value;
322
323 if (is_user) {
324 db = "passwd";
325 if (lookup_wksids_name2sid(winname, NULL, NULL, NULL, NULL,
326 NULL, NULL) == IDMAP_SUCCESS) {
327 filter = _F_GETPWWNAMWK;
328 udata = _F_GETPWWNAMWK_SSD;
329 wksid = 1;
330 } else if (windomain != NULL) {
331 filter = _F_GETPWWNAMDOM;
332 udata = _F_GETPWWNAMDOM_SSD;
333 } else {
334 *q->rc = IDMAP_ERR_DOMAIN_NOTFOUND;
335 goto errout;
336 }
337 pwd_attrs[2] = qs->nldap_winname_attr;
338 attrs = pwd_attrs;
339 } else {
340 db = "group";
341 if (lookup_wksids_name2sid(winname, NULL, NULL, NULL, NULL,
342 NULL, NULL) == IDMAP_SUCCESS) {
343 filter = _F_GETGRWNAMWK;
344 udata = _F_GETGRWNAMWK_SSD;
345 wksid = 1;
346 } else if (windomain != NULL) {
347 filter = _F_GETGRWNAMDOM;
348 udata = _F_GETGRWNAMDOM_SSD;
349 } else {
350 *q->rc = IDMAP_ERR_DOMAIN_NOTFOUND;
351 goto errout;
352 }
353 grp_attrs[2] = qs->nldap_winname_attr;
354 attrs = grp_attrs;
355 }
356
357 /*
358 * Sanitize names. No need to sanitize qs->nldap_winname_attr
359 * because if it contained any of the special characters then
360 * it would have been rejected by the function that reads it
361 * from the SMF config. LDAP attribute names can only contain
362 * letters, digits or hyphens.
363 */
364 s_winname = sanitize_for_ldap_filter(winname);
365 if (s_winname == NULL) {
366 *q->rc = IDMAP_ERR_MEMORY;
367 goto errout;
368 }
369 /* windomain could be NULL for names of well-known SIDs */
370 if (windomain != NULL) {
371 s_windomain = sanitize_for_ldap_filter(windomain);
372 if (s_windomain == NULL) {
373 *q->rc = IDMAP_ERR_MEMORY;
374 goto errout;
375 }
376 }
377
378 /* Construct the filter and udata using snprintf. */
379 if (wksid) {
380 flen = snprintf(NULL, 0, filter, qs->nldap_winname_attr,
381 s_winname) + 1;
382 ulen = snprintf(NULL, 0, udata, qs->nldap_winname_attr,
383 s_winname) + 1;
384 } else {
385 flen = snprintf(NULL, 0, filter, qs->nldap_winname_attr,
386 s_winname, s_windomain) + 1;
387 ulen = snprintf(NULL, 0, udata, qs->nldap_winname_attr,
388 s_winname, s_windomain) + 1;
389 }
390
391 q->filter = malloc(flen);
392 if (q->filter == NULL) {
393 *q->rc = IDMAP_ERR_MEMORY;
394 goto errout;
395 }
396 q->udata = malloc(ulen);
397 if (q->udata == NULL) {
398 *q->rc = IDMAP_ERR_MEMORY;
399 goto errout;
400 }
401
402 if (wksid) {
403 (void) snprintf(q->filter, flen, filter,
404 qs->nldap_winname_attr, s_winname);
405 (void) snprintf(q->udata, ulen, udata,
406 qs->nldap_winname_attr, s_winname);
407 } else {
408 (void) snprintf(q->filter, flen, filter,
409 qs->nldap_winname_attr, s_winname, s_windomain);
410 (void) snprintf(q->udata, ulen, udata,
411 qs->nldap_winname_attr, s_winname, s_windomain);
412 }
413
414 if (s_winname != winname)
415 free(s_winname);
416 if (s_windomain != windomain)
417 free(s_windomain);
418
419 q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
420 merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
421 &q->errorp, &q->lrc, NULL, q->udata);
422
423 if (IS_NLDAP_RC_FATAL(q->lrc))
424 return (nldaprc2retcode(q->lrc));
425 return (IDMAP_SUCCESS);
426
427 errout:
428 /* query q and its content will be freed by batch_release */
429 if (s_winname != winname)
430 free(s_winname);
431 if (s_windomain != windomain)
432 free(s_windomain);
433 return (*q->rc);
434 }
435
436 /*
437 * Add a lookup by uid/gid request to the batch.
438 */
439 static
440 idmap_retcode
idmap_nldap_bypid_batch_add(idmap_nldap_query_state_t * qs,uid_t pid,int is_user,char ** dn,char ** attr,char ** value,char ** winname,char ** windomain,char ** unixname,idmap_retcode * rc)441 idmap_nldap_bypid_batch_add(idmap_nldap_query_state_t *qs,
442 uid_t pid, int is_user, char **dn, char **attr, char **value,
443 char **winname, char **windomain,
444 char **unixname, idmap_retcode *rc)
445 {
446 idmap_nldap_q_t *q;
447 const char *db, *filter, *udata;
448 int len;
449 const char **attrs;
450 const char *pwd_attrs[] = {UID, NULL, NULL};
451 const char *grp_attrs[] = {CN, NULL, NULL};
452
453 q = &(qs->queries[qs->qid++]);
454 q->winname = winname;
455 q->windomain = windomain;
456 q->unixname = unixname;
457 q->rc = rc;
458 q->is_user = is_user;
459 q->dn = dn;
460 q->attr = attr;
461 q->value = value;
462
463 if (is_user) {
464 db = "passwd";
465 filter = _F_GETPWUID;
466 udata = _F_GETPWUID_SSD;
467 pwd_attrs[1] = qs->nldap_winname_attr;
468 attrs = pwd_attrs;
469 } else {
470 db = "group";
471 filter = _F_GETGRGID;
472 udata = _F_GETGRGID_SSD;
473 grp_attrs[1] = qs->nldap_winname_attr;
474 attrs = grp_attrs;
475 }
476
477 len = snprintf(NULL, 0, filter, pid) + 1;
478 q->filter = malloc(len);
479 if (q->filter == NULL) {
480 *q->rc = IDMAP_ERR_MEMORY;
481 return (IDMAP_ERR_MEMORY);
482 }
483 (void) snprintf(q->filter, len, filter, pid);
484
485 len = snprintf(NULL, 0, udata, pid) + 1;
486 q->udata = malloc(len);
487 if (q->udata == NULL) {
488 *q->rc = IDMAP_ERR_MEMORY;
489 return (IDMAP_ERR_MEMORY);
490 }
491 (void) snprintf(q->udata, len, udata, pid);
492
493 q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
494 merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
495 &q->errorp, &q->lrc, NULL, q->udata);
496
497 if (IS_NLDAP_RC_FATAL(q->lrc))
498 return (nldaprc2retcode(q->lrc));
499 return (IDMAP_SUCCESS);
500 }
501
502 /*
503 * Add a lookup by user/group name request to the batch.
504 */
505 static
506 idmap_retcode
idmap_nldap_byunixname_batch_add(idmap_nldap_query_state_t * qs,const char * unixname,int is_user,char ** dn,char ** attr,char ** value,char ** winname,char ** windomain,uid_t * pid,idmap_retcode * rc)507 idmap_nldap_byunixname_batch_add(idmap_nldap_query_state_t *qs,
508 const char *unixname, int is_user,
509 char **dn, char **attr, char **value,
510 char **winname, char **windomain, uid_t *pid, idmap_retcode *rc)
511 {
512 idmap_nldap_q_t *q;
513 const char *db, *filter, *udata;
514 int len;
515 char *s_unixname = NULL;
516 const char **attrs;
517 const char *pwd_attrs[] = {UIDNUMBER, NULL, NULL};
518 const char *grp_attrs[] = {GIDNUMBER, NULL, NULL};
519
520 q = &(qs->queries[qs->qid++]);
521 q->winname = winname;
522 q->windomain = windomain;
523 q->pid = pid;
524 q->rc = rc;
525 q->is_user = is_user;
526 q->dn = dn;
527 q->attr = attr;
528 q->value = value;
529
530 if (is_user) {
531 db = "passwd";
532 filter = _F_GETPWNAM;
533 udata = _F_GETPWNAM_SSD;
534 pwd_attrs[1] = qs->nldap_winname_attr;
535 attrs = pwd_attrs;
536 } else {
537 db = "group";
538 filter = _F_GETGRNAM;
539 udata = _F_GETGRNAM_SSD;
540 grp_attrs[1] = qs->nldap_winname_attr;
541 attrs = grp_attrs;
542 }
543
544 s_unixname = sanitize_for_ldap_filter(unixname);
545 if (s_unixname == NULL) {
546 *q->rc = IDMAP_ERR_MEMORY;
547 return (IDMAP_ERR_MEMORY);
548 }
549
550 len = snprintf(NULL, 0, filter, s_unixname) + 1;
551 q->filter = malloc(len);
552 if (q->filter == NULL) {
553 if (s_unixname != unixname)
554 free(s_unixname);
555 *q->rc = IDMAP_ERR_MEMORY;
556 return (IDMAP_ERR_MEMORY);
557 }
558 (void) snprintf(q->filter, len, filter, s_unixname);
559
560 len = snprintf(NULL, 0, udata, s_unixname) + 1;
561 q->udata = malloc(len);
562 if (q->udata == NULL) {
563 if (s_unixname != unixname)
564 free(s_unixname);
565 *q->rc = IDMAP_ERR_MEMORY;
566 return (IDMAP_ERR_MEMORY);
567 }
568 (void) snprintf(q->udata, len, udata, s_unixname);
569
570 if (s_unixname != unixname)
571 free(s_unixname);
572
573 q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
574 merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
575 &q->errorp, &q->lrc, NULL, q->udata);
576
577 if (IS_NLDAP_RC_FATAL(q->lrc))
578 return (nldaprc2retcode(q->lrc));
579 return (IDMAP_SUCCESS);
580 }
581
582 /*
583 * Free the batch
584 */
585 static
586 void
idmap_nldap_lookup_batch_release(idmap_nldap_query_state_t * qs)587 idmap_nldap_lookup_batch_release(idmap_nldap_query_state_t *qs)
588 {
589 idmap_nldap_q_t *q;
590 int i;
591
592 if (qs->batch != NULL)
593 (void) __ns_ldap_list_batch_release(qs->batch);
594 for (i = 0; i < qs->qid; i++) {
595 q = &(qs->queries[i]);
596 free(q->filter);
597 free(q->udata);
598 if (q->errorp != NULL)
599 (void) __ns_ldap_freeError(&q->errorp);
600 if (q->result != NULL)
601 (void) __ns_ldap_freeResult(&q->result);
602 }
603 free(qs);
604 }
605
606 /*
607 * Process all requests added to the batch and then free the batch.
608 * The results for individual requests will be accessible using the
609 * pointers passed during idmap_nldap_lookup_batch_end.
610 */
611 static
612 idmap_retcode
idmap_nldap_lookup_batch_end(idmap_nldap_query_state_t * qs)613 idmap_nldap_lookup_batch_end(idmap_nldap_query_state_t *qs)
614 {
615 idmap_nldap_q_t *q;
616 int i;
617 ns_ldap_entry_t *entry;
618 char **val, *end, *str, *name, *dom;
619 idmap_retcode rc = IDMAP_SUCCESS;
620
621 (void) __ns_ldap_list_batch_end(qs->batch);
622 qs->batch = NULL;
623 for (i = 0; i < qs->qid; i++) {
624 q = &(qs->queries[i]);
625 *q->rc = nldaprc2retcode(q->lrc);
626 if (*q->rc != IDMAP_SUCCESS)
627 continue;
628 if (q->result == NULL ||
629 !q->result->entries_count ||
630 (entry = q->result->entry) == NULL ||
631 !entry->attr_count) {
632 *q->rc = IDMAP_ERR_NOTFOUND;
633 continue;
634 }
635 /* Get uid/gid */
636 if (q->pid != NULL) {
637 val = __ns_ldap_getAttr(entry,
638 (q->is_user) ? UIDNUMBER : GIDNUMBER);
639 if (val != NULL && *val != NULL)
640 *q->pid = strtoul(*val, &end, 10);
641 }
642 /* Get unixname */
643 if (q->unixname != NULL) {
644 val = __ns_ldap_getAttr(entry,
645 (q->is_user) ? UID : CN);
646 if (val != NULL && *val != NULL) {
647 *q->unixname = strdup(*val);
648 if (*q->unixname == NULL) {
649 rc = *q->rc = IDMAP_ERR_MEMORY;
650 goto out;
651 }
652 }
653 }
654 /* Get DN for how info */
655 if (q->dn != NULL) {
656 val = __ns_ldap_getAttr(entry, DN);
657 if (val != NULL && *val != NULL) {
658 *q->dn = strdup(*val);
659 if (*q->dn == NULL) {
660 rc = *q->rc = IDMAP_ERR_MEMORY;
661 goto out;
662 }
663 }
664 }
665 /* Get nldap name mapping attr name for how info */
666 if (q->attr != NULL) {
667 *q->attr = strdup(qs->nldap_winname_attr);
668 if (*q->attr == NULL) {
669 rc = *q->rc = IDMAP_ERR_MEMORY;
670 goto out;
671 }
672 }
673 /* Get nldap name mapping attr value for how info */
674 val = __ns_ldap_getAttr(entry, qs->nldap_winname_attr);
675 if (val == NULL || *val == NULL)
676 continue;
677 if (q->value != NULL) {
678 *q->value = strdup(*val);
679 if (*q->value == NULL) {
680 rc = *q->rc = IDMAP_ERR_MEMORY;
681 goto out;
682 }
683 }
684
685 /* Get winname and windomain */
686 if (q->winname == NULL && q->windomain == NULL)
687 continue;
688 /*
689 * We need to split the value into winname and
690 * windomain. The value could be either in NT4
691 * style (i.e. dom\name) or AD-style (i.e. name@dom).
692 * We choose the first '\\' if it's in NT4 style and
693 * the last '@' if it's in AD-style for the split.
694 */
695 name = dom = NULL;
696 if (lookup_wksids_name2sid(*val, NULL, NULL, NULL, NULL, NULL,
697 NULL) == IDMAP_SUCCESS) {
698 name = *val;
699 dom = NULL;
700 } else if ((str = strchr(*val, '\\')) != NULL) {
701 *str = '\0';
702 name = str + 1;
703 dom = *val;
704 } else if ((str = strrchr(*val, '@')) != NULL) {
705 *str = '\0';
706 name = *val;
707 dom = str + 1;
708 } else {
709 idmapdlog(LOG_INFO, "Domain-less "
710 "winname (%s) found in Native LDAP", *val);
711 *q->rc = IDMAP_ERR_NS_LDAP_BAD_WINNAME;
712 continue;
713 }
714 if (q->winname != NULL) {
715 *q->winname = strdup(name);
716 if (*q->winname == NULL) {
717 rc = *q->rc = IDMAP_ERR_MEMORY;
718 goto out;
719 }
720 }
721 if (q->windomain != NULL && dom != NULL) {
722 *q->windomain = strdup(dom);
723 if (*q->windomain == NULL) {
724 rc = *q->rc = IDMAP_ERR_MEMORY;
725 goto out;
726 }
727 }
728 }
729
730 out:
731 (void) idmap_nldap_lookup_batch_release(qs);
732 return (rc);
733 }
734
735 /* ARGSUSED */
736 idmap_retcode
nldap_lookup_batch(lookup_state_t * state,idmap_mapping_batch * batch,idmap_ids_res * result)737 nldap_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch,
738 idmap_ids_res *result)
739 {
740 idmap_retcode retcode, rc1;
741 int i, add;
742 idmap_mapping *req;
743 idmap_id_res *res;
744 idmap_nldap_query_state_t *qs = NULL;
745 idmap_how *how;
746
747 if (state->nldap_nqueries == 0)
748 return (IDMAP_SUCCESS);
749
750 /* Create nldap lookup batch */
751 retcode = idmap_nldap_lookup_batch_start(state->nldap_nqueries, &qs);
752 if (retcode != IDMAP_SUCCESS) {
753 idmapdlog(LOG_ERR,
754 "Failed to create batch for native LDAP lookup");
755 goto out;
756 }
757
758 qs->nldap_winname_attr = state->nldap_winname_attr;
759 qs->defdom = state->defdom;
760
761 /* Add requests to the batch */
762 for (i = 0, add = 0; i < batch->idmap_mapping_batch_len; i++) {
763 req = &batch->idmap_mapping_batch_val[i];
764 res = &result->ids.ids_val[i];
765 retcode = IDMAP_SUCCESS;
766
767 /* Skip if not marked for nldap lookup */
768 if (!(req->direction & _IDMAP_F_LOOKUP_NLDAP))
769 continue;
770
771 if (IS_ID_SID(req->id1)) {
772
773 /* win2unix request: */
774
775 /*
776 * When processing a win2unix request, nldap lookup
777 * is performed after AD lookup or a successful
778 * name-cache lookup. Therefore we should already
779 * have sid, winname and sidtype. Note that
780 * windomain could be NULL e.g. well-known SIDs.
781 */
782 assert(req->id1name != NULL &&
783 (res->id.idtype == IDMAP_UID ||
784 res->id.idtype == IDMAP_GID));
785
786 /* Skip if we already have pid and unixname */
787 if (req->id2name != NULL &&
788 res->id.idmap_id_u.uid != IDMAP_SENTINEL_PID) {
789 res->retcode = IDMAP_SUCCESS;
790 continue;
791 }
792
793 /* Clear leftover value */
794 free(req->id2name);
795 req->id2name = NULL;
796
797 /* Lookup nldap by winname to get pid and unixname */
798 add = 1;
799 idmap_how_clear(&res->info.how);
800 res->info.src = IDMAP_MAP_SRC_NEW;
801 how = &res->info.how;
802 how->map_type = IDMAP_MAP_TYPE_DS_NLDAP;
803 retcode = idmap_nldap_bywinname_batch_add(
804 qs, req->id1name, req->id1domain,
805 (res->id.idtype == IDMAP_UID) ? 1 : 0,
806 &how->idmap_how_u.nldap.dn,
807 &how->idmap_how_u.nldap.attr,
808 &how->idmap_how_u.nldap.value,
809 &req->id2name, &res->id.idmap_id_u.uid,
810 &res->retcode);
811
812 } else if (IS_ID_UID(req->id1) || IS_ID_GID(req->id1)) {
813
814 /* unix2win request: */
815
816 /* Skip if we already have winname */
817 if (req->id2name != NULL) {
818 res->retcode = IDMAP_SUCCESS;
819 continue;
820 }
821
822 /* Clear old value */
823 free(req->id2domain);
824 req->id2domain = NULL;
825
826 /* Set how info */
827 idmap_how_clear(&res->info.how);
828 res->info.src = IDMAP_MAP_SRC_NEW;
829 how = &res->info.how;
830 how->map_type = IDMAP_MAP_TYPE_DS_NLDAP;
831
832 /* Lookup nldap by pid or unixname to get winname */
833 if (req->id1.idmap_id_u.uid != IDMAP_SENTINEL_PID) {
834 add = 1;
835 retcode = idmap_nldap_bypid_batch_add(
836 qs, req->id1.idmap_id_u.uid,
837 (req->id1.idtype == IDMAP_UID) ? 1 : 0,
838 &how->idmap_how_u.nldap.dn,
839 &how->idmap_how_u.nldap.attr,
840 &how->idmap_how_u.nldap.value,
841 &req->id2name, &req->id2domain,
842 (req->id1name == NULL) ?
843 &req->id1name : NULL,
844 &res->retcode);
845 } else if (req->id1name != NULL) {
846 add = 1;
847 retcode = idmap_nldap_byunixname_batch_add(
848 qs, req->id1name,
849 (req->id1.idtype == IDMAP_UID) ? 1 : 0,
850 &how->idmap_how_u.nldap.dn,
851 &how->idmap_how_u.nldap.attr,
852 &how->idmap_how_u.nldap.value,
853 &req->id2name, &req->id2domain,
854 &req->id1.idmap_id_u.uid, &res->retcode);
855 }
856
857 }
858
859 /*
860 * nldap_batch_add API returns error only on fatal failures
861 * otherwise it returns success and the actual status
862 * is stored in the individual request (res->retcode).
863 * Stop adding requests to this batch on fatal failures
864 * (i.e. if retcode != success)
865 */
866 if (retcode != IDMAP_SUCCESS)
867 break;
868 }
869
870 if (!add)
871 idmap_nldap_lookup_batch_release(qs);
872 else if (retcode != IDMAP_SUCCESS)
873 idmap_nldap_lookup_batch_release(qs);
874 else
875 retcode = idmap_nldap_lookup_batch_end(qs);
876
877 out:
878 for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
879 req = &batch->idmap_mapping_batch_val[i];
880 res = &result->ids.ids_val[i];
881 if (!(req->direction & _IDMAP_F_LOOKUP_NLDAP))
882 continue;
883
884 /* Reset nldap flag */
885 req->direction &= ~(_IDMAP_F_LOOKUP_NLDAP);
886
887 /*
888 * As noted earlier retcode != success if there were fatal
889 * errors during batch_start and batch_adds. If so then set
890 * the status of each nldap request to that error.
891 */
892 if (retcode != IDMAP_SUCCESS) {
893 res->retcode = retcode;
894 continue;
895 }
896 if (!add)
897 continue;
898
899 /*
900 * If we successfully retrieved winname from nldap entry
901 * then lookup winname2sid locally. If not found locally
902 * then mark this request for AD lookup.
903 */
904 if (res->retcode == IDMAP_SUCCESS &&
905 req->id2name != NULL &&
906 res->id.idmap_id_u.sid.prefix == NULL &&
907 (IS_ID_UID(req->id1) || IS_ID_GID(req->id1))) {
908
909 rc1 = lookup_name2sid(state->cache,
910 req->id2name, req->id2domain, -1,
911 NULL, NULL,
912 &res->id.idmap_id_u.sid.prefix,
913 &res->id.idmap_id_u.sid.rid,
914 &res->id.idtype,
915 req, 1);
916 if (rc1 == IDMAP_ERR_NOTFOUND) {
917 req->direction |= _IDMAP_F_LOOKUP_AD;
918 state->ad_nqueries++;
919 } else
920 res->retcode = rc1;
921 }
922
923 /*
924 * Unset non-fatal errors in individual request. This allows
925 * the next pass to process other mapping mechanisms for
926 * this request.
927 */
928 if (res->retcode != IDMAP_SUCCESS &&
929 res->retcode != IDMAP_ERR_NS_LDAP_BAD_WINNAME &&
930 !(IDMAP_FATAL_ERROR(res->retcode))) {
931 idmap_how_clear(&res->info.how);
932 res->retcode = IDMAP_SUCCESS;
933 }
934 }
935
936 state->nldap_nqueries = 0;
937 return (retcode);
938 }
939