xref: /illumos-gate/usr/src/cmd/idmap/idmap/namemaps.c (revision 1fdeec65)
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  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 
26 #include <errno.h>
27 #include <ldap.h>
28 #include <sasl/sasl.h>
29 #include <libintl.h>
30 #include <strings.h>
31 #include <syslog.h>
32 #include <stdarg.h>
33 
34 #include "addisc.h"
35 #include "libadutils.h"
36 #include "idmap_priv.h"
37 #include "ns_sldap.h"
38 #include "namemaps.h"
39 
40 /* From adutils.c: */
41 
42 /* A single DS */
43 struct idmap_nm_handle {
44 	LDAP			*ad;		/* LDAP connection */
45 	/* LDAP DS info */
46 	char			*ad_host;
47 	int			ad_port;
48 
49 	/* hardwired to SASL GSSAPI only for now */
50 	char			*saslmech;
51 	unsigned		saslflags;
52 	char			*windomain;
53 	char			*ad_unixuser_attr;
54 	char			*ad_unixgroup_attr;
55 	char			*nldap_winname_attr;
56 	char			*default_domain;
57 	bool_t			is_nldap;
58 	bool_t			is_ad;
59 	int			direction;
60 	ns_cred_t		nsc;
61 };
62 
63 /* PRINTFLIKE1 */
64 static
65 void
66 namemap_log(char *fmt, ...)
67 {
68 	va_list va;
69 
70 	va_start(va, fmt);
71 	(void) vfprintf(stderr, fmt, va);
72 	va_end(va);
73 	(void) fprintf(stderr, "\n");
74 }
75 
76 static
77 idmap_stat
78 string2auth(const char *from, ns_auth_t *na)
79 {
80 	if (from == NULL) {
81 		na->type = NS_LDAP_AUTH_SASL;
82 		na->tlstype = NS_LDAP_TLS_SASL;
83 		na->saslmech = NS_LDAP_SASL_GSSAPI;
84 		na->saslopt = NS_LDAP_SASLOPT_PRIV |
85 		    NS_LDAP_SASLOPT_INT;
86 		return (IDMAP_SUCCESS);
87 	}
88 
89 	if (strcasecmp(from, "simple") == 0) {
90 		na->type = NS_LDAP_AUTH_SIMPLE;
91 		na->tlstype = NS_LDAP_TLS_NONE;
92 		na->saslmech = NS_LDAP_SASL_NONE;
93 		na->saslopt = NS_LDAP_SASLOPT_NONE;
94 	} else if (strcasecmp(from, "sasl/CRAM-MD5") == 0) {
95 		na->type = NS_LDAP_AUTH_SASL;
96 		na->tlstype = NS_LDAP_TLS_SASL;
97 		na->saslmech = NS_LDAP_SASL_CRAM_MD5;
98 		na->saslopt = NS_LDAP_SASLOPT_NONE;
99 	} else if (strcasecmp(from, "sasl/DIGEST-MD5") == 0) {
100 		na->type = NS_LDAP_AUTH_SASL;
101 		na->tlstype = NS_LDAP_TLS_SASL;
102 		na->saslmech = NS_LDAP_SASL_DIGEST_MD5;
103 		na->saslopt = NS_LDAP_SASLOPT_NONE;
104 	} else if (strcasecmp(from, "sasl/GSSAPI") == 0) {
105 		na->type = NS_LDAP_AUTH_SASL;
106 		na->tlstype = NS_LDAP_TLS_SASL;
107 		na->saslmech = NS_LDAP_SASL_GSSAPI;
108 		na->saslopt = NS_LDAP_SASLOPT_PRIV |
109 		    NS_LDAP_SASLOPT_INT;
110 	} else if (strcasecmp(from, "tls:simple") == 0) {
111 		na->type = NS_LDAP_AUTH_TLS;
112 		na->tlstype = NS_LDAP_TLS_SIMPLE;
113 		na->saslmech = NS_LDAP_SASL_NONE;
114 		na->saslopt = NS_LDAP_SASLOPT_NONE;
115 	} else if (strcasecmp(from, "tls:sasl/CRAM-MD5") == 0) {
116 		na->type = NS_LDAP_AUTH_TLS;
117 		na->tlstype = NS_LDAP_TLS_SASL;
118 		na->saslmech = NS_LDAP_SASL_CRAM_MD5;
119 		na->saslopt = NS_LDAP_SASLOPT_NONE;
120 	} else if (strcasecmp(from, "tls:sasl/DIGEST-MD5") == 0) {
121 		na->type = NS_LDAP_AUTH_TLS;
122 		na->tlstype = NS_LDAP_TLS_SASL;
123 		na->saslmech = NS_LDAP_SASL_DIGEST_MD5;
124 		na->saslopt = NS_LDAP_SASLOPT_NONE;
125 	} else {
126 		namemap_log(
127 		    gettext("Invalid authentication method \"%s\" specified\n"),
128 		    from);
129 		return (IDMAP_ERR_ARG);
130 	}
131 
132 	return (IDMAP_SUCCESS);
133 }
134 
135 
136 
137 static
138 idmap_stat
139 strings2cred(ns_cred_t *nsc, char *user, char *passwd, char *auth)
140 {
141 	idmap_stat rc;
142 	(void) memset(nsc, 0, sizeof (ns_cred_t));
143 
144 	if ((rc = string2auth(auth, &nsc->auth)) != IDMAP_SUCCESS)
145 		return (rc);
146 
147 	if (user != NULL) {
148 		nsc->cred.unix_cred.userID = strdup(user);
149 		if (nsc->cred.unix_cred.userID == NULL)
150 			return (IDMAP_ERR_MEMORY);
151 	}
152 
153 	if (passwd != NULL) {
154 		nsc->cred.unix_cred.passwd = strdup(passwd);
155 		if (nsc->cred.unix_cred.passwd == NULL) {
156 			free(nsc->cred.unix_cred.userID);
157 			return (IDMAP_ERR_MEMORY);
158 		}
159 	}
160 
161 	return (IDMAP_SUCCESS);
162 }
163 
164 
165 
166 
167 
168 /*ARGSUSED*/
169 static int
170 idmap_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts)
171 {
172 	sasl_interact_t	*interact;
173 
174 	if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
175 		return (LDAP_PARAM_ERROR);
176 
177 	/* There should be no extra arguemnts for SASL/GSSAPI authentication */
178 	for (interact = prompts; interact->id != SASL_CB_LIST_END;
179 	    interact++) {
180 		interact->result = NULL;
181 		interact->len = 0;
182 	}
183 	return (LDAP_SUCCESS);
184 }
185 
186 static
187 idmap_stat
188 idmap_open_ad_conn(idmap_nm_handle_t *adh)
189 {
190 	int zero = 0;
191 	int timeoutms = 30 * 1000;
192 	int ldversion, ldap_rc;
193 	idmap_stat rc = IDMAP_SUCCESS;
194 
195 	/* Open and bind an LDAP connection */
196 	adh->ad = ldap_init(adh->ad_host, adh->ad_port);
197 	if (adh->ad == NULL) {
198 		namemap_log(
199 		    gettext("ldap_init() to server %s port %d failed. (%s)"),
200 		    CHECK_NULL(adh->ad_host),
201 		    adh->ad_port, strerror(errno));
202 		rc = IDMAP_ERR_INTERNAL;
203 		goto out;
204 	}
205 	ldversion = LDAP_VERSION3;
206 	(void) ldap_set_option(adh->ad, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
207 	(void) ldap_set_option(adh->ad, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
208 	(void) ldap_set_option(adh->ad, LDAP_OPT_TIMELIMIT, &zero);
209 	(void) ldap_set_option(adh->ad, LDAP_OPT_SIZELIMIT, &zero);
210 	(void) ldap_set_option(adh->ad, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
211 	(void) ldap_set_option(adh->ad, LDAP_OPT_RESTART, LDAP_OPT_ON);
212 	ldap_rc = ldap_sasl_interactive_bind_s(adh->ad, "" /* binddn */,
213 	    adh->saslmech, NULL, NULL, adh->saslflags, &idmap_saslcallback,
214 	    NULL);
215 
216 	if (ldap_rc != LDAP_SUCCESS) {
217 		(void) ldap_unbind(adh->ad);
218 		adh->ad = NULL;
219 		namemap_log(
220 		    gettext("ldap_sasl_interactive_bind_s() to server "
221 		    "%s port %d failed. (%s)"), CHECK_NULL(adh->ad_host),
222 		    adh->ad_port, ldap_err2string(ldap_rc));
223 		rc = IDMAP_ERR_INTERNAL;
224 	}
225 
226 out:
227 	return (rc);
228 }
229 
230 static
231 idmap_stat
232 idmap_init_nldap(idmap_nm_handle_t *p)
233 {
234 /*
235  * For now, there is nothing to initialize in nldap. This is just to
236  * make it future-proof, especially standalone libsldap-proof
237  */
238 	p->is_nldap = TRUE;
239 	return (0);
240 }
241 
242 static
243 idmap_stat
244 idmap_init_ad(idmap_nm_handle_t *p)
245 {
246 	idmap_stat	rc = IDMAP_SUCCESS;
247 	idmap_ad_disc_ds_t	*dc = NULL;
248 	ad_disc_t	ad_ctx;
249 
250 	ad_ctx = ad_disc_init();
251 	if (ad_ctx == NULL) {
252 		namemap_log(
253 		    gettext("AD autodiscovery initialization failed"));
254 		return (IDMAP_ERR_INTERNAL);
255 	}
256 	ad_disc_refresh(ad_ctx);
257 
258 
259 	/* Based on the supplied or default domain, find the proper AD: */
260 	if (ad_disc_set_DomainName(ad_ctx, p->windomain)) {
261 		rc = IDMAP_ERR_INTERNAL;
262 		namemap_log(
263 		    gettext("Setting a domain name \"%s\" for autodiscovery"
264 		    " failed, most likely not enough memory"), p->windomain);
265 		goto cleanup;
266 	}
267 
268 	dc = ad_disc_get_DomainController(ad_ctx, AD_DISC_GLOBAL, NULL);
269 	if (dc == NULL) {
270 		rc = IDMAP_ERR_ARG;
271 		namemap_log(
272 		    gettext("A domain controller for the "
273 		    "domain \"%s\" not found."), p->windomain);
274 		goto cleanup;
275 	}
276 
277 
278 	p->ad_port = dc->port;
279 	p->ad_host = strdup(dc->host);
280 
281 	if (p->ad_host == NULL) {
282 		rc = IDMAP_ERR_MEMORY;
283 		goto cleanup;
284 	}
285 
286 	p->saslflags = LDAP_SASL_INTERACTIVE;
287 	p->saslmech = strdup("GSSAPI");
288 
289 	if (p->saslmech == NULL) {
290 		rc = IDMAP_ERR_MEMORY;
291 		goto cleanup;
292 	}
293 
294 	rc = idmap_open_ad_conn(p);
295 
296 	if (rc != IDMAP_SUCCESS)
297 		goto cleanup;
298 
299 	p->is_ad = TRUE;
300 
301 cleanup:
302 	ad_disc_fini(ad_ctx);
303 	free(dc);
304 	return (rc);
305 }
306 
307 void
308 idmap_fini_namemaps(idmap_nm_handle_t *p)
309 {
310 	if (p == NULL)
311 		return;
312 
313 	if (p->ad_unixgroup_attr != NULL)
314 		free(p->ad_unixgroup_attr);
315 
316 	if (p->ad_unixuser_attr != NULL)
317 		free(p->ad_unixuser_attr);
318 
319 	if (p->nldap_winname_attr)
320 		free(p->nldap_winname_attr);
321 
322 	if (p->windomain != NULL)
323 		free(p->windomain);
324 
325 	if (p->default_domain != NULL)
326 		free(p->default_domain);
327 
328 	if (p->saslmech != NULL)
329 		free(p->saslmech);
330 
331 	if (p->ad_host != NULL)
332 		free(p->ad_host);
333 
334 	if (p->nsc.cred.unix_cred.userID != NULL) {
335 		free(p->nsc.cred.unix_cred.userID);
336 	}
337 
338 	if (p->nsc.cred.unix_cred.passwd != NULL) {
339 		/* No archeology: */
340 		(void) memset(p->nsc.cred.unix_cred.passwd, 0,
341 		    strlen(p->nsc.cred.unix_cred.passwd));
342 		free(p->nsc.cred.unix_cred.passwd);
343 	}
344 
345 	if (p->ad)
346 		(void) ldap_unbind(p->ad);
347 	free(p);
348 
349 }
350 
351 
352 
353 idmap_stat
354 idmap_init_namemaps(idmap_nm_handle_t **adh,
355     char *user, char *passwd, char *auth, char *windomain,
356     int direction)
357 {
358 	idmap_stat rc;
359 	idmap_nm_handle_t *p;
360 
361 	p = (idmap_nm_handle_t *)calloc(1, sizeof (idmap_nm_handle_t));
362 	if (p == NULL)
363 		return (IDMAP_ERR_MEMORY);
364 
365 	rc = idmap_get_prop_str(PROP_DEFAULT_DOMAIN,
366 	    &p->default_domain);
367 	if (rc != IDMAP_SUCCESS) {
368 		namemap_log(
369 		    gettext("Error obtaining default domain from idmapd (%s)"),
370 		    idmap_stat2string(rc));
371 		goto cleanup;
372 	}
373 
374 	rc = idmap_get_prop_str(PROP_AD_UNIXUSER_ATTR,
375 	    &p->ad_unixuser_attr);
376 	if (rc != IDMAP_SUCCESS) {
377 		namemap_log(
378 		    gettext("Error obtaining AD unixuser attribute (%s)"),
379 		    idmap_stat2string(rc));
380 		goto cleanup;
381 	}
382 
383 	rc = idmap_get_prop_str(PROP_AD_UNIXGROUP_ATTR,
384 	    &p->ad_unixgroup_attr);
385 	if (rc != IDMAP_SUCCESS) {
386 		namemap_log(
387 		    gettext("Error obtaining AD unixgroup attribute (%s)"),
388 		    idmap_stat2string(rc));
389 		goto cleanup;
390 	}
391 
392 
393 	rc = idmap_get_prop_str(PROP_NLDAP_WINNAME_ATTR,
394 	    &p->nldap_winname_attr);
395 	if (rc != IDMAP_SUCCESS) {
396 		namemap_log(
397 		    gettext("Error obtaining AD unixgroup attribute (%s)"),
398 		    idmap_stat2string(rc));
399 		goto cleanup;
400 	}
401 
402 	if (windomain != NULL) {
403 		p->windomain = strdup(windomain);
404 		if (p->windomain == NULL) {
405 			rc = IDMAP_ERR_MEMORY;
406 			goto cleanup;
407 		}
408 	} else if (!EMPTY_STRING(p->default_domain)) {
409 		p->windomain = strdup(p->default_domain);
410 		if (p->windomain == NULL) {
411 			rc = IDMAP_ERR_MEMORY;
412 			goto cleanup;
413 		}
414 	} else if (direction == IDMAP_DIRECTION_W2U) {
415 		namemap_log(
416 		    gettext("Windows domain not given and idmapd daemon"
417 		    " didn't provide a default one"));
418 		rc = IDMAP_ERR_ARG;
419 		goto cleanup;
420 	}
421 
422 	p->direction = direction;
423 
424 	if ((p->ad_unixuser_attr != NULL || p->ad_unixgroup_attr != NULL) &&
425 	    direction != IDMAP_DIRECTION_U2W) {
426 		rc = idmap_init_ad(p);
427 		if (rc != IDMAP_SUCCESS) {
428 			goto cleanup;
429 		}
430 	}
431 
432 	if (p->nldap_winname_attr != NULL && direction != IDMAP_DIRECTION_W2U) {
433 		rc = idmap_init_nldap(p);
434 		if (rc != IDMAP_SUCCESS) {
435 			goto cleanup;
436 		}
437 
438 		rc = strings2cred(&p->nsc, user, passwd, auth);
439 		if (rc != IDMAP_SUCCESS) {
440 			goto cleanup;
441 		}
442 	}
443 
444 cleanup:
445 
446 	if (rc == IDMAP_SUCCESS) {
447 		*adh = p;
448 		return (IDMAP_SUCCESS);
449 	}
450 
451 	/* There was an error: */
452 	idmap_fini_namemaps(*adh);
453 	return (rc);
454 }
455 
456 static
457 char *
458 dns2dn(const char *dns, const char *prefix)
459 {
460 	int num_lvl = 1;
461 	char *buf;
462 	const char *it, *new_it;
463 
464 	for (it = dns; it != NULL; it = strchr(it, '.')) {
465 		it ++;
466 		num_lvl ++;
467 	}
468 
469 	buf = (char *)malloc(strlen(prefix) + strlen(dns) + 4 * num_lvl);
470 	(void) strcpy(buf, prefix);
471 
472 
473 	it = dns;
474 	for (;;) {
475 		new_it = strchr(it, '.');
476 		(void) strcat(buf, "DC=");
477 		if (new_it == NULL) {
478 			(void) strcat(buf, it);
479 			break;
480 		} else {
481 			(void) strncat(buf, it, new_it - it);
482 			(void) strcat(buf, ",");
483 		}
484 
485 		it = new_it + 1;
486 	}
487 
488 	return (buf);
489 }
490 
491 
492 static
493 idmap_stat
494 extract_attribute(idmap_nm_handle_t *p, LDAPMessage *entry, char *name,
495     char **value)
496 {
497 	char	**values = NULL;
498 	idmap_stat rc = IDMAP_SUCCESS;
499 	/* No value means it is not requested */
500 	if (value == NULL)
501 		return (IDMAP_SUCCESS);
502 
503 	values = ldap_get_values(p->ad, entry, name);
504 	if (values == NULL || values[0] == NULL)
505 		*value = NULL;
506 	else {
507 		*value = strdup(values[0]);
508 		if (*value == NULL)
509 			rc = IDMAP_ERR_MEMORY;
510 	}
511 errout:
512 	ldap_value_free(values);
513 	return (rc);
514 }
515 
516 
517 /* Split winname to its name and domain part */
518 static
519 idmap_stat
520 split_fqwn(char *fqwn, char **name, char **domain)
521 {
522 	char *at;
523 
524 	*name = NULL;
525 	*domain = NULL;
526 
527 	at = strchr(fqwn, '@');
528 	if (at == NULL) {
529 		at = strchr(fqwn, '\\');
530 	}
531 	if (at == NULL) {
532 	/* There is no domain - leave domain NULL */
533 		*name = strdup(fqwn);
534 		if (*name == NULL)
535 			goto errout;
536 		return (IDMAP_SUCCESS);
537 	}
538 
539 
540 	*domain = strdup(at+1);
541 	if (*domain == NULL)
542 		goto errout;
543 	*name = (char *)malloc(at - fqwn + 1);
544 	if (*name == NULL)
545 		goto errout;
546 	(void) strlcpy(*name, fqwn, at - fqwn + 1);
547 
548 	if (*at == '\\') {
549 		char *it = *name;
550 		*name = *domain;
551 		*domain = it;
552 	}
553 
554 	return (IDMAP_SUCCESS);
555 
556 
557 errout:
558 	free(*name);
559 	*name = NULL;
560 	free(*domain);
561 	*domain = NULL;
562 	return (IDMAP_ERR_MEMORY);
563 }
564 
565 static
566 idmap_stat
567 unixname2dn(idmap_nm_handle_t *p, char *unixname, int is_user, char **dn,
568     char **winname, char **windomain)
569 {
570 	idmap_stat rc = IDMAP_SUCCESS;
571 	int rc_ns;
572 
573 
574 	char filter[255];
575 	static const char *attribs[3];
576 	ns_ldap_result_t *res;
577 	ns_ldap_error_t *errorp = NULL;
578 	char **attrs;
579 
580 
581 	attribs[0] = p->nldap_winname_attr;
582 	attribs[1] = "dn";
583 	attribs[2] = NULL;
584 
585 	(void) snprintf(filter, sizeof (filter), is_user ? "uid=%s" : "cn=%s",
586 	    unixname);
587 
588 	rc_ns = __ns_ldap_list(is_user ? "passwd" : "group",
589 	    filter, NULL, attribs, NULL, 0, &res, &errorp, NULL, NULL);
590 
591 
592 	if (rc_ns == NS_LDAP_NOTFOUND) {
593 		namemap_log(is_user ? gettext("User %s not found.")
594 		    : gettext("Group %s not found."),  unixname);
595 		return (IDMAP_ERR_NOTFOUND);
596 	} else if (rc_ns != NS_LDAP_SUCCESS) {
597 		char *msg = "Cause unidentified";
598 		if (errorp != NULL) {
599 			(void) __ns_ldap_err2str(errorp->status, &msg);
600 		}
601 		namemap_log(gettext("Ldap list failed (%s)."), msg);
602 		return (IDMAP_ERR_ARG);
603 	}
604 
605 	if (res == NULL) {
606 		namemap_log(gettext("User %s not found"), unixname);
607 		return (IDMAP_ERR_ARG);
608 	}
609 
610 	if (winname != NULL && windomain != NULL) {
611 		attrs = __ns_ldap_getAttr(&res->entry[0],
612 		    p->nldap_winname_attr);
613 		if (attrs != NULL && attrs[0] != NULL) {
614 			rc = split_fqwn(attrs[0], winname, windomain);
615 		} else {
616 			*winname = *windomain = NULL;
617 		}
618 	}
619 
620 	if (dn != NULL) {
621 		attrs = __ns_ldap_getAttr(&res->entry[0], "dn");
622 		if (attrs == NULL || attrs[0] == NULL) {
623 			namemap_log(gettext("dn for %s not found"),
624 			    unixname);
625 			return (IDMAP_ERR_ARG);
626 		}
627 		*dn = strdup(attrs[0]);
628 	}
629 
630 
631 	return (rc);
632 
633 }
634 
635 #define	FILTER	"(sAMAccountName=%s)"
636 
637 /* Puts the values of attributes to unixuser and unixgroup, unless NULL */
638 
639 static
640 idmap_stat
641 winname2dn(idmap_nm_handle_t *p, char *winname,
642     int *is_wuser, char **dn, char **unixuser, char **unixgroup)
643 {
644 	idmap_stat rc = IDMAP_SUCCESS;
645 	char *base;
646 	char *filter;
647 	int flen;
648 	char *attribs[4];
649 	int i;
650 	LDAPMessage *results = NULL;
651 	LDAPMessage *entry;
652 	int ldap_rc;
653 
654 	/* Query: */
655 
656 	base = dns2dn(p->windomain, "");
657 	if (base == NULL) {
658 		return (IDMAP_ERR_MEMORY);
659 	}
660 
661 	i = 0;
662 	attribs[i++] = "objectClass";
663 	if (unixuser != NULL)
664 		attribs[i++] = p->ad_unixuser_attr;
665 	if (unixgroup != NULL)
666 		attribs[i++] = p->ad_unixgroup_attr;
667 	attribs[i] = NULL;
668 
669 	flen = snprintf(NULL, 0, FILTER, winname) + 1;
670 	if ((filter = (char *)malloc(flen)) == NULL) {
671 		free(base);
672 		return (IDMAP_ERR_MEMORY);
673 	}
674 	(void) snprintf(filter, flen, FILTER, winname);
675 
676 	ldap_rc = ldap_search_s(p->ad, base, LDAP_SCOPE_SUBTREE, filter,
677 	    attribs, 0, &results);
678 
679 	free(base);
680 	free(filter);
681 
682 	if (ldap_rc != LDAP_SUCCESS) {
683 		namemap_log(
684 		    gettext("Ldap query to server %s port %d failed. (%s)"),
685 		    p->ad_host, p->ad_port, ldap_err2string(ldap_rc));
686 		(void) ldap_msgfree(results);
687 		return (IDMAP_ERR_OTHER);
688 	}
689 
690 
691 	for (entry = ldap_first_entry(p->ad, results), *dn = NULL;
692 	    entry != NULL;
693 	    entry = ldap_next_entry(p->ad, entry)) {
694 		char	**values = NULL;
695 		int i = 0;
696 		values = ldap_get_values(p->ad, entry, "objectClass");
697 
698 		if (values == NULL) {
699 			(void) ldap_msgfree(results);
700 			return (IDMAP_ERR_MEMORY);
701 		}
702 
703 		for (i = 0; i < ldap_count_values(values); i++) {
704 		/*
705 		 * is_wuser can be IDMAP_UNKNOWN, in that case we accept
706 		 * both User/Group
707 		 */
708 			if (*is_wuser != IDMAP_NO &&
709 			    strcasecmp(values[i], "User") == 0 ||
710 			    *is_wuser != IDMAP_YES &&
711 			    strcasecmp(values[i], "Group") == 0) {
712 				*dn = ldap_get_dn(p->ad, entry);
713 				if (*dn == NULL) {
714 					ldap_value_free(values);
715 					(void) ldap_msgfree(results);
716 					return (IDMAP_ERR_MEMORY);
717 				}
718 				*is_wuser = strcasecmp(values[i], "User") == 0
719 				    ? IDMAP_YES : IDMAP_NO;
720 				break;
721 			}
722 		}
723 
724 		ldap_value_free(values);
725 		if (*dn != NULL)
726 			break;
727 	}
728 
729 	if (*dn == NULL) {
730 		namemap_log(
731 		    *is_wuser == IDMAP_YES ? gettext("User %s@%s not found") :
732 		    *is_wuser == IDMAP_NO ? gettext("Group %s@%s not found") :
733 		    gettext("%s@%s not found"), winname, p->windomain);
734 		return (IDMAP_ERR_NOTFOUND);
735 	}
736 
737 	if (unixuser != NULL)
738 		rc = extract_attribute(p, entry, p->ad_unixuser_attr,
739 		    unixuser);
740 
741 	if (rc == IDMAP_SUCCESS && unixgroup != NULL)
742 		rc = extract_attribute(p, entry, p->ad_unixgroup_attr,
743 		    unixgroup);
744 
745 	(void) ldap_msgfree(results);
746 
747 	return (rc);
748 }
749 
750 
751 /* set the given attribute to the given value. If value is NULL, unset it */
752 static
753 idmap_stat
754 idmap_ad_set(idmap_nm_handle_t *p, char *dn, char *attr, char *value)
755 {
756 	idmap_stat rc = IDMAP_SUCCESS;
757 	int ldap_rc;
758 	char *new_values[2] = {NULL, NULL};
759 	LDAPMod *mods[2] = {NULL, NULL};
760 
761 	mods[0] = (LDAPMod *)calloc(1, sizeof (LDAPMod));
762 	mods[0]->mod_type = strdup(attr);
763 	if (value != NULL) {
764 		mods[0]->mod_op = LDAP_MOD_REPLACE;
765 		new_values[0] = strdup(value);
766 		mods[0]->mod_values = new_values;
767 	} else {
768 		mods[0]->mod_op = LDAP_MOD_DELETE;
769 		mods[0]->mod_values = NULL;
770 	}
771 
772 	ldap_rc = ldap_modify_s(p->ad, dn, mods);
773 	if (ldap_rc != LDAP_SUCCESS) {
774 		namemap_log(
775 		    gettext("Ldap modify of %s, attribute %s failed. (%s)"),
776 		    dn, attr, ldap_err2string(ldap_rc));
777 		rc = IDMAP_ERR_INTERNAL;
778 	}
779 
780 
781 	ldap_mods_free(mods, 0);
782 	return (rc);
783 }
784 
785 
786 /*
787  * This function takes the p argument just for the beauty of the symmetry
788  * with idmap_ad_set (and for future enhancements).
789  */
790 static
791 idmap_stat
792 /* LINTED E_FUNC_ARG_UNUSED */
793 idmap_nldap_set(idmap_nm_handle_t *p, ns_cred_t *nsc, char *dn, char *attr,
794     char *value, bool_t is_new, int is_user)
795 {
796 	int ldaprc;
797 	ns_ldap_error_t *errorp = NULL;
798 	ns_ldap_attr_t	*attrs[2];
799 
800 
801 
802 	attrs[0] = (ns_ldap_attr_t *)malloc(sizeof (ns_ldap_attr_t));
803 	if (attrs == NULL)
804 		return (IDMAP_ERR_MEMORY);
805 
806 	attrs[0]->attrname = attr;
807 
808 	if (value != NULL) {
809 		char **newattr = (char **)calloc(2, sizeof (char *));
810 		if (newattr == NULL) {
811 			free(attrs[0]);
812 			return (IDMAP_ERR_MEMORY);
813 		}
814 		newattr[0] = value;
815 		newattr[1] = NULL;
816 
817 		attrs[0]->attrvalue = newattr;
818 		attrs[0]->value_count = 1;
819 	} else {
820 		attrs[0]->attrvalue = NULL;
821 		attrs[0]->value_count = 0;
822 	}
823 
824 
825 	attrs[1] = NULL;
826 
827 	if (value == NULL) {
828 		ldaprc = __ns_ldap_delAttr(
829 		    is_user == IDMAP_YES ? "passwd": "group",
830 		    dn, (const ns_ldap_attr_t * const *)attrs,
831 		    nsc, 0, &errorp);
832 	} else if (is_new)
833 		ldaprc = __ns_ldap_addAttr(
834 		    is_user == IDMAP_YES ? "passwd": "group",
835 		    dn, (const ns_ldap_attr_t * const *)attrs,
836 		    nsc, 0, &errorp);
837 	else
838 		ldaprc = __ns_ldap_repAttr(
839 		    is_user == IDMAP_YES ? "passwd": "group",
840 		    dn, (const ns_ldap_attr_t * const *)attrs,
841 		    nsc, 0, &errorp);
842 
843 	if (ldaprc != NS_LDAP_SUCCESS) {
844 		char *msg = "Cause unidentified";
845 		if (errorp != NULL) {
846 			(void) __ns_ldap_err2str(errorp->status, &msg);
847 		}
848 		namemap_log(
849 		    gettext("__ns_ldap_addAttr/rep/delAttr failed (%s)"),
850 		    msg);
851 		return (IDMAP_ERR_ARG);
852 	}
853 
854 	return (IDMAP_SUCCESS);
855 }
856 
857 idmap_stat
858 idmap_set_namemap(idmap_nm_handle_t *p, char *winname, char *unixname,
859     int is_user, int is_wuser, int direction)
860 {
861 	idmap_stat	rc = IDMAP_SUCCESS;
862 	char		*dn = NULL;
863 	char		*oldwinname = NULL;
864 	char		*oldwindomain = NULL;
865 
866 	if (direction == IDMAP_DIRECTION_W2U) {
867 		if (!p->is_ad) {
868 			rc = IDMAP_ERR_ARG;
869 			namemap_log(
870 			    gettext("AD namemaps aren't set up."));
871 			goto cleanup;
872 		}
873 
874 		rc = winname2dn(p, winname, &is_wuser,
875 		    &dn, NULL, NULL);
876 		if (rc != IDMAP_SUCCESS)
877 			goto cleanup;
878 
879 		rc = idmap_ad_set(p, dn, is_user ? p->ad_unixuser_attr :
880 		    p->ad_unixgroup_attr, unixname);
881 		if (rc != IDMAP_SUCCESS)
882 			goto cleanup;
883 
884 	}
885 
886 
887 	if (direction == IDMAP_DIRECTION_U2W) {
888 		char *fullname;
889 
890 		if (!p->is_nldap) {
891 			rc = IDMAP_ERR_ARG;
892 			namemap_log(
893 			    gettext("Native ldap namemaps aren't set up."));
894 			goto cleanup;
895 		}
896 
897 
898 		rc = unixname2dn(p, unixname, is_user, &dn,
899 		    &oldwinname, &oldwindomain);
900 		if (rc != IDMAP_SUCCESS)
901 			goto cleanup;
902 
903 		if (p->windomain == NULL) {
904 			fullname = strdup(winname);
905 			if (fullname == NULL)
906 				rc = IDMAP_ERR_MEMORY;
907 				goto cleanup;
908 		} else {
909 			fullname = malloc(strlen(winname) +
910 			    strlen(p->windomain) + 2);
911 			if (fullname == NULL) {
912 				rc = IDMAP_ERR_MEMORY;
913 				goto cleanup;
914 			}
915 
916 			(void) snprintf(fullname,
917 			    strlen(winname) + strlen(p->windomain) + 2,
918 			    "%s\\%s", p->windomain, winname);
919 		}
920 		rc = idmap_nldap_set(p, &p->nsc, dn, p->nldap_winname_attr,
921 		    fullname, oldwinname == NULL ? TRUE : FALSE, is_user);
922 
923 		free(fullname);
924 		free(oldwindomain);
925 		free(oldwinname);
926 
927 		if (rc != IDMAP_SUCCESS)
928 			goto cleanup;
929 
930 	}
931 
932 cleanup:
933 	if (dn != NULL)
934 		free(dn);
935 
936 	if (oldwindomain != NULL)
937 		free(oldwindomain);
938 
939 	if (oldwinname != NULL)
940 		free(oldwinname);
941 
942 	return (rc);
943 
944 }
945 
946 
947 idmap_stat
948 idmap_unset_namemap(idmap_nm_handle_t *p, char *winname, char *unixname,
949     int is_user, int is_wuser, int direction)
950 {
951 	idmap_stat	rc = IDMAP_SUCCESS;
952 	char		*dn = NULL;
953 	char		*oldwinname = NULL;
954 	char		*oldwindomain = NULL;
955 
956 	if (direction == IDMAP_DIRECTION_W2U) {
957 		if (!p->is_ad) {
958 			rc = IDMAP_ERR_ARG;
959 			namemap_log(
960 			    gettext("AD namemaps aren't set up."));
961 			goto cleanup;
962 		}
963 
964 		rc = winname2dn(p, winname, &is_wuser,
965 		    &dn, NULL, NULL);
966 		if (rc != IDMAP_SUCCESS)
967 			goto cleanup;
968 
969 		rc = idmap_ad_set(p, dn, is_user ? p->ad_unixuser_attr :
970 		    p->ad_unixgroup_attr, unixname);
971 		if (rc != IDMAP_SUCCESS)
972 			goto cleanup;
973 
974 	} else { /* direction == IDMAP_DIRECTION_U2W */
975 		if (!p->is_nldap) {
976 			rc = IDMAP_ERR_ARG;
977 			namemap_log(
978 			    gettext("Native ldap namemaps aren't set up."));
979 			goto cleanup;
980 		}
981 
982 		rc = unixname2dn(p, unixname, is_user, &dn, NULL, NULL);
983 		if (rc != IDMAP_SUCCESS)
984 			goto cleanup;
985 
986 		rc = idmap_nldap_set(p, &p->nsc, dn, p->nldap_winname_attr,
987 		    NULL, TRUE, is_user);
988 		if (rc != IDMAP_SUCCESS)
989 			goto cleanup;
990 
991 	}
992 
993 cleanup:
994 	if (oldwindomain != NULL)
995 		free(oldwindomain);
996 	if (oldwinname != NULL)
997 		free(oldwinname);
998 	if (dn != NULL)
999 		free(dn);
1000 	return (rc);
1001 }
1002 
1003 idmap_stat
1004 idmap_get_namemap(idmap_nm_handle_t *p, int *is_source_ad, char **winname,
1005     char **windomain, int *is_wuser, char **unixuser, char **unixgroup)
1006 {
1007 	idmap_stat	rc = IDMAP_SUCCESS;
1008 	char		*dn = NULL;
1009 
1010 	*is_source_ad = IDMAP_UNKNOWN;
1011 	if (*winname != NULL) {
1012 		*is_source_ad = IDMAP_YES;
1013 
1014 		if (p->is_ad == NULL) {
1015 			rc = IDMAP_ERR_ARG;
1016 			namemap_log(
1017 			    gettext("AD namemaps are not active."));
1018 			goto cleanup;
1019 			/* In future maybe resolve winname and try nldap? */
1020 		}
1021 
1022 		rc = winname2dn(p, *winname, is_wuser, &dn, unixuser,
1023 		    unixgroup);
1024 		if (rc != IDMAP_SUCCESS) {
1025 			namemap_log(
1026 			    gettext("Winname %s@%s not found in AD."),
1027 			    *winname, p->windomain);
1028 		}
1029 	} else if (*unixuser != NULL ||	*unixgroup != NULL) {
1030 		char *unixname;
1031 		int is_user;
1032 
1033 		*is_source_ad = IDMAP_NO;
1034 
1035 		if (p->is_nldap == NULL) {
1036 			rc = IDMAP_ERR_ARG;
1037 			namemap_log(
1038 			    gettext("Native ldap namemaps aren't active."));
1039 			goto cleanup;
1040 			/* In future maybe resolve unixname and try AD? */
1041 		}
1042 
1043 		if (*unixuser != NULL) {
1044 			is_user = IDMAP_YES;
1045 			unixname = *unixuser;
1046 		} else if (*unixgroup != NULL) {
1047 			is_user = IDMAP_NO;
1048 			unixname = *unixgroup;
1049 		}
1050 
1051 		rc = unixname2dn(p, unixname, is_user, NULL, winname,
1052 		    windomain);
1053 		if (rc != IDMAP_SUCCESS) {
1054 			namemap_log(
1055 			    gettext("%s %s not found in native ldap."),
1056 			    is_user == IDMAP_YES ? "UNIX user" : "UNIX group",
1057 			    unixname);
1058 			goto cleanup;
1059 		}
1060 	} else {
1061 		rc = IDMAP_ERR_ARG;
1062 		goto cleanup;
1063 	}
1064 
1065 cleanup:
1066 	return (rc);
1067 }
1068