diff mbox

[01/11] libadmin: Introduce admin_t objects

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

Commit Message

Chuck Lever Dec. 2, 2013, 8:32 p.m. UTC
In order to communicate with NSDBs, we have nsdb_t.  Let's add a
similar object for communicating with remote ADMIN services.

These objects are materialized via admin_new(), which takes a
hostname and a nettype.  They are released with admin_free().

The new API hides RPC- and GSS-related details inside libadmin.  It
should then be simple to add full ADMIN support directly into other
tools, such as nsdbparams, without having to drag in a lot of
redundant code.

Note: one new sparse error appears:

  rpc/auth.h:218:16: error: undefined identifier '__sync_sub_and_fetch'

Sparse does not recognize gcc built-ins that provide atomic
manipulation of integers.  This is a harmless error.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 configure.ac                  |    5 
 src/fedfsc/Makefile.am        |    2 
 src/include/Makefile.am       |    4 
 src/include/admin.h           |  286 +++++++++++++++
 src/include/fedfs.h           |    5 
 src/libadmin/Makefile.am      |    5 
 src/libadmin/admin-internal.h |   61 +++
 src/libadmin/admin.c          |  386 ++++++++++++++++++++
 src/libadmin/gss.c            |  281 ++++++++++++++
 src/libadmin/junction.c       |  801 +++++++++++++++++++++++++++++++++++++++++
 src/libadmin/nsdb.c           |  407 +++++++++++++++++++++
 src/libadmin/null.c           |   74 ++++
 12 files changed, 2313 insertions(+), 4 deletions(-)
 create mode 100644 src/include/admin.h
 create mode 100644 src/libadmin/admin-internal.h
 create mode 100644 src/libadmin/admin.c
 create mode 100644 src/libadmin/gss.c
 create mode 100644 src/libadmin/junction.c
 create mode 100644 src/libadmin/nsdb.c
 create mode 100644 src/libadmin/null.c
diff mbox

Patch

diff --git a/configure.ac b/configure.ac
index f92f731..3620b83 100644
--- a/configure.ac
+++ b/configure.ac
@@ -145,6 +145,11 @@  AC_CHECK_LIB([attr], [fgetxattr],
 		 AC_DEFINE([HAVE_LIBATTR], [1],
 			   [Define if you have libattr])],
 		[AC_MSG_ERROR([libattr not found.])])
+AC_CHECK_LIB([gssapi_krb5], [gss_acquire_cred],
+		[AC_SUBST([LIBGSSAPI_KRB5], ["-lgssapi_krb5"])
+		 AC_DEFINE([HAVE_LIBGSSAPI_KRB5], [1],
+			   [Define if you have libgssapi_krb5])],
+		[AC_MSG_ERROR([libgssapi_krb5 not found.])])
 
 # Checks for header files.
 AC_CHECK_HEADERS([fcntl.h langinfo.h locale.h memory.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h syslog.h termios.h unistd.h wchar.h])
diff --git a/src/fedfsc/Makefile.am b/src/fedfsc/Makefile.am
index df204d3..ec5928a 100644
--- a/src/fedfsc/Makefile.am
+++ b/src/fedfsc/Makefile.am
@@ -36,7 +36,7 @@  LDADD			= $(top_builddir)/src/libadmin/libadmin.la \
 			  $(top_builddir)/src/libxlog/libxlog.la \
 			  $(LIBTIRPC) $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) \
-			  $(LIBCRYPTO) $(LIBSSL)
+			  $(LIBCRYPTO) $(LIBSSL) $(LIBGSSAPI_KRB5)
 
 CLEANFILES		= cscope.in.out cscope.out cscope.po.out *~
 DISTCLEANFILES		= Makefile.in
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index bda3d3c..869db4b 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -24,8 +24,8 @@ 
 ##
 
 include_HEADERS		= nfs-plugin.h
-noinst_HEADERS		= fedfs_admin.h fedfs.h getsrvinfo.h gpl-boiler.h \
-			  junction.h list.h nsdb.h parse_opt.h \
+noinst_HEADERS		= admin.h fedfs_admin.h fedfs.h getsrvinfo.h \
+			  gpl-boiler.h junction.h list.h nsdb.h parse_opt.h \
 			  token.h xlog.h
 
 CLEANFILES		= cscope.in.out cscope.out cscope.po.out *~
diff --git a/src/include/admin.h b/src/include/admin.h
new file mode 100644
index 0000000..2887db9
--- /dev/null
+++ b/src/include/admin.h
@@ -0,0 +1,286 @@ 
+/*
+ * @file src/include/admin.h
+ * @brief Common public declarations for the ADMIN API
+ */
+
+/*
+ * Copyright 2013 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#ifndef _FEDFS_ADMIN_API_H_
+#define _FEDFS_ADMIN_API_H_
+
+#include <stdbool.h>
+#include <netdb.h>
+
+#include "fedfs_admin.h"
+#include "fedfs.h"
+#include "nsdb.h"
+
+/*
+ * Client-side failures are reported as an errno, via the return value
+ * of the API functions.
+ *
+ * Server-side failures are reported as a FedFsStatus code, extracted with
+ * the admin_status() function.
+ *
+ * Client API error codes
+ *
+ * 0:			RPC successful, check ad_srv_status for server result
+ * EACCES:		Security failure
+ * EIO:			RPC call or network transport failure
+ * EINVAL:		Function was passed invalid parameters, or parameters
+ *			that could not be marshalled
+ * ENAMETOOLONG:	Problem parsing outgoing or incoming path_array
+ * ENOMEM:		Local resource allocation error occurred
+ * ENOTCONN:		admin_t object is not connected to remote ADMIN service
+ * EOPNOTSUPP:		Client does not support this operation
+ */
+
+/**
+ ** Arguments and results
+ **/
+
+/**
+ * NSDB
+ */
+struct admin_nsdb {
+	const char		*an_hostname;
+	uint16_t		 an_port;
+};
+
+/**
+ * FileSet Name
+ */
+struct admin_fsn {
+	const char		*af_uuid;
+	struct admin_nsdb	 af_nsdb;
+};
+
+/**
+ * FileSet Location
+ */
+struct admin_fsl {
+	struct admin_fsl	*al_next;
+	const char		*al_uuid;
+	const char		*al_hostname;
+	uint16_t		 al_port;
+	char * const		*al_pathname;
+};
+
+/**
+ * X.509 certificate material
+ */
+struct admin_cert {
+	unsigned int		 ac_len;
+	const char		*ac_data;
+};
+
+/**
+ * Free an in-memory FSN data structure
+ */
+void		 admin_free_fsn(struct admin_fsn *fsn);
+
+/**
+ * Free a list of in-memory FSL data structures
+ */
+void		 admin_free_fsls(struct admin_fsl *fsls);
+
+/**
+ * Free a certificate buffer
+ */
+void		 admin_free_cert(struct admin_cert *cert);
+
+/**
+ ** API for managing admin_t objects
+ **/
+
+/**
+ * Object that internally represents an ADMIN service
+ */
+struct fedfs_admin;
+typedef struct fedfs_admin *admin_t;
+
+/**
+ * Construct a new admin_t object
+ */
+int		 admin_create(const char *hostname, const char *nettype,
+				const char *security, admin_t *result);
+
+/**
+ * Release all resources associated with an admin_t object
+ */
+void		 admin_release(admin_t host);
+
+/**
+ * Get ADMIN service's DNS hostname
+ */
+const char	*admin_hostname(const admin_t host);
+
+/**
+ * Get the length of ADMIN service's DNS hostname
+ */
+size_t		 admin_hostname_len(const admin_t host);
+
+/**
+ * Get the RPC nettype used to connect to ADMIN service
+ */
+const char	*admin_nettype(const admin_t host);
+
+/**
+ * Get the authentication flavor used to connect to ADMIN service
+ */
+uint32_t	 admin_flavor(const admin_t host);
+
+/**
+ * Get the FedFsStatus code returned by the last ADMIN service operation
+ */
+FedFsStatus	 admin_status(const admin_t host);
+
+/**
+ * LDAP status if server returned FEDFS_ERR_NSDB_LDAP_VAL
+ */
+int		 admin_ldaperr(const admin_t host);
+
+/**
+ * Predicate: is "host" connected to a remote ADMIN service?
+ */
+_Bool		 admin_is_connected(admin_t host);
+
+/**
+ * Return an RPC create error message string
+ */
+const char	*admin_open_perror(const char *prefix);
+
+/**
+ * Return an RPC error message string
+ */
+const char	*admin_perror(admin_t host, const char *prefix);
+
+
+/**
+ ** ADMIN operations defined in the
+ ** ADMIN protocol draft, Chapter 5)
+ **/
+
+/**
+ * FEDFS_NULL (5.1)
+ */
+int		 admin_null(admin_t host);
+
+/**
+ * FEDFS_CREATE_JUNCTION (5.2)
+ */
+int		 admin_create_junction(admin_t host,
+				char * const *path_array,
+				struct admin_fsn *fsn);
+
+/**
+ * FEDFS_DELETE_JUNCTION (5.3)
+ */
+int		 admin_delete_junction(admin_t host,
+				char * const *path_array);
+
+/**
+ * FEDFS_LOOKUP_JUNCTION (5.4) - ResolveType None
+ */
+int		 admin_lookup_junction_none(admin_t host,
+				char * const *path_array,
+				struct admin_fsn **fsn);
+
+/**
+ * FEDFS_LOOKUP_JUNCTION (5.4) - ResolveType Cached
+ */
+int		 admin_lookup_junction_cached(admin_t host,
+				char * const *path_array,
+				struct admin_fsn **fsn,
+				struct admin_fsl **fsls);
+
+/**
+ * FEDFS_LOOKUP_JUNCTION (5.4) - ResolveType Nsdb
+ */
+int		 admin_lookup_junction_nsdb(admin_t host,
+				char * const *path_array,
+				struct admin_fsn **fsn,
+				struct admin_fsl **fsls);
+
+/**
+ * FEDFS_CREATE_REPLICATION (5.5)
+ */
+int		 admin_create_replication(admin_t host,
+				char * const *path_array,
+				struct admin_fsn *fsn);
+
+/**
+ * FEDFS_DELETE_REPLICATION (5.6)
+ */
+int		 admin_delete_replication(admin_t host,
+				char * const *path_array);
+/**
+ * FEDFS_LOOKUP_REPLICATION (5.7) - ResolveType None
+ */
+int		 admin_lookup_replication_none(admin_t host,
+				char * const *path_array,
+				struct admin_fsn **fsn);
+
+/**
+ * FEDFS_LOOKUP_REPLICATION (5.7) - ResolveType Cached
+ */
+int		 admin_lookup_replication_cached(admin_t host,
+				char * const *path_array,
+				struct admin_fsn **fsn,
+				struct admin_fsl **fsls);
+
+/**
+ * FEDFS_LOOKUP_REPLICATION (5.7) - ResolveType Nsdb
+ */
+int		 admin_lookup_replication_nsdb(admin_t host,
+				char * const *path_array,
+				struct admin_fsn **fsn,
+				struct admin_fsl **fsls);
+
+/**
+ * FEDFS_SET_NSDB_PARAMS (5.8) - ConnectionSec NONE
+ */
+int		 admin_set_nsdb_params_none(admin_t host,
+				const struct admin_nsdb *nsdb);
+
+/**
+ * FEDFS_SET_NSDB_PARAMS (5.8) - ConnectionSec TLS
+ */
+int		 admin_set_nsdb_params_tls(admin_t host,
+				const struct admin_nsdb *nsdb,
+				const struct admin_cert *cert);
+
+/**
+ * FEDFS_GET_NSDB_PARAMS (5.9)
+ */
+int		 admin_get_nsdb_params(admin_t host,
+				struct admin_nsdb *nsdb,
+				FedFsConnectionSec *sectype,
+				struct admin_cert **cert);
+
+/**
+ * FEDFS_GET_LIMITED_NSDB_PARAMS (5.10)
+ */
+int		 admin_get_limited_nsdb_params(admin_t host,
+				struct admin_nsdb *nsdb,
+				FedFsConnectionSec *sectype);
+
+#endif	/* !_FEDFS_ADMIN_API_H_ */
diff --git a/src/include/fedfs.h b/src/include/fedfs.h
index 0a2186c..d309e11 100644
--- a/src/include/fedfs.h
+++ b/src/include/fedfs.h
@@ -47,6 +47,11 @@ 
 #define FEDFS_DATABASE_FILE		"nsdbparam.sqlite3"
 
 /**
+ * From draft-cel-nfsv4-federated-fs-security-addendum
+ */
+#define FEDFS_ADMIN_GSS_SERVICE_NAME	"fedfs-admin"
+
+/**
  * Initial number of seconds to wait after receiving FEDFS_ERR_DELAY
  */
 #define FEDFS_DELAY_MIN_SECS	2
diff --git a/src/libadmin/Makefile.am b/src/libadmin/Makefile.am
index 7351254..3b12c34 100644
--- a/src/libadmin/Makefile.am
+++ b/src/libadmin/Makefile.am
@@ -23,8 +23,11 @@ 
 ##	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 ##
 
+noinst_HEADERS		= admin-internal.h
+
 noinst_LTLIBRARIES	= libadmin.la
-libadmin_la_SOURCES	= fedfs_admin_xdr.c
+libadmin_la_SOURCES	= admin.c fedfs_admin_xdr.c gss.c junction.c \
+			  nsdb.c null.c
 
 CLEANFILES		= cscope.in.out cscope.out cscope.po.out *~
 DISTCLEANFILES		= Makefile.in
diff --git a/src/libadmin/admin-internal.h b/src/libadmin/admin-internal.h
new file mode 100644
index 0000000..2cae3e8
--- /dev/null
+++ b/src/libadmin/admin-internal.h
@@ -0,0 +1,61 @@ 
+/*
+ * @file src/libadmin/admin-internal.h
+ * @brief Private declarations for the ADMIN API
+ */
+
+/*
+ * Copyright 2013 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#ifndef _FEDFS_ADMIN_INTERNAL_H_
+#define _FEDFS_ADMIN_INTERNAL_H_
+
+#include <time.h>
+#include <rpc/clnt.h>
+#include <rpc/auth_gss.h>
+
+#include "fedfs_admin.h"
+#include "admin.h"
+
+/**
+ * object that internally represents an ADMIN service
+ */
+struct fedfs_admin {
+	char			*ad_hostname;
+	char			*ad_nettype;
+	int			 ad_secflavor;
+	rpc_gss_svc_t		 ad_gss_svc;
+	CLIENT			*ad_client;
+	enum clnt_stat		 ad_rpc_status;
+	struct timeval		 ad_timeout;
+	FedFsStatus		 ad_srv_status;
+	int			 ad_ldaperr;
+};
+
+/**
+ * Reset error status values
+ */
+void		 admin_reset(admin_t host);
+
+/**
+ * Create an AUTH and GSS context
+ */
+int		 admin_authgss_create(CLIENT *clnt, admin_t host, AUTH **auth);
+
+#endif	/* !_FEDFS_ADMIN_INTERNAL_H_ */
diff --git a/src/libadmin/admin.c b/src/libadmin/admin.c
new file mode 100644
index 0000000..ba3ead3
--- /dev/null
+++ b/src/libadmin/admin.c
@@ -0,0 +1,386 @@ 
+/**
+ * @file src/libadmin/admin.c
+ * @brief Manage admin_t objects
+ */
+
+/*
+ * Copyright 2013 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "fedfs_admin.h"
+#include "fedfs.h"
+#include "admin-internal.h"
+#include "admin.h"
+#include "xlog.h"
+
+/**
+ * Default RPC request timeout
+ */
+static const struct timeval admin_rpc_timeout = { 25, 0 };
+
+/**
+ * Return admin_t's hostname
+ *
+ * @param host an initialized admin_t
+ * @return NUL-terminated C string containing "host's" hostname
+ *
+ * Lifetime of this string is the same as the lifetime of the
+ * admin_t.  Caller must not free this string, and must not use
+ * it after the admin_t is freed.
+ */
+const char *admin_hostname(const admin_t host)
+{
+	if (host == NULL)
+		return NULL;
+	return host->ad_hostname;
+}
+
+/**
+ * Return length of admin_t's hostname, in bytes
+ *
+ * @param host an initialized admin_t
+ * @return the number of bytes in "host's" hostname, excluding the terminating NUL
+ */
+size_t admin_hostname_len(const admin_t host)
+{
+	if (host == NULL)
+		return 0;
+	return strlen(host->ad_hostname);
+}
+
+/**
+ * Return admin_t's nettype
+ *
+ * @param host an initialized admin_t
+ * @return NUL-terminated C string containing "host's" nettype
+ *
+ * Lifetime of this string is the same as the lifetime of the
+ * admin_t.  Caller must not free this string, and must not use
+ * it after the admin_t is freed.
+ */
+const char *admin_nettype(const admin_t host)
+{
+	if (host == NULL)
+		return NULL;
+	return host->ad_nettype;
+}
+
+/**
+ * Predicate: is "host" connected to a remote ADMIN service?
+ *
+ * @param host an initialized admin_t struct
+ * @return true if the "host" is connected
+ */
+_Bool
+admin_is_connected(admin_t host)
+{
+	return host->ad_client != NULL;
+}
+
+/**
+ * Create an AUTH_UNIX Auth
+ *
+ * @param auth OUT: a fresh AUTH object
+ * @return zero or an errno
+ *
+ * Caller must destroy returned object with auth_destroy()
+ */
+static int
+admin_authunix_create(AUTH **auth)
+{
+	AUTH *result;
+
+	result = authunix_create_default();
+	if (result == NULL) {
+		xlog(D_GENERAL, "%s", clnt_spcreateerror(__func__));
+		return EACCES;
+	}
+
+	*auth = result;
+	return 0;
+}
+
+/**
+ * Connect to a remote ADMIN service
+ *
+ * @param host an initialized admin_t struct
+ * @return zero or an errno
+ */
+static int
+admin_open(admin_t host)
+{
+	CLIENT *clnt;
+	AUTH *auth;
+	int err;
+
+	if (admin_is_connected(host))
+		return 0;
+
+	xlog(D_CALL, "opening admin_t for %s",
+		admin_hostname(host));
+
+	clnt = clnt_create(admin_hostname(host),
+				FEDFS_PROG, FEDFS_V1, admin_nettype(host));
+	if (clnt == NULL)
+		return ENOTCONN;
+
+	switch (host->ad_secflavor) {
+	case AUTH_UNIX:
+		err = admin_authunix_create(&auth);
+		break;
+	case RPCSEC_GSS:
+		err = admin_authgss_create(clnt, host, &auth);
+		break;
+	default:
+		xlog(D_GENERAL, "%s: Unsupported security flavor", __func__);
+		return EINVAL;
+	}
+	if (err != 0) {
+		(void)clnt_destroy(clnt);
+		return err;
+	}
+
+	host->ad_client = clnt;
+	clnt->cl_auth = auth;
+	return 0;
+}
+
+/**
+ * Free TI-RPC library and network resources
+ *
+ * @param host an initialized admin_t struct
+ */
+static int
+admin_close(admin_t host)
+{
+	if (!admin_is_connected(host))
+		return ENOTCONN;
+
+	xlog(D_CALL, "closing admin_t for %s",
+		admin_hostname(host));
+
+	auth_destroy(host->ad_client->cl_auth);
+	(void)clnt_destroy(host->ad_client);
+	host->ad_client = NULL;
+	return 0;
+}
+
+/**
+ * Release all resources associated with an admin_t object
+ *
+ * @param host admin_t allocated by admin_new()
+ *
+ * This API performs an implicit admin_close().
+ */
+void
+admin_release(admin_t host)
+{
+	if (host == NULL)
+		return;
+
+	xlog(D_CALL, "freeing admin_t for %s",
+		admin_hostname(host));
+
+	(void)admin_close(host);
+
+	free(host->ad_hostname);
+	free(host->ad_nettype);
+	free(host);
+}
+
+/**
+ * Reset error status values
+ *
+ * @param host admin_t allocated by admin_new()
+ */
+void
+admin_reset(admin_t host)
+{
+	host->ad_srv_status = FEDFS_ERR_SVRFAULT;
+	host->ad_rpc_status = RPC_FAILED;
+	host->ad_ldaperr = LDAP_OTHER;
+}
+
+/**
+ * Materialize a new admin_t object
+ *
+ * @param hostname NUL-terminated UTF-8 string containing ADMIN server's hostname
+ * @param nettype NUL-terminated C string containing nettype to use for connection
+ * @param security NUL-terminated C string containing name of security mode
+ * @param result OUT: an initialized admin_t
+ * @return zero or an errno
+ *
+ * Caller must free returned object with admin_release()
+ */
+static int
+admin_new(const char *hostname, const char *nettype, const char *security,
+		admin_t *result)
+{
+	rpc_gss_svc_t svc;
+	admin_t new;
+	int flavor;
+
+	svc = RPCSEC_GSS_SVC_NONE;
+	if (strcasecmp(security, "sys") == 0)
+		flavor = AUTH_UNIX;
+	else if (strcasecmp(security, "unix") == 0)
+		flavor = AUTH_UNIX;
+	else if (strcasecmp(security, "krb5") == 0) {
+		flavor = RPCSEC_GSS;
+	} else if (strcasecmp(security, "krb5i") == 0) {
+		flavor = RPCSEC_GSS;
+		svc = RPCSEC_GSS_SVC_INTEGRITY;
+	} else if (strcasecmp(security, "krb5p") == 0) {
+		flavor = RPCSEC_GSS;
+		svc = RPCSEC_GSS_SVC_PRIVACY;
+	} else
+		return EINVAL;
+
+	new = calloc(1, sizeof(struct fedfs_admin));
+	if (new == NULL)
+		return ENOMEM;
+
+	new->ad_hostname = strdup(hostname);
+	new->ad_nettype = strdup(nettype);
+	new->ad_secflavor = flavor;
+	new->ad_gss_svc = svc;
+	new->ad_client = NULL;
+	new->ad_timeout = admin_rpc_timeout;
+
+	if (new->ad_hostname == NULL || new->ad_nettype == NULL) {
+		admin_release(new);
+		return ENOMEM;
+	}
+
+	xlog(D_CALL, "created admin_t for %s", hostname);
+
+	admin_reset(new);
+	*result = new;
+	return 0;
+}
+
+/**
+ * Create an admin_t and open it
+ *
+ * @param hostname NUL-terminated UTF-8 string containing ADMIN server's hostname
+ * @param nettype NUL-terminated C string containing nettype to use for connection
+ * @param security NUL-terminated C string containing name of security mode
+ * @param result OUT: an initialized admin_t
+ * @return zero or an errno
+ *
+ * Caller must free returned object with admin_release()
+ */
+int
+admin_create(const char *hostname, const char *nettype, const char *security,
+		admin_t *result)
+{
+	admin_t new;
+	int err;
+
+	if (hostname == NULL || nettype == NULL ||
+	    result == NULL || security == NULL)
+		return EINVAL;
+
+	if (strlen(hostname) == 0 || strlen(nettype) == 0 ||
+	    strlen(security) == 0)
+		return EINVAL;
+
+	err = admin_new(hostname, nettype, security, &new);
+	if (err != 0)
+		return err;
+
+	err = admin_open(new);
+	if (err != 0)
+		return err;
+
+	*result = new;
+	return 0;
+}
+
+/**
+ * Server status code returned by the last FedFS operation
+ *
+ * @param host an initialized admin_t
+ * @return a FedFsStatus code
+ */
+FedFsStatus
+admin_status(const admin_t host)
+{
+	if (host == NULL)
+		return FEDFS_ERR_SVRFAULT;
+	return host->ad_srv_status;
+}
+
+/**
+ * LDAP status if server returned FEDFS_ERR_NSDB_LDAP_VAL
+ *
+ * @param host an initialized admin_t
+ * @return an LDAP operation return code
+ */
+int
+admin_ldaperr(const admin_t host)
+{
+	if (host == NULL)
+		return LDAP_OTHER;
+	return host->ad_ldaperr;
+}
+
+/**
+ * Return an RPC create error message string
+ *
+ * @param prefix NUL-terminated C string containing prefix message
+ * @return NUL-terminated C string containing an error message, or NULL
+ *
+ * Caller must not free the returned buffer.
+ */
+const char *
+admin_open_perror(const char *prefix)
+{
+	if (prefix == NULL)
+		return NULL;
+	return clnt_spcreateerror(prefix);
+}
+
+/**
+ * Return an RPC error message string
+ *
+ * @param host an initialized admin_t
+ * @param prefix NUL-terminated C string containing prefix message
+ * @return NUL-terminated C string containing an error message, or NULL
+ *
+ * Caller must not free the returned buffer.
+ */
+const char *
+admin_perror(admin_t host, const char *prefix)
+{
+	if (host == NULL || prefix == NULL || host->ad_client == NULL)
+		return NULL;
+	if (!admin_is_connected(host))
+		return NULL;
+	return clnt_sperror(host->ad_client, prefix);
+}
diff --git a/src/libadmin/gss.c b/src/libadmin/gss.c
new file mode 100644
index 0000000..65b8785
--- /dev/null
+++ b/src/libadmin/gss.c
@@ -0,0 +1,281 @@ 
+/**
+ * @file src/libadmin/gss.c
+ * @brief RPCSEC GSS utility functions
+ */
+
+/*
+ * Copyright 2013 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <netdb.h>
+#include <gssapi/gssapi.h>
+
+#include "fedfs_admin.h"
+#include "fedfs.h"
+#include "admin-internal.h"
+#include "admin.h"
+#include "xlog.h"
+
+/**
+ * OID for Kerberos v5 GSS mechanism (RFC 2743, section 1.1.4)
+ */
+static gss_OID_desc	admin_gss_krb5_oid =
+				{ 9, "\052\206\110\206\367\022\001\002\002" };
+
+/**
+ * List of GSS mechanisms supported by this implementation
+ */
+static gss_OID_set_desc	admin_gss_mechs = { 1, &admin_gss_krb5_oid };
+
+/**
+ * Log a major GSS error
+ *
+ * @param prefix NUL-terminated C string containing log entry prefix
+ * @param maj_stat major status to report
+ * @param min_stat minor status to report
+ */
+static void
+admin_log_gss_major_error(const char *prefix, OM_uint32 maj_stat,
+		OM_uint32 min_stat)
+{
+	gss_buffer_desc maj_msg, min_msg;
+	OM_uint32 min, msg_ctx;
+
+	msg_ctx = 0;
+	gss_display_status(&min, maj_stat, GSS_C_GSS_CODE,
+				GSS_C_NULL_OID, &msg_ctx, &maj_msg);
+	gss_display_status(&min, min_stat, GSS_C_MECH_CODE,
+				GSS_C_NULL_OID, &msg_ctx, &min_msg);
+
+	xlog(D_GENERAL, "%s: %s - %s",
+		prefix, (char *)maj_msg.value, (char *)min_msg.value);
+
+	(void)gss_release_buffer(&min, &min_msg);
+	(void)gss_release_buffer(&min, &maj_msg);
+}
+
+/**
+ * Log an unspecified GSS error
+ *
+ * @param prefix NUL-terminated C string containing log entry prefix
+ * @param min_stat minor status to report
+ */
+static void
+admin_log_gss_unspec_error(const char *prefix, OM_uint32 min_stat)
+{
+	gss_buffer_desc min_msg;
+	OM_uint32 min, msg_ctx;
+
+	msg_ctx = 0;
+	gss_display_status(&min, min_stat, GSS_C_MECH_CODE,
+				GSS_C_NULL_OID, &msg_ctx, &min_msg);
+
+	xlog(D_GENERAL, "%s: %s",
+		prefix, (char *)min_msg.value);
+
+	(void)gss_release_buffer(&min, &min_msg);
+}
+
+/**
+ * Log a GSS error
+ *
+ * @param prefix NUL-terminated C string containing log entry prefix
+ * @param maj_stat major status to report
+ * @param min_stat minor status to report
+ */
+static void
+admin_log_gss_error(const char *prefix, OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+	if (GSS_ROUTINE_ERROR(maj_stat) != GSS_S_FAILURE)
+		admin_log_gss_major_error(prefix, maj_stat, min_stat);
+	else
+		admin_log_gss_unspec_error(prefix, min_stat);
+}
+
+/**
+ * Return the GSS service name for the ADMIN server
+ *
+ * @param server NUL-terminated C string containing hostname of server
+ * @return a NUL-terminated C string containing the GSS service name
+ *
+ * Caller must free the returned string with free(3).
+ */
+static char *
+admin_get_gss_svc_name(const char *server)
+{
+	struct addrinfo hint, *ai;
+	char *buffer;
+	size_t len;
+	int err;
+
+	memset(&hint, 0, sizeof(hint));
+	hint.ai_socktype = SOCK_DGRAM;
+	hint.ai_family = AF_UNSPEC;
+	hint.ai_flags = AI_CANONNAME;
+
+	err = getaddrinfo(server, NULL, &hint, &ai);
+	if (err) {
+		xlog(D_GENERAL, "%s: %s", __func__, gai_strerror(err));
+		return NULL;
+	}
+
+	len = strlen(FEDFS_ADMIN_GSS_SERVICE_NAME) + 1 +
+		strlen(ai->ai_canonname) + 1;
+
+	buffer = calloc(len, sizeof(char));
+	if (buffer == NULL) {
+		goto out;
+	}
+
+	buffer[0] = '\0';
+	strcpy(buffer, FEDFS_ADMIN_GSS_SERVICE_NAME);
+	strcat(buffer, "@");
+	strcat(buffer, ai->ai_canonname);
+
+	xlog(D_CALL, "Using service name '%s'", buffer);
+
+out:
+	freeaddrinfo(ai);
+	return buffer;
+}
+
+/**
+ * Acquire a GSS credential for a user
+ *
+ * @param name string form of a UID
+ * @param cred OUT: fresh credential
+ * @return zero or an errno
+ *
+ * Caller must free the returned credential with gss_release_cred()
+ */
+static int
+admin_acquire_cred(gss_name_t name, gss_cred_id_t *cred)
+{
+	OM_uint32 maj_stat, min_stat;
+	gss_cred_id_t result;
+
+	maj_stat = gss_acquire_cred(&min_stat, name, GSS_C_INDEFINITE,
+					&admin_gss_mechs, GSS_C_INITIATE,
+					&result, NULL, NULL);
+	if (maj_stat != GSS_S_COMPLETE) {
+		admin_log_gss_error("Failed to acquire credential",
+					maj_stat, min_stat);
+		return EKEYEXPIRED;
+	}
+
+	*cred = result;
+	return 0;
+}
+
+/**
+ * Construct a GSS credential for the current user
+ *
+ * @param cred OUT: fresh credential
+ * @return zero or an errno
+ *
+ * Caller must free the returned credential with gss_release_cred()
+ */
+static int
+admin_acquire_user_cred(gss_cred_id_t *cred)
+{
+	OM_uint32 maj_stat, min_stat;
+	gss_buffer_desc name_buf;
+	gss_name_t name;
+	int retval, len;
+	char buf[16];
+
+	len = snprintf(buf, sizeof(buf), "%u", geteuid());
+	name_buf.value = buf;
+	name_buf.length = len;
+
+	maj_stat = gss_import_name(&min_stat, &name_buf,
+					(gss_OID)GSS_C_NT_STRING_UID_NAME, &name);
+	if (maj_stat != GSS_S_COMPLETE) {
+		admin_log_gss_error("Failed to import name",
+					maj_stat, min_stat);
+		return ENOMEM;
+	}
+
+	retval = admin_acquire_cred(name, cred);
+
+	(void)gss_release_name(&min_stat, &name);
+	return retval;
+}
+
+/**
+ * Create an AUTH and an associated GSS context
+ *
+ * @param clnt RPC client
+ * @param host an initialized admin_t struct
+ * @param auth OUT: a fresh AUTH object
+ * @return zero or an errno
+ *
+ * Caller must destroy returned object with auth_destroy()
+ */
+int
+admin_authgss_create(CLIENT *clnt, admin_t host, AUTH **auth)
+{
+	struct rpc_gss_sec sec;
+	OM_uint32 min_stat;
+	char *svc_name;
+	int retval;
+	AUTH *tmp;
+
+	xlog(D_CALL, "Creating GSS context for server %s",
+		admin_hostname(host));
+
+	retval = ENOMEM;
+	svc_name = admin_get_gss_svc_name(admin_hostname(host));
+	if (svc_name == NULL)
+		goto out;
+
+	retval = admin_acquire_user_cred(&sec.cred);
+	if (retval != 0)
+		goto out;
+
+	sec.mech = &admin_gss_krb5_oid;
+	sec.qop = GSS_C_QOP_DEFAULT;
+	sec.svc = host->ad_gss_svc;
+	sec.req_flags = GSS_C_MUTUAL_FLAG;
+
+	tmp = authgss_create_default(clnt, svc_name, &sec);
+	if (tmp == NULL) {
+		xlog(D_GENERAL, "cf_stat = %d", rpc_createerr.cf_stat);
+		xlog(D_GENERAL, "%s", clnt_spcreateerror(__func__));
+		return EACCES;
+	}
+
+	*auth = tmp;
+	retval = 0;
+
+	(void)gss_release_cred(&min_stat, &sec.cred);
+
+out:
+	free(svc_name);
+	return retval;
+}
diff --git a/src/libadmin/junction.c b/src/libadmin/junction.c
new file mode 100644
index 0000000..1bb8a06
--- /dev/null
+++ b/src/libadmin/junction.c
@@ -0,0 +1,801 @@ 
+/**
+ * @file src/libadmin/junction.c
+ * @brief Handle junction-related ADMIN RPC operations
+ */
+
+/*
+ * Copyright 2013 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <uuid/uuid.h>
+
+#include "fedfs_admin.h"
+#include "fedfs.h"
+#include "admin-internal.h"
+#include "admin.h"
+#include "xlog.h"
+
+/**
+ * Unmarshal a UUID
+ *
+ * @param uuid UUID in packed wire format
+ * @return NUL-terminated C string containing a text UUID
+ */
+__attribute_malloc__
+static char *
+admin_get_uuid(FedFsUuid uuid)
+{
+	char buf[FEDFS_UUID_STRLEN];
+	uuid_t uu;
+
+	memcpy(uu, uuid, sizeof(uu));
+	uuid_unparse(uu, buf);
+	return strdup(buf);
+}
+
+/**
+ * Allocate an in-memory FSN data structure
+ *
+ * @return a freshly allocated FSN, or NULL
+ */
+__attribute_malloc__
+static struct admin_fsn *
+admin_new_fsn(void)
+{
+	return calloc(1, sizeof(struct admin_fsn));
+}
+
+/**
+ * Free an in-memory FSN data structure
+ *
+ * @param fsn FSN to free
+ */
+void
+admin_free_fsn(struct admin_fsn *fsn)
+{
+	if (fsn == NULL)
+		return;
+
+	free((void *)fsn->af_nsdb.an_hostname);
+	free((void *)fsn->af_uuid);
+	free(fsn);
+}
+
+/**
+ * Dig returned FSN out of lookup results
+ *
+ * @param result RPC lookup results
+ * @param fsn OUT: in-memory FSN structure
+ * @return zero or an errno
+ *
+ * The caller must free "fsn" with admin_free_fsn().
+ */
+static int
+admin_set_fsn(FedFsFsn result, struct admin_fsn **fsn)
+{
+	struct admin_fsn *new;
+
+	new = admin_new_fsn();
+	if (new == NULL)
+		return ENOMEM;
+
+	new->af_uuid = admin_get_uuid(result.fsnUuid);
+	if (new->af_uuid == NULL) {
+		admin_free_fsn(new);
+		return ENOMEM;
+	}
+
+	if (result.nsdbName.hostname.utf8string_val == NULL ||
+	    result.nsdbName.hostname.utf8string_len == 0)
+		goto out;
+
+	new->af_nsdb.an_hostname =
+		strndup(result.nsdbName.hostname.utf8string_val,
+			result.nsdbName.hostname.utf8string_len);
+	if (new->af_nsdb.an_hostname == NULL) {
+		admin_free_fsn(new);
+		return ENOMEM;
+	}
+	new->af_nsdb.an_port = result.nsdbName.port;
+
+out:
+	*fsn = new;
+	return 0;
+}
+
+/**
+ * Allocate an in-memory FSL data structure
+ *
+ * @return a freshly allocated FSL, or NULL
+ */
+__attribute_malloc__
+static struct admin_fsl *
+admin_new_fsl(void)
+{
+	return calloc(1, sizeof(struct admin_fsl));
+}
+
+/**
+ * Free one in-memory FSL data structure
+ *
+ * @param fsl FSL to free
+ */
+static void
+admin_free_fsl(struct admin_fsl *fsl)
+{
+	if (fsl == NULL)
+		return;
+
+	nsdb_free_string_array((char **)fsl->al_pathname);
+	free((void *)fsl->al_hostname);
+	free((void *)fsl->al_uuid);
+	free(fsl);
+}
+
+/**
+ * Free a list of in-memory FSL data structures
+ *
+ * @param fsls FSL list to free
+ */
+void
+admin_free_fsls(struct admin_fsl *fsls)
+{
+	struct admin_fsl *fsl;
+
+	while (fsls != NULL) {
+		fsl = fsls;
+		fsls = fsl->al_next;
+		admin_free_fsl(fsl);
+	}
+}
+
+/**
+ * Materialize one NFS FSL and link it onto the list
+ *
+ * @param result one NFS FSL in the result
+ * @param fsls OUT: list of in-memory FSL structures
+ * @return zero or an errno
+ */
+static int
+admin_add_nfsfsl(FedFsNfsFsl result, struct admin_fsl **fsls)
+{
+	struct admin_fsl *new = NULL;
+	int retval;
+
+	retval = ENAMETOOLONG;
+	if (result.hostname.utf8string_val == NULL ||
+	    result.hostname.utf8string_len == 0)
+		goto out_err;
+
+	retval = ENOMEM;
+	new = admin_new_fsl();
+	if (new == NULL)
+		goto out_err;
+
+	new->al_uuid = admin_get_uuid(result.fslUuid);
+	if (new->al_uuid == NULL)
+		goto out_err;
+
+	new->al_hostname = strndup(result.hostname.utf8string_val,
+				   result.hostname.utf8string_len);
+	if (new->al_hostname == NULL)
+		goto out_err;
+	new->al_port = result.port;
+
+	retval = ENAMETOOLONG;
+	if (nsdb_fedfspathname_to_path_array(result.path,
+					(char ***)&new->al_pathname) != FEDFS_OK)
+		goto out_err;
+
+	new->al_next = *fsls;
+	*fsls = new;
+	return 0;
+
+out_err:
+	admin_free_fsl(new);
+	return retval;
+}
+
+/**
+ * Materialize one FSL and link it onto the list
+ *
+ * @param results one FSL in the result
+ * @param fsls OUT: list of in-memory FSL structures
+ * @return zero or an errno
+ */
+static int
+admin_add_fsl(FedFsFsl results, struct admin_fsl **fsls)
+{
+	switch (results.type) {
+	case FEDFS_NFS_FSL:
+		return admin_add_nfsfsl(results.FedFsFsl_u.nfsFsl, fsls);
+	default:
+		return 0;
+	}
+}
+
+/**
+ * Dig returned FSLs out of lookup results
+ *
+ * @param results RPC lookup results
+ * @param fsls OUT: list of in-memory FSL structures
+ * @return zero or an errno
+ *
+ * The caller must free "fsls" with admin_free_fsls().
+ */
+static int
+admin_set_fsls(FedFsLookupResOk results, struct admin_fsl **fsls)
+{
+	struct admin_fsl *new = NULL;
+	int retval = 0;
+	u_int i;
+
+	for (i = 0; i < results.fsl.fsl_len && retval == 0; i++)
+		retval = admin_add_fsl(results.fsl.fsl_val[i], &new);
+
+	*fsls = new;
+	return retval;
+}
+
+/**
+ * Create a junction or replication on a remote fileserver
+ *
+ * @param host an initialized and opened admin_t
+ * @param procedure RPC procedure to call
+ * @param arg call arguments
+ * @return zero or an errno
+ */
+static int
+admin_create_rpc(admin_t host, rpcproc_t procedure, FedFsCreateArgs *arg)
+{
+	FedFsStatus result;
+	unsigned int delay;
+
+	delay = FEDFS_DELAY_MIN_SECS;
+	do {
+		xlog(D_CALL, "sending CREATE to %s",
+			admin_hostname(host));
+
+		memset((char *)&result, 0, sizeof(result));
+		host->ad_rpc_status = clnt_call(host->ad_client, procedure,
+				(xdrproc_t)xdr_FedFsCreateArgs,
+				(caddr_t)arg,
+				(xdrproc_t)xdr_FedFsStatus, (caddr_t)&result,
+				host->ad_timeout);
+
+		xlog(D_CALL, "RPC CREATE returned %d",
+			host->ad_rpc_status);
+
+		if (host->ad_rpc_status == RPC_AUTHERROR)
+			return EACCES;
+		if (host->ad_rpc_status != RPC_SUCCESS)
+			return EIO;
+
+		if (result != FEDFS_ERR_DELAY)
+			break;
+
+		(void)sleep(delay);
+		delay = fedfs_delay(delay);
+	} while (1);
+
+	host->ad_srv_status = result;
+	return 0;
+}
+
+/**
+ * Create a junction or replication on a remote fileserver
+ *
+ * @param host an initialized admin_t
+ * @param procedure RPC procedure to call
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param fsn FileSet Name to set
+ * @return zero or an errno
+ */
+static int
+admin_create_call(admin_t host, rpcproc_t procedure,
+		char * const *path_array, struct admin_fsn *fsn)
+{
+	FedFsCreateArgs arg;
+	int retval;
+	uuid_t uu;
+
+	if (host == NULL)
+		return EINVAL;
+	admin_reset(host);
+
+	if (path_array == NULL || fsn == NULL)
+		return EINVAL;
+
+	if (!admin_is_connected(host))
+		return ENOTCONN;
+
+	memset(&arg, 0, sizeof(arg));
+	if (uuid_parse(fsn->af_uuid, uu) != 0)
+		return EINVAL;
+	memcpy(arg.fsn.fsnUuid, uu, sizeof(FedFsUuid));
+	arg.fsn.nsdbName.hostname.utf8string_len =
+					strlen(fsn->af_nsdb.an_hostname);
+	arg.fsn.nsdbName.hostname.utf8string_val =
+					(char *)fsn->af_nsdb.an_hostname;
+	arg.fsn.nsdbName.port = fsn->af_nsdb.an_port;
+
+	arg.path.type = FEDFS_PATH_SYS;
+	if (nsdb_path_array_to_fedfspathname(path_array,
+				&arg.path.FedFsPath_u.adminPath) != FEDFS_OK)
+		return ENAMETOOLONG;
+
+	retval = admin_create_rpc(host, procedure, &arg);
+
+	nsdb_free_fedfspathname(&arg.path.FedFsPath_u.adminPath);
+	return retval;
+}
+
+/**
+ * Delete a junction or replication on a remote fileserver
+ *
+ * @param host an initialized and opened admin_t
+ * @param procedure RPC procedure to call
+ * @param arg call arguments
+ * @return zero or an errno
+ */
+static int
+admin_delete_rpc(admin_t host, rpcproc_t procedure, FedFsPath *arg)
+{
+	FedFsStatus result;
+	unsigned int delay;
+
+	delay = FEDFS_DELAY_MIN_SECS;
+	do {
+		xlog(D_CALL, "sending DELETE to %s",
+			admin_hostname(host));
+
+		memset((char *)&result, 0, sizeof(result));
+		host->ad_rpc_status = clnt_call(host->ad_client, procedure,
+				(xdrproc_t)xdr_FedFsPath, (caddr_t)&arg,
+				(xdrproc_t)xdr_FedFsStatus, (caddr_t)&result,
+				host->ad_timeout);
+
+		xlog(D_CALL, "RPC DELETE returned %d",
+			host->ad_rpc_status);
+
+		if (host->ad_rpc_status == RPC_AUTHERROR)
+			return EACCES;
+		if (host->ad_rpc_status != RPC_SUCCESS)
+			return EIO;
+
+		if (result != FEDFS_ERR_DELAY)
+			break;
+
+		(void)sleep(delay);
+		delay = fedfs_delay(delay);
+	} while (1);
+
+	host->ad_srv_status = result;
+	return 0;
+}
+
+/**
+ * Delete a junction or replication on a remote fileserver
+ *
+ * @param host an initialized admin_t
+ * @param procedure RPC procedure to call
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @return zero or an errno
+ */
+static int
+admin_delete_call(admin_t host, rpcproc_t procedure,
+		char * const *path_array)
+{
+	FedFsPath arg;
+	int retval;
+
+	if (host == NULL)
+		return EINVAL;
+	admin_reset(host);
+
+	if (path_array == NULL)
+		return EINVAL;
+
+	if (!admin_is_connected(host))
+		return ENOTCONN;
+
+	memset(&arg, 0, sizeof(arg));
+	if (nsdb_path_array_to_fedfspathname(path_array,
+					&arg.FedFsPath_u.adminPath) != FEDFS_OK)
+		return ENAMETOOLONG;
+
+	retval = admin_delete_rpc(host, procedure, &arg);
+
+	nsdb_free_fedfspathname(&arg.FedFsPath_u.adminPath);
+	return retval;
+}
+
+/**
+ * Request FSN stored in a remote junction
+ *
+ * @param host an initialized and opened admin_t
+ * @param procedure RPC procedure to call
+ * @param arg call arguments
+ * @param result OUT: call results (filled-in)
+ * @return zero or an errno
+ */
+static int
+admin_lookup_rpc(admin_t host, rpcproc_t procedure,
+		FedFsLookupArgs *arg, FedFsLookupRes *result)
+{
+	unsigned int delay;
+
+	delay = FEDFS_DELAY_MIN_SECS;
+	do {
+		xlog(D_CALL, "sending LOOKUP to %s",
+			admin_hostname(host));
+
+		memset((char *)result, 0, sizeof(*result));
+		host->ad_rpc_status = clnt_call(host->ad_client,
+				procedure,
+				(xdrproc_t)xdr_FedFsLookupArgs, (caddr_t)&arg,
+				(xdrproc_t)xdr_FedFsLookupRes, (caddr_t)result,
+				host->ad_timeout);
+
+		xlog(D_CALL, "RPC DELETE returned %d",
+			host->ad_rpc_status);
+
+		if (host->ad_rpc_status == RPC_AUTHERROR)
+			return EACCES;
+		if (host->ad_rpc_status != RPC_SUCCESS)
+			return EIO;
+
+		if (result->status != FEDFS_ERR_DELAY)
+			break;
+
+		clnt_freeres(host->ad_client,
+			(xdrproc_t)xdr_FedFsLookupRes, (caddr_t)result);
+		(void)sleep(delay);
+		delay = fedfs_delay(delay);
+	} while (1);
+
+	host->ad_srv_status = result->status;
+	return 0;
+}
+
+/**
+ * Request FSN stored in a remote junction
+ *
+ * @param host an initialized admin_t
+ * @param procedure RPC procedure to call
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param fsn OUT: the FSN stored in the requested junction
+ * @return zero or an errno
+ *
+ * The caller must free "fsn" with admin_free_fsn().
+ */
+static int
+admin_lookup_none_call(admin_t host, rpcproc_t procedure,
+		char * const *path_array, struct admin_fsn **fsn)
+{
+	FedFsLookupRes result;
+	FedFsLookupArgs arg;
+	int retval;
+
+	if (host == NULL)
+		return EINVAL;
+	admin_reset(host);
+
+	if (path_array == NULL || fsn == NULL)
+		return EINVAL;
+
+	if (!admin_is_connected(host))
+		return ENOTCONN;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.resolve = FEDFS_RESOLVE_NONE;
+	arg.path.type = FEDFS_PATH_SYS;
+	if (nsdb_path_array_to_fedfspathname(path_array,
+				&arg.path.FedFsPath_u.adminPath) != FEDFS_OK)
+		return ENAMETOOLONG;
+
+	retval = admin_lookup_rpc(host, procedure, &arg, &result);
+
+	nsdb_free_fedfspathname(&arg.path.FedFsPath_u.adminPath);
+
+	if (retval == 0 && result.status == FEDFS_OK)
+		retval = admin_set_fsn(result.FedFsLookupRes_u.resok.fsn, fsn);
+
+	clnt_freeres(host->ad_client,
+			(xdrproc_t)xdr_FedFsLookupRes, (caddr_t)&result);
+	return retval;
+}
+
+/**
+ * Request FSN stored in a remote junction and FSLs that FSN resolves to
+ *
+ * @param host an initialized admin_t
+ * @param procedure RPC procedure to call
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param type whether to perturb the server's FSL cache
+ * @param fsn OUT: the FSN stored in the requested junction
+ * @param fsls OUT: list of FSLs cached on the fileserver
+ * @return zero or an errno
+ *
+ * The caller must free "fsn" with admin_free_fsn().  The caller must
+ * free "fsls" with admin_free_fsls().
+ */
+static int
+admin_lookup_typed_call(admin_t host, rpcproc_t procedure,
+		char * const *path_array, FedFsResolveType type,
+		struct admin_fsn **fsn, struct admin_fsl **fsls)
+{
+	struct admin_fsn *tmp_fsn;
+	struct admin_fsl *tmp_fsls;
+	FedFsLookupRes result;
+	FedFsLookupArgs arg;
+	int retval;
+
+	if (host == NULL)
+		return EINVAL;
+	admin_reset(host);
+
+	if (path_array == NULL || fsn == NULL || fsls == NULL)
+		return EINVAL;
+
+	if (!admin_is_connected(host))
+		return ENOTCONN;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.resolve = type;
+	arg.path.type = FEDFS_PATH_SYS;
+	if (nsdb_path_array_to_fedfspathname(path_array,
+				&arg.path.FedFsPath_u.adminPath) != FEDFS_OK)
+		return ENAMETOOLONG;
+
+	retval = admin_lookup_rpc(host, procedure, &arg, &result);
+
+	nsdb_free_fedfspathname(&arg.path.FedFsPath_u.adminPath);
+
+	if (retval != 0)
+		goto out;
+
+	switch (result.status) {
+	case FEDFS_OK:
+		retval = admin_set_fsn(result.FedFsLookupRes_u.resok.fsn, &tmp_fsn);
+		if (retval != 0)
+			break;
+
+		retval = admin_set_fsls(result.FedFsLookupRes_u.resok, &tmp_fsls);
+		if (retval != 0) {
+			admin_free_fsn(tmp_fsn);
+			break;
+		}
+
+		*fsn = tmp_fsn;
+		*fsls = tmp_fsls;
+		break;
+	case FEDFS_ERR_NSDB_LDAP_VAL:
+		host->ad_ldaperr = result.FedFsLookupRes_u.ldapResultCode;
+		break;
+	default:
+		break;
+	}
+
+out:
+	clnt_freeres(host->ad_client,
+			(xdrproc_t)xdr_FedFsLookupRes, (caddr_t)&result);
+	return retval;
+}
+
+/**
+ * FEDFS_CREATE_JUNCTION (5.2) - Create a junction on a remote fileserver
+ *
+ * @param host an initialized admin_t
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param fsn FileSet Name to set
+ * @return zero or an errno
+ */
+int
+admin_create_junction(admin_t host, char * const *path_array,
+		struct admin_fsn *fsn)
+{
+	return admin_create_call(host, FEDFS_CREATE_JUNCTION,
+					path_array, fsn);
+}
+
+/**
+ * FEDFS_DELETE_JUNCTION (5.3) - Delete a junction on a remote fileserver
+ *
+ * @param host an initialized admin_t
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @return zero or an errno
+ */
+int
+admin_delete_junction(admin_t host, char * const *path_array)
+{
+	return admin_delete_call(host, FEDFS_DELETE_JUNCTION,
+					path_array);
+}
+
+/**
+ * FEDFS_LOOKUP_JUNCTION (5.4) - Request FSN stored in a remote junction
+ *
+ * @param host an initialized admin_t
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param fsn OUT: the FSN stored in the requested junction
+ * @return zero or an errno
+ *
+ * The caller must free "fsn" with admin_free_fsn().
+ */
+int
+admin_lookup_junction_none(admin_t host, char * const *path_array,
+		struct admin_fsn **fsn)
+{
+	return admin_lookup_none_call(host, FEDFS_LOOKUP_JUNCTION,
+					path_array, fsn);
+}
+
+/**
+ * FEDFS_LOOKUP_JUNCTION (5.4) - Request FSLs cached by remote server
+ *
+ * @param host an initialized admin_t
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param fsn OUT: the FSN stored in the requested junction
+ * @param fsls OUT: list of FSLs cached on the fileserver
+ * @return zero or an errno
+ *
+ * The fileserver does not perform FSN resolution unless it hasn't
+ * already done so.
+ *
+ * The caller must free "fsn" with admin_free_fsn().  The caller must
+ * free "fsls" with admin_free_fsls().
+ */
+int
+admin_lookup_junction_cached(admin_t host, char * const *path_array,
+		struct admin_fsn **fsn, struct admin_fsl **fsls)
+{
+	return admin_lookup_typed_call(host, FEDFS_LOOKUP_JUNCTION,
+					path_array, FEDFS_RESOLVE_CACHE,
+					fsn, fsls);
+}
+
+/**
+ * FEDFS_LOOKUP_JUNCTION (5.4) - Request FSLs from NSDB via remote server
+ *
+ * @param host an initialized admin_t
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param fsn OUT: the FSN stored in the requested junction
+ * @param fsls OUT: list of FSLs cached on the fileserver
+ * @return zero or an errno
+ *
+ * The fileserver performs a fresh FSN resolution and renews its
+ * FSL cache before returning.
+ *
+ * The caller must free "fsn" with admin_free_fsn().  The caller must
+ * free "fsls" with admin_free_fsls().
+ */
+int
+admin_lookup_junction_nsdb(admin_t host, char * const *path_array,
+		struct admin_fsn **fsn, struct admin_fsl **fsls)
+{
+	return admin_lookup_typed_call(host, FEDFS_LOOKUP_JUNCTION,
+					path_array, FEDFS_RESOLVE_NSDB,
+					fsn, fsls);
+}
+
+/**
+ * FEDFS_CREATE_REPLICATION (5.5)
+ *
+ * @param host an initialized admin_t
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param fsn FileSet Name to set
+ * @return zero or an errno
+ */
+int
+admin_create_replication(admin_t host, char * const*path_array,
+		struct admin_fsn *fsn)
+{
+	return admin_create_call(host, FEDFS_CREATE_REPLICATION,
+					path_array, fsn);
+}
+
+/**
+ * FEDFS_DELETE_REPLICATION (5.6)
+ *
+ * @param host an initialized admin_t
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @return zero or an errno
+ */
+int
+admin_delete_replication(admin_t host, char * const*path_array)
+{
+	return admin_delete_call(host, FEDFS_DELETE_REPLICATION,
+					path_array);
+}
+
+/**
+ * FEDFS_LOOKUP_REPLICATION (5.7) - Request FSN stored in a remote replication
+ *
+ * @param host an initialized admin_t
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param fsn OUT: the FSN stored in the requested junction
+ * @return zero or an errno
+ *
+ * The caller must free "fsn" with admin_free_fsn().
+ */
+int
+admin_lookup_replication_none(admin_t host, char * const*path_array,
+		struct admin_fsn **fsn)
+{
+	return admin_lookup_none_call(host, FEDFS_LOOKUP_REPLICATION,
+					path_array, fsn);
+}
+
+/**
+ * FEDFS_LOOKUP_REPLICATION (5.7) - Request FSLs cached by remote server
+ *
+ * @param host an initialized admin_t
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param fsn OUT: the FSN stored in the requested junction
+ * @param fsls OUT: list of FSLs cached on the fileserver
+ * @return zero or an errno
+ *
+ * The fileserver does not perform FSN resolution unless it hasn't
+ * already done so.
+ *
+ * The caller must free "fsn" with admin_free_fsn().  The caller must
+ * free "fsls" with admin_free_fsls().
+ */
+int
+admin_lookup_replication_cached(admin_t host, char * const*path_array,
+		struct admin_fsn **fsn, struct admin_fsl **fsls)
+{
+	return admin_lookup_typed_call(host, FEDFS_LOOKUP_REPLICATION,
+					path_array, FEDFS_RESOLVE_CACHE,
+					fsn, fsls);
+}
+
+/**
+ * FEDFS_LOOKUP_REPLICATION (5.7)
+ *
+ * @param host an initialized admin_t
+ * @param path_array an array of NUL-terminated C strings containing pathname components
+ * @param fsn OUT: the FSN stored in the requested junction
+ * @param fsls OUT: list of FSLs cached on the fileserver
+ * @return zero or an errno
+ *
+ * The fileserver performs a fresh FSN resolution and renews its
+ * FSL cache before returning.
+ *
+ * The caller must free "fsn" with admin_free_fsn().  The caller must
+ * free "fsls" with admin_free_fsls().
+ */
+int
+admin_lookup_replication_nsdb(admin_t host, char * const*path_array,
+		struct admin_fsn **fsn, struct admin_fsl **fsls)
+{
+	return admin_lookup_typed_call(host, FEDFS_LOOKUP_REPLICATION,
+					path_array, FEDFS_RESOLVE_NSDB,
+					fsn, fsls);
+}
diff --git a/src/libadmin/nsdb.c b/src/libadmin/nsdb.c
new file mode 100644
index 0000000..b038ce4
--- /dev/null
+++ b/src/libadmin/nsdb.c
@@ -0,0 +1,407 @@ 
+/**
+ * @file src/libadmin/nsdb.c
+ * @brief Handle NSDB-related ADMIN RPC operations
+ */
+
+/*
+ * Copyright 2013 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <uuid/uuid.h>
+
+#include "fedfs_admin.h"
+#include "fedfs.h"
+#include "admin-internal.h"
+#include "admin.h"
+#include "xlog.h"
+
+/**
+ * Set up a FedFsNsdbName argument
+ *
+ * @param nsdb NSDB hostname and port
+ * @param arg OUT: FedFsNsdbName (filled-in)
+ */
+static void
+admin_set_nsdb_arg(const struct admin_nsdb *nsdb, FedFsNsdbName *arg)
+{
+	arg->hostname.utf8string_len = strlen(nsdb->an_hostname);
+	arg->hostname.utf8string_val = (char *)nsdb->an_hostname;
+	arg->port = nsdb->an_port;
+}
+
+/**
+ * Allocate a structure for carrying an x.509 certificate
+ *
+ * @return a freshly allocated admin_cert, or NULL
+ */
+__attribute_malloc__
+static struct admin_cert *
+admin_new_cert(void)
+{
+	return calloc(1, sizeof(struct admin_cert));
+}
+
+/**
+ * Free an admin_cert
+ *
+ * @param cert admin_cert to free
+ */
+void
+admin_free_cert(struct admin_cert *cert)
+{
+	if (cert == NULL)
+		return;
+
+	free((void *)cert->ac_data);
+	free(cert);
+}
+
+/**
+ * Dig returned certificate out of results
+ *
+ * @param result RPC results
+ * @param sectype OUT: connection security for specified NSDB
+ * @param cert OUT: returned x.509 certificate data in DER format
+ * @return zero or an errno
+ *
+ * Caller must free "cert" with admin_free_cert().
+ */
+static int
+admin_set_nsdb_secdata(FedFsGetNsdbParamsRes result,
+		FedFsConnectionSec *sectype, struct admin_cert **cert)
+{
+	FedFsNsdbParams *params = &result.FedFsGetNsdbParamsRes_u.params;
+	unsigned int len = params->FedFsNsdbParams_u.secData.secData_len;
+	char *buf = params->FedFsNsdbParams_u.secData.secData_val;
+	struct admin_cert *new;
+
+	new = admin_new_cert();
+	if (new == NULL)
+		return ENOMEM;
+
+	new->ac_data = malloc(len);
+	if (new->ac_data == NULL) {
+		free(new);
+		return ENOMEM;
+	}
+
+	memcpy((void *)new->ac_data, buf, len);
+	new->ac_len = len;
+	*cert = new;
+	*sectype = params->secType;
+	return 0;
+}
+
+/**
+ * Set NSDB connection parameters on a remote fileserver
+ *
+ * @param host an initialized and opened admin_t
+ * @param arg call arguments
+ * @return zero or an errno
+ */
+static int
+admin_set_nsdb_params_rpc(admin_t host, FedFsSetNsdbParamsArgs *arg)
+{
+	FedFsStatus result;
+	unsigned int delay;
+
+	delay = FEDFS_DELAY_MIN_SECS;
+	do {
+		xlog(D_CALL, "sending SET_NSDB_PARAMS to %s",
+			admin_hostname(host));
+
+		memset((char *)&result, 0, sizeof(result));
+		host->ad_rpc_status = clnt_call(host->ad_client,
+			FEDFS_SET_NSDB_PARAMS,
+			(xdrproc_t)xdr_FedFsSetNsdbParamsArgs, (caddr_t)arg,
+			(xdrproc_t)xdr_FedFsStatus, (caddr_t)&result,
+			host->ad_timeout);
+
+		xlog(D_CALL, "RPC SET_NSDB_PARAMS returned %d",
+			host->ad_rpc_status);
+
+		if (host->ad_rpc_status == RPC_AUTHERROR)
+			return EACCES;
+		if (host->ad_rpc_status != RPC_SUCCESS)
+			return EIO;
+
+		if (result != FEDFS_ERR_DELAY)
+			break;
+
+		(void)sleep(delay);
+		delay = fedfs_delay(delay);
+	} while (1);
+
+	host->ad_srv_status = result;
+	return 0;
+}
+
+/**
+ * FEDFS_SET_NSDB_PARAMS (5.8) - Set NSDB connection parameters on a remote fileserver
+ *
+ * @param host an initialized admin_t
+ * @param nsdb hostname and port of an NSDB service
+ * @return zero or an errno
+ */
+int
+admin_set_nsdb_params_none(admin_t host, const struct admin_nsdb *nsdb)
+{
+	FedFsSetNsdbParamsArgs arg;
+
+	if (host == NULL)
+		return EINVAL;
+	admin_reset(host);
+
+	if (nsdb == NULL)
+		return EINVAL;
+
+	if (!admin_is_connected(host))
+		return ENOTCONN;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.params.secType = FEDFS_SEC_NONE;
+	admin_set_nsdb_arg(nsdb, &arg.nsdbName);
+
+	return admin_set_nsdb_params_rpc(host, &arg);
+}
+
+/**
+ * FEDFS_SET_NSDB_PARAMS (5.8) - Set NSDB connection parameters on a remote fileserver
+ *
+ * @param host an initialized admin_t
+ * @param nsdb hostname and port of an NSDB service
+ * @param cert x.509 certificate data in DER format
+ * @return zero or an errno
+ */
+int
+admin_set_nsdb_params_tls(admin_t host, const struct admin_nsdb *nsdb,
+		const struct admin_cert *cert)
+{
+	FedFsSetNsdbParamsArgs arg;
+	int retval;
+
+	if (host == NULL)
+		return EINVAL;
+	admin_reset(host);
+
+	if (nsdb == NULL || cert == NULL)
+		return EINVAL;
+
+	if (!admin_is_connected(host))
+		return ENOTCONN;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.params.secType = FEDFS_SEC_TLS;
+	arg.params.FedFsNsdbParams_u.secData.secData_len = cert->ac_len;
+	arg.params.FedFsNsdbParams_u.secData.secData_val =
+							(char *)cert->ac_data;
+	admin_set_nsdb_arg(nsdb, &arg.nsdbName);
+
+	retval = admin_set_nsdb_params_rpc(host, &arg);
+
+	free(arg.params.FedFsNsdbParams_u.secData.secData_val);
+	return retval;
+}
+
+/**
+ * Retrieve NSDB connection parameters
+ *
+ * @param host an initialized and opened admin_t
+ * @param arg call arguments
+ * @param result call result (filled-in)
+ * @return zero or an errno
+ */
+static int
+admin_get_nsdb_params_rpc(admin_t host, FedFsNsdbName *arg,
+		FedFsGetNsdbParamsRes *result)
+{
+	unsigned int delay;
+
+	delay = FEDFS_DELAY_MIN_SECS;
+	do {
+		xlog(D_CALL, "sending GET_NSDB_PARAMS to %s",
+			admin_hostname(host));
+
+		memset((char *)result, 0, sizeof(*result));
+		host->ad_rpc_status = clnt_call(host->ad_client,
+			FEDFS_GET_NSDB_PARAMS,
+			(xdrproc_t)xdr_FedFsNsdbName, (caddr_t)&arg,
+			(xdrproc_t)xdr_FedFsGetNsdbParamsRes, (caddr_t)&result,
+			host->ad_timeout);
+
+		xlog(D_CALL, "RPC GET_NSDB_PARAMS returned %d",
+			host->ad_rpc_status);
+
+		if (host->ad_rpc_status == RPC_AUTHERROR)
+			return EACCES;
+		if (host->ad_rpc_status != RPC_SUCCESS)
+			return EIO;
+
+		if (result->status != FEDFS_ERR_DELAY)
+			break;
+
+		clnt_freeres(host->ad_client,
+				(xdrproc_t)xdr_FedFsGetNsdbParamsRes,
+				(caddr_t)result);
+		(void)sleep(delay);
+		delay = fedfs_delay(delay);
+	} while (1);
+
+	host->ad_srv_status = result->status;
+	return 0;
+}
+
+/**
+ * FEDFS_GET_NSDB_PARAMS (5.9)
+ *
+ * @param host an initialized admin_t
+ * @param nsdb hostname and port of an NSDB service
+ * @param sectype OUT: connection security for specified NSDB
+ * @param cert OUT: returned x.509 certificate data in DER format
+ * @return zero or an errno
+ *
+ * Caller must free "cert" with admin_free_cert().
+ */
+int
+admin_get_nsdb_params(admin_t host, struct admin_nsdb *nsdb,
+		FedFsConnectionSec *sectype, struct admin_cert **cert)
+{
+	FedFsGetNsdbParamsRes result;
+	FedFsNsdbName arg;
+	int retval;
+
+	if (host == NULL)
+		return EINVAL;
+	admin_reset(host);
+
+	if (nsdb == NULL || sectype == NULL || cert == NULL)
+		return EINVAL;
+
+	if (!admin_is_connected(host))
+		return ENOTCONN;
+
+	memset(&arg, 0, sizeof(arg));
+	admin_set_nsdb_arg(nsdb, &arg);
+
+	retval = admin_get_nsdb_params_rpc(host, &arg, &result);
+
+	if (retval == 0 && result.status == FEDFS_OK)
+		retval = admin_set_nsdb_secdata(result, sectype, cert);
+
+	clnt_freeres(host->ad_client, (xdrproc_t)xdr_FedFsGetNsdbParamsRes,
+							(caddr_t)&result);
+	return retval;
+}
+
+/**
+ * Retrieve limited NSDB connection parameters
+ *
+ * @param host an initialized and opened admin_t
+ * @param arg call arguments
+ * @param result call result (filled-in)
+ * @return zero or an errno
+ */
+static int
+admin_get_limited_nsdb_params_rpc(admin_t host, FedFsNsdbName *arg,
+		FedFsGetLimitedNsdbParamsRes *result)
+{
+	unsigned int delay;
+
+	delay = FEDFS_DELAY_MIN_SECS;
+	do {
+		xlog(D_CALL, "sending GET_LIMITED_NSDB_PARAMS to %s",
+			admin_hostname(host));
+
+		memset((char *)result, 0, sizeof(*result));
+		host->ad_rpc_status = clnt_call(host->ad_client,
+			FEDFS_GET_LIMITED_NSDB_PARAMS,
+			(xdrproc_t)xdr_FedFsNsdbName, (caddr_t)arg,
+			(xdrproc_t)xdr_FedFsGetLimitedNsdbParamsRes,
+			(caddr_t)result, host->ad_timeout);
+
+		xlog(D_CALL, "RPC GET_LIMITED_NSDB_PARAMS returned %d",
+			host->ad_rpc_status);
+
+		if (host->ad_rpc_status == RPC_AUTHERROR)
+			return EACCES;
+		if (host->ad_rpc_status != RPC_SUCCESS)
+			return EIO;
+
+		if (result->status != FEDFS_ERR_DELAY)
+			break;
+
+		clnt_freeres(host->ad_client,
+				(xdrproc_t)xdr_FedFsGetLimitedNsdbParamsRes,
+				(caddr_t)result);
+		(void)sleep(delay);
+		delay = fedfs_delay(delay);
+	} while (1);
+
+	host->ad_srv_status = result->status;
+	return 0;
+}
+
+/**
+ * FEDFS_GET_LIMITED_NSDB_PARAMS (5.10)
+ *
+ * @param host an initialized admin_t
+ * @param nsdb hostname and port of an NSDB service
+ * @param sectype OUT: connection security for specified NSDB
+ * @return zero or an errno
+ */
+int
+admin_get_limited_nsdb_params(admin_t host, struct admin_nsdb *nsdb,
+		FedFsConnectionSec *sectype)
+{
+	FedFsGetLimitedNsdbParamsRes result;
+	FedFsNsdbName arg;
+	int retval;
+
+	if (host == NULL)
+		return EINVAL;
+	admin_reset(host);
+
+	if (nsdb == NULL || sectype == NULL)
+		return EINVAL;
+
+	if (!admin_is_connected(host))
+		return ENOTCONN;
+
+	memset(&arg, 0, sizeof(arg));
+	admin_set_nsdb_arg(nsdb, &arg);
+
+	retval = admin_get_limited_nsdb_params_rpc(host, &arg, &result);
+
+	if (retval == 0 && result.status == FEDFS_OK)
+		*sectype = result.FedFsGetLimitedNsdbParamsRes_u.secType;
+
+	clnt_freeres(host->ad_client,
+			(xdrproc_t)xdr_FedFsGetLimitedNsdbParamsRes,
+			(caddr_t)&result);
+	return retval;
+}
diff --git a/src/libadmin/null.c b/src/libadmin/null.c
new file mode 100644
index 0000000..1d51a28
--- /dev/null
+++ b/src/libadmin/null.c
@@ -0,0 +1,74 @@ 
+/**
+ * @file src/libadmin/null.c
+ * @brief Handle NULL ADMIN RPC operation
+ */
+
+/*
+ * Copyright 2013 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "fedfs_admin.h"
+#include "admin-internal.h"
+#include "xlog.h"
+
+/**
+ * FEDFS_NULL (5.1) - Send a NULL ADMIN request (ping) to a remote fileserver
+ *
+ * @param host an initialized admin_t
+ * @return zero or an errno
+ *
+ * The RPC procedure does not return a result.  We fill in a
+ * status of FEDFS_OK if the RPC succeeds.
+ */
+int
+admin_null(admin_t host)
+{
+	char result;
+
+	if (host == NULL)
+		return EINVAL;
+	admin_reset(host);
+
+	if (!admin_is_connected(host))
+		return ENOTCONN;
+
+	xlog(D_CALL, "sending NULL to %s",
+		admin_hostname(host));
+
+	memset((char *)&result, 0, sizeof(result));
+	host->ad_rpc_status = clnt_call(host->ad_client,
+			FEDFS_NULL,
+			(xdrproc_t)xdr_void, (caddr_t)NULL,
+			(xdrproc_t)xdr_void, (caddr_t)&result,
+			host->ad_timeout);
+
+	xlog(D_CALL, "RPC NULL returned %d",
+		host->ad_rpc_status);
+
+	if (host->ad_rpc_status == RPC_AUTHERROR)
+		return EACCES;
+	if (host->ad_rpc_status != RPC_SUCCESS)
+		return EIO;
+
+	host->ad_srv_status = FEDFS_OK;
+	return 0;
+}