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 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
26 */
27
28 /*
29 * Processes name2sid & sid2name batched lookups for a given user or
30 * computer from an AD Directory server using GSSAPI authentication
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <alloca.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <lber.h>
39 #include <ldap.h>
40 #include <sasl/sasl.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <pthread.h>
44 #include <synch.h>
45 #include <atomic.h>
46 #include <errno.h>
47 #include <assert.h>
48 #include <limits.h>
49 #include <time.h>
50 #include <sys/u8_textprep.h>
51 #include "libadutils.h"
52 #include "nldaputils.h"
53 #include "idmapd.h"
54
55 /* Attribute names and filter format strings */
56 #define SAN "sAMAccountName"
57 #define OBJSID "objectSid"
58 #define OBJCLASS "objectClass"
59 #define UIDNUMBER "uidNumber"
60 #define GIDNUMBER "gidNumber"
61 #define UIDNUMBERFILTER "(&(objectclass=user)(uidNumber=%u))"
62 #define GIDNUMBERFILTER "(&(objectclass=group)(gidNumber=%u))"
63 #define SANFILTER "(sAMAccountName=%s)"
64 #define OBJSIDFILTER "(|(objectSid=%s)(sIDHistory=%s))"
65
66 void idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc,
67 int qid, void *argp);
68
69 /*
70 * A place to put the results of a batched (async) query
71 *
72 * There is one of these for every query added to a batch object
73 * (idmap_query_state, see below).
74 */
75 typedef struct idmap_q {
76 /*
77 * data used for validating search result entries for name->SID
78 * lookups
79 */
80 char *ecanonname; /* expected canon name */
81 char *edomain; /* expected domain name */
82 idmap_id_type esidtype; /* expected SID type */
83 /* results */
84 char **canonname; /* actual canon name */
85 char **domain; /* name of domain of object */
86 char **sid; /* stringified SID */
87 rid_t *rid; /* RID */
88 idmap_id_type *sid_type; /* user or group SID? */
89 char **unixname; /* unixname for name mapping */
90 char **dn; /* DN of entry */
91 char **attr; /* Attr for name mapping */
92 char **value; /* value for name mapping */
93 posix_id_t *pid; /* Posix ID found via IDMU */
94 idmap_retcode *rc;
95 adutils_rc ad_rc;
96 adutils_result_t *result;
97
98 /*
99 * The LDAP search entry result is placed here to be processed
100 * when the search done result is received.
101 */
102 LDAPMessage *search_res; /* The LDAP search result */
103 } idmap_q_t;
104
105 /* Batch context structure; typedef is in header file */
106 struct idmap_query_state {
107 adutils_query_state_t *qs;
108 int qsize; /* Queue size */
109 uint32_t qcount; /* Number of queued requests */
110 const char *ad_unixuser_attr;
111 const char *ad_unixgroup_attr;
112 int directory_based_mapping; /* enum */
113 char *default_domain;
114 idmap_q_t queries[1]; /* array of query results */
115 };
116
117 static pthread_t reaperid = 0;
118
119 /*
120 * Keep connection management simple for now, extend or replace later
121 * with updated libsldap code.
122 */
123 #define ADREAPERSLEEP 60
124
125 /*
126 * Idle connection reaping side of connection management
127 *
128 * Every minute wake up and look for connections that have been idle for
129 * five minutes or more and close them.
130 */
131 /*ARGSUSED*/
132 static
133 void *
adreaper(void * arg __unused)134 adreaper(void *arg __unused)
135 {
136 timespec_t ts;
137
138 ts.tv_sec = ADREAPERSLEEP;
139 ts.tv_nsec = 0;
140
141 for (;;) {
142 /*
143 * nanosleep(3RT) is thead-safe (no SIGALRM) and more
144 * portable than usleep(3C)
145 */
146 (void) nanosleep(&ts, NULL);
147 adutils_reap_idle_connections();
148 }
149 return (NULL);
150 }
151
152 /*
153 * Take ad_host_config_t information, create a ad_host_t,
154 * populate it and add it to the list of hosts.
155 */
156
157 int
idmap_add_ds(adutils_ad_t * ad,const char * host,int port)158 idmap_add_ds(adutils_ad_t *ad, const char *host, int port)
159 {
160 int ret = -1;
161
162 if (adutils_add_ds(ad, host, port) == ADUTILS_SUCCESS)
163 ret = 0;
164
165 /* Start reaper if it doesn't exist */
166 if (ret == 0 && reaperid == 0)
167 (void) pthread_create(&reaperid, NULL, adreaper, NULL);
168 return (ret);
169 }
170
171 static
172 idmap_retcode
map_adrc2idmaprc(adutils_rc adrc)173 map_adrc2idmaprc(adutils_rc adrc)
174 {
175 switch (adrc) {
176 case ADUTILS_SUCCESS:
177 return (IDMAP_SUCCESS);
178 case ADUTILS_ERR_NOTFOUND:
179 return (IDMAP_ERR_NOTFOUND);
180 case ADUTILS_ERR_MEMORY:
181 return (IDMAP_ERR_MEMORY);
182 case ADUTILS_ERR_DOMAIN:
183 return (IDMAP_ERR_DOMAIN);
184 case ADUTILS_ERR_OTHER:
185 return (IDMAP_ERR_OTHER);
186 case ADUTILS_ERR_RETRIABLE_NET_ERR:
187 return (IDMAP_ERR_RETRIABLE_NET_ERR);
188 default:
189 return (IDMAP_ERR_INTERNAL);
190 }
191 /* NOTREACHED */
192 }
193
194 idmap_retcode
idmap_lookup_batch_start(adutils_ad_t * ad,int nqueries,int directory_based_mapping,const char * default_domain,idmap_query_state_t ** state)195 idmap_lookup_batch_start(adutils_ad_t *ad, int nqueries,
196 int directory_based_mapping, const char *default_domain,
197 idmap_query_state_t **state)
198 {
199 idmap_query_state_t *new_state;
200 adutils_rc rc;
201
202 *state = NULL;
203
204 assert(ad != NULL);
205
206 new_state = calloc(1, sizeof (idmap_query_state_t) +
207 (nqueries - 1) * sizeof (idmap_q_t));
208 if (new_state == NULL)
209 return (IDMAP_ERR_MEMORY);
210
211 if ((rc = adutils_lookup_batch_start(ad, nqueries,
212 idmap_ldap_res_search_cb, new_state, &new_state->qs))
213 != ADUTILS_SUCCESS) {
214 idmap_lookup_release_batch(&new_state);
215 return (map_adrc2idmaprc(rc));
216 }
217
218 new_state->default_domain = strdup(default_domain);
219 if (new_state->default_domain == NULL) {
220 idmap_lookup_release_batch(&new_state);
221 return (IDMAP_ERR_MEMORY);
222 }
223
224 new_state->directory_based_mapping = directory_based_mapping;
225 new_state->qsize = nqueries;
226 *state = new_state;
227 return (IDMAP_SUCCESS);
228 }
229
230 /*
231 * Set unixuser_attr and unixgroup_attr for AD-based name mapping
232 */
233 void
idmap_lookup_batch_set_unixattr(idmap_query_state_t * state,const char * unixuser_attr,const char * unixgroup_attr)234 idmap_lookup_batch_set_unixattr(idmap_query_state_t *state,
235 const char *unixuser_attr, const char *unixgroup_attr)
236 {
237 state->ad_unixuser_attr = unixuser_attr;
238 state->ad_unixgroup_attr = unixgroup_attr;
239 }
240
241 /*
242 * Take parsed attribute values from a search result entry and check if
243 * it is the result that was desired and, if so, set the result fields
244 * of the given idmap_q_t.
245 *
246 * Except for dn and attr, all strings are consumed, either by transferring
247 * them over into the request results (where the caller will eventually free
248 * them) or by freeing them here. Note that this aligns with the "const"
249 * declarations below.
250 */
251 static
252 void
idmap_setqresults(idmap_q_t * q,char * san,const char * dn,const char * attr,char * value,char * sid,rid_t rid,int sid_type,char * unixname,posix_id_t pid)253 idmap_setqresults(
254 idmap_q_t *q,
255 char *san,
256 const char *dn,
257 const char *attr,
258 char *value,
259 char *sid,
260 rid_t rid,
261 int sid_type,
262 char *unixname,
263 posix_id_t pid)
264 {
265 char *domain;
266 int err1;
267
268 assert(dn != NULL);
269
270 if ((domain = adutils_dn2dns(dn)) == NULL)
271 goto out;
272
273 if (q->ecanonname != NULL && san != NULL) {
274 /* Check that this is the canonname that we were looking for */
275 if (u8_strcmp(q->ecanonname, san, 0,
276 U8_STRCMP_CI_LOWER, /* no normalization, for now */
277 U8_UNICODE_LATEST, &err1) != 0 || err1 != 0)
278 goto out;
279 }
280
281 if (q->edomain != NULL) {
282 /* Check that this is the domain that we were looking for */
283 if (!domain_eq(q->edomain, domain))
284 goto out;
285 }
286
287 /* Copy the DN and attr and value */
288 if (q->dn != NULL)
289 *q->dn = strdup(dn);
290
291 if (q->attr != NULL && attr != NULL)
292 *q->attr = strdup(attr);
293
294 if (q->value != NULL && value != NULL) {
295 *q->value = value;
296 value = NULL;
297 }
298
299 /* Set results */
300 if (q->sid) {
301 *q->sid = sid;
302 sid = NULL;
303 }
304 if (q->rid)
305 *q->rid = rid;
306 if (q->sid_type)
307 *q->sid_type = sid_type;
308 if (q->unixname) {
309 *q->unixname = unixname;
310 unixname = NULL;
311 }
312 if (q->domain != NULL) {
313 *q->domain = domain;
314 domain = NULL;
315 }
316 if (q->canonname != NULL) {
317 /*
318 * The caller may be replacing the given winname by its
319 * canonical name and therefore free any old name before
320 * overwriting the field by the canonical name.
321 */
322 free(*q->canonname);
323 *q->canonname = san;
324 san = NULL;
325 }
326
327 if (q->pid != NULL && pid != IDMAP_SENTINEL_PID) {
328 *q->pid = pid;
329 }
330
331 q->ad_rc = ADUTILS_SUCCESS;
332
333 out:
334 /* Free unused attribute values */
335 free(san);
336 free(sid);
337 free(domain);
338 free(unixname);
339 free(value);
340 }
341
342 #define BVAL_CASEEQ(bv, str) \
343 (((*(bv))->bv_len == (sizeof (str) - 1)) && \
344 strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0)
345
346 /*
347 * Extract the class of the result entry. Returns 1 on success, 0 on
348 * failure.
349 */
350 static
351 int
idmap_bv_objclass2sidtype(BerValue ** bvalues,int * sid_type)352 idmap_bv_objclass2sidtype(BerValue **bvalues, int *sid_type)
353 {
354 BerValue **cbval;
355
356 *sid_type = IDMAP_SID;
357 if (bvalues == NULL)
358 return (0);
359
360 /*
361 * We consider Computer to be a subclass of User, so we can just
362 * ignore Computer entries and pay attention to the accompanying
363 * User entries.
364 */
365 for (cbval = bvalues; *cbval != NULL; cbval++) {
366 if (BVAL_CASEEQ(cbval, "group")) {
367 *sid_type = IDMAP_GSID;
368 break;
369 } else if (BVAL_CASEEQ(cbval, "user")) {
370 *sid_type = IDMAP_USID;
371 break;
372 }
373 /*
374 * "else if (*sid_type = IDMAP_USID)" then this is a
375 * new sub-class of user -- what to do with it??
376 */
377 }
378
379 return (1);
380 }
381
382 /*
383 * Handle a given search result entry
384 */
385 static
386 void
idmap_extract_object(idmap_query_state_t * state,idmap_q_t * q,LDAPMessage * res,LDAP * ld)387 idmap_extract_object(idmap_query_state_t *state, idmap_q_t *q,
388 LDAPMessage *res, LDAP *ld)
389 {
390 BerValue **bvalues;
391 const char *attr = NULL;
392 char *value = NULL;
393 char *unix_name = NULL;
394 char *dn;
395 char *san = NULL;
396 char *sid = NULL;
397 rid_t rid = 0;
398 int sid_type;
399 int ok;
400 posix_id_t pid = IDMAP_SENTINEL_PID;
401
402 assert(q->rc != NULL);
403 assert(q->domain == NULL || *q->domain == NULL);
404
405 if ((dn = ldap_get_dn(ld, res)) == NULL)
406 return;
407
408 bvalues = ldap_get_values_len(ld, res, OBJCLASS);
409 if (bvalues == NULL) {
410 /*
411 * Didn't find objectclass. Something's wrong with our
412 * AD data.
413 */
414 idmapdlog(LOG_ERR, "%s has no %s", dn, OBJCLASS);
415 goto out;
416 }
417 ok = idmap_bv_objclass2sidtype(bvalues, &sid_type);
418 ldap_value_free_len(bvalues);
419 if (!ok) {
420 /*
421 * Didn't understand objectclass. Something's wrong with our
422 * AD data.
423 */
424 idmapdlog(LOG_ERR, "%s has unexpected %s", dn, OBJCLASS);
425 goto out;
426 }
427
428 if (state->directory_based_mapping == DIRECTORY_MAPPING_IDMU &&
429 q->pid != NULL) {
430 if (sid_type == IDMAP_USID)
431 attr = UIDNUMBER;
432 else if (sid_type == IDMAP_GSID)
433 attr = GIDNUMBER;
434 if (attr != NULL) {
435 bvalues = ldap_get_values_len(ld, res, attr);
436 if (bvalues != NULL) {
437 value = adutils_bv_str(bvalues[0]);
438 if (!adutils_bv_uint(bvalues[0], &pid)) {
439 idmapdlog(LOG_ERR,
440 "%s has Invalid %s value \"%s\"",
441 dn, attr, value);
442 }
443 ldap_value_free_len(bvalues);
444 }
445 }
446 }
447
448 if (state->directory_based_mapping == DIRECTORY_MAPPING_NAME &&
449 q->unixname != NULL) {
450 /*
451 * If the caller has requested unixname then determine the
452 * AD attribute name that will have the unixname, and retrieve
453 * its value.
454 */
455 idmap_id_type esidtype;
456 /*
457 * Determine the target type.
458 *
459 * If the caller specified one, use that. Otherwise, give the
460 * same type that as we found for the Windows user.
461 */
462 esidtype = q->esidtype;
463 if (esidtype == IDMAP_SID)
464 esidtype = sid_type;
465
466 if (esidtype == IDMAP_USID)
467 attr = state->ad_unixuser_attr;
468 else if (esidtype == IDMAP_GSID)
469 attr = state->ad_unixgroup_attr;
470
471 if (attr != NULL) {
472 bvalues = ldap_get_values_len(ld, res, attr);
473 if (bvalues != NULL) {
474 unix_name = adutils_bv_str(bvalues[0]);
475 ldap_value_free_len(bvalues);
476 value = strdup(unix_name);
477 }
478 }
479 }
480
481 bvalues = ldap_get_values_len(ld, res, SAN);
482 if (bvalues != NULL) {
483 san = adutils_bv_str(bvalues[0]);
484 ldap_value_free_len(bvalues);
485 }
486
487 if (q->sid != NULL) {
488 bvalues = ldap_get_values_len(ld, res, OBJSID);
489 if (bvalues != NULL) {
490 sid = adutils_bv_objsid2sidstr(bvalues[0], &rid);
491 ldap_value_free_len(bvalues);
492 }
493 }
494
495 idmap_setqresults(q, san, dn,
496 attr, value,
497 sid, rid, sid_type,
498 unix_name, pid);
499
500 out:
501 ldap_memfree(dn);
502 }
503
504 void
idmap_ldap_res_search_cb(LDAP * ld,LDAPMessage ** res,int rc,int qid,void * argp)505 idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, int qid,
506 void *argp)
507 {
508 idmap_query_state_t *state = (idmap_query_state_t *)argp;
509 idmap_q_t *q = &(state->queries[qid]);
510
511 switch (rc) {
512 case LDAP_RES_SEARCH_RESULT:
513 if (q->search_res != NULL) {
514 idmap_extract_object(state, q, q->search_res, ld);
515 (void) ldap_msgfree(q->search_res);
516 q->search_res = NULL;
517 } else
518 q->ad_rc = ADUTILS_ERR_NOTFOUND;
519 break;
520 case LDAP_RES_SEARCH_ENTRY:
521 if (q->search_res == NULL) {
522 q->search_res = *res;
523 *res = NULL;
524 }
525 break;
526 default:
527 break;
528 }
529 }
530
531 static
532 void
idmap_cleanup_batch(idmap_query_state_t * batch)533 idmap_cleanup_batch(idmap_query_state_t *batch)
534 {
535 int i;
536
537 for (i = 0; i < batch->qcount; i++) {
538 if (batch->queries[i].ecanonname != NULL)
539 free(batch->queries[i].ecanonname);
540 batch->queries[i].ecanonname = NULL;
541 if (batch->queries[i].edomain != NULL)
542 free(batch->queries[i].edomain);
543 batch->queries[i].edomain = NULL;
544 }
545 }
546
547 /*
548 * This routine frees the idmap_query_state_t structure
549 */
550 void
idmap_lookup_release_batch(idmap_query_state_t ** state)551 idmap_lookup_release_batch(idmap_query_state_t **state)
552 {
553 if (state == NULL || *state == NULL)
554 return;
555 adutils_lookup_batch_release(&(*state)->qs);
556 idmap_cleanup_batch(*state);
557 free((*state)->default_domain);
558 free(*state);
559 *state = NULL;
560 }
561
562 idmap_retcode
idmap_lookup_batch_end(idmap_query_state_t ** state)563 idmap_lookup_batch_end(idmap_query_state_t **state)
564 {
565 adutils_rc ad_rc;
566 int i;
567 idmap_query_state_t *id_qs = *state;
568
569 ad_rc = adutils_lookup_batch_end(&id_qs->qs);
570
571 /*
572 * Map adutils rc to idmap_retcode in each
573 * query because consumers in dbutils.c
574 * expects idmap_retcode.
575 */
576 for (i = 0; i < id_qs->qcount; i++) {
577 *id_qs->queries[i].rc =
578 map_adrc2idmaprc(id_qs->queries[i].ad_rc);
579 }
580 idmap_lookup_release_batch(state);
581 return (map_adrc2idmaprc(ad_rc));
582 }
583
584 /*
585 * Send one prepared search, queue up msgid, process what results are
586 * available
587 */
588 static
589 idmap_retcode
idmap_batch_add1(idmap_query_state_t * state,const char * filter,char * ecanonname,char * edomain,idmap_id_type esidtype,char ** dn,char ** attr,char ** value,char ** canonname,char ** dname,char ** sid,rid_t * rid,idmap_id_type * sid_type,char ** unixname,posix_id_t * pid,idmap_retcode * rc)590 idmap_batch_add1(idmap_query_state_t *state, const char *filter,
591 char *ecanonname, char *edomain, idmap_id_type esidtype,
592 char **dn, char **attr, char **value,
593 char **canonname, char **dname,
594 char **sid, rid_t *rid, idmap_id_type *sid_type, char **unixname,
595 posix_id_t *pid,
596 idmap_retcode *rc)
597 {
598 adutils_rc ad_rc;
599 int qid, i;
600 idmap_q_t *q;
601 char *attrs[20]; /* Plenty */
602
603 qid = atomic_inc_32_nv(&state->qcount) - 1;
604 q = &(state->queries[qid]);
605
606 assert(qid < state->qsize);
607
608 /*
609 * Remember the expected canonname, domainname and unix type
610 * so we can check the results * against it
611 */
612 q->ecanonname = ecanonname;
613 q->edomain = edomain;
614 q->esidtype = esidtype;
615
616 /* Remember where to put the results */
617 q->canonname = canonname;
618 q->sid = sid;
619 q->domain = dname;
620 q->rid = rid;
621 q->sid_type = sid_type;
622 q->rc = rc;
623 q->unixname = unixname;
624 q->dn = dn;
625 q->attr = attr;
626 q->value = value;
627 q->pid = pid;
628
629 /* Add attributes that are not always needed */
630 i = 0;
631 attrs[i++] = SAN;
632 attrs[i++] = OBJSID;
633 attrs[i++] = OBJCLASS;
634
635 if (unixname != NULL) {
636 /* Add unixuser/unixgroup attribute names to the attrs list */
637 if (esidtype != IDMAP_GSID &&
638 state->ad_unixuser_attr != NULL)
639 attrs[i++] = (char *)state->ad_unixuser_attr;
640 if (esidtype != IDMAP_USID &&
641 state->ad_unixgroup_attr != NULL)
642 attrs[i++] = (char *)state->ad_unixgroup_attr;
643 }
644
645 if (pid != NULL) {
646 if (esidtype != IDMAP_GSID)
647 attrs[i++] = UIDNUMBER;
648 if (esidtype != IDMAP_USID)
649 attrs[i++] = GIDNUMBER;
650 }
651
652 attrs[i] = NULL;
653
654 /*
655 * Provide sane defaults for the results in case we never hear
656 * back from the DS before closing the connection.
657 *
658 * In particular we default the result to indicate a retriable
659 * error. The first complete matching result entry will cause
660 * this to be set to IDMAP_SUCCESS, and the end of the results
661 * for this search will cause this to indicate "not found" if no
662 * result entries arrived or no complete ones matched the lookup
663 * we were doing.
664 */
665 *rc = IDMAP_ERR_RETRIABLE_NET_ERR;
666 if (sid_type != NULL)
667 *sid_type = IDMAP_SID;
668 if (sid != NULL)
669 *sid = NULL;
670 if (dname != NULL)
671 *dname = NULL;
672 if (rid != NULL)
673 *rid = 0;
674 if (dn != NULL)
675 *dn = NULL;
676 if (attr != NULL)
677 *attr = NULL;
678 if (value != NULL)
679 *value = NULL;
680
681 /*
682 * Don't set *canonname to NULL because it may be pointing to the
683 * given winname. Later on if we get a canonical name from AD the
684 * old name if any will be freed before assigning the new name.
685 */
686
687 /*
688 * Invoke the mother of all APIs i.e. the adutils API
689 */
690 ad_rc = adutils_lookup_batch_add(state->qs, filter,
691 (const char **)attrs,
692 edomain, &q->result, &q->ad_rc);
693 return (map_adrc2idmaprc(ad_rc));
694 }
695
696 idmap_retcode
idmap_name2sid_batch_add1(idmap_query_state_t * state,const char * name,const char * dname,idmap_id_type esidtype,char ** dn,char ** attr,char ** value,char ** canonname,char ** sid,rid_t * rid,idmap_id_type * sid_type,char ** unixname,posix_id_t * pid,idmap_retcode * rc)697 idmap_name2sid_batch_add1(idmap_query_state_t *state,
698 const char *name, const char *dname, idmap_id_type esidtype,
699 char **dn, char **attr, char **value,
700 char **canonname, char **sid, rid_t *rid,
701 idmap_id_type *sid_type, char **unixname,
702 posix_id_t *pid, idmap_retcode *rc)
703 {
704 idmap_retcode retcode;
705 char *filter, *s_name;
706 char *ecanonname, *edomain; /* expected canonname */
707
708 /*
709 * Strategy: search the global catalog for user/group by
710 * sAMAccountName = user/groupname with "" as the base DN and by
711 * userPrincipalName = user/groupname@domain. The result
712 * entries will be checked to conform to the name and domain
713 * name given here. The DN, sAMAccountName, userPrincipalName,
714 * objectSid and objectClass of the result entries are all we
715 * need to figure out which entries match the lookup, the SID of
716 * the user/group and whether it is a user or a group.
717 */
718
719 if ((ecanonname = strdup(name)) == NULL)
720 return (IDMAP_ERR_MEMORY);
721
722 if (dname == NULL || *dname == '\0') {
723 /* 'name' not qualified and dname not given */
724 dname = state->default_domain;
725 edomain = strdup(dname);
726 if (edomain == NULL) {
727 free(ecanonname);
728 return (IDMAP_ERR_MEMORY);
729 }
730 } else {
731 if ((edomain = strdup(dname)) == NULL) {
732 free(ecanonname);
733 return (IDMAP_ERR_MEMORY);
734 }
735 }
736
737 if (!adutils_lookup_check_domain(state->qs, dname)) {
738 free(ecanonname);
739 free(edomain);
740 return (IDMAP_ERR_DOMAIN_NOTFOUND);
741 }
742
743 s_name = sanitize_for_ldap_filter(name);
744 if (s_name == NULL) {
745 free(ecanonname);
746 free(edomain);
747 return (IDMAP_ERR_MEMORY);
748 }
749
750 /* Assemble filter */
751 (void) asprintf(&filter, SANFILTER, s_name);
752 if (s_name != name)
753 free(s_name);
754 if (filter == NULL) {
755 free(ecanonname);
756 free(edomain);
757 return (IDMAP_ERR_MEMORY);
758 }
759
760 retcode = idmap_batch_add1(state, filter, ecanonname, edomain,
761 esidtype, dn, attr, value, canonname, NULL, sid, rid, sid_type,
762 unixname, pid, rc);
763
764 free(filter);
765
766 return (retcode);
767 }
768
769 idmap_retcode
idmap_sid2name_batch_add1(idmap_query_state_t * state,const char * sid,const rid_t * rid,idmap_id_type esidtype,char ** dn,char ** attr,char ** value,char ** name,char ** dname,idmap_id_type * sid_type,char ** unixname,posix_id_t * pid,idmap_retcode * rc)770 idmap_sid2name_batch_add1(idmap_query_state_t *state,
771 const char *sid, const rid_t *rid, idmap_id_type esidtype,
772 char **dn, char **attr, char **value,
773 char **name, char **dname, idmap_id_type *sid_type,
774 char **unixname, posix_id_t *pid, idmap_retcode *rc)
775 {
776 idmap_retcode retcode;
777 int ret;
778 char *filter;
779 char cbinsid[ADUTILS_MAXHEXBINSID + 1];
780
781 /*
782 * Strategy: search [the global catalog] for user/group by
783 * objectSid = SID with empty base DN. The DN, sAMAccountName
784 * and objectClass of the result are all we need to figure out
785 * the name of the SID and whether it is a user, a group or a
786 * computer.
787 */
788
789 if (!adutils_lookup_check_sid_prefix(state->qs, sid))
790 return (IDMAP_ERR_DOMAIN_NOTFOUND);
791
792 ret = adutils_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid));
793 if (ret != 0)
794 return (IDMAP_ERR_SID);
795
796 /* Assemble filter */
797 (void) asprintf(&filter, OBJSIDFILTER, cbinsid, cbinsid);
798 if (filter == NULL)
799 return (IDMAP_ERR_MEMORY);
800
801 retcode = idmap_batch_add1(state, filter, NULL, NULL, esidtype,
802 dn, attr, value, name, dname, NULL, NULL, sid_type, unixname,
803 pid, rc);
804
805 free(filter);
806
807 return (retcode);
808 }
809
810 idmap_retcode
idmap_unixname2sid_batch_add1(idmap_query_state_t * state,const char * unixname,int is_user,int is_wuser,char ** dn,char ** attr,char ** value,char ** sid,rid_t * rid,char ** name,char ** dname,idmap_id_type * sid_type,idmap_retcode * rc)811 idmap_unixname2sid_batch_add1(idmap_query_state_t *state,
812 const char *unixname, int is_user, int is_wuser,
813 char **dn, char **attr, char **value,
814 char **sid, rid_t *rid, char **name,
815 char **dname, idmap_id_type *sid_type, idmap_retcode *rc)
816 {
817 idmap_retcode retcode;
818 char *filter, *s_unixname;
819 const char *attrname;
820
821 /* Get unixuser or unixgroup AD attribute name */
822 attrname = (is_user) ?
823 state->ad_unixuser_attr : state->ad_unixgroup_attr;
824 if (attrname == NULL)
825 return (IDMAP_ERR_NOTFOUND);
826
827 s_unixname = sanitize_for_ldap_filter(unixname);
828 if (s_unixname == NULL)
829 return (IDMAP_ERR_MEMORY);
830
831 /* Assemble filter */
832 (void) asprintf(&filter, "(&(objectclass=%s)(%s=%s))",
833 is_wuser ? "user" : "group", attrname, s_unixname);
834 if (s_unixname != unixname)
835 free(s_unixname);
836 if (filter == NULL) {
837 return (IDMAP_ERR_MEMORY);
838 }
839
840 retcode = idmap_batch_add1(state, filter, NULL, NULL,
841 IDMAP_POSIXID, dn, NULL, NULL, name, dname, sid, rid, sid_type,
842 NULL, NULL, rc);
843
844 if (retcode == IDMAP_SUCCESS && attr != NULL) {
845 if ((*attr = strdup(attrname)) == NULL)
846 retcode = IDMAP_ERR_MEMORY;
847 }
848
849 if (retcode == IDMAP_SUCCESS && value != NULL) {
850 if ((*value = strdup(unixname)) == NULL)
851 retcode = IDMAP_ERR_MEMORY;
852 }
853
854 free(filter);
855
856 return (retcode);
857 }
858
859 idmap_retcode
idmap_pid2sid_batch_add1(idmap_query_state_t * state,posix_id_t pid,int is_user,char ** dn,char ** attr,char ** value,char ** sid,rid_t * rid,char ** name,char ** dname,idmap_id_type * sid_type,idmap_retcode * rc)860 idmap_pid2sid_batch_add1(idmap_query_state_t *state,
861 posix_id_t pid, int is_user,
862 char **dn, char **attr, char **value,
863 char **sid, rid_t *rid, char **name,
864 char **dname, idmap_id_type *sid_type, idmap_retcode *rc)
865 {
866 idmap_retcode retcode;
867 char *filter;
868 const char *attrname;
869
870 /* Assemble filter */
871 if (is_user) {
872 (void) asprintf(&filter, UIDNUMBERFILTER, pid);
873 attrname = UIDNUMBER;
874 } else {
875 (void) asprintf(&filter, GIDNUMBERFILTER, pid);
876 attrname = GIDNUMBER;
877 }
878 if (filter == NULL)
879 return (IDMAP_ERR_MEMORY);
880
881 retcode = idmap_batch_add1(state, filter, NULL, NULL,
882 IDMAP_POSIXID, dn, NULL, NULL, name, dname, sid, rid, sid_type,
883 NULL, NULL, rc);
884
885 if (retcode == IDMAP_SUCCESS && attr != NULL) {
886 if ((*attr = strdup(attrname)) == NULL)
887 retcode = IDMAP_ERR_MEMORY;
888 }
889
890 if (retcode == IDMAP_SUCCESS && value != NULL) {
891 (void) asprintf(value, "%u", pid);
892 if (*value == NULL)
893 retcode = IDMAP_ERR_MEMORY;
894 }
895
896 free(filter);
897
898 return (retcode);
899 }
900