From 7d8a15fbf86811e5ba71b1b35b2d829e231f08de Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Thu, 10 Aug 2023 13:05:52 +0200 Subject: [PATCH] LU-17023 krb: use a Kerberos realm different from default It makes sense to give the ability to specify a Kerberos realm that is different from the default realm as returned by krb5_get_default_realm(). On client side, the desired realm needs to be specified via the new '-R' option to lgss_keyring. This can be specified in the config file /etc/request-key.d/lgssc.conf to replace the default domain, e.g.: create lgssc * * /usr/sbin/lgss_keyring -R DOMAIN.COM %o %k %t %d %c %u %g %T %P %S On server side, the desired realm can be specified via the new '-R' parameter of the lsvcgssd daemon, replacing the default realm. This patch adds sanity-krb5 test_1b to exercise the new realm options, by just re-using the same realm as the test system is configured to use. And former test_1 is renamed test_1a. Test-Parameters: kerberos=true testlist=sanity-krb5 Signed-off-by: Sebastien Buisson Change-Id: I9c91d5cb9904781d546e77b1e46115fed433618f Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/51914 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Andreas Dilger Reviewed-by: Aurelien Degremont Reviewed-by: Oleg Drokin --- lustre/tests/sanity-krb5.sh | 38 ++++++++++++++++++-- lustre/tests/test-framework.sh | 13 ++++--- lustre/utils/gss/Makefile.am | 2 +- lustre/utils/gss/gss_util.c | 49 +++++++++++-------------- lustre/utils/gss/gss_util.h | 3 +- lustre/utils/gss/lgss_keyring.c | 71 ++++++++++++++++++++++++++---------- lustre/utils/gss/lgss_krb5_utils.c | 68 +++++++++++++++++------------------ lustre/utils/gss/lgss_krb5_utils.h | 2 ++ lustre/utils/gss/lgss_utils.c | 10 ++++++ lustre/utils/gss/lgss_utils.h | 1 + lustre/utils/gss/lsupport.c | 50 ++++++++++++++++++++++++++ lustre/utils/gss/lsupport.h | 4 +++ lustre/utils/gss/svcgssd.c | 73 +++++++++++++++++++++++--------------- 13 files changed, 262 insertions(+), 122 deletions(-) diff --git a/lustre/tests/sanity-krb5.sh b/lustre/tests/sanity-krb5.sh index 2cfce7a..017f18a 100755 --- a/lustre/tests/sanity-krb5.sh +++ b/lustre/tests/sanity-krb5.sh @@ -195,7 +195,7 @@ run_test 0 "start multiple gss daemons" set_flavor_all krb5p -test_1() { +test_1a() { local file=$DIR/$tdir/$tfile mkdir $DIR/$tdir || error "mkdir $DIR/$tdir failed" @@ -211,7 +211,41 @@ test_1() { $RUNAS touch $file || error "should not fail" [ -f $file ] || error "$file not found" } -run_test 1 "access with or without krb5 credential" +run_test 1a "access with or without krb5 credential" + +test_1b() { + local file=$DIR/$tdir/$tfile + local lgssconf=/etc/request-key.d/lgssc.conf + local clients=$CLIENTS + local realm + + [ -z $clients ] && clients=$HOSTNAME + zconf_umount_clients $clients $MOUNT || error "umount clients failed" + + echo "stop gss daemons..." + stop_gss_daemons + + # get local realm from krb5.conf, assume the same for all nodes + realm=$(grep default_realm /etc/krb5.conf | awk '{print $3}') + + # add -R option to lgss_keyring on local client + cp $lgssconf $TMP/lgssc.conf + stack_trap "yes | cp $TMP/lgssc.conf $lgssconf" EXIT + sed -i s+lgss_keyring+\&\ \-R\ $realm+ $lgssconf + + # add -R option to lsvcgssd + echo "bring up gss daemons..." + start_gss_daemons '' '' "-R $realm" + stack_trap "stop_gss_daemons ; start_gss_daemons" EXIT + + zconf_mount_clients $clients $MOUNT || error "mount clients failed" + + mkdir $DIR/$tdir || error "mkdir $DIR/$tdir failed" + chmod 0777 $DIR/$tdir || error "chmod $DIR/$tdir failed" + $RUNAS touch $file || error "touch $file failed" + [ -f $file ] || error "$file not found" +} +run_test 1b "Use specified realm" test_2() { local file1=$DIR/$tdir/$tfile-1 diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh index 46c9d04..7abd2083 100755 --- a/lustre/tests/test-framework.sh +++ b/lustre/tests/test-framework.sh @@ -1000,10 +1000,11 @@ send_sigint() { start_gss_daemons() { local nodes=$1 local daemon=$2 + local options=$3 if [ "$nodes" ] && [ "$daemon" ] ; then echo "Starting gss daemon on nodes: $nodes" - do_nodes $nodes "$daemon" || return 8 + do_nodes $nodes "$daemon" "$options" || return 8 return 0 fi @@ -1011,18 +1012,20 @@ start_gss_daemons() { echo "Starting gss daemon on mds: $nodes" if $GSS_SK; then # Start all versions, in case of switching - do_nodes $nodes "$LSVCGSSD -vvv -s -m -o -z" || return 1 + do_nodes $nodes "$LSVCGSSD -vvv -s -m -o -z $options" || + return 1 else - do_nodes $nodes "$LSVCGSSD -vvv" || return 1 + do_nodes $nodes "$LSVCGSSD -vvv $options" || return 1 fi nodes=$(comma_list $(osts_nodes)) echo "Starting gss daemon on ost: $nodes" if $GSS_SK; then # Start all versions, in case of switching - do_nodes $nodes "$LSVCGSSD -vvv -s -m -o -z" || return 3 + do_nodes $nodes "$LSVCGSSD -vvv -s -m -o -z $options" || + return 3 else - do_nodes $nodes "$LSVCGSSD -vvv" || return 3 + do_nodes $nodes "$LSVCGSSD -vvv $options" || return 3 fi # starting on clients diff --git a/lustre/utils/gss/Makefile.am b/lustre/utils/gss/Makefile.am index 78d1296..be33ffe 100644 --- a/lustre/utils/gss/Makefile.am +++ b/lustre/utils/gss/Makefile.am @@ -59,7 +59,7 @@ l_idmap_SOURCES = \ \ lsupport.h -l_idmap_LDADD = $(top_builddir)/lustre/utils/liblustreapi.la +l_idmap_LDADD = $(top_builddir)/lustre/utils/liblustreapi.la $(KRBLIBS) lgss_keyring_SOURCES = \ lgss_keyring.c \ diff --git a/lustre/utils/gss/gss_util.c b/lustre/utils/gss/gss_util.c index df5a8d3..4c983a5 100644 --- a/lustre/utils/gss/gss_util.c +++ b/lustre/utils/gss/gss_util.c @@ -207,8 +207,9 @@ int extract_realm_name(gss_buffer_desc *name, char **realm) c = strchr(sname, '@'); if (!c) { - printerr(2, "no realm found in principal, use default\n"); - *realm = strdup(this_realm); + printerr(2, "no realm in principal, using '%s' instead\n", + krb5_this_realm); + *realm = strdup(krb5_this_realm); if (!*realm) { printerr(0, "failed to duplicate default realm\n"); rc = -ENOMEM; @@ -381,36 +382,26 @@ out: * FIXME should be in krb5_util.c *********************************/ -/* realm of this node */ -char *this_realm = NULL; - -int gssd_get_local_realm(void) +void gssd_cleanup_realms(void) { krb5_context context = NULL; krb5_error_code code; - int retval = -1; - if (this_realm != NULL) - return 0; - - code = krb5_init_context(&context); - if (code) { - printerr(0, "ERROR: get default realm: init ctx: %s\n", - error_message(code)); - goto out; - } - - code = krb5_get_default_realm(context, &this_realm); - if (code) { - printerr(0, "ERROR: get default realm: %s\n", - error_message(code)); - goto out; + if (krb5_this_realm) { + code = krb5_init_context(&context); + if (code) { + printerr(0, "ERROR: cleanup realms: init ctx: %s\n", + error_message(code)); + } else { + krb5_free_string(context, krb5_this_realm); + krb5_this_realm = NULL; + krb5_free_context(context); + } } - retval = 0; - - printerr(1, "Local realm: %s\n", this_realm); -out: - krb5_free_context(context); - return retval; + if (mgs_local_realm) + free(mgs_local_realm); + if (mds_local_realm) + free(mds_local_realm); + if (oss_local_realm) + free(oss_local_realm); } - diff --git a/lustre/utils/gss/gss_util.h b/lustre/utils/gss/gss_util.h index b53c256..648b347 100644 --- a/lustre/utils/gss/gss_util.h +++ b/lustre/utils/gss/gss_util.h @@ -41,12 +41,11 @@ # endif #endif -extern char *this_realm; extern gss_cred_id_t gssd_creds; void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat, const gss_OID mech); int gssd_check_mechs(void); -int gssd_get_local_realm(void); +void gssd_cleanup_realms(void); #endif /* _GSS_UTIL_H_ */ diff --git a/lustre/utils/gss/lgss_keyring.c b/lustre/utils/gss/lgss_keyring.c index 4883e1e..e0bb94b 100644 --- a/lustre/utils/gss/lgss_keyring.c +++ b/lustre/utils/gss/lgss_keyring.c @@ -49,12 +49,14 @@ #include #include #include +#include #include #include #include #include "lsupport.h" #include "lgss_utils.h" +#include "lgss_krb5_utils.h" #include "write_bytes.h" #include "context.h" @@ -953,23 +955,47 @@ static int prepare_and_instantiate(struct lgss_cred *cred, key_serial_t keyid, int main(int argc, char *argv[]) { - struct keyring_upcall_param uparam; - key_serial_t keyid; - key_serial_t sring; - pid_t child; - int req_fd[2] = { -1, -1 }; - int reply_fd[2] = { -1, -1 }; - struct lgss_mech_type *mech; - struct lgss_cred *cred; - char path[PATH_MAX] = ""; - int other_ns = 0; - int rc = 0; - struct stat parent_ns = { .st_ino = 0 }; - struct stat caller_ns = { .st_ino = 0 }; + struct keyring_upcall_param uparam; + key_serial_t keyid; + key_serial_t sring; + pid_t child; + int req_fd[2] = { -1, -1 }; + int reply_fd[2] = { -1, -1 }; + struct lgss_mech_type *mech; + struct lgss_cred *cred; + char path[PATH_MAX] = ""; + int other_ns = 0; + int rc = 0, opt; + struct stat parent_ns = { .st_ino = 0 }; + struct stat caller_ns = { .st_ino = 0 }; + + static struct option long_opts[] = { + { .name = "realm", .has_arg = required_argument, .val = 'R'}, + { .name = NULL, } }; set_log_level(); logmsg(LL_TRACE, "start parsing parameters\n"); + + /* one possible option before upcall parameters: -R REALM */ + while ((opt = getopt_long(argc, argv, "R:", long_opts, NULL)) != EOF) { + switch (opt) { + case 'R': + lgss_client_realm = optarg; + break; + default: + logmsg(LL_ERR, "invalid parameter %s\n", + argv[optind - 1]); + return 1; + } + } + + if (lgss_client_realm) { + /* shift args to meet expected upcall parameters */ + argc -= optind - 1; + argv += optind - 1; + } + /* * parse & sanity check upcall parameters * expected to be called with: @@ -1234,6 +1260,7 @@ out_pipe: close(req_fd[1]); close(reply_fd[0]); close(reply_fd[1]); + lgss_fini(cred); return rc; } else { if (uparam.kup_pid) @@ -1243,7 +1270,7 @@ out_pipe: rc = prepare_and_instantiate(cred, keyid, uparam.kup_uid); if (rc != 0) - return rc; + goto out_reg; /* * fork a child to do the real gss negotiation @@ -1252,13 +1279,19 @@ out_pipe: if (child == -1) { logmsg(LL_ERR, "key %08x: can't create child: %s\n", keyid, strerror(errno)); - return 1; + rc = 1; + goto out_reg; } else if (child == 0) { - return lgssc_kr_negotiate(keyid, cred, &uparam, - req_fd, reply_fd); + rc = lgssc_kr_negotiate(keyid, cred, &uparam, + req_fd, reply_fd); + goto out_reg; + } else { + logmsg(LL_TRACE, "forked child %d\n", child); + return rc; } - logmsg(LL_TRACE, "forked child %d\n", child); - return 0; +out_reg: + lgss_fini(cred); + return rc; } } diff --git a/lustre/utils/gss/lgss_krb5_utils.c b/lustre/utils/gss/lgss_krb5_utils.c index d59e49f..ebe09fb 100644 --- a/lustre/utils/gss/lgss_krb5_utils.c +++ b/lustre/utils/gss/lgss_krb5_utils.c @@ -133,6 +133,8 @@ #include "lgss_utils.h" #include "lgss_krb5_utils.h" +char *lgss_client_realm; + static void lgss_krb5_mutex_lock(void) { if (lgss_mutex_lock(LGSS_MUTEX_KRB5)) { @@ -155,7 +157,6 @@ const char *krb5_cred_root_suffix = "lustre_root"; const char *krb5_cred_mds_suffix = "lustre_mds"; const char *krb5_cred_oss_suffix = "lustre_oss"; -char *krb5_this_realm = NULL; char *krb5_keytab_file = "/etc/krb5.keytab"; static int lgss_krb5_set_ccache_name(const char *ccname) @@ -173,35 +174,6 @@ static int lgss_krb5_set_ccache_name(const char *ccname) } static -int lgss_krb5_get_local_realm(void) -{ - krb5_context context = NULL; - krb5_error_code code; - int retval = -1; - - if (krb5_this_realm != NULL) - return 0; - - code = krb5_init_context(&context); - if (code) { - logmsg(LL_ERR, "init ctx: %s\n", krb5_err_msg(code)); - return -1; - } - - code = krb5_get_default_realm(context, &krb5_this_realm); - if (code) { - logmsg(LL_ERR, "get default realm: %s\n", krb5_err_msg(code)); - goto out; - } - - logmsg(LL_DEBUG, "Local realm: %s\n", krb5_this_realm); - retval = 0; -out: - krb5_free_context(context); - return retval; -} - -static int princ_is_local_realm(krb5_context ctx, krb5_principal princ) { return (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, princ), @@ -985,8 +957,13 @@ static int lgss_krb5_prepare_cred(struct lgss_cred *cred) cred->lc_mech_cred = NULL; if (cred->lc_root_flags != 0) { - if (lgss_krb5_get_local_realm()) + rc = gss_get_realm(lgss_client_realm); + if (rc) { + logmsg(LL_ERR, "ERROR: no Kerberos realm: %s\n", + error_message(rc)); return -1; + } + logmsg(LL_DEBUG, "Kerberos realm: %s\n", krb5_this_realm); rc = lkrb5_prepare_root_cred(cred); } else { @@ -1002,10 +979,29 @@ void lgss_krb5_release_cred(struct lgss_cred *cred) cred->lc_mech_cred = NULL; } -struct lgss_mech_type lgss_mech_krb5 = +static void lgss_krb5_fini(void) +{ + krb5_context context = NULL; + krb5_error_code code; + + if (krb5_this_realm) { + code = krb5_init_context(&context); + if (code) { + logmsg(LL_ERR, "ERROR: krb5 fini: init ctx: %s\n", + error_message(code)); + } else { + krb5_free_string(context, krb5_this_realm); + krb5_this_realm = NULL; + krb5_free_context(context); + } + } +} + +struct lgss_mech_type lgss_mech_krb5 = { - .lmt_name = "krb5", - .lmt_mech_n = LGSS_MECH_KRB5, - .lmt_prepare_cred = lgss_krb5_prepare_cred, - .lmt_release_cred = lgss_krb5_release_cred, + .lmt_name = "krb5", + .lmt_mech_n = LGSS_MECH_KRB5, + .lmt_prepare_cred = lgss_krb5_prepare_cred, + .lmt_release_cred = lgss_krb5_release_cred, + .lmt_fini = lgss_krb5_fini, }; diff --git a/lustre/utils/gss/lgss_krb5_utils.h b/lustre/utils/gss/lgss_krb5_utils.h index 5a136f6..74b10ad 100644 --- a/lustre/utils/gss/lgss_krb5_utils.h +++ b/lustre/utils/gss/lgss_krb5_utils.h @@ -25,6 +25,8 @@ extern struct lgss_mech_type lgss_mech_sk; #define LGSS_USER_CRED_DIR "/run/user/%U" #define LGSS_DEFAULT_CRED_PREFIX "krb5cc" +extern char *lgss_client_realm; + /* * convenient macros, these perhaps need further cleanup */ diff --git a/lustre/utils/gss/lgss_utils.c b/lustre/utils/gss/lgss_utils.c index 1a077fe..35641c3 100644 --- a/lustre/utils/gss/lgss_utils.c +++ b/lustre/utils/gss/lgss_utils.c @@ -449,6 +449,16 @@ int lgss_validate_cred(struct lgss_cred *cred, gss_buffer_desc *token, return 0; } +void lgss_fini(struct lgss_cred *cred) +{ + struct lgss_mech_type *mech = cred->lc_mech; + + logmsg(LL_TRACE, "finishing %s\n", mech->lmt_name); + + if (mech && mech->lmt_fini) + mech->lmt_fini(); +} + /**************************************** * helper functions * ****************************************/ diff --git a/lustre/utils/gss/lgss_utils.h b/lustre/utils/gss/lgss_utils.h index c708bd4..bfd7858 100644 --- a/lustre/utils/gss/lgss_utils.h +++ b/lustre/utils/gss/lgss_utils.h @@ -173,6 +173,7 @@ void lgss_release_cred(struct lgss_cred *cred); int lgss_using_cred(struct lgss_cred *cred); int lgss_validate_cred(struct lgss_cred *cred, gss_buffer_desc *token, gss_buffer_desc *ctx_token); +void lgss_fini(struct lgss_cred *cred); int lgss_get_service_str(char **string, uint32_t lsvc, uint64_t tgt_nid); diff --git a/lustre/utils/gss/lsupport.c b/lustre/utils/gss/lsupport.c index eda236f..545afa3 100644 --- a/lustre/utils/gss/lsupport.c +++ b/lustre/utils/gss/lsupport.c @@ -555,3 +555,53 @@ int lookup_mapping(char *princ, lnet_nid_t nid, uid_t *uid) printerr(LL_INFO, "no mapping for %s/%#Lx\n", princ, nid); return -1; } + +/* realm of this node */ +char *krb5_this_realm; + +static int gss_get_provided_realm(char *realm) +{ + if (krb5_this_realm) + return 0; + + if (!realm) + return -1; + + krb5_this_realm = strdup(realm); + return 0; +} + +static int gss_get_local_realm(void) +{ + krb5_context context = NULL; + krb5_error_code code; + + if (krb5_this_realm != NULL) + return 0; + + code = krb5_init_context(&context); + if (code) + return code; + + code = krb5_get_default_realm(context, &krb5_this_realm); + krb5_free_context(context); + + if (code) + return code; + + return 0; +} + +int gss_get_realm(char *realm) +{ + int rc; + + /* Try to use provided realm first. + * If no provided realm, get default local realm. + */ + rc = gss_get_provided_realm(realm); + if (rc) + rc = gss_get_local_realm(); + + return rc; +} diff --git a/lustre/utils/gss/lsupport.h b/lustre/utils/gss/lsupport.h index 8cdf274..bd60f1e 100644 --- a/lustre/utils/gss/lsupport.h +++ b/lustre/utils/gss/lsupport.h @@ -44,6 +44,8 @@ #include #include +#include + #define GSSD_CLI (0) #define GSSD_SVC (1) @@ -63,6 +65,7 @@ void gssd_exit_unique(int type); #define LUSTRE_GSS_MECH_SHIFT 16 extern const char * lustre_svc_name[]; +extern char *krb5_this_realm; enum lgss_mech { LGSS_MECH_KRB5 = 0, @@ -104,5 +107,6 @@ uid_t parse_uid(char *uidstr); void load_mapping(void); int mapping_empty(void); int lookup_mapping(char *princ, lnet_nid_t nid, uid_t *uid); +int gss_get_realm(char *realm); #endif /* __LSUPPORT_H__ */ diff --git a/lustre/utils/gss/svcgssd.c b/lustre/utils/gss/svcgssd.c index ff74036..32ad64c 100644 --- a/lustre/utils/gss/svcgssd.c +++ b/lustre/utils/gss/svcgssd.c @@ -168,6 +168,8 @@ sig_die(int signal) { /* destroy krb5 machine creds */ cleanup_mapping(); + /* cleanup allocated strings for realms */ + gssd_cleanup_realms(); printerr(LL_WARN, "exiting on signal %d\n", signal); exit(1); } @@ -182,19 +184,20 @@ sig_hup(int signal) static void usage(FILE *fp, char *progname) { - fprintf(fp, "usage: %s [ -fnvmogk ]\n", + fprintf(fp, "usage: %s [ -fnvmogkRsz ]\n", progname); - fprintf(stderr, "-f - Run in foreground\n"); - fprintf(stderr, "-n - Don't establish kerberos credentials\n"); - fprintf(stderr, "-v - Verbosity\n"); - fprintf(stderr, "-m - Service MDS\n"); - fprintf(stderr, "-o - Service OSS\n"); - fprintf(stderr, "-g - Service MGS\n"); - fprintf(stderr, "-k - Enable kerberos support\n"); + fprintf(stderr, "-f - Run in foreground\n"); + fprintf(stderr, "-n - Don't establish kerberos credentials\n"); + fprintf(stderr, "-v - Verbosity\n"); + fprintf(stderr, "-m - Service MDS\n"); + fprintf(stderr, "-o - Service OSS\n"); + fprintf(stderr, "-g - Service MGS\n"); + fprintf(stderr, "-k - Enable kerberos support\n"); + fprintf(stderr, "-R REALM - Kerberos Realm to use, instead of default\n"); #ifdef HAVE_OPENSSL_SSK - fprintf(stderr, "-s - Enable shared secret key support\n"); + fprintf(stderr, "-s - Enable shared secret key support\n"); #endif - fprintf(stderr, "-z - Enable gssnull support\n"); + fprintf(stderr, "-z - Enable gssnull support\n"); exit(fp == stderr); } @@ -207,36 +210,37 @@ main(int argc, char *argv[]) int verbosity = 0; int opt; int must_srv_mds = 0, must_srv_oss = 0, must_srv_mgs = 0; + char *realm = NULL; char *progname; - while ((opt = getopt(argc, argv, "fnvmogksz")) != -1) { + while ((opt = getopt(argc, argv, "fgkmnoR:svz")) != -1) { switch (opt) { case 'f': fg = 1; break; - case 'n': - get_creds = 0; + case 'g': + get_creds = 1; + must_srv_mgs = 1; break; - case 'v': - verbosity++; + case 'h': + usage(stdout, argv[0]); + break; + case 'k': + krb_enabled = 1; break; case 'm': get_creds = 1; must_srv_mds = 1; break; + case 'n': + get_creds = 0; + break; case 'o': get_creds = 1; must_srv_oss = 1; break; - case 'g': - get_creds = 1; - must_srv_mgs = 1; - break; - case 'k': - krb_enabled = 1; - break; - case 'h': - usage(stdout, argv[0]); + case 'R': + realm = optarg; break; case 's': #ifdef HAVE_OPENSSL_SSK @@ -247,6 +251,9 @@ main(int argc, char *argv[]) usage(stderr, argv[0]); #endif break; + case 'v': + verbosity++; + break; case 'z': null_enabled = 1; break; @@ -272,21 +279,30 @@ main(int argc, char *argv[]) #endif } + + if (realm && !krb_enabled) { + fprintf(stderr, "error: need -k option if -R is used\n"); + usage(stderr, argv[0]); + } + initerr(progname, verbosity, fg); /* For kerberos use gss mechanisms but ignore for sk and null */ if (krb_enabled) { + int ret; + if (gssd_check_mechs()) { printerr(LL_ERR, "ERROR: problem with gssapi library\n"); exit(1); } - if (gssd_get_local_realm()) { - printerr(LL_ERR, - "ERROR: Can't get Local Kerberos realm\n"); + ret = gss_get_realm(realm); + if (ret) { + printerr(LL_ERR, "ERROR: no Kerberos realm: %s\n", + error_message(ret)); exit(1); } - + printerr(LL_WARN, "Kerberos realm: %s\n", krb5_this_realm); if (get_creds && gssd_prepare_creds(must_srv_mgs, must_srv_mds, must_srv_oss)) { @@ -316,6 +332,7 @@ main(int argc, char *argv[]) svcgssd_run(); cleanup_mapping(); + gssd_cleanup_realms(); printerr(LL_ERR, "svcgssd_run returned!\n"); abort(); } -- 1.8.3.1