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