| /* |
| * libnfsidmap.c |
| * |
| * nfs idmapping library, primarily for nfs4 client/server kernel idmapping |
| * and for userland nfs4 idmapping by acl libraries. |
| * |
| * Copyright (c) 2004 The Regents of the University of Michigan. |
| * All rights reserved. |
| * |
| * Marius Aamodt Eriksen <marius@umich.edu> |
| * J. Bruce Fields <bfields@umich.edu> |
| * |
| * 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 the University 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 ``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 REGENTS 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 <sys/types.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <pwd.h> |
| #include <grp.h> |
| #include <netdb.h> |
| #include <err.h> |
| #include <syslog.h> |
| #include <stdarg.h> |
| #include <dlfcn.h> |
| #include <ctype.h> |
| #include <resolv.h> |
| #include <arpa/nameser.h> |
| #include <arpa/nameser_compat.h> |
| |
| #include "nfsidmap.h" |
| #include "nfsidmap_private.h" |
| #include "nfsidmap_plugin.h" |
| #include "conffile.h" |
| |
| #pragma GCC visibility push(hidden) |
| |
| void nfs4_cleanup_name_mapping(void); |
| static char *default_domain; |
| static struct mapping_plugin **nfs4_plugins = NULL; |
| static struct mapping_plugin **gss_plugins = NULL; |
| uid_t nobody_uid = (uid_t)-1; |
| gid_t nobody_gid = (gid_t)-1; |
| |
| #ifndef PATH_PLUGINS |
| #define PATH_PLUGINS "/usr/lib/libnfsidmap" |
| #endif |
| #define PLUGIN_INIT_FUNC "libnfsidmap_plugin_init" |
| |
| |
| #ifndef PATH_IDMAPDCONF |
| #define PATH_IDMAPDCONF "/etc/idmapd.conf" |
| #endif |
| |
| #ifndef IDMAPD_DEFAULT_DOMAIN |
| #define IDMAPD_DEFAULT_DOMAIN "localdomain" |
| #endif |
| |
| #ifndef NFS4DNSTXTREC |
| #define NFS4DNSTXTREC "_nfsv4idmapdomain" |
| #endif |
| |
| #ifndef NS_MAXMSG |
| #define NS_MAXMSG 65535 |
| #endif |
| |
| /* Default logging fuction */ |
| static void default_logger(const char *fmt, ...) |
| { |
| va_list vp; |
| |
| va_start(vp, fmt); |
| vsyslog(LOG_WARNING, fmt, vp); |
| va_end(vp); |
| } |
| |
| #pragma GCC visibility pop |
| nfs4_idmap_log_function_t idmap_log_func = default_logger; |
| int idmap_verbosity = 0; |
| #pragma GCC visibility push(hidden) |
| |
| static int id_as_chars(char *name, uid_t *id) |
| { |
| long int value; |
| |
| if (name == NULL) |
| return 0; |
| value = strtol(name, NULL, 10); |
| if (value == 0) { |
| /* zero value ids are valid */ |
| if (strcmp(name, "0") != 0) |
| return 0; |
| } |
| *id = (int)value; |
| return 1; |
| } |
| |
| static int dns_txt_query(char *domain, char **nfs4domain) |
| { |
| char *txtname = NFS4DNSTXTREC; |
| unsigned char *msg, *eom, *mptr; |
| char *answ; |
| int len, status = -1; |
| HEADER *hdr; |
| |
| msg = calloc(1, NS_MAXMSG); |
| if (msg == NULL) |
| return -1; |
| |
| answ = calloc(1, NS_MAXMSG); |
| if (answ == NULL) { |
| free(msg); |
| return -1; |
| } |
| |
| if (res_init() < 0) { |
| IDMAP_LOG(2, ("libnfsidmap: res_init() failed for %s.%s: %s\n", |
| txtname, domain, hstrerror(h_errno))); |
| goto freemem; |
| } |
| len = res_querydomain(txtname, domain, C_IN, T_TXT, msg, NS_MAXMSG); |
| if (len < 0) { |
| IDMAP_LOG(2, ("libnfsidmap: res_querydomain() failed for %s.%s: %s\n", |
| txtname, domain, hstrerror(h_errno))); |
| goto freemem; |
| } |
| hdr = (HEADER *)msg; |
| |
| /* See if there is an answer */ |
| if (ntohs(hdr->ancount) < 1) { |
| IDMAP_LOG(2, ("libnfsidmap: No TXT record for %s.%s\n", |
| txtname, domain)); |
| goto freemem; |
| } |
| /* find the EndOfMessage */ |
| eom = msg + len; |
| |
| /* skip header */ |
| mptr = &msg[HFIXEDSZ]; |
| |
| /* skip name field in question section */ |
| mptr += dn_skipname(mptr, eom) + QFIXEDSZ; |
| |
| /* read in the question */ |
| len = dn_expand(msg, eom, mptr, answ, NS_MAXDNAME); |
| if (len < 0) { /* does this really matter?? */ |
| IDMAP_LOG(2, ("libnfsidmap: No question section for %s.%s: %s\n", |
| txtname, domain, hstrerror(h_errno))); |
| goto freemem; |
| } |
| |
| /* |
| * Now, dissect the answer section, Note: if there |
| * are more than one answer only the first |
| * one will be used. |
| */ |
| |
| /* skip passed the name field */ |
| mptr += dn_skipname(mptr, eom); |
| /* skip pass the type class and ttl fields */ |
| mptr += 2 + 2 + 4; |
| |
| /* make sure there is some data */ |
| GETSHORT(len, mptr); |
| if (len < 0) { |
| IDMAP_LOG(2, ("libnfsidmap: No data in answer for %s.%s\n", |
| txtname, domain)); |
| goto freemem; |
| } |
| /* get the lenght field */ |
| len = (int)*mptr++; |
| /* copy the data */ |
| memcpy(answ, mptr, len); |
| answ[len] = '\0'; |
| |
| *nfs4domain = strdup(answ); |
| status = 0; |
| |
| freemem: |
| free(msg); |
| free(answ); |
| |
| return (status); |
| } |
| |
| static int domain_from_dns(char **domain) |
| { |
| struct hostent *he; |
| char hname[64], *c; |
| |
| if (gethostname(hname, sizeof(hname)) == -1) |
| return -1; |
| if ((he = gethostbyname(hname)) == NULL) |
| return -1; |
| if ((c = strchr(he->h_name, '.')) == NULL || *++c == '\0') |
| return -1; |
| /* |
| * Query DNS to see if the _nfsv4idmapdomain TXT record exists |
| * If so use it... |
| */ |
| if (dns_txt_query(c, domain) < 0) |
| *domain = strdup(c); |
| |
| return 0; |
| } |
| |
| static int load_translation_plugin(char *method, struct mapping_plugin *plgn) |
| { |
| void *dl = NULL; |
| struct trans_func *trans = NULL; |
| libnfsidmap_plugin_init_t init_func = NULL; |
| char plgname[128]; |
| int ret = 0; |
| |
| /* Look for library using search path first to allow overriding */ |
| snprintf(plgname, sizeof(plgname), "%s.so", method); |
| |
| dl = dlopen(plgname, RTLD_NOW | RTLD_LOCAL); |
| if (dl != NULL) { |
| /* Is it really one of our libraries */ |
| init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC); |
| if (init_func == NULL) { |
| dlclose(dl); |
| dl = NULL; |
| } |
| } |
| |
| if (dl == NULL) { |
| /* Fallback to hard-coded path */ |
| snprintf(plgname, sizeof(plgname), "%s/%s.so", PATH_PLUGINS, method); |
| |
| dl = dlopen(plgname, RTLD_NOW | RTLD_LOCAL); |
| if (dl == NULL) { |
| IDMAP_LOG(1, ("libnfsidmap: Unable to load plugin: %s: %s", |
| plgname, dlerror())); |
| return -1; |
| } |
| init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC); |
| if (init_func == NULL) { |
| IDMAP_LOG(1, ("libnfsidmap: Unable to get init function: %s: %s", |
| plgname, dlerror())); |
| dlclose(dl); |
| return -1; |
| } |
| } |
| trans = init_func(); |
| if (trans == NULL) { |
| IDMAP_LOG(1, ("libnfsidmap: Failed to initialize plugin %s", |
| PLUGIN_INIT_FUNC, plgname)); |
| dlclose(dl); |
| return -1; |
| } |
| if (trans->init) { |
| ret = trans->init(); |
| if (ret) { |
| IDMAP_LOG(1, ("libnfsidmap: Failed in %s's init(), " |
| "returned %d", plgname, ret)); |
| dlclose(dl); |
| return -1; |
| } |
| } |
| plgn->dl_handle = dl; |
| plgn->trans = trans; |
| IDMAP_LOG(1, ("libnfsidmap: loaded plugin %s for method %s", |
| plgname, method)); |
| |
| return 0; |
| } |
| |
| static void unload_plugins(struct mapping_plugin **plgns) |
| { |
| int i; |
| for (i = 0; plgns[i] != NULL; i++) { |
| if (plgns[i]->dl_handle && dlclose(plgns[i]->dl_handle)) |
| IDMAP_LOG(1, ("libnfsidmap: failed to " |
| "unload plugin for method = %s", |
| plgns[i]->trans->name)); |
| free(plgns[i]); |
| } |
| free(plgns); |
| } |
| |
| static int load_plugins(struct conf_list *methods, |
| struct mapping_plugin ***plugins) |
| { |
| int ret = -1, i = 0; |
| struct mapping_plugin **plgns; |
| struct conf_list_node *m; |
| |
| plgns = calloc(methods->cnt + 1, sizeof(struct mapping_plugin *)); |
| if (plgns == NULL) |
| return -1; |
| plgns[methods->cnt] = NULL; |
| for (m = TAILQ_FIRST(&methods->fields), i = 0; m; |
| m = TAILQ_NEXT(m, link), i++) { |
| plgns[i] = calloc(1, sizeof(struct mapping_plugin)); |
| if (plgns[i] == NULL) |
| goto out; |
| if (load_translation_plugin(m->field, plgns[i]) == -1) { |
| IDMAP_LOG(0, ("libnfsidmap: requested translation " |
| "method, '%s', is not available", |
| m->field)); |
| goto out; |
| } |
| } |
| ret = 0; |
| *plugins = plgns; |
| out: |
| if (ret) |
| unload_plugins(plgns); |
| return ret; |
| } |
| |
| static char *get_default_domain(void) |
| { |
| int ret; |
| |
| if (default_domain) |
| return default_domain; |
| ret = domain_from_dns(&default_domain); |
| if (ret) { |
| IDMAP_LOG(0, ("Unable to determine a default nfsv4 domain; " |
| " consider specifying one in idmapd.conf")); |
| default_domain = ""; |
| } |
| return default_domain; |
| } |
| |
| void nfs4_cleanup_name_mapping(void) |
| { |
| if (nfs4_plugins) |
| unload_plugins(nfs4_plugins); |
| if (gss_plugins) |
| unload_plugins(gss_plugins); |
| nfs4_plugins = gss_plugins = NULL; |
| } |
| |
| #pragma GCC visibility pop |
| |
| const char * nfsidmap_conf_path = PATH_IDMAPDCONF; |
| |
| int nfs4_init_name_mapping(char *conffile) |
| { |
| int ret = -ENOENT; |
| int dflt = 0; |
| struct conf_list *nfs4_methods, *gss_methods; |
| char *nobody_user, *nobody_group; |
| |
| /* XXX: need to be able to reload configurations... */ |
| if (nfs4_plugins) /* already succesfully initialized */ |
| return 0; |
| if (conffile) |
| nfsidmap_conf_path = conffile; |
| conf_init_file(nfsidmap_conf_path); |
| |
| default_domain = conf_get_str("General", "Domain"); |
| if (default_domain == NULL) { |
| dflt = 1; |
| ret = domain_from_dns(&default_domain); |
| if (ret) { |
| IDMAP_LOG(1, ("libnfsidmap: Unable to determine " |
| "the NFSv4 domain; Using '%s' as the NFSv4 domain " |
| "which means UIDs will be mapped to the 'Nobody-User' " |
| "user defined in %s", |
| IDMAPD_DEFAULT_DOMAIN, PATH_IDMAPDCONF)); |
| default_domain = IDMAPD_DEFAULT_DOMAIN; |
| } |
| } |
| IDMAP_LOG(1, ("libnfsidmap: using%s domain: %s", |
| (dflt ? " (default)" : ""), default_domain)); |
| |
| struct conf_list *local_realms = get_local_realms(); |
| if (local_realms == NULL) return -ENOMEM; |
| |
| if (idmap_verbosity >= 1) { |
| struct conf_list_node *r; |
| char *buf = NULL; |
| int siz=0; |
| |
| if (local_realms) { |
| TAILQ_FOREACH(r, &local_realms->fields, link) { |
| siz += (strlen(r->field)+4); |
| } |
| buf = malloc(siz); |
| if (buf) { |
| *buf = 0; |
| TAILQ_FOREACH(r, &local_realms->fields, link) { |
| sprintf(buf+strlen(buf), "'%s' ", r->field); |
| } |
| IDMAP_LOG(1, ("libnfsidmap: Realms list: %s", buf)); |
| free(buf); |
| } |
| } else |
| IDMAP_LOG(1, ("libnfsidmap: Realms list: <NULL> ")); |
| } |
| |
| nfs4_methods = conf_get_list("Translation", "Method"); |
| if (nfs4_methods) { |
| IDMAP_LOG(1, ("libnfsidmap: processing 'Method' list")); |
| if (load_plugins(nfs4_methods, &nfs4_plugins) == -1) { |
| conf_free_list(nfs4_methods); |
| return -ENOENT; |
| } |
| } else { |
| struct conf_list list; |
| struct conf_list_node node; |
| |
| TAILQ_INIT(&list.fields); |
| list.cnt = 1; |
| node.field = "nsswitch"; |
| TAILQ_INSERT_TAIL (&list.fields, &node, link); |
| |
| if (load_plugins(&list, &nfs4_plugins) == -1) |
| return -ENOENT; |
| } |
| |
| gss_methods = conf_get_list("Translation", "GSS-Methods"); |
| if (gss_methods) { |
| IDMAP_LOG(1, ("libnfsidmap: processing 'GSS-Methods' list")); |
| if (load_plugins(gss_methods, &gss_plugins) == -1) |
| goto out; |
| } |
| |
| nobody_user = conf_get_str("Mapping", "Nobody-User"); |
| if (nobody_user) { |
| size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); |
| struct passwd *buf; |
| struct passwd *pw = NULL; |
| int err; |
| |
| buf = malloc(sizeof(*buf) + buflen); |
| if (buf) { |
| err = getpwnam_r(nobody_user, buf, ((char *)buf) + sizeof(*buf), buflen, &pw); |
| if (err == 0 && pw != NULL) |
| nobody_uid = pw->pw_uid; |
| else |
| IDMAP_LOG(1, ("libnfsidmap: Nobody-User (%s) not found: %s", |
| nobody_user, strerror(errno))); |
| free(buf); |
| } else |
| IDMAP_LOG(0,("libnfsidmap: Nobody-User: no memory : %s", |
| nobody_user, strerror(errno))); |
| } |
| |
| nobody_group = conf_get_str("Mapping", "Nobody-Group"); |
| if (nobody_group) { |
| size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX); |
| struct group *buf; |
| struct group *gr = NULL; |
| int err; |
| |
| buf = malloc(sizeof(*buf) + buflen); |
| if (buf) { |
| err = getgrnam_r(nobody_group, buf, ((char *)buf) + sizeof(*buf), buflen, &gr); |
| if (err == 0 && gr != NULL) |
| nobody_gid = gr->gr_gid; |
| else |
| IDMAP_LOG(1, ("libnfsidmap: Nobody-Group (%s) not found: %s", |
| nobody_group, strerror(errno))); |
| free(buf); |
| } else |
| IDMAP_LOG(0,("libnfsidmap: Nobody-Group: no memory : %s", |
| nobody_group, strerror(errno))); |
| } |
| |
| ret = 0; |
| out: |
| if (ret) { |
| if (nfs4_plugins) |
| unload_plugins(nfs4_plugins); |
| if (gss_plugins) { |
| unload_plugins(gss_plugins); |
| } |
| nfs4_plugins = gss_plugins = NULL; |
| } |
| |
| if (gss_methods) |
| conf_free_list(gss_methods); |
| |
| if (nfs4_methods) |
| conf_free_list(nfs4_methods); |
| |
| return ret ? -ENOENT: 0; |
| } |
| |
| void nfs4_term_name_mapping(void) |
| { |
| if (nfs4_plugins) |
| unload_plugins(nfs4_plugins); |
| if (gss_plugins) |
| unload_plugins(gss_plugins); |
| |
| nfs4_plugins = gss_plugins = NULL; |
| |
| free_local_realms(); |
| conf_cleanup(); |
| } |
| |
| int |
| nfs4_get_default_domain(char *UNUSED(server), char *domain, size_t len) |
| { |
| char *d = get_default_domain(); |
| |
| if (strlen(d) + 1 > len) |
| return -ERANGE; |
| strcpy(domain, d); |
| return 0; |
| } |
| |
| /* |
| * Run through each configured translation method for |
| * function "funcname". |
| * If "prefer_gss" is true, then use the gss_plugins list, |
| * if present. Otherwise, use the default nfs4_plugins list. |
| * |
| * If the plugin function returns -ENOENT, then continue |
| * to the next plugin. |
| */ |
| #define RUN_TRANSLATIONS(funcname, prefer_gss, args...) \ |
| do { \ |
| int ret, i; \ |
| struct mapping_plugin **plgns; \ |
| \ |
| ret = nfs4_init_name_mapping(NULL); \ |
| if (ret) \ |
| return ret; \ |
| \ |
| if ((prefer_gss) && gss_plugins) \ |
| plgns = gss_plugins; \ |
| else \ |
| plgns = nfs4_plugins; \ |
| \ |
| for (i = 0; plgns[i] != NULL; i++) { \ |
| if (plgns[i]->trans->funcname == NULL) \ |
| continue; \ |
| \ |
| IDMAP_LOG(4, ("%s: calling %s->%s", __func__, \ |
| plgns[i]->trans->name, #funcname)); \ |
| \ |
| ret = plgns[i]->trans->funcname(args); \ |
| \ |
| IDMAP_LOG(4, ("%s: %s->%s returned %d", \ |
| __func__, plgns[i]->trans->name, \ |
| #funcname, ret)); \ |
| \ |
| if (ret == -ENOENT) \ |
| continue; \ |
| \ |
| break; \ |
| } \ |
| IDMAP_LOG(4, ("%s: final return value is %d", \ |
| __func__, ret)); \ |
| return ret; \ |
| } while (0) |
| |
| int nfs4_uid_to_name(uid_t uid, char *domain, char *name, size_t len) |
| { |
| RUN_TRANSLATIONS(uid_to_name, 0, uid, domain, name, len); |
| } |
| |
| int nfs4_gid_to_name(gid_t gid, char *domain, char *name, size_t len) |
| { |
| RUN_TRANSLATIONS(gid_to_name, 0, gid, domain, name, len); |
| } |
| |
| int nfs4_uid_to_owner(uid_t uid, char *domain, char *name, size_t len) |
| { |
| if (nfs4_uid_to_name(uid, domain, name, len)) |
| sprintf(name, "%u", uid); |
| return 0; |
| } |
| |
| int nfs4_gid_to_group_owner(gid_t gid, char *domain, char *name, size_t len) |
| { |
| if (nfs4_gid_to_name(gid, domain, name, len)) |
| sprintf(name, "%u", gid); |
| return 0; |
| } |
| |
| int nfs4_name_to_uid(char *name, uid_t *uid) |
| { |
| RUN_TRANSLATIONS(name_to_uid, 0, name, uid); |
| } |
| |
| int nfs4_name_to_gid(char *name, gid_t *gid) |
| { |
| RUN_TRANSLATIONS(name_to_gid, 0, name, gid); |
| } |
| |
| static int set_id_to_nobody(uid_t *id, uid_t is_uid) |
| { |
| int rc = 0; |
| const char name[] = "nobody@"; |
| char nobody[strlen(name) + strlen(get_default_domain()) + 1]; |
| |
| /* First try to see whether a Nobody-User/Nobody-Group was |
| * configured, before we try to do a full lookup for the |
| * NFS nobody user. */ |
| if (is_uid && nobody_uid != (uid_t)-1) { |
| *id = (uid_t)nobody_uid; |
| return 0; |
| } else if (!is_uid && nobody_gid != (gid_t)-1) { |
| *id = (uid_t)nobody_gid; |
| return 0; |
| } |
| |
| strcpy(nobody, name); |
| strcat(nobody, get_default_domain()); |
| |
| if (is_uid) |
| rc = nfs4_name_to_uid(nobody, id); |
| else |
| rc = nfs4_name_to_gid(nobody, id); |
| |
| if (rc) { |
| *id = -2; |
| rc = 0; |
| } |
| return rc; |
| } |
| |
| int nfs4_owner_to_uid(char *name, uid_t *uid) |
| { |
| int rc = nfs4_name_to_uid(name, uid); |
| if (rc && id_as_chars(name, uid)) |
| rc = 0; |
| else if (rc) |
| rc = set_id_to_nobody(uid, 1); |
| return rc; |
| } |
| |
| int nfs4_group_owner_to_gid(char *name, gid_t *gid) |
| { |
| int rc = nfs4_name_to_gid(name, gid); |
| if (rc && id_as_chars(name, gid)) |
| rc = 0; |
| else if (rc) |
| rc = set_id_to_nobody((uid_t *)gid, 0); |
| return rc; |
| } |
| |
| int nfs4_gss_princ_to_ids(char *secname, char *princ, uid_t *uid, gid_t *gid) |
| { |
| RUN_TRANSLATIONS(princ_to_ids, 1, secname, princ, uid, gid, NULL); |
| } |
| |
| int nfs4_gss_princ_to_grouplist(char *secname, char *princ, |
| gid_t *groups, int *ngroups) |
| { |
| RUN_TRANSLATIONS(gss_princ_to_grouplist, 1, secname, princ, |
| groups, ngroups, NULL); |
| } |
| |
| int nfs4_gss_princ_to_ids_ex(char *secname, char *princ, uid_t *uid, |
| gid_t *gid, extra_mapping_params **ex) |
| { |
| RUN_TRANSLATIONS(princ_to_ids, 1, secname, princ, uid, gid, ex); |
| } |
| |
| int nfs4_gss_princ_to_grouplist_ex(char *secname, char *princ, gid_t *groups, |
| int *ngroups, extra_mapping_params **ex) |
| { |
| RUN_TRANSLATIONS(gss_princ_to_grouplist, 1, secname, princ, |
| groups, ngroups, ex); |
| } |
| |
| void nfs4_set_debug(int dbg_level, void (*logger)(const char *, ...)) |
| { |
| if (logger) |
| idmap_log_func = logger; |
| idmap_verbosity = dbg_level; |
| IDMAP_LOG(0, ("Setting log level to %d\n", idmap_verbosity)); |
| } |
| |
| const char *nfsidmap_config_get(const char *section, const char *tag) |
| { |
| return conf_get_section(section, NULL, tag); |
| } |