diff --git a/Makefile.am b/Makefile.am
index 8964b37..bc5e517 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -57,9 +57,8 @@ endif
 
 if CONFIG_CIFSACL
 bin_PROGRAMS += getcifsacl
-getcifsacl_SOURCES = getcifsacl.c
-getcifsacl_LDADD = $(WBCLIENT_LIBS)
-getcifsacl_CFLAGS = $(WBCLIENT_CFLAGS)
+getcifsacl_SOURCES = getcifsacl.c idmap_plugin.c
+getcifsacl_LDADD = -ldl
 man_MANS += getcifsacl.1
 
 bin_PROGRAMS += setcifsacl
@@ -69,4 +68,12 @@ setcifsacl_CFLAGS = $(WBCLIENT_CFLAGS)
 man_MANS += setcifsacl.1
 endif
 
+if CONFIG_PLUGIN
+plugindir = $(pkglibdir)
+plugin_PROGRAMS = idmapwb.so
+
+idmapwb.so: idmapwb.c
+	$(CC) $(CFLAGS) $(AM_CFLAGS) $(WBCLIENT_CFLAGS) $(LDFLAGS) -shared -fpic -o $@ $+ $(WBCLIENT_LIBS)
+endif
+
 SUBDIRS = contrib
diff --git a/cifsidmap.h b/cifsidmap.h
index 9907618..c307333 100644
--- a/cifsidmap.h
+++ b/cifsidmap.h
@@ -34,4 +34,49 @@ struct cifs_sid {
 	uint32_t sub_auth[SID_MAX_SUB_AUTHORITIES];
 } __attribute__((packed));
 
+/* Plugins should implement the following functions: */
+
+/**
+ * cifs_idmap_init_plugin - Initialize the plugin interface
+ * @handle - return pointer for an opaque handle
+ * @errmsg - pointer to error message pointer
+ *
+ * This function should do whatever is required to establish a context
+ * for later ID mapping operations. The "handle" is an opaque context
+ * cookie that will be passed in on subsequent ID mapping operations.
+ * The errmsg is used to pass back an error string both during the init
+ * and in subsequent idmapping functions. On any error, the plugin
+ * should point *errmsg at a string describing that error. Returns 0
+ * on success and non-zero on error.
+ *
+ * int cifs_idmap_init_plugin(void **handle, const char **errmsg);
+ */
+
+/**
+ * cifs_idmap_exit_plugin - Destroy an idmapping context
+ * @handle - context handle that should be destroyed
+ *
+ * When programs are finished with the idmapping plugin, they'll call
+ * this function to destroy any context that was created during the
+ * init_plugin. The handle passed back in was the one given by the init
+ * routine.
+ *
+ * void cifs_idmap_exit_plugin(void *handle);
+ */
+
+/**
+ * cifs_idmap_sid_to_str - convert cifs_sid to a string
+ * @handle - context handle
+ * @sid    - pointer to a cifs_sid
+ * @name   - return pointer for the name
+ *
+ * This function should convert the given cifs_sid to a string
+ * representation in a heap-allocated buffer. The caller of this
+ * function is expected to free "name" on success. Returns 0 on
+ * success and non-zero on error.
+ *
+ * int cifs_idmap_sid_to_str(void *handle, const struct cifs_sid *sid,
+ * 				char **name);
+ */
+
 #endif /* _CIFSIDMAP_H */
diff --git a/configure.ac b/configure.ac
index b6791ab..9652ad2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,6 +52,14 @@ AC_ARG_ENABLE(systemd,
 	enable_systemd=$enableval,
 	enable_systemd="maybe")
 
+# "with" options
+AC_ARG_WITH(idmap-plugin,
+	[AC_HELP_STRING([--with-idmap-plugin=/path/to/plugin],
+			[Define the path to the plugin that the idmapping infrastructure should use @<:@default=/etc/cifs-utils/idmap-plugin@:>@])],
+	pluginpath=$withval,
+	pluginpath="/etc/cifs-utils/idmap-plugin")
+AC_DEFINE_UNQUOTED(IDMAP_PLUGIN_PATH, "$pluginpath", [Location of plugin that ID mapping infrastructure should use. (usually a symlink to real plugin)])
+
 # check for ROOTSBINDIR environment var
 if test -z $ROOTSBINDIR; then
 	ROOTSBINDIR="/sbin"
@@ -230,6 +238,7 @@ AM_CONDITIONAL(CONFIG_CIFSUPCALL, [test "$enable_cifsupcall" != "no"])
 AM_CONDITIONAL(CONFIG_CIFSCREDS, [test "$enable_cifscreds" != "no"])
 AM_CONDITIONAL(CONFIG_CIFSIDMAP, [test "$enable_cifsidmap" != "no"])
 AM_CONDITIONAL(CONFIG_CIFSACL, [test "$enable_cifsacl" != "no"])
+AM_CONDITIONAL(CONFIG_PLUGIN, [test "$enable_cifsidmap" != "no" -o "$enable_cifsacl" != "no"])
 
 LIBCAP_NG_PATH
 
diff --git a/getcifsacl.c b/getcifsacl.c
index 550429c..c0b051d 100644
--- a/getcifsacl.c
+++ b/getcifsacl.c
@@ -33,10 +33,13 @@
 #include <stddef.h>
 #include <errno.h>
 #include <limits.h>
-#include <wbclient.h>
 #include <ctype.h>
 #include <sys/xattr.h>
 #include "cifsacl.h"
+#include "idmap_plugin.h"
+
+static void *plugin_handle;
+static bool plugin_loaded;
 
 static void
 print_each_ace_mask(uint32_t mask)
@@ -169,61 +172,33 @@ print_ace_type(uint8_t acetype, int raw)
 	}
 }
 
-/*
- * Winbind keeps wbcDomainSid fields in host-endian. Copy fields from the
- * csid to the wsid, while converting the subauthority fields from LE.
- */
 static void
-csid_to_wsid(struct wbcDomainSid *wsid, const struct cifs_sid *csid)
+print_sid(struct cifs_sid *csid, int raw)
 {
-	int i;
-	uint8_t num_subauth = (csid->num_subauth <= WBC_MAXSUBAUTHS) ?
-				csid->num_subauth : WBC_MAXSUBAUTHS;
-
-	wsid->sid_rev_num = csid->revision;
-	wsid->num_auths = num_subauth;
-	for (i = 0; i < NUM_AUTHS; i++)
-		wsid->id_auth[i] = csid->authority[i];
-	for (i = 0; i < num_subauth; i++)
-		wsid->sub_auths[i] = le32toh(csid->sub_auth[i]);
-}
-
-static void
-print_sid(struct cifs_sid *sidptr, int raw)
-{
-	int i;
-	wbcErr rc;
-	char *domain_name = NULL;
-	char *sidname = NULL;
-	enum wbcSidType sntype;
+	int i, rc;
+	char *name;
 	unsigned long long id_auth_val;
-	struct wbcDomainSid wsid;
 
-	csid_to_wsid(&wsid, sidptr);
+	if (raw || !plugin_loaded)
+		goto print_sid_raw;
 
-	if (raw)
+	rc = sid_to_str(plugin_handle, csid, &name);
+	if (rc)
 		goto print_sid_raw;
 
-	rc = wbcLookupSid(&wsid, &domain_name, &sidname, &sntype);
-	if (WBC_ERROR_IS_OK(rc)) {
-		printf("%s", domain_name);
-		if (strlen(domain_name))
-			printf("%c", '\\');
-		printf("%s", sidname);
-		wbcFreeMemory(domain_name);
-		wbcFreeMemory(sidname);
-		return;
-	}
+	printf("%s", name);
+	free(name);
+	return;
 
 print_sid_raw:
-	printf("S-%hhu", wsid.sid_rev_num);
+	printf("S-%hhu", csid->revision);
 
-	id_auth_val = (unsigned long long)wsid.id_auth[5];
-	id_auth_val += (unsigned long long)wsid.id_auth[4] << 8;
-	id_auth_val += (unsigned long long)wsid.id_auth[3] << 16;
-	id_auth_val += (unsigned long long)wsid.id_auth[2] << 24;
-	id_auth_val += (unsigned long long)wsid.id_auth[1] << 32;
-	id_auth_val += (unsigned long long)wsid.id_auth[0] << 48;
+	id_auth_val = (unsigned long long)csid->authority[5];
+	id_auth_val += (unsigned long long)csid->authority[4] << 8;
+	id_auth_val += (unsigned long long)csid->authority[3] << 16;
+	id_auth_val += (unsigned long long)csid->authority[2] << 24;
+	id_auth_val += (unsigned long long)csid->authority[1] << 32;
+	id_auth_val += (unsigned long long)csid->authority[0] << 48;
 
 	/*
 	 * MS-DTYP states that if the authority is >= 2^32, then it should be
@@ -234,8 +209,8 @@ print_sid_raw:
 	else
 		printf("-0x%llx", id_auth_val);
 
-	for (i = 0; i < wsid.num_auths; i++)
-		printf("-%u", wsid.sub_auths[i]);
+	for (i = 0; i < csid->num_subauth; i++)
+		printf("-%u", csid->sub_auth[i]);
 }
 
 static void
@@ -368,7 +343,8 @@ getcifsacl_usage(const char *prog)
 int
 main(const int argc, char *const argv[])
 {
-	int c, raw = 0;
+	int c, ret = 0;
+	bool raw = false;
 	ssize_t attrlen;
 	size_t bufsize = BUFSIZE;
 	char *filename, *attrval;
@@ -379,7 +355,7 @@ main(const int argc, char *const argv[])
 			printf("Version: %s\n", VERSION);
 			goto out;
 		case 'r':
-			raw = 1;
+			raw = true;
 			break;
 		default:
 			break;
@@ -392,20 +368,31 @@ main(const int argc, char *const argv[])
 		filename = argv[1];
 	else {
 		getcifsacl_usage(basename(argv[0]));
-		return 0;
+		goto out;
+	}
+
+	if (!raw && !plugin_loaded) {
+		ret = init_plugin(&plugin_handle);
+		if (ret)
+			printf("WARNING: unable to initialize idmapping plugin: %s\n",
+				plugin_errmsg);
+		else
+			plugin_loaded = true;
 	}
 
 cifsacl:
 	if (bufsize >= XATTR_SIZE_MAX) {
 		printf("buffer to allocate exceeds max size of %d\n",
 				XATTR_SIZE_MAX);
-		return -1;
+		ret = -1;
+		goto out;
 	}
 
 	attrval = malloc(bufsize * sizeof(char));
 	if (!attrval) {
 		printf("error allocating memory for attribute value buffer\n");
-		return -1;
+		ret = -1;
+		goto out;
 	}
 
 	attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
@@ -421,7 +408,8 @@ cifsacl:
 	parse_sec_desc((struct cifs_ntsd *)attrval, attrlen, raw);
 
 	free(attrval);
-
 out:
-	return 0;
+	if (plugin_loaded)
+		exit_plugin(plugin_handle);
+	return ret;
 }
diff --git a/idmap_plugin.c b/idmap_plugin.c
new file mode 100644
index 0000000..4dbeca7
--- /dev/null
+++ b/idmap_plugin.c
@@ -0,0 +1,99 @@
+/*
+ * ID Mapping Plugin interface for cifs-utils
+ * Copyright (C) 2012 Jeff Layton (jlayton@samba.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include "cifsacl.h"
+
+const char *plugin_errmsg;
+static void *plugin;
+
+static void *
+resolve_symbol(const char *symbol_name)
+{
+	void *symbol;
+
+	dlerror();
+	symbol = dlsym(plugin, symbol_name);
+	if (!symbol)
+		plugin_errmsg = dlerror();
+	return symbol;
+}
+
+/*
+ * open the plugin. Note that we leave it open over the life of the
+ * program. It gets closed on exit.
+ */
+static int
+open_plugin(void)
+{
+	if (plugin)
+		return 0;
+
+	plugin = dlopen(IDMAP_PLUGIN_PATH, RTLD_LAZY);
+	if (!plugin) {
+		plugin_errmsg = dlerror();
+		return -EIO;
+	}
+
+	return 0;
+}
+
+int
+init_plugin(void **handle)
+{
+	int ret;
+	int (*init)(void **, const char **);
+
+	ret = open_plugin();
+	if (ret)
+		return ret;
+
+	init = resolve_symbol("cifs_idmap_init_plugin");
+	if (!init)
+		return -ENOSYS;
+	return (*init)(handle, &plugin_errmsg);
+}
+
+void
+exit_plugin(void *handle)
+{
+	int (*exit)(void *);
+
+	exit = resolve_symbol("cifs_idmap_exit_plugin");
+	if (exit)
+		(*exit)(handle);
+}
+
+int
+sid_to_str(void *handle, const struct cifs_sid *sid, char **name)
+{
+	int (*entry)(void *, const struct cifs_sid *, char **);
+
+	*(void **)(&entry) = resolve_symbol("cifs_idmap_sid_to_str");
+	if (!entry)
+		return -ENOSYS;
+
+	return (*entry)(handle, sid, name);
+}
diff --git a/idmap_plugin.h b/idmap_plugin.h
new file mode 100644
index 0000000..277bb12
--- /dev/null
+++ b/idmap_plugin.h
@@ -0,0 +1,46 @@
+/*
+ * ID Mapping Plugin interface for cifs-utils
+ * Copyright (C) 2012 Jeff Layton (jlayton@samba.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cifsidmap.h"
+
+#ifndef _IDMAP_PLUGIN_H
+#define _IDMAP_PLUGIN_H
+
+/*
+ * On error, plugin functions will set this pointer to a string description
+ * of the error. The string should not be freed.
+ */
+extern const char *plugin_errmsg;
+
+/*
+ * External API. Programs should call this to use the plugin functionality.
+ */
+
+/*
+ * Initialize plugin. Returns an opaque handle that should be passed to
+ * other idmapping functions.
+ */
+extern int init_plugin(void **handle);
+
+/* Close out an init'ed handle */
+extern void exit_plugin(void *handle);
+
+/* Convert cifs_sid to a string. Caller must free *name on success */
+extern int sid_to_str(void *handle, const struct cifs_sid *sid, char **name);
+
+#endif /* _IDMAP_PLUGIN_H */
diff --git a/idmapwb.c b/idmapwb.c
new file mode 100644
index 0000000..858028f
--- /dev/null
+++ b/idmapwb.c
@@ -0,0 +1,115 @@
+/*
+ * Winbind ID Mapping Plugin
+ * Copyright (C) 2012 Jeff Layton (jlayton@samba.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdint.h>
+#include <endian.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wbclient.h>
+
+#include "cifsidmap.h"
+
+static const char **plugin_errmsg;
+
+/*
+ * Winbind keeps wbcDomainSid fields in host-endian. Copy fields from the
+ * csid to the wsid, while converting the subauthority fields from LE.
+ */
+static void
+csid_to_wsid(struct wbcDomainSid *wsid, const struct cifs_sid *csid)
+{
+	int i;
+	uint8_t num_subauth = (csid->num_subauth <= WBC_MAXSUBAUTHS) ?
+				csid->num_subauth : WBC_MAXSUBAUTHS;
+
+	wsid->sid_rev_num = csid->revision;
+	wsid->num_auths = num_subauth;
+	for (i = 0; i < NUM_AUTHS; i++)
+		wsid->id_auth[i] = csid->authority[i];
+	for (i = 0; i < num_subauth; i++)
+		wsid->sub_auths[i] = le32toh(csid->sub_auth[i]);
+}
+
+int
+cifs_idmap_sid_to_str(void *handle __attribute__ ((unused)),
+			const struct cifs_sid *csid, char **string)
+{
+	int rc;
+	wbcErr wbcrc;
+	char *domain = NULL;
+	char *name = NULL;
+	enum wbcSidType sntype;
+	struct wbcDomainSid wsid;
+	size_t len;
+
+	csid_to_wsid(&wsid, csid);
+
+	wbcrc = wbcLookupSid(&wsid, &domain, &name, &sntype);
+	if (!WBC_ERROR_IS_OK(wbcrc)) {
+		*plugin_errmsg = wbcErrorString(wbcrc);
+		return -EIO;
+	}
+
+	/* +1 for '\\' and +1 for NULL terminator */
+	len = strlen(domain) + 1 + strlen(name) + 1;
+
+	*string = malloc(len);
+	if (!*string) {
+		*plugin_errmsg = "Unable to allocate memory";
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	rc = snprintf(*string, len, "%s\\%s", domain, name);
+	if (rc >= (long)len) {
+		free(*string);
+		*plugin_errmsg = "Resulting string was truncated";
+		*string = NULL;
+		rc = -EIO;
+	} else {
+		rc = 0;
+	}
+out:
+	wbcFreeMemory(domain);
+	wbcFreeMemory(name);
+	return rc;
+}
+
+/*
+ * For the winbind plugin, we don't need to do anything special on
+ * init or exit
+ */
+int
+cifs_idmap_init_plugin(void **handle __attribute__((unused)), const char **errmsg)
+{
+	plugin_errmsg = errmsg;
+	return 0;
+}
+
+void
+cifs_idmap_exit_plugin(void *handle __attribute__((unused)))
+{
+	return;
+}
