diff mbox

[8/8] libnsdb: simplify NCE discovery

Message ID 20131113215441.23593.41634.stgit@seurat.1015granger.net
State Accepted
Headers show

Commit Message

Chuck Lever Nov. 13, 2013, 9:54 p.m. UTC
When constructing an NSDB, part of the process currently involves
adding a fedfsNceDN attribute to one or more root suffix entries in
an LDAP server's root DSE.

Simo Sorce (FreeIPA) points out it may be difficult or impossible
for some LDAP server implementations to allow modification of their
root DSE.  Or it could be a problem for some deployments to allow
root DSE modification.  For this reason, LDAP applications typically
use an approach that does not require root DSE modification.

My own experience with OpenLDAP and 389-ds is that root DSE
modification is quite awkward.  Long-term, we'd like to replace
fedfsNsdbContainerInfo and fedfsNceDN with a form of NCE discovery
that is simpler to configure.

Old-style NCE discovery works like this: For each of the server's
naming contexts, an NSDB client performs this query:

  ldapsearch -b "naming_context" -s base (objectClass=*) fedfsNceDN

The fedfsNceDN attribute contains the full distinguished name of
the NCE residing under that naming context (root suffix).

New-style NCE discovery works like this:  An NCE contains an
auxiliary object class called fedfsNsdbContainerEntry.  For each of
the server's naming contexts, an NSDB client performs this query:

  ldapsearch -b "naming_context" -s subtree \
		(objectClass=fedfsNsdbContainerEntry)

The response carries the distinguished name of the NCE residing
under that naming context, or NO_SUCH_OBJECT.

Our client maintains compatibility with old-style NSDBs by using
new-style discovery first, and then trying old-style discovery if
new-style discovery fails.  For now, the client can throw an error
if it discovers more than one NCE in a single naming context.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 src/libnsdb/fileserver.c |  184 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 171 insertions(+), 13 deletions(-)
diff mbox

Patch

diff --git a/src/libnsdb/fileserver.c b/src/libnsdb/fileserver.c
index 4cda9d7..fdd0878 100644
--- a/src/libnsdb/fileserver.c
+++ b/src/libnsdb/fileserver.c
@@ -357,7 +357,7 @@  nsdb_parse_ncedn_entry(LDAP *ld, LDAPMessage *entry, char **dn)
 }
 
 /**
- * Get the naming context's NSDB DN, if it has one
+ * Get the naming context's NSDB DN, if it has one (old-style)
  *
  * @param host an initialized and bound nsdb_t object
  * @param naming_context NUL-terminated C string containing one naming context
@@ -376,8 +376,8 @@  nsdb_parse_ncedn_entry(LDAP *ld, LDAPMessage *entry, char **dn)
  *
  * The full DN for the NSDB container entry is returned in "dn."
  */
-FedFsStatus
-nsdb_get_ncedn_s(nsdb_t host, const char *naming_context, char **dn,
+static FedFsStatus
+nsdb_old_get_ncedn_s(nsdb_t host, const char *naming_context, char **dn,
 		unsigned int *ldap_err)
 {
 	LDAPMessage *response, *message;
@@ -386,16 +386,6 @@  nsdb_get_ncedn_s(nsdb_t host, const char *naming_context, char **dn,
 	char *tmp = NULL;
 	int rc;
 
-	if (host->fn_ldap == NULL) {
-		xlog(L_ERROR, "%s: NSDB not open", __func__);
-		return FEDFS_ERR_INVAL;
-	}
-
-	if (dn == NULL || ldap_err == NULL) {
-		xlog(L_ERROR, "%s: Invalid parameter", __func__);
-		return FEDFS_ERR_INVAL;
-	}
-
 	rc = nsdb_search_nsdb_attr_s(ld, naming_context, "(objectClass=*)",
 						"fedfsNceDN", &response);
 	switch (rc) {
@@ -466,6 +456,174 @@  out:
 }
 
 /**
+ * Extract DN for naming context's NSDB Container Entry
+ *
+ * @param ld an initialized LDAP descriptor
+ * @param entry an LDAP_RES_SEARCH_ENTRY message
+ * @param dn OUT: pointer to a NUL-terminated C string containing resulting DN
+ * @return a FedFsStatus code
+ *
+ * Caller must free "dn" with free(3)
+ */
+static FedFsStatus
+nsdb_parse_ncedn(LDAP *ld, LDAPMessage *entry, char **dn)
+{
+	FedFsStatus retval;
+	char *tmp;
+
+	retval = FEDFS_ERR_SVRFAULT;
+
+	tmp = ldap_get_dn(ld, entry);
+	if (tmp == NULL) {
+		xlog(D_GENERAL, "%s: ldap_get_dn failed",
+			__func__);
+		goto out;
+	}
+
+	*dn = strdup(tmp);
+	if (*dn == NULL) {
+		xlog(D_GENERAL, "%s: strdup failed",
+			__func__);
+		goto out;
+	}
+
+	retval = FEDFS_OK;
+
+out:
+	ldap_memfree(tmp);
+	return retval;
+}
+
+/**
+ * Get the naming context's NSDB DN, if it has one (new-style)
+ *
+ * @param host an initialized and bound nsdb_t object
+ * @param naming_context NUL-terminated C string containing one naming context
+ * @param dn OUT: pointer to a NUL-terminated C string containing full DN of NSDB container
+ * @param ldap_err OUT: possibly an LDAP error code
+ * @return a FedFsStatus code
+ *
+ * Caller must free "dn" with free(3)
+ *
+ * ldapsearch equivalent:
+ *
+ * @verbatim
+
+   ldapsearch -b "naming_context" (objectClass=fedfsNsdbContainerEntry)
+   @endverbatim
+ *
+ * The full DN for the NSDB container entry is returned in "dn."
+ */
+static FedFsStatus
+nsdb_new_get_ncedn_s(nsdb_t host, const char *naming_context, char **dn,
+		unsigned int *ldap_err)
+{
+	LDAPMessage *response, *message;
+	LDAP *ld = host->fn_ldap;
+	FedFsStatus retval;
+	unsigned count;
+	int rc;
+
+	rc = nsdb_search_nsdb_all_s(ld, naming_context, LDAP_SCOPE_SUBTREE,
+					"(objectClass=fedfsNsdbContainerEntry)",
+					&response);
+	switch (rc) {
+	case LDAP_SUCCESS:
+	case LDAP_REFERRAL:
+		break;
+	case LDAP_NO_SUCH_OBJECT:
+		xlog(D_GENERAL, "%s: %s does not contain an NCE",
+			__func__, naming_context);
+		return FEDFS_ERR_NSDB_NONCE;
+	default:
+		xlog(D_GENERAL, "%s: Failed to retrieve naming_context "
+			"entry %s: %s", __func__, naming_context,
+			ldap_err2string(rc));
+		*ldap_err = rc;
+		return FEDFS_ERR_NSDB_LDAP_VAL;
+	}
+	if (response == NULL) {
+		xlog(D_GENERAL, "%s: Empty LDAP response\n", __func__);
+		return FEDFS_ERR_NSDB_FAULT;
+	}
+
+	rc = ldap_count_messages(ld, response);
+	if (rc == -1) {
+		xlog(D_GENERAL, "%s: Empty LDAP response\n", __func__);
+		retval = FEDFS_ERR_NSDB_FAULT;
+		goto out;
+	}
+	xlog(D_CALL, "%s: received %d messages", __func__, rc);
+
+	retval = FEDFS_OK;
+	for (message = ldap_first_message(ld, response), count = 0;
+	     message != NULL && retval == FEDFS_OK;
+	     message = ldap_next_message(ld, message)) {
+		switch (ldap_msgtype(message)) {
+		case LDAP_RES_SEARCH_ENTRY:
+			if (++count > 1) {
+				xlog(D_CALL, "%s: more than one NCE under %s",
+					__func__, naming_context);
+				retval = FEDFS_ERR_NSDB_NONCE;
+				goto out;
+			}
+			retval = nsdb_parse_ncedn(ld, message, dn);
+			if (retval == FEDFS_OK)
+				xlog(D_CALL, "%s: %s contains NCE %s",
+					__func__, naming_context, *dn);
+			goto out;
+		case LDAP_RES_SEARCH_RESULT:
+			retval = nsdb_parse_result(ld, message,
+							&host->fn_referrals,
+							ldap_err);
+			break;
+		default:
+			xlog(L_ERROR, "%s: Unrecognized LDAP message type",
+				__func__);
+			retval = FEDFS_ERR_NSDB_FAULT;
+		}
+	}
+
+out:
+	ldap_msgfree(response);
+	return retval;
+}
+
+/**
+ * Get the naming context's NSDB DN, if it has one
+ *
+ * @param host an initialized and bound nsdb_t object
+ * @param naming_context NUL-terminated C string containing one naming context
+ * @param dn OUT: pointer to a NUL-terminated C string containing full DN of NSDB container
+ * @param ldap_err OUT: possibly an LDAP error code
+ * @return a FedFsStatus code
+ *
+ * Caller must free "dn" with free(3)
+ */
+FedFsStatus
+nsdb_get_ncedn_s(nsdb_t host, const char *naming_context, char **dn,
+		unsigned int *ldap_err)
+{
+	FedFsStatus retval;
+
+	if (host->fn_ldap == NULL) {
+		xlog(L_ERROR, "%s: NSDB not open", __func__);
+		return FEDFS_ERR_INVAL;
+	}
+
+	if (dn == NULL || ldap_err == NULL) {
+		xlog(L_ERROR, "%s: Invalid parameter", __func__);
+		return FEDFS_ERR_INVAL;
+	}
+
+	retval = nsdb_new_get_ncedn_s(host, naming_context, dn, ldap_err);
+	if (retval != FEDFS_OK)
+		retval = nsdb_old_get_ncedn_s(host, naming_context,
+							dn, ldap_err);
+	return retval;
+}
+
+/**
  * Parse namingContext attribute
  *
  * @param ld an initialized LDAP descriptor