xref: /illumos-gate/usr/src/lib/libsldap/common/ns_sasl.c (revision 9e26e16f703d2dfcc0689de957c21efcb72473e6)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <strings.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <thread.h>
35 #include <synch.h>
36 #include <sasl/sasl.h>
37 #include <sys/socket.h>
38 #include <netdb.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <syslog.h>
42 #include <ctype.h>
43 #include <libscf.h>
44 #include <libintl.h>
45 #include <locale.h>
46 #include "ns_sldap.h"
47 #include "ns_internal.h"
48 
49 static int self_gssapi_only = 0;
50 static mutex_t self_gssapi_only_lock = DEFAULTMUTEX;
51 
52 #define	DNS_FMRI	"svc:/network/dns/client:default"
53 #define	MSGSIZE		256
54 
55 #define	NSSWITCH_CONF	"/etc/nsswitch.conf"
56 
57 /*
58  * Error Handling
59  */
60 #define	CLIENT_FPRINTF if (mode_verbose && !mode_quiet) (void) fprintf
61 
62 /*
63  * One time initializtion
64  */
65 int		sasl_gssapi_inited = 0;
66 static mutex_t	sasl_gssapi_lock = DEFAULTMUTEX;
67 int
68 __s_api_sasl_gssapi_init(void) {
69 	int rc = NS_LDAP_SUCCESS;
70 	(void) mutex_lock(&sasl_gssapi_lock);
71 	if (!sasl_gssapi_inited) {
72 			if (getuid() == 0) {
73 				if (system(
74 					"/usr/sbin/cryptoadm disable metaslot")
75 					== 0) {
76 					syslog(LOG_WARNING,
77 						"libsldap: Metaslot disabled "
78 						"for self credential mode");
79 					sasl_gssapi_inited = 1;
80 				} else {
81 					syslog(LOG_ERR,
82 						"libsldap: Can't disable "
83 						"Metaslot for self credential "
84 						"mode");
85 					rc = NS_LDAP_INTERNAL;
86 				}
87 			}
88 	}
89 	(void) mutex_unlock(&sasl_gssapi_lock);
90 
91 	return (rc);
92 }
93 
94 /*
95  * nscd calls this function to set self_gssapi_only flag so libsldap performs
96  * sasl/GSSAPI bind only. Also see comments of __ns_ldap_self_gssapi_config.
97  *
98  * Input: flag 0 use any kind of connection
99  *             1 use self/gssapi connection only
100  */
101 void
102 __ns_ldap_self_gssapi_only_set(int flag) {
103 	(void) mutex_lock(&self_gssapi_only_lock);
104 	self_gssapi_only = flag;
105 	(void) mutex_unlock(&self_gssapi_only_lock);
106 }
107 /*
108  * Get the flag value of self_gssapi_only
109  */
110 int
111 __s_api_self_gssapi_only_get(void) {
112 	int flag;
113 	(void) mutex_lock(&self_gssapi_only_lock);
114 	flag = self_gssapi_only;
115 	(void) mutex_unlock(&self_gssapi_only_lock);
116 	return (flag);
117 }
118 /*
119  * nscd calls this function to detect the current native ldap configuration.
120  * The output are
121  * NS_LDAP_SELF_GSSAPI_CONFIG_NONE: No credential level self and
122  *                                  no authentication method sasl/GSSAPI is
123  *                                  configured.
124  * NS_LDAP_SELF_GSSAPI_CONFIG_ONLY: Only credential level self and
125  *                                  authentication method sasl/GSSAPI are
126  *                                  configured.
127  * NS_LDAP_SELF_GSSAPI_CONFIG_MIXED: More than one credential level are
128  *                                   configured, including self.
129  *                                   More than one authentication method
130  *                                   are configured, including sasl/GSSAPI.
131  *
132  * __s_api_crosscheck makes sure self and sasl/GSSAPI pair up if they do
133  * get configured.
134  *
135  * When nscd detects it's MIXED case, it calls __ns_ldap_self_gssapi_only_set
136  * to force libsldap to do sasl/GSSAPI bind only for per-user lookup.
137  *
138  * Return: NS_LDAP_SUCCESS
139  *         OTHERWISE - FAILURE
140  *
141  * Output: config. See comments above.
142  *
143  */
144 int
145 __ns_ldap_self_gssapi_config(ns_ldap_self_gssapi_config_t *config) {
146 	int	self = 0, other_level = 0, gssapi = 0, other_method = 0;
147 	ns_auth_t	**aMethod = NULL, **aNext = NULL;
148 	int		**cLevel = NULL, **cNext = NULL, rc;
149 	ns_ldap_error_t	*errp = NULL;
150 
151 	if (config == NULL)
152 		return (NS_LDAP_INVALID_PARAM);
153 	else
154 		*config = NS_LDAP_SELF_GSSAPI_CONFIG_NONE;
155 
156 	/* Get the credential level list */
157 	if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
158 		(void ***)&cLevel, &errp)) != NS_LDAP_SUCCESS) {
159 		if (errp)
160 			(void) __ns_ldap_freeError(&errp);
161 		if (cLevel)
162 			(void) __ns_ldap_freeParam((void ***)&cLevel);
163 		return (rc);
164 	}
165 	if (errp)
166 		(void) __ns_ldap_freeError(&errp);
167 	/* Get the authentication method list */
168 	if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
169 		(void ***)&aMethod, &errp)) != NS_LDAP_SUCCESS) {
170 		if (errp)
171 			(void) __ns_ldap_freeError(&errp);
172 		if (cLevel)
173 			(void) __ns_ldap_freeParam((void ***)&cLevel);
174 		if (aMethod)
175 			(void) __ns_ldap_freeParam((void ***)&aMethod);
176 		return (rc);
177 	}
178 	if (errp)
179 		(void) __ns_ldap_freeError(&errp);
180 
181 	if (cLevel == NULL || aMethod == NULL) {
182 		if (cLevel)
183 			(void) __ns_ldap_freeParam((void ***)&cLevel);
184 		if (aMethod)
185 			(void) __ns_ldap_freeParam((void ***)&aMethod);
186 		return (NS_LDAP_SUCCESS);
187 	}
188 
189 	for (cNext = cLevel; *cNext != NULL; cNext++) {
190 		if (**cNext == NS_LDAP_CRED_SELF)
191 			self++;
192 		else
193 			other_level++;
194 	}
195 	for (aNext = aMethod; *aNext != NULL; aNext++) {
196 		if ((*aNext)->saslmech == NS_LDAP_SASL_GSSAPI)
197 			gssapi++;
198 		else
199 			other_method++;
200 	}
201 
202 	if (self > 0 && gssapi > 0) {
203 		if (other_level == 0 && other_method == 0)
204 			*config = NS_LDAP_SELF_GSSAPI_CONFIG_ONLY;
205 		else
206 			*config = NS_LDAP_SELF_GSSAPI_CONFIG_MIXED;
207 	}
208 
209 	if (cLevel)
210 		(void) __ns_ldap_freeParam((void ***)&cLevel);
211 	if (aMethod)
212 		(void) __ns_ldap_freeParam((void ***)&aMethod);
213 	return (NS_LDAP_SUCCESS);
214 }
215 
216 int
217 __s_api_sasl_bind_callback(
218 	/* LINTED E_FUNC_ARG_UNUSED */
219 	LDAP		*ld,
220 	/* LINTED E_FUNC_ARG_UNUSED */
221 	unsigned	flags,
222 	void		*defaults,
223 	void		*in)
224 {
225 	char		*ret = NULL;
226 	sasl_interact_t *interact = in;
227 	ns_sasl_cb_param_t	*cred = (ns_sasl_cb_param_t *)defaults;
228 
229 
230 	while (interact->id != SASL_CB_LIST_END) {
231 
232 		switch (interact->id) {
233 
234 		case SASL_CB_GETREALM:
235 			ret =   cred->realm;
236 			break;
237 		case SASL_CB_AUTHNAME:
238 			ret = cred->authid;
239 			break;
240 		case SASL_CB_PASS:
241 			ret = cred->passwd;
242 			break;
243 		case SASL_CB_USER:
244 			ret = cred->authzid;
245 			break;
246 		case SASL_CB_NOECHOPROMPT:
247 		case SASL_CB_ECHOPROMPT:
248 		default:
249 			break;
250 		}
251 
252 		if (ret) {
253 			interact->result = strdup(ret);
254 			if (interact->result == NULL)
255 				return (LDAP_NO_MEMORY);
256 
257 			interact->len = strlen(ret);
258 		} else {
259 			interact->result = NULL;
260 			interact->len = 0;
261 		}
262 		interact++;
263 	}
264 
265 	return (LDAP_SUCCESS);
266 }
267 
268 /*
269  * Find "dbase: service1 [...] services2" in fname and return
270  * " service1 [...] services2"
271  * e.g.
272  * Find "hosts: files dns" and return " files dns"
273  */
274 static char *
275 __ns_nsw_getconfig(const char *dbase, const char *fname, int *errp)
276 {
277 	FILE *fp = NULL;
278 	char *linep, *retp = NULL;
279 	char lineq[BUFSIZ], db_colon[BUFSIZ];
280 
281 	if ((fp = fopen(fname, "rF")) == NULL) {
282 		*errp = NS_LDAP_CONFIG;
283 		return (NULL);
284 	}
285 	*errp = NS_LDAP_SUCCESS;
286 
287 	while (linep = fgets(lineq, BUFSIZ, fp)) {
288 		char			*tokenp, *comment;
289 
290 		/*
291 		 * Ignore portion of line following the comment character '#'.
292 		 */
293 		if ((comment = strchr(linep, '#')) != NULL) {
294 			*comment = '\0';
295 		}
296 		if ((*linep == '\0') || isspace(*linep)) {
297 			continue;
298 		}
299 		(void) snprintf(db_colon, BUFSIZ, "%s:", dbase);
300 		if ((tokenp = strstr(linep, db_colon)) == NULL) {
301 			continue; /* ignore this line */
302 		} else {
303 			/* skip "dbase:" */
304 			retp = strdup(tokenp + strlen(db_colon));
305 			if (retp == NULL)
306 				*errp = NS_LDAP_MEMORY;
307 		}
308 	}
309 
310 	(void) fclose(fp);
311 	return (retp);
312 }
313 /*
314  *  Test the configurations of the "hosts" and "ipnodes"
315  *  dns has to be present and appear before ldap
316  *  e.g.
317  *  "dns" , "dns files" "dns ldap files", "files dns" are allowed.
318  *
319  *  Kerberos requires dns or it'd fail.
320  */
321 static int
322 test_dns_nsswitch(int foreground,
323 		const char *fname,
324 		ns_ldap_error_t **errpp) {
325 	int	ldap, dns, i, pserr, rc = NS_LDAP_SUCCESS;
326 	char	*db[3] = {"hosts", "ipnodes", NULL};
327 	char	buf[MSGSIZE], *conf = NULL, *token = NULL, *last = NULL;
328 
329 	for (i = 0; db[i] != NULL; i++) {
330 		conf = __ns_nsw_getconfig(db[i], fname, &pserr);
331 
332 		if (conf == NULL) {
333 			(void) snprintf(buf, MSGSIZE,
334 				gettext("Parsing %s to find \"%s:\" "
335 					"failed. err: %d"),
336 					fname, db[i], pserr);
337 			if (foreground) {
338 				(void) fprintf(stderr, "%s\n", buf);
339 			} else {
340 				MKERROR(LOG_ERR, *errpp, NS_LDAP_CONFIG,
341 					strdup(buf), NS_LDAP_MEMORY);
342 			}
343 			return (pserr);
344 		}
345 		ldap = dns = 0;
346 		token = strtok_r(conf, " ", &last);
347 		while (token != NULL) {
348 			if (strncmp(token, "dns", 3) == 0) {
349 				if (ldap) {
350 					(void) snprintf(buf, MSGSIZE,
351 						gettext("%s: ldap can't appear "
352 						"before dns"), db[i]);
353 					if (foreground) {
354 						(void) fprintf(stderr,
355 								"start: %s\n",
356 								buf);
357 					} else {
358 						MKERROR(LOG_ERR, *errpp,
359 							NS_LDAP_CONFIG,
360 							strdup(buf),
361 							NS_LDAP_MEMORY);
362 					}
363 					free(conf);
364 					return (NS_LDAP_CONFIG);
365 				} else {
366 					dns++;
367 				}
368 			} else if (strncmp(token, "ldap", 4) == 0) {
369 				ldap++;
370 			}
371 			/* next token */
372 			token = strtok_r(NULL, " ", &last);
373 		}
374 		if (conf) {
375 			free(conf);
376 			conf = NULL;
377 		}
378 		if (!dns) {
379 			(void) snprintf(buf, MSGSIZE,
380 				gettext("%s: dns is not defined in "
381 				"%s"), db[i], fname);
382 			if (foreground) {
383 				(void) fprintf(stderr, "start: %s\n", buf);
384 			} else {
385 				MKERROR(LOG_ERR, *errpp, NS_LDAP_CONFIG,
386 					strdup(buf), NS_LDAP_MEMORY);
387 			}
388 			rc = NS_LDAP_CONFIG;
389 			break;
390 		}
391 	}
392 	return (rc);
393 }
394 
395 static boolean_t
396 is_service(const char *fmri, const char *state) {
397 	char		*st;
398 	boolean_t	result = B_FALSE;
399 
400 	if ((st = smf_get_state(fmri)) != NULL) {
401 		if (strcmp(st, state) == 0)
402 			result = B_TRUE;
403 		free(st);
404 	}
405 	return (result);
406 }
407 
408 
409 /*
410  * This function checks dns prerequisites for sasl/GSSAPI bind.
411  * It's called only if config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY ||
412  *   config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED.
413  */
414 int
415 __ns_ldap_check_dns_preq(int foreground,
416 		int mode_verbose,
417 		int mode_quiet,
418 		const char *fname,
419 		ns_ldap_self_gssapi_config_t config,
420 		ns_ldap_error_t **errpp) {
421 
422 	char	buf[MSGSIZE];
423 	int	retcode = NS_LDAP_SUCCESS;
424 	int	loglevel;
425 
426 	if (errpp)
427 		*errpp = NULL;
428 	else
429 		return (NS_LDAP_INVALID_PARAM);
430 
431 	if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE)
432 		/* Shouldn't happen. Check this value just in case  */
433 		return (NS_LDAP_SUCCESS);
434 
435 	if ((retcode = test_dns_nsswitch(foreground, fname, errpp)) !=
436 							NS_LDAP_SUCCESS)
437 		return (retcode);
438 
439 	if (is_service(DNS_FMRI, SCF_STATE_STRING_ONLINE)) {
440 		if (foreground) {
441 			CLIENT_FPRINTF(stdout, "start: %s\n",
442 					gettext("DNS client is enabled"));
443 		} else {
444 			syslog(LOG_INFO, "%s",
445 					gettext("DNS client is enabled"));
446 		}
447 		return (NS_LDAP_SUCCESS);
448 	} else {
449 		if (config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY) {
450 			(void) snprintf(buf, MSGSIZE,
451 				gettext("%s: DNS client is not enabled. "
452 					"Run \"svcadm enable %s\". %s."),
453 					"Error", DNS_FMRI, "Abort");
454 			loglevel = LOG_ERR;
455 			retcode = NS_LDAP_CONFIG;
456 		} else if (config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED) {
457 			(void) snprintf(buf, MSGSIZE,
458 				gettext("%s: DNS client is not enabled. "
459 					"Run \"svcadm enable %s\". %s."
460 					"Fall back to other cred level/bind. "),
461 					"Warning", DNS_FMRI, "Continue");
462 			loglevel = LOG_INFO;
463 			retcode = NS_LDAP_SUCCESS;
464 		}
465 
466 		if (foreground) {
467 			(void) fprintf(stderr, "start: %s\n", buf);
468 		} else {
469 			MKERROR(loglevel, *errpp, retcode, strdup(buf),
470 				NS_LDAP_MEMORY);
471 		}
472 		return (retcode);
473 	}
474 }
475 
476 /*
477  * Check if sasl/GSSAPI works
478  */
479 int
480 __ns_ldap_check_gssapi_preq(int foreground,
481 		int mode_verbose,
482 		int mode_quiet,
483 		ns_ldap_self_gssapi_config_t config,
484 		ns_ldap_error_t **errpp) {
485 
486 	int	rc;
487 	char	*attr[2] = {"dn", NULL}, buf[MSGSIZE];
488 	ns_cred_t	cred;
489 	ns_ldap_result_t *result = NULL;
490 	int	loglevel;
491 
492 	if (errpp)
493 		*errpp = NULL;
494 	else
495 		return (NS_LDAP_INVALID_PARAM);
496 
497 	if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE)
498 		/* Don't need to check */
499 		return (NS_LDAP_SUCCESS);
500 
501 	(void) memset(&cred, 0, sizeof (ns_cred_t));
502 
503 	cred.auth.type = NS_LDAP_AUTH_SASL;
504 	cred.auth.tlstype = NS_LDAP_TLS_NONE;
505 	cred.auth.saslmech = NS_LDAP_SASL_GSSAPI;
506 
507 	rc = __ns_ldap_list(NULL, (const char *)"objectclass=*",
508 		NULL, (const char **)attr, &cred,
509 		NS_LDAP_SCOPE_BASE, &result, errpp, NULL, NULL);
510 	if (result)
511 		(void) __ns_ldap_freeResult(&result);
512 
513 	if (rc == NS_LDAP_SUCCESS) {
514 		if (foreground) {
515 			CLIENT_FPRINTF(stdout, "start: %s\n",
516 					gettext("sasl/GSSAPI bind works"));
517 		} else {
518 			syslog(LOG_INFO, "%s",
519 					gettext("sasl/GSSAPI bind works"));
520 		}
521 		return (NS_LDAP_SUCCESS);
522 	} else {
523 		if (config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY) {
524 			(void) snprintf(buf, MSGSIZE,
525 				gettext("%s: sasl/GSSAPI bind is not "
526 					"working. %s."),
527 					"Error", "Abort");
528 			loglevel = LOG_ERR;
529 		} else if (config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED) {
530 			(void) snprintf(buf, MSGSIZE,
531 				gettext("%s: sasl/GSSAPI bind is not "
532 					"working. Fall back to other cred "
533 					"level/bind. %s."),
534 					"Warning", "Continue");
535 			loglevel = LOG_INFO;
536 			/* reset return code */
537 			rc = NS_LDAP_SUCCESS;
538 		}
539 
540 		if (foreground) {
541 			(void) fprintf(stderr, "start: %s\n", buf);
542 		} else {
543 			MKERROR(loglevel, *errpp, rc, strdup(buf),
544 				NS_LDAP_MEMORY);
545 		}
546 		return (rc);
547 	}
548 }
549 /*
550  * This is called by ldap_cachemgr to check dns and gssapi prequisites.
551  */
552 int
553 __ns_ldap_check_all_preq(int foreground,
554 		int mode_verbose,
555 		int mode_quiet,
556 		ns_ldap_self_gssapi_config_t config,
557 		ns_ldap_error_t **errpp) {
558 
559 	int	rc;
560 
561 	if (errpp)
562 		*errpp = NULL;
563 	else
564 		return (NS_LDAP_INVALID_PARAM);
565 
566 	if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE)
567 		/* Don't need to check */
568 		return (NS_LDAP_SUCCESS);
569 
570 	if ((rc = __ns_ldap_check_dns_preq(foreground,
571 			mode_verbose, mode_quiet, NSSWITCH_CONF,
572 			config, errpp)) != NS_LDAP_SUCCESS)
573 		return (rc);
574 	if ((rc = __ns_ldap_check_gssapi_preq(foreground,
575 			mode_verbose, mode_quiet, config, errpp)) !=
576 			NS_LDAP_SUCCESS)
577 		return (rc);
578 
579 	return (NS_LDAP_SUCCESS);
580 }
581