@@ -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
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(-)