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