]> granicus.if.org Git - sudo/commitdiff
Initial bits of non-unix group support using Quest Authentication Services
authorTodd C. Miller <Todd.Miller@courtesan.com>
Sun, 17 May 2009 22:19:38 +0000 (22:19 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Sun, 17 May 2009 22:19:38 +0000 (22:19 +0000)
LICENSE
Makefile.in
config.h.in
match.c
nonunix.h [new file with mode: 0644]
sudo.c
vasgroups.c [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
index 786b7a096eee5a953eaf9e9ad528c38d567fbce7..41bff79fed034fcd224e7a6affec366ac89bf432 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -48,3 +48,31 @@ bear the following UCB license:
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGE.
+
+nonunix.h and vasgroups.c bear the following license:
+
+   Copyright (c) 2006 Quest Software, Inc.  All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+
+   1. Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+   3. Neither the name of Quest Software, Inc. nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+   POSSIBILITY OF SUCH DAMAGE. 
index d892f8a8da681a007c17b1a322cb752531a6f399..12cea29096e2054d992902032ab0c1a083a578de 100644 (file)
@@ -109,8 +109,8 @@ SRCS = aix.c alias.c alloc.c audit.c bsm_audit.c check.c closefrom.c \
        mkstemp.c memrchr.c parse.c pwutil.c  set_perms.c sigaction.c \
        snprintf.c strcasecmp.c strerror.c strlcat.c strlcpy.c sudo.c \
        sudo_noexec.c sudo_edit.c sudo_nss.c term.c testsudoers.c tgetpass.c \
-       toke.c toke.l tsgetgrpw.c utimes.c visudo.c zero_bytes.c redblack.c \
-       selinux.c sesh.c $(AUTH_SRCS)
+       toke.c toke.l tsgetgrpw.c utimes.c vasgroups.c visudo.c zero_bytes.c \
+       redblack.c selinux.c sesh.c $(AUTH_SRCS)
 
 AUTH_SRCS = auth/afs.c auth/aix_auth.c auth/bsdauth.c auth/dce.c auth/fwtk.c \
            auth/kerb4.c auth/kerb5.c auth/pam.c auth/passwd.c auth/rfc1938.c \
@@ -119,9 +119,9 @@ AUTH_SRCS = auth/afs.c auth/aix_auth.c auth/bsdauth.c auth/dce.c auth/fwtk.c \
 
 HDRS = bsm_audit.h compat.h def_data.h defaults.h error.h ins_2001.h \
        ins_classic.h ins_csops.h ins_goons.h insults.h interfaces.h lbuf.h \
-       list.h logging.h parse.h sudo.h sudo_nss.h gram.h version.h \
-       auth/sudo_auth.h emul/charclass.h emul/fnmatch.h emul/glob.h \
-       emul/timespec.h emul/utime.h redblack.h
+       list.h logging.h nonunix.h redblack.h parse.h sudo.h sudo_nss.h gram.h \
+       version.h auth/sudo_auth.h emul/charclass.h emul/fnmatch.h emul/glob.h \
+       emul/timespec.h emul/utime.h
 
 AUTH_OBJS = sudo_auth.o @AUTH_OBJS@
 
@@ -321,6 +321,8 @@ tsgetgrpw.o: $(srcdir)/tsgetgrpw.c $(SUDODEP)
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/tsgetgrpw.c
 utimes.o: $(srcdir)/utimes.c $(srcdir)/compat.h $(srcdir)/emul/utime.h config.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/utimes.c
+vasgroups.o: $(srcdir)/vasgroups.c $(srcdir)/nonunix.h $(SUDODEP)
+       $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/vasgroups.c
 visudo.o: $(srcdir)/visudo.c $(SUDODEP) $(srcdir)/version.h $(devdir)/gram.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/visudo.c
 zero_bytes.o: $(srcdir)/zero_bytes.c $(srcdir)/compat.h config.h
index 9bf2c62fa8bda0297b4e9e27c49f862f530791f7..658c01544d93352e5c069fa023cb1cd5298dfb6d 100644 (file)
@@ -98,6 +98,9 @@
 /* Define to 1 if you have the `dispcrypt' function. */
 #undef HAVE_DISPCRYPT
 
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
 /* Define to 1 if your glob.h defines the GLOB_BRACE and GLOB_TILDE flags. */
 #undef HAVE_EXTENDED_GLOB
 
 /* The message given when a bad password is entered. */
 #undef INCORRECT_PASSWORD
 
+/* The name of libvas.so */
+#undef LIBVAS_SO
+
 /* The syslog facility sudo will use. */
 #undef LOGFAC
 
 /* Define to 1 if you want a different ticket file for each tty. */
 #undef USE_TTY_TICKETS
 
+/* Define to 1 if using a non-unix group lookup implementation. */
+#undef USING_NONUNIX_GROUPS
+
 /* Define to avoid using the passwd/shadow file for authentication. */
 #undef WITHOUT_PASSWD
 
diff --git a/match.c b/match.c
index 32f674e324bdb68d52cbac03901f5c4b0971e830..52350f03256c8870e0e3de19eb5337276377ecec 100644 (file)
--- a/match.c
+++ b/match.c
@@ -89,6 +89,9 @@
 #ifndef HAVE_EXTENDED_GLOB
 # include "emul/glob.h"
 #endif /* HAVE_EXTENDED_GLOB */
+#ifdef USING_NONUNIX_GROUPS
+# include "nonunix.h"
+#endif /* USING_NONUNIX_GROUPS */
 
 #ifndef lint
 __unused static const char rcsid[] = "$Sudo$";
@@ -825,6 +828,11 @@ usergr_matches(group, user, pw)
     if (*group++ != '%')
        return(FALSE);
 
+#ifdef USING_NONUNIX_GROUPS
+    if (*group == ':')
+       return(sudo_nonunix_groupcheck(++group, user, pw));   
+#endif /* USING_NONUNIX_GROUPS */
+
     /* look up user's primary gid in the passwd file */
     if (pw == NULL && (pw = sudo_getpwnam(user)) == NULL)
        return(FALSE);
diff --git a/nonunix.h b/nonunix.h
new file mode 100644 (file)
index 0000000..09de9d2
--- /dev/null
+++ b/nonunix.h
@@ -0,0 +1,46 @@
+/*
+ * (c) 2006 Quest Software, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ *   3. Neither the name of Quest Software, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef _NONUNIX_H
+#define _NONUNIX_H
+
+void
+sudo_nonunix_groupcheck_init(void);
+
+void
+sudo_nonunix_groupcheck_cleanup(void);
+
+int
+sudo_nonunix_groupcheck( const char* group, const char* user, const struct passwd* pwd );
+
+int
+sudo_nonunix_groupcheck_available(void);
+
+#endif /* _NONUNIX_H */
diff --git a/sudo.c b/sudo.c
index a3594b215e4c87b6c12725bdb0af77b0a4bb763f..565f96a893d1cd7cc66bf8d188c69c16f82b8289 100644 (file)
--- a/sudo.c
+++ b/sudo.c
 #include "interfaces.h"
 #include "version.h"
 
+#ifdef USING_NONUNIX_GROUPS
+# include "nonunix.h"
+#endif
+
 #ifndef lint
 __unused static const char rcsid[] = "$Sudo$";
 #endif /* lint */
@@ -271,6 +275,10 @@ main(argc, argv, envp)
 
     init_vars(sudo_mode, envp);                /* XXX - move this later? */
 
+#ifdef USING_NONUNIX_GROUPS
+    sudo_nonunix_groupcheck_init();    /* initialise nonunix groups impl */
+#endif /* USING_NONUNIX_GROUPS */
+
     /* Parse nsswitch.conf for sudoers order. */
     snl = sudo_read_nss();
 
@@ -355,6 +363,12 @@ main(argc, argv, envp)
                break;
        }
     }
+
+#ifdef USING_NONUNIX_GROUPCHECK
+    /* Finished with the groupcheck code */
+    sudo_nonunix_groupcheck_cleanup();
+#endif
+
     if (safe_cmnd == NULL)
        safe_cmnd = estrdup(user_cmnd);
 
diff --git a/vasgroups.c b/vasgroups.c
new file mode 100644 (file)
index 0000000..225687f
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * (c) 2006 Quest Software, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ *   3. Neither the name of Quest Software, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include <vas.h>
+
+#include "compat.h"
+#include "logging.h"
+#include "nonunix.h"
+#include "sudo.h"
+
+
+/* Pseudo-boolean types */
+#undef TRUE
+#undef FALSE
+#define FALSE 0
+#define TRUE  1
+
+
+static vas_ctx_t *sudo_vas_ctx;
+static vas_id_t  *sudo_vas_id;
+/* Don't use VAS_NAME_FLAG_NO_CACHE or lookups just won't work.
+ * -tedp, 2006-08-29 */
+static const int update_flags = 0;
+static int sudo_vas_available = 0;
+static char *err_msg = NULL;
+static void *libvas_handle = NULL;
+
+/* libvas functions */
+static vas_err_t       (*v_ctx_alloc) (vas_ctx_t **ctx);
+static void            (*v_ctx_free) (vas_ctx_t *ctx);
+static vas_err_t       (*v_id_alloc) (vas_ctx_t *ctx, const char *name, vas_id_t **id);
+static void            (*v_id_free) (vas_ctx_t *ctx, vas_id_t *id);
+static vas_err_t       (*v_id_establish_cred_keytab) (vas_ctx_t *ctx, vas_id_t *id, int credflags, const char *keytab);
+static vas_err_t       (*v_user_init) (vas_ctx_t *ctx, vas_id_t *id, const char *name, int flags, vas_user_t **user);
+static void            (*v_user_free) (vas_ctx_t *ctx, vas_user_t *user);
+static vas_err_t       (*v_group_init) (vas_ctx_t *ctx, vas_id_t *id, const char *name, int flags, vas_group_t **group);
+static void            (*v_group_free) (vas_ctx_t *ctx, vas_group_t *group);
+static vas_err_t       (*v_user_is_member) (vas_ctx_t *ctx, vas_id_t *id, vas_user_t *user, vas_group_t *group);
+static const char*     (*v_err_get_string) (vas_ctx_t *ctx, int with_cause);
+
+
+static int     resolve_vas_funcs(void);
+
+
+/**
+ * Whether nonunix group lookups are available.
+ * @return 1 if available, 0 if not.
+ */
+int
+sudo_nonunix_groupcheck_available(void)
+{
+    return sudo_vas_available;
+}
+
+
+/**
+ * Check if the user is in the group
+ * @param group group name which can be in DOMAIN\sam format or just the group
+ *              name
+ * @param user user name
+ * @param pwd (unused)
+ * @return 1 if user is a member of the group, 0 if not (or error occurred)
+ */
+int
+sudo_nonunix_groupcheck( const char* group, const char* user, const struct passwd* pwd )
+{
+    static int          error_cause_shown = FALSE;
+    int                 rval = FALSE;
+    vas_err_t           vaserr;
+    vas_user_t*         vas_user = NULL;
+    vas_group_t*        vas_group = NULL;
+
+    if (!sudo_vas_available) {
+       if (error_cause_shown == FALSE) {
+           /* Produce the saved error reason */
+           log_error(NO_MAIL|NO_EXIT, "Non-unix group checking unavailable: %s",
+                   err_msg ? err_msg
+                   : "(unknown cause)");
+           error_cause_shown = TRUE;
+       }
+       return 0;
+    }
+
+    /* resolve the user and group. The user will be a real Unix account name,
+     * while the group may be a unix name, or any group name accepted by
+     * vas_name_to_dn, which means any of:
+     * - Group Name
+     * - Group Name@FULLY.QUALIFIED.DOMAIN
+     * - CN=sudoers,CN=Users,DC=rcdev,DC=vintela,DC=com
+     * - S-1-2-34-5678901234-5678901234-5678901234-567
+     *
+     * XXX - we may get non-VAS user accounts here. You can add local users to an
+     * Active Directory group through override files. Should we handle that case?
+     * */
+    if( (vaserr = v_user_init( sudo_vas_ctx, sudo_vas_id, user, update_flags, &vas_user )) != VAS_ERR_SUCCESS ) {
+       if (vaserr == VAS_ERR_NOT_FOUND) {
+            /* No such user in AD. Probably a local user. */
+           vaserr = VAS_ERR_SUCCESS;
+       }
+        goto FINISHED;
+    }
+        
+    if( (vaserr = v_group_init( sudo_vas_ctx, sudo_vas_id, group, update_flags, &vas_group )) != VAS_ERR_SUCCESS ) {
+        goto FINISHED;
+    }
+
+    /* do the membership check */
+    if( (vaserr = v_user_is_member( sudo_vas_ctx, sudo_vas_id, vas_user, vas_group )) == VAS_ERR_SUCCESS ) {
+        rval = TRUE;
+    }
+    else if (vaserr == VAS_ERR_NOT_FOUND) {
+       /* fake the vaserr code so no error is triggered */
+       vaserr = VAS_ERR_SUCCESS;
+    }
+
+
+FINISHED: /* cleanups */
+    if (vaserr != VAS_ERR_SUCCESS) {
+       log_error(NO_MAIL|MSG_ONLY, "Error while checking group membership "
+               "for user \"%s\", group \"%s\", error: %s%s.", user, group,
+               v_err_get_string(sudo_vas_ctx, 1),
+               /* A helpful hint if there seems to be a non-FQDN as the domain */
+               (strchr(group, '@') && !strchr(group, '.'))
+                ? "\nMake sure the fully qualified domain name is specified"
+                : "");
+    }
+    if( vas_group )              v_group_free( sudo_vas_ctx, vas_group );
+    if( vas_user )              v_user_free( sudo_vas_ctx, vas_user );
+
+    return(rval);
+}
+
+
+static void
+set_err_msg(const char *msg, ...) {
+    va_list ap;
+
+    if (!msg) /* assert */
+       return;
+
+    if (err_msg)
+       free(err_msg);
+
+    va_start(ap, msg);
+
+    if (vasprintf(&err_msg, msg, ap) == -1)
+       err_msg = NULL;
+       
+    va_end(ap);
+}
+
+
+/**
+ * Initialise nonunix_groupcheck state.
+ */
+void
+sudo_nonunix_groupcheck_init(void)
+{
+    vas_err_t vaserr;
+    void *libvas;
+
+    if (err_msg) {
+       free(err_msg);
+       err_msg = NULL;
+    }
+
+    libvas = dlopen(LIBVAS_SO, RTLD_LAZY);
+    if (!libvas) {
+       set_err_msg("dlopen() failed: %s", dlerror());
+       return;
+    }
+
+    libvas_handle = libvas;
+
+    if (resolve_vas_funcs() != 0)
+       return;
+
+    if (VAS_ERR_SUCCESS == (vaserr = v_ctx_alloc(&sudo_vas_ctx))) {
+
+       if (VAS_ERR_SUCCESS == (vaserr = v_id_alloc(sudo_vas_ctx, "host/", &sudo_vas_id))) {
+       
+           if (update_flags & VAS_NAME_FLAG_NO_LDAP) {
+               sudo_vas_available = 1;
+               return; /* OK */
+           } else { /* Get a keytab */
+               if ((vaserr = v_id_establish_cred_keytab( sudo_vas_ctx,
+                                                   sudo_vas_id,
+                                                     VAS_ID_FLAG_USE_MEMORY_CCACHE
+                                                   | VAS_ID_FLAG_KEEP_COPY_OF_CRED
+                                                   | VAS_ID_FLAG_NO_INITIAL_TGT,
+                                                   NULL )) == VAS_ERR_SUCCESS) {
+                   sudo_vas_available = 1;
+                   return; /* OK */
+               }
+
+               if (!err_msg)
+                   set_err_msg("unable to establish creds: %s",
+                           v_err_get_string(sudo_vas_ctx, 1));
+           }
+
+           v_id_free(sudo_vas_ctx, sudo_vas_id);
+           sudo_vas_id = NULL;
+       }
+
+       /* This is the last opportunity to get an error message from libvas */
+       if (!err_msg)
+           set_err_msg("Error initializing non-unix group checking: %s",
+                   v_err_get_string(sudo_vas_ctx, 1));
+
+       v_ctx_free(sudo_vas_ctx);
+       sudo_vas_ctx = NULL;
+    }
+
+    if (!err_msg)
+       set_err_msg("Failed to get a libvas handle for non-unix group checking (unknown cause)");
+
+    sudo_vas_available = 0;
+}
+
+
+/**
+ * Clean up nonunix_groupcheck state.
+ */
+void
+sudo_nonunix_groupcheck_cleanup()
+{
+    if (err_msg) {
+       free(err_msg);
+       err_msg = NULL;
+    }
+
+    if (sudo_vas_available) {
+       v_id_free(sudo_vas_ctx, sudo_vas_id);
+       sudo_vas_id = NULL;
+
+       v_ctx_free(sudo_vas_ctx);
+       sudo_vas_ctx = NULL;
+
+       sudo_vas_available = FALSE;
+    }
+
+    if (libvas_handle) {
+       if (dlclose(libvas_handle) != 0)
+           log_error(NO_MAIL|NO_EXIT, "dlclose() failed: %s", dlerror());
+       libvas_handle = NULL;
+    }
+}
+
+#define RESOLVE_OR_ERR(fptr, sym) \
+    do { \
+       void *_fptr = dlsym(libvas_handle, (sym)); \
+       if (!_fptr) { \
+           set_err_msg("dlsym() failed: %s", dlerror()); \
+           return -1; \
+       } \
+       fptr = _fptr; \
+    } while (0)
+
+
+/**
+ * Resolve all the libvas functions.
+ * Returns -1 and sets err_msg if something went wrong, or 0 on success.
+ */
+int
+resolve_vas_funcs(void)
+{
+    if (!libvas_handle) /* assert */
+       return -1;
+
+    RESOLVE_OR_ERR(v_ctx_alloc,        "vas_ctx_alloc");
+    RESOLVE_OR_ERR(v_ctx_free, "vas_ctx_free");
+    RESOLVE_OR_ERR(v_id_alloc, "vas_id_alloc");
+    RESOLVE_OR_ERR(v_id_free,  "vas_id_free");
+    RESOLVE_OR_ERR(v_id_establish_cred_keytab, "vas_id_establish_cred_keytab");
+    RESOLVE_OR_ERR(v_user_init,        "vas_user_init");
+    RESOLVE_OR_ERR(v_user_free,        "vas_user_free");
+    RESOLVE_OR_ERR(v_group_init,       "vas_group_init");
+    RESOLVE_OR_ERR(v_group_free,       "vas_group_free");
+    RESOLVE_OR_ERR(v_user_is_member,   "vas_user_is_member");
+    RESOLVE_OR_ERR(v_err_get_string,   "vas_err_get_string");
+
+    return 0;
+}