summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsldap/common/ns_sasl.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libsldap/common/ns_sasl.c')
-rw-r--r--usr/src/lib/libsldap/common/ns_sasl.c580
1 files changed, 580 insertions, 0 deletions
diff --git a/usr/src/lib/libsldap/common/ns_sasl.c b/usr/src/lib/libsldap/common/ns_sasl.c
new file mode 100644
index 0000000000..fc708b735a
--- /dev/null
+++ b/usr/src/lib/libsldap/common/ns_sasl.c
@@ -0,0 +1,580 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <thread.h>
+#include <synch.h>
+#include <sasl/sasl.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <libscf.h>
+#include <libintl.h>
+#include <locale.h>
+#include "ns_sldap.h"
+#include "ns_internal.h"
+
+static int self_gssapi_only = 0;
+static mutex_t self_gssapi_only_lock = DEFAULTMUTEX;
+
+#define DNS_FMRI "svc:/network/dns/client:default"
+#define MSGSIZE 256
+
+#define NSSWITCH_CONF "/etc/nsswitch.conf"
+
+/*
+ * Error Handling
+ */
+#define CLIENT_FPRINTF if (mode_verbose && !mode_quiet) (void) fprintf
+
+/*
+ * One time initializtion
+ */
+int sasl_gssapi_inited = 0;
+static mutex_t sasl_gssapi_lock = DEFAULTMUTEX;
+int
+__s_api_sasl_gssapi_init(void) {
+ int rc = NS_LDAP_SUCCESS;
+ (void) mutex_lock(&sasl_gssapi_lock);
+ if (!sasl_gssapi_inited) {
+ if (getuid() == 0) {
+ if (system(
+ "/usr/sbin/cryptoadm disable metaslot")
+ == 0) {
+ syslog(LOG_WARNING,
+ "libsldap: Metaslot disabled "
+ "for self credential mode");
+ sasl_gssapi_inited = 1;
+ } else {
+ syslog(LOG_ERR,
+ "libsldap: Can't disable "
+ "Metaslot for self credential "
+ "mode");
+ rc = NS_LDAP_INTERNAL;
+ }
+ }
+ }
+ (void) mutex_unlock(&sasl_gssapi_lock);
+
+ return (rc);
+}
+
+/*
+ * nscd calls this function to set self_gssapi_only flag so libsldap performs
+ * sasl/GSSAPI bind only. Also see comments of __ns_ldap_self_gssapi_config.
+ *
+ * Input: flag 0 use any kind of connection
+ * 1 use self/gssapi connection only
+ */
+void
+__ns_ldap_self_gssapi_only_set(int flag) {
+ (void) mutex_lock(&self_gssapi_only_lock);
+ self_gssapi_only = flag;
+ (void) mutex_unlock(&self_gssapi_only_lock);
+}
+/*
+ * Get the flag value of self_gssapi_only
+ */
+int
+__s_api_self_gssapi_only_get(void) {
+ int flag;
+ (void) mutex_lock(&self_gssapi_only_lock);
+ flag = self_gssapi_only;
+ (void) mutex_unlock(&self_gssapi_only_lock);
+ return (flag);
+}
+/*
+ * nscd calls this function to detect the current native ldap configuration.
+ * The output are
+ * NS_LDAP_SELF_GSSAPI_CONFIG_NONE: No credential level self and
+ * no authentication method sasl/GSSAPI is
+ * configured.
+ * NS_LDAP_SELF_GSSAPI_CONFIG_ONLY: Only credential level self and
+ * authentication method sasl/GSSAPI are
+ * configured.
+ * NS_LDAP_SELF_GSSAPI_CONFIG_MIXED: More than one credential level are
+ * configured, including self.
+ * More than one authentication method
+ * are configured, including sasl/GSSAPI.
+ *
+ * __s_api_crosscheck makes sure self and sasl/GSSAPI pair up if they do
+ * get configured.
+ *
+ * When nscd detects it's MIXED case, it calls __ns_ldap_self_gssapi_only_set
+ * to force libsldap to do sasl/GSSAPI bind only for per-user lookup.
+ *
+ * Return: NS_LDAP_SUCCESS
+ * OTHERWISE - FAILURE
+ *
+ * Output: config. See comments above.
+ *
+ */
+int
+__ns_ldap_self_gssapi_config(ns_ldap_self_gssapi_config_t *config) {
+ int self = 0, other_level = 0, gssapi = 0, other_method = 0;
+ ns_auth_t **aMethod = NULL, **aNext = NULL;
+ int **cLevel = NULL, **cNext = NULL, rc;
+ ns_ldap_error_t *errp = NULL;
+
+ if (config == NULL)
+ return (NS_LDAP_INVALID_PARAM);
+ else
+ *config = NS_LDAP_SELF_GSSAPI_CONFIG_NONE;
+
+ /* Get the credential level list */
+ if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
+ (void ***)&cLevel, &errp)) != NS_LDAP_SUCCESS) {
+ if (errp)
+ (void) __ns_ldap_freeError(&errp);
+ if (cLevel)
+ (void) __ns_ldap_freeParam((void ***)&cLevel);
+ return (rc);
+ }
+ if (errp)
+ (void) __ns_ldap_freeError(&errp);
+ /* Get the authentication method list */
+ if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
+ (void ***)&aMethod, &errp)) != NS_LDAP_SUCCESS) {
+ if (errp)
+ (void) __ns_ldap_freeError(&errp);
+ if (cLevel)
+ (void) __ns_ldap_freeParam((void ***)&cLevel);
+ if (aMethod)
+ (void) __ns_ldap_freeParam((void ***)&aMethod);
+ return (rc);
+ }
+ if (errp)
+ (void) __ns_ldap_freeError(&errp);
+
+ if (cLevel == NULL || aMethod == NULL) {
+ if (cLevel)
+ (void) __ns_ldap_freeParam((void ***)&cLevel);
+ if (aMethod)
+ (void) __ns_ldap_freeParam((void ***)&aMethod);
+ return (NS_LDAP_SUCCESS);
+ }
+
+ for (cNext = cLevel; *cNext != NULL; cNext++) {
+ if (**cNext == NS_LDAP_CRED_SELF)
+ self++;
+ else
+ other_level++;
+ }
+ for (aNext = aMethod; *aNext != NULL; aNext++) {
+ if ((*aNext)->saslmech == NS_LDAP_SASL_GSSAPI)
+ gssapi++;
+ else
+ other_method++;
+ }
+
+ if (self > 0 && gssapi > 0) {
+ if (other_level == 0 && other_method == 0)
+ *config = NS_LDAP_SELF_GSSAPI_CONFIG_ONLY;
+ else
+ *config = NS_LDAP_SELF_GSSAPI_CONFIG_MIXED;
+ }
+
+ if (cLevel)
+ (void) __ns_ldap_freeParam((void ***)&cLevel);
+ if (aMethod)
+ (void) __ns_ldap_freeParam((void ***)&aMethod);
+ return (NS_LDAP_SUCCESS);
+}
+
+int
+__s_api_sasl_bind_callback(
+ /* LINTED E_FUNC_ARG_UNUSED */
+ LDAP *ld,
+ /* LINTED E_FUNC_ARG_UNUSED */
+ unsigned flags,
+ void *defaults,
+ void *in)
+{
+ char *ret = NULL;
+ sasl_interact_t *interact = in;
+ ns_sasl_cb_param_t *cred = (ns_sasl_cb_param_t *)defaults;
+
+
+ while (interact->id != SASL_CB_LIST_END) {
+
+ switch (interact->id) {
+
+ case SASL_CB_GETREALM:
+ ret = cred->realm;
+ break;
+ case SASL_CB_AUTHNAME:
+ ret = cred->authid;
+ break;
+ case SASL_CB_PASS:
+ ret = cred->passwd;
+ break;
+ case SASL_CB_USER:
+ ret = cred->authzid;
+ break;
+ case SASL_CB_NOECHOPROMPT:
+ case SASL_CB_ECHOPROMPT:
+ default:
+ break;
+ }
+
+ if (ret) {
+ interact->result = strdup(ret);
+ if (interact->result == NULL)
+ return (LDAP_NO_MEMORY);
+
+ interact->len = strlen(ret);
+ } else {
+ interact->result = NULL;
+ interact->len = 0;
+ }
+ interact++;
+ }
+
+ return (LDAP_SUCCESS);
+}
+
+/*
+ * Find "dbase: service1 [...] services2" in fname and return
+ * " service1 [...] services2"
+ * e.g.
+ * Find "hosts: files dns" and return " files dns"
+ */
+static char *
+__ns_nsw_getconfig(const char *dbase, const char *fname, int *errp)
+{
+ FILE *fp = NULL;
+ char *linep, *retp = NULL;
+ char lineq[BUFSIZ], db_colon[BUFSIZ];
+
+ if ((fp = fopen(fname, "rF")) == NULL) {
+ *errp = NS_LDAP_CONFIG;
+ return (NULL);
+ }
+ *errp = NS_LDAP_SUCCESS;
+
+ while (linep = fgets(lineq, BUFSIZ, fp)) {
+ char *tokenp, *comment;
+
+ /*
+ * Ignore portion of line following the comment character '#'.
+ */
+ if ((comment = strchr(linep, '#')) != NULL) {
+ *comment = '\0';
+ }
+ if ((*linep == '\0') || isspace(*linep)) {
+ continue;
+ }
+ (void) snprintf(db_colon, BUFSIZ, "%s:", dbase);
+ if ((tokenp = strstr(linep, db_colon)) == NULL) {
+ continue; /* ignore this line */
+ } else {
+ /* skip "dbase:" */
+ retp = strdup(tokenp + strlen(db_colon));
+ if (retp == NULL)
+ *errp = NS_LDAP_MEMORY;
+ }
+ }
+
+ (void) fclose(fp);
+ return (retp);
+}
+/*
+ * Test the configurations of the "hosts" and "ipnodes"
+ * dns has to be present and appear before ldap
+ * e.g.
+ * "dns" , "dns files" "dns ldap files", "files dns" are allowed.
+ *
+ * Kerberos requires dns or it'd fail.
+ */
+static int
+test_dns_nsswitch(int foreground,
+ const char *fname,
+ ns_ldap_error_t **errpp) {
+ int ldap, dns, i, pserr, rc = NS_LDAP_SUCCESS;
+ char *db[3] = {"hosts", "ipnodes", NULL};
+ char buf[MSGSIZE], *conf = NULL, *token = NULL, *last = NULL;
+
+ for (i = 0; db[i] != NULL; i++) {
+ conf = __ns_nsw_getconfig(db[i], fname, &pserr);
+
+ if (conf == NULL) {
+ (void) snprintf(buf, MSGSIZE,
+ gettext("Parsing %s to find \"%s:\" "
+ "failed. err: %d"),
+ fname, db[i], pserr);
+ if (foreground) {
+ (void) fprintf(stderr, "%s\n", buf);
+ } else {
+ MKERROR(LOG_ERR, *errpp, NS_LDAP_CONFIG,
+ strdup(buf), NS_LDAP_MEMORY);
+ }
+ return (pserr);
+ }
+ ldap = dns = 0;
+ token = strtok_r(conf, " ", &last);
+ while (token != NULL) {
+ if (strncmp(token, "dns", 3) == 0) {
+ if (ldap) {
+ (void) snprintf(buf, MSGSIZE,
+ gettext("%s: ldap can't appear "
+ "before dns"), db[i]);
+ if (foreground) {
+ (void) fprintf(stderr,
+ "start: %s\n",
+ buf);
+ } else {
+ MKERROR(LOG_ERR, *errpp,
+ NS_LDAP_CONFIG,
+ strdup(buf),
+ NS_LDAP_MEMORY);
+ }
+ free(conf);
+ return (NS_LDAP_CONFIG);
+ } else {
+ dns++;
+ }
+ } else if (strncmp(token, "ldap", 4) == 0) {
+ ldap++;
+ }
+ /* next token */
+ token = strtok_r(NULL, " ", &last);
+ }
+ if (conf) {
+ free(conf);
+ conf = NULL;
+ }
+ if (!dns) {
+ (void) snprintf(buf, MSGSIZE,
+ gettext("%s: dns is not defined in "
+ "%s"), db[i], fname);
+ if (foreground) {
+ (void) fprintf(stderr, "start: %s\n", buf);
+ } else {
+ MKERROR(LOG_ERR, *errpp, NS_LDAP_CONFIG,
+ strdup(buf), NS_LDAP_MEMORY);
+ }
+ rc = NS_LDAP_CONFIG;
+ break;
+ }
+ }
+ return (rc);
+}
+
+static boolean_t
+is_service(const char *fmri, const char *state) {
+ char *st;
+ boolean_t result = B_FALSE;
+
+ if ((st = smf_get_state(fmri)) != NULL) {
+ if (strcmp(st, state) == 0)
+ result = B_TRUE;
+ free(st);
+ }
+ return (result);
+}
+
+
+/*
+ * This function checks dns prerequisites for sasl/GSSAPI bind.
+ * It's called only if config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY ||
+ * config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED.
+ */
+int
+__ns_ldap_check_dns_preq(int foreground,
+ int mode_verbose,
+ int mode_quiet,
+ const char *fname,
+ ns_ldap_self_gssapi_config_t config,
+ ns_ldap_error_t **errpp) {
+
+ char buf[MSGSIZE];
+ int retcode = NS_LDAP_SUCCESS;
+ int loglevel;
+
+ if (errpp)
+ *errpp = NULL;
+ else
+ return (NS_LDAP_INVALID_PARAM);
+
+ if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE)
+ /* Shouldn't happen. Check this value just in case */
+ return (NS_LDAP_SUCCESS);
+
+ if ((retcode = test_dns_nsswitch(foreground, fname, errpp)) !=
+ NS_LDAP_SUCCESS)
+ return (retcode);
+
+ if (is_service(DNS_FMRI, SCF_STATE_STRING_ONLINE)) {
+ if (foreground) {
+ CLIENT_FPRINTF(stdout, "start: %s\n",
+ gettext("DNS client is enabled"));
+ } else {
+ syslog(LOG_INFO, "%s",
+ gettext("DNS client is enabled"));
+ }
+ return (NS_LDAP_SUCCESS);
+ } else {
+ if (config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY) {
+ (void) snprintf(buf, MSGSIZE,
+ gettext("%s: DNS client is not enabled. "
+ "Run \"svcadm enable %s\". %s."),
+ "Error", DNS_FMRI, "Abort");
+ loglevel = LOG_ERR;
+ retcode = NS_LDAP_CONFIG;
+ } else if (config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED) {
+ (void) snprintf(buf, MSGSIZE,
+ gettext("%s: DNS client is not enabled. "
+ "Run \"svcadm enable %s\". %s."
+ "Fall back to other cred level/bind. "),
+ "Warning", DNS_FMRI, "Continue");
+ loglevel = LOG_INFO;
+ retcode = NS_LDAP_SUCCESS;
+ }
+
+ if (foreground) {
+ (void) fprintf(stderr, "start: %s\n", buf);
+ } else {
+ MKERROR(loglevel, *errpp, retcode, strdup(buf),
+ NS_LDAP_MEMORY);
+ }
+ return (retcode);
+ }
+}
+
+/*
+ * Check if sasl/GSSAPI works
+ */
+int
+__ns_ldap_check_gssapi_preq(int foreground,
+ int mode_verbose,
+ int mode_quiet,
+ ns_ldap_self_gssapi_config_t config,
+ ns_ldap_error_t **errpp) {
+
+ int rc;
+ char *attr[2] = {"dn", NULL}, buf[MSGSIZE];
+ ns_cred_t cred;
+ ns_ldap_result_t *result = NULL;
+ int loglevel;
+
+ if (errpp)
+ *errpp = NULL;
+ else
+ return (NS_LDAP_INVALID_PARAM);
+
+ if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE)
+ /* Don't need to check */
+ return (NS_LDAP_SUCCESS);
+
+ (void) memset(&cred, 0, sizeof (ns_cred_t));
+
+ cred.auth.type = NS_LDAP_AUTH_SASL;
+ cred.auth.tlstype = NS_LDAP_TLS_NONE;
+ cred.auth.saslmech = NS_LDAP_SASL_GSSAPI;
+
+ rc = __ns_ldap_list(NULL, (const char *)"objectclass=*",
+ NULL, (const char **)attr, &cred,
+ NS_LDAP_SCOPE_BASE, &result, errpp, NULL, NULL);
+ if (result)
+ (void) __ns_ldap_freeResult(&result);
+
+ if (rc == NS_LDAP_SUCCESS) {
+ if (foreground) {
+ CLIENT_FPRINTF(stdout, "start: %s\n",
+ gettext("sasl/GSSAPI bind works"));
+ } else {
+ syslog(LOG_INFO, "%s",
+ gettext("sasl/GSSAPI bind works"));
+ }
+ return (NS_LDAP_SUCCESS);
+ } else {
+ if (config == NS_LDAP_SELF_GSSAPI_CONFIG_ONLY) {
+ (void) snprintf(buf, MSGSIZE,
+ gettext("%s: sasl/GSSAPI bind is not "
+ "working. %s."),
+ "Error", "Abort");
+ loglevel = LOG_ERR;
+ } else if (config == NS_LDAP_SELF_GSSAPI_CONFIG_MIXED) {
+ (void) snprintf(buf, MSGSIZE,
+ gettext("%s: sasl/GSSAPI bind is not "
+ "working. Fall back to other cred "
+ "level/bind. %s."),
+ "Warning", "Continue");
+ loglevel = LOG_INFO;
+ /* reset return code */
+ rc = NS_LDAP_SUCCESS;
+ }
+
+ if (foreground) {
+ (void) fprintf(stderr, "start: %s\n", buf);
+ } else {
+ MKERROR(loglevel, *errpp, rc, strdup(buf),
+ NS_LDAP_MEMORY);
+ }
+ return (rc);
+ }
+}
+/*
+ * This is called by ldap_cachemgr to check dns and gssapi prequisites.
+ */
+int
+__ns_ldap_check_all_preq(int foreground,
+ int mode_verbose,
+ int mode_quiet,
+ ns_ldap_self_gssapi_config_t config,
+ ns_ldap_error_t **errpp) {
+
+ int rc;
+
+ if (errpp)
+ *errpp = NULL;
+ else
+ return (NS_LDAP_INVALID_PARAM);
+
+ if (config == NS_LDAP_SELF_GSSAPI_CONFIG_NONE)
+ /* Don't need to check */
+ return (NS_LDAP_SUCCESS);
+
+ if ((rc = __ns_ldap_check_dns_preq(foreground,
+ mode_verbose, mode_quiet, NSSWITCH_CONF,
+ config, errpp)) != NS_LDAP_SUCCESS)
+ return (rc);
+ if ((rc = __ns_ldap_check_gssapi_preq(foreground,
+ mode_verbose, mode_quiet, config, errpp)) !=
+ NS_LDAP_SUCCESS)
+ return (rc);
+
+ return (NS_LDAP_SUCCESS);
+}