/*
* 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 (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <lber.h>
#include <ldap.h>
#include <uuid/uuid.h>
#include <nfs/fedfs.h>
#include <sasl/sasl.h>
#include <syslog.h>
#include "fedfs_impl.h"
typedef struct cb_param {
char *mech;
char *authid;
char *authzid;
char *passwd;
char *realm;
} cb_param_t;
/*ARGSUSED*/
static int
saslcallback(LDAP *ld, unsigned flags, void *defaults, void *in)
{
char *ret = NULL;
sasl_interact_t *interact = in;
cb_param_t *cred = (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) {
/*
* No need to do strdup(ret), the data is always
* available in 'defaults' and libldap won't
* free it either. strdup(ret) causes memory
* leak.
*/
interact->result = ret;
interact->len = strlen(ret);
} else {
interact->result = NULL;
interact->len = 0;
}
interact++;
}
return (LDAP_SUCCESS);
}
LDAP *
nsdb_connect(char *nsdb, int port, char *admin, char *password)
{
LDAP *ld = NULL;
char *host, *url = NULL, *prefix, *certpath = NULL;
char *dn, *pw, *smf_dn = NULL, *smf_pw = NULL, *digest_md5_name = NULL;
int rc, sec = FEDFS_SEC_NONE, len, do_starttls;
cb_param_t sasl_param;
nsdb_info_t *info;
int opt = LDAP_VERSION3;
#ifdef DEBUG
(void) fprintf(stderr, "nsdb_connect args: %s/%d, %s/%s\n",
nsdb, port, admin, password);
#endif
/*
* Honor the "force_loopback" property if set
*/
if (fedfs_use_loopback() == 1)
host = "localhost";
else
host = nsdb;
dn = admin;
pw = password;
/*
* See what info we have in SMF
*/
info = fedfs_smf_lookup(host, port, 0);
if (info != NULL) {
if (info->sectype != NULL)
sec = sectype_to_int(info->sectype);
if (info->certpath != NULL)
certpath = strdup(info->certpath);
if (info->binddn != NULL)
smf_dn = strdup(info->binddn);
if (info->bindpw != NULL)
smf_pw = strdup(info->bindpw);
}
fedfs_smf_lookup_free(info);
/*
* If we need encryption, set it up globally.
*/
if (sec != FEDFS_SEC_NONE && certpath != NULL) {
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
certpath);
if (rc != LDAP_SUCCESS) {
#ifdef DEBUG
(void) fprintf(stderr, "ldap_set_option(CERT) "
"failed: %s\n", ldap_err2string(rc));
#endif
syslog(LOG_ERR, "nsdb_connect: "
"ldap_set_option(CERT) failed: %s\n",
ldap_err2string(rc));
goto out;
}
#ifdef DEBUG
else {
(void) fprintf(stderr, "ldap_set_option(CERT) OK\n");
}
#endif
}
retry:
/*
* Authentication needs some thinking about admin user
*/
if (dn != NULL) {
digest_md5_name = malloc(strlen(dn) + 5);
/* 5 = strlen("dn: ") + 1 */
if (digest_md5_name == NULL)
goto out;
(void) strcpy(digest_md5_name, "dn: ");
(void) strcat(digest_md5_name, dn);
}
else
digest_md5_name = NULL;
/*
* Figure out what we want to do if we get to SASL bind
*/
if (sec == FEDFS_SEC_NONE) {
prefix = "ldap";
do_starttls = 0;
sasl_param.mech = "SIMPLE";
sasl_param.authid = dn;
sasl_param.authzid = dn;
sasl_param.passwd = pw;
} else if (port == LDAPS_PORT) {
prefix = "ldaps";
do_starttls = 0;
sasl_param.mech = "DIGEST-MD5";
sasl_param.authid = digest_md5_name;
sasl_param.authzid = digest_md5_name;
sasl_param.passwd = pw;
} else {
prefix = "ldap";
do_starttls = 1;
sasl_param.mech = "DIGEST-MD5";
sasl_param.authid = digest_md5_name;
sasl_param.authzid = digest_md5_name;
sasl_param.passwd = pw;
}
len = strlen(host) + 26;
url = malloc(len);
if (url == NULL)
goto out;
rc = snprintf(url, len, "%s://%s:%d", prefix, host, port);
#ifdef DEBUG
(void) fprintf(stderr, "nsdb_connect: url %s\n", url);
#endif
/*
* Get a handle to an LDAP connection to host:port
*/
rc = ldap_initialize(&ld, url);
if (rc != LDAP_SUCCESS) {
#ifdef DEBUG
(void) fprintf(stderr, "ldap_initialize failed: %s\n",
ldap_err2string(rc));
#endif
syslog(LOG_WARNING, "nsdb_connect: ldap_initialize "
"failed: %s\n", ldap_err2string(rc));
goto out;
}
#ifdef DEBUG
(void) fprintf(stderr, "nsdb_connect: past ldap_initialize\n");
#endif
/*
* We like LDAPv3.
*/
(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &opt);
/*
* Start TLS negotiation now if we want it.
*/
if (do_starttls) {
rc = ldap_start_tls_s(ld, NULL, NULL);
if (rc != LDAP_SUCCESS) {
#ifdef DEBUG
(void) fprintf(stderr, "ldap_start_tls_s() "
"failed: %s\n", ldap_err2string(rc));
#endif
syslog(LOG_ERR, "nsdb_connect: ldap_start_tls_s() "
"failed: %s\n", ldap_err2string(rc));
(void) ldap_unbind(ld);
ld = NULL;
goto out;
}
#ifdef DEBUG
else {
(void) fprintf(stderr, "ldap_start_tls_s() OK\n");
}
#endif
}
/*
* Try simple bind first, nice for anonymous access.
*/
rc = ldap_simple_bind_s(ld, dn, pw);
if (rc != LDAP_SUCCESS) {
#ifdef DEBUG
(void) fprintf(stderr, "ldap_simple_bind_s() failed: %s\n",
ldap_err2string(rc));
#endif
syslog(LOG_WARNING, "nsdb_connect: ldap_simple_bind_s() "
"failed: %s\n", ldap_err2string(rc));
}
#ifdef DEBUG
else
(void) fprintf(stderr, "ldap_simple_bind_s() OK\n");
#endif
if (rc == LDAP_SUCCESS)
goto out;
/*
* Try SASL bind next.
*/
rc = ldap_sasl_interactive_bind_s(
ld, NULL, sasl_param.mech,
NULL, NULL, LDAP_SASL_QUIET,
saslcallback, &sasl_param);
if (rc != LDAP_SUCCESS) {
#ifdef DEBUG
(void) fprintf(stderr, "ldap_sasl_interactive_bind_s() "
"failed: %s\n", ldap_err2string(rc));
#endif
syslog(LOG_WARNING, "nsdb_connect: "
"ldap_sasl_interactive_bind_s() failed: %s\n",
ldap_err2string(rc));
}
#ifdef DEBUG
else
(void) fprintf(stderr, "ldap_sasl_interactive_bind_s() OK\n");
#endif
if (rc == LDAP_SUCCESS)
goto out;
(void) ldap_unbind(ld);
ld = NULL;
if (dn == NULL && smf_dn != NULL && smf_pw != NULL) {
dn = smf_dn;
pw = smf_pw;
free(url);
goto retry;
}
out:
free(certpath);
free(smf_dn);
free(smf_pw);
free(digest_md5_name);
free(url);
return (ld);
}
void
nsdb_free_attrs(LDAPMod *attrs[])
{
int i;
if (attrs == NULL)
return;
for (i = 0; attrs[i]; i++) {
free(attrs[i]->mod_type);
free(attrs[i]->mod_values[0]);
free(attrs[i]->mod_values[1]);
free(attrs[i]->mod_values);
free(attrs[i]);
}
free(attrs);
}
static int
alloc_attrs(LDAPMod **attrs, int num)
{
int i;
if (attrs == NULL)
return (-1);
for (i = 0; i < (num - 1); i++) {
attrs[i] = (LDAPMod *)calloc(1, sizeof (LDAPMod));
if (attrs[i] == NULL) {
nsdb_free_attrs(attrs);
return (-1);
}
}
return (0);
}
static char **
char_to_array(char *val0, char *val1, char *val2)
{
char **vals;
if (val0 == NULL)
return (NULL);
vals = calloc(4, sizeof (char *));
if (vals == NULL)
return (NULL);
vals[0] = strdup(val0);
if (val1)
vals[1] = strdup(val1);
if (val2)
vals[2] = strdup(val2);
return (vals);
}
static void
free_annotations(char **result, int num)
{
int i;
for (i = 0; i < num; i++)
free(result[i]);
free(result);
}
static char **
split_annotations(char *annotations, int *num)
{
char *s, *t, **r, **result = NULL;
int len, n = 0;
s = annotations;
do {
t = s;
s = strchr(t, ',');
if (s != NULL) {
len = s - t;
s++;
} else
len = strlen(t);
r = realloc(result, (n + 2) * sizeof (char *));
if (r == NULL) {
if (result != NULL)
free_annotations(result, n + 1);
return (NULL);
}
result = r;
result[n] = calloc(1, len + 1);
if (result[n] == NULL) {
free_annotations(result, n + 1);
return (NULL);
}
bcopy(t, result[n], len);
result[++n] = NULL;
} while (s != NULL);
*num = n;
return (result);
}
LDAPMod **
nsdb_create_fsn(char *nsdb, char *annotations, char *fsnuuid)
{
LDAPMod **attrs;
int i = 0, n, anum = 0;
char **asplit;
if (nsdb == NULL || fsnuuid == NULL)
return (NULL);
if (annotations != NULL)
asplit = split_annotations(annotations, &anum);
n = 4 + (anum > 0);
attrs = calloc(n, sizeof (LDAPMod *));
if (attrs == NULL)
return (NULL);
if (alloc_attrs(attrs, n) == -1)
return (NULL);
attrs[i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsFsnUuid");
attrs[i]->mod_values = char_to_array(fsnuuid, NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNsdbName");
attrs[i]->mod_values = char_to_array(nsdb, NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("objectClass");
attrs[i]->mod_values = char_to_array("fedfsFsn", "top", NULL);
if (anum > 0) {
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsAnnotation");
attrs[i]->mod_values = asplit;
}
attrs[++i] = NULL;
return (attrs);
}
LDAPMod **
nsdb_create_fsl(char *nsdb, char *host, char *path, char *annotations,
char *fsnuuid, char *fsluuid)
{
LDAPMod **attrs;
struct berval **bvals;
int i = 0, n, anum = 0;
int xplen;
char *xpath;
char **asplit;
if (nsdb == NULL || host == NULL || path == NULL ||
fsnuuid == NULL || fsluuid == NULL)
return (NULL);
if (annotations != NULL)
asplit = split_annotations(annotations, &anum);
xplen = path2xdr(path, &xpath);
if (xplen == 0)
return (NULL);
n = 27 + (anum > 0);
attrs = calloc(n, sizeof (LDAPMod *));
if (attrs == NULL)
return (NULL);
if (alloc_attrs(attrs, n) == -1)
return (NULL);
attrs[i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNsdbName");
attrs[i]->mod_values = char_to_array(nsdb, NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsFslHost");
attrs[i]->mod_values = char_to_array(host, NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
attrs[i]->mod_type = strdup("fedfsNfsPath");
bvals = (struct berval **)calloc(2, sizeof (struct berval *));
bvals[0] = (struct berval *)malloc(sizeof (struct berval));
bvals[0]->bv_val = xpath;
bvals[0]->bv_len = xplen;
attrs[i]->mod_bvalues = bvals;
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsFslUuid");
attrs[i]->mod_values = char_to_array(fsluuid, NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsFsnUuid");
attrs[i]->mod_values = char_to_array(fsnuuid, NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsFslTTL");
attrs[i]->mod_values = char_to_array("300", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsClassChange");
attrs[i]->mod_values = char_to_array("0", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsClassFileid");
attrs[i]->mod_values = char_to_array("1", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsClassHandle");
attrs[i]->mod_values = char_to_array("1", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsClassReaddir");
attrs[i]->mod_values = char_to_array("1", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsClassSimul");
attrs[i]->mod_values = char_to_array("1", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsClassWritever");
attrs[i]->mod_values = char_to_array("1", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsCurrency");
attrs[i]->mod_values = char_to_array("1", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsGenFlagGoing");
attrs[i]->mod_values = char_to_array("FALSE", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsGenFlagSplit");
attrs[i]->mod_values = char_to_array("FALSE", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsGenFlagWritable");
attrs[i]->mod_values = char_to_array("FALSE", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsMajorVer");
attrs[i]->mod_values = char_to_array("4", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsMinorVer");
attrs[i]->mod_values = char_to_array("0", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsReadOrder");
attrs[i]->mod_values = char_to_array("1", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsReadRank");
attrs[i]->mod_values = char_to_array("2", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsTransFlagRdma");
attrs[i]->mod_values = char_to_array("FALSE", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsValidFor");
attrs[i]->mod_values = char_to_array("2", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsVarSub");
attrs[i]->mod_values = char_to_array("TRUE", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsWriteOrder");
attrs[i]->mod_values = char_to_array("4", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsNfsWriteRank");
attrs[i]->mod_values = char_to_array("3", NULL, NULL);
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("objectClass");
attrs[i]->mod_values = char_to_array("fedfsFSL", "fedfsNFsFsl", "top");
if (anum > 0) {
attrs[++i]->mod_op = LDAP_MOD_ADD;
attrs[i]->mod_type = strdup("fedfsAnnotation");
attrs[i]->mod_values = asplit;
}
attrs[++i] = NULL;
return (attrs);
}
char *
nsdb_gen_uuid()
{
uuid_t uu;
char uuid[UUID_PRINTABLE_STRING_LENGTH];
uuid_generate_time(uu);
uuid_unparse(uu, uuid);
return (strdup(uuid));
}