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