Re: support for Kerberos credential cache locations other than FILE:?

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

 



On 12/04/2014 12:22 AM, James Ralston wrote:
> The MIT Kerberos library has supported credential cache locations
> other than "FILE:" for a while now:
> 
>     http://web.mit.edu/kerberos/krb5-devel/doc/basic/ccache_def.html
> 
> In fact, RHEL7 sets the default credential cache to use the kernel
> keyring, via the new(ish) "default_ccache_name" option in
> /etc/krb5.conf:
> 
>     [libdefaults]
>     default_ccache_name = KEYRING:persistent:%{uid}
> 
> However, this will break forwarding Kerberos credentials via
> GSSAPIDelegateCredentials. 

It seems to work for me using rhel-7's openssh sshd:

tom@ipa-server $ klist
Ticket cache: DIR::/tmp/597000004/tkthx4K48
Default principal: tom@VIRT

Valid starting       Expires              Service principal
12/10/2014 19:08:41  12/11/2014 19:08:41  krbtgt/VIRT@VIRT

12/10/2014 19:08:50  12/11/2014 19:08:41  host/rhel-7-devel.virt@VIRT



tom@ipa-server $ ssh -o GSSAPIDelegateCredentials=yes
tom@xxxxxxxxxxxxxxxxx
Last login: Wed Dec 10 19:29:31 2014 from master.virt



tom@rhel-7-devel $ klist
Ticket cache: KEYRING:persistent:597000004:krb_ccache_t0MhS7l
Default principal: tom@VIRT

Valid starting       Expires              Service principal
12/10/2014 19:29:50  12/11/2014 19:08:41  krbtgt/VIRT@VIRT


And using sshd from stock openssh, it works for me too:

tom@ipa-server $ ssh -o GSSAPIDelegateCredentials=yes -o
GSSAPIAuthentication=yes  tom@xxxxxxxxxxxxxxxxx
Last login: Wed Dec 10 19:35:22 2014 from ipa-server.virt

tom@rhel-7-devel $ klist
Ticket cache: FILE:/tmp/krb5cc_597000004_rubDAg07mz
Default principal: tom@VIRT

Valid starting       Expires              Service principal
12/10/2014 19:44:19  12/11/2014 19:08:41  krbtgt/VIRT@VIRT

and sshd logs show:
sshd[27716]: debug1: Setting KRB5CCNAME to
FILE:/tmp/krb5cc_597000004_rubDAg07mz

> ...

> Based on this, I would expect openssh to blast over the library
> default, but when I test, I see $KRB5CCNAME set to (e.g.)
> KEYRING:persistent:12345, but without credentials in it.  (And tracing
> the sshd process shows that it never attempted to create any
> file-based credential cache.)
> 
> So, two questions:
> 
> First, does anyone know what happens to the credential cache openssh
> creates when the library default location is a keyring?  Openssh isn't
> logging any errors, so I don't think the various krb5 library
> functions are failing.  Has anyone else already played around with
> this?

Is it possible that you don't use GSSAPIAuthentication but e.g.
PublicKeyAuthentication? Can you see an attempt to send credentials in
ssh client logs? Using 'ssh -vv ...' might help.


> 
> Second, is there a reason why openssh hardcodes the ccname location,
> instead of using krb5_cc_default_name() to obtain the library default?
> 
> The only reason I can see for doing this is because if the library
> default starts with FILE: or DIR:, then you need to append
> "XXXXXXXXXX" (if necessary) and then use mkstemp() to get a
> non-predictable location.  So that would be a little more effort than
> what openssh currently does.
> 
> However, this would enable openssh to support Kerberos credential
> types other than "FILE:", so I'd argue it's worth it.
> 

Please try attached patch or krb5_cc_default_name branch at [1].

It adds support for using Kerberos credential cache locations based on
system wide configuration in /etc/krb5.conf. It tries to read a value of:

[libdefaults]
default_ccache_name = KEYRING:persistent:%{uid}

and parse it. If it's not able to get the value or parse it, it falls
back to the original FILE: template.

The patch also adds support for DIR and KEYRING types.


[1]
https://github.com/bachradsusi/openssh-portable/tree/krb5_cc_default_name

Petr
-- 
Petr Lautrbach

From 530b2e88029237646048468fb17ea359e899d5ec Mon Sep 17 00:00:00 2001
From: Petr Lautrbach <plautrba@xxxxxxxxxx>
Date: Wed, 10 Dec 2014 19:13:38 +0100
Subject: [PATCH] Add support for using kerberos credential location set by
 "default_ccache_name" in /etc/krb5.conf

---
 auth-krb5.c     | 171 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 configure.ac    |   2 +-
 gss-serv-krb5.c |  23 ++++++--
 gss-serv.c      |  19 +++++--
 4 files changed, 178 insertions(+), 37 deletions(-)

diff --git a/auth-krb5.c b/auth-krb5.c
index 0089b18..1139134 100644
--- a/auth-krb5.c
+++ b/auth-krb5.c
@@ -52,6 +52,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <krb5.h>
+#include <profile.h>
 
 extern ServerOptions	 options;
 
@@ -78,7 +79,7 @@ auth_krb5_password(Authctxt *authctxt, const char *password)
 #endif
 	krb5_error_code problem;
 	krb5_ccache ccache = NULL;
-	int len;
+	const char *ccache_type, *ccache_name;
 	char *client, *platform_client;
 	const char *errmsg;
 
@@ -179,12 +180,23 @@ auth_krb5_password(Authctxt *authctxt, const char *password)
 		goto out;
 #endif
 
-	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
+	ccache_type = krb5_cc_get_type(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
+	ccache_name = krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
 
-	len = strlen(authctxt->krb5_ticket_file) + 6;
-	authctxt->krb5_ccname = xmalloc(len);
-	snprintf(authctxt->krb5_ccname, len, "FILE:%s",
-	    authctxt->krb5_ticket_file);
+	if (ccache_type == NULL || ccache_name == NULL)
+		goto out;
+
+	if (ccache_name[0] == ':')
+		/* DIR caches has two forms: DIR:dirname or DIR::pathname */
+		authctxt->krb5_ticket_file = xstrdup(ccache_name + 1);
+	else
+		authctxt->krb5_ticket_file = xstrdup(ccache_name);
+
+#ifdef USE_CCAPI
+	xasprintf(&authctxt->krb5_ccname, "API:%s", ccache_name);
+#else
+	xasprintf(&authctxt->krb5_ccname, "%s:%s", ccache_type, ccache_name);
+#endif
 
 #ifdef USE_PAM
 	if (options.use_pam)
@@ -236,36 +248,147 @@ krb5_cleanup_proc(Authctxt *authctxt)
 		krb5_free_context(authctxt->krb5_ctx);
 		authctxt->krb5_ctx = NULL;
 	}
+	if (authctxt->krb5_ccname) {
+		free(authctxt->krb5_ccname);
+		authctxt->krb5_ccname = NULL;
+	}
+	if (authctxt->krb5_ticket_file) {
+		free(authctxt->krb5_ticket_file);
+		authctxt->krb5_ticket_file = NULL;
+	}
+}
+
+int
+ssh_asprintf_append(char **dsc, const char *fmt, ...) {
+	char *src, *old;
+	va_list ap;
+	int i;
+
+	va_start(ap, fmt);
+	i = vasprintf(&src, fmt, ap);
+	va_end(ap);
+
+	if (i == -1 || src == NULL)
+		return -1;
+
+	old = *dsc;
+
+	i = xasprintf(dsc, "%s%s", *dsc, src);
+	if (i == -1 || src == NULL) {
+		free(src);
+		return -1;
+	}
+
+	free(old);
+	free(src);
+
+	return i;
+}
+
+int
+ssh_krb5_expand_template(char **result, const char *template) {
+	char *p_n, *p_o, *r, *tmp_template;
+
+	if (template == NULL)
+		return -1;
+
+	tmp_template = p_n = p_o = xstrdup(template);
+	r = xstrdup("");
+
+	while ((p_n = strstr(p_o, "%{")) != NULL) {
+
+		*p_n++ = '\0';
+		if (ssh_asprintf_append(&r, "%s", p_o) == -1)
+			goto cleanup;
+
+		if (strncmp(p_n, "{uid}", 5) == 0 || strncmp(p_n, "{euid}", 6) == 0 ||
+			strncmp(p_n, "{USERID}", 8) == 0) {
+			p_o = strchr(p_n, '}') + 1;
+			if (ssh_asprintf_append(&r, "%d", geteuid()) == -1)
+				goto cleanup;
+			continue;
+		}
+		else if (strncmp(p_n, "{TEMP}", 6) == 0) {
+			p_o = strchr(p_n, '}') + 1;
+			if (ssh_asprintf_append(&r, "/tmp") == -1)
+				goto cleanup;
+			continue;
+		} else {
+			p_o = strchr(p_n, '}') + 1;
+			p_o = '\0';
+			debug("%s: unsupported token %s in %s", __func__, p_n, template);
+			/* unknown token, fallback to the default */
+			goto cleanup;
+		}
+	}
+
+	if (ssh_asprintf_append(&r, "%s", p_o) == -1)
+		goto cleanup;
+
+	*result = r;
+	free(tmp_template);
+	return 0;
+
+cleanup:
+	free(r);
+	free(tmp_template);
+	return -1;
+}
+
+krb5_error_code
+ssh_krb5_get_cctemplate(krb5_context ctx, char **ccname) {
+	profile_t p;
+	int ret = 0;
+	char *value = NULL;
+
+	ret = krb5_get_profile(ctx, &p);
+	if (ret)
+		return ret;
+
+	ret = profile_get_string(p, "libdefaults", "default_ccache_name", NULL, NULL, &value);
+	if (ret)
+		return ret;
+
+	ret = ssh_krb5_expand_template(ccname, value);
+
+	return ret;
 }
 
 #ifndef HEIMDAL
 krb5_error_code
 ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
 	int tmpfd, ret, oerrno;
-	char ccname[40];
+	char *ccname;
+#ifdef USE_CCAPI
+	char cctemplate[] = "API:krb5cc_%d";
+#else
 	mode_t old_umask;
+	char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX";
 
-	ret = snprintf(ccname, sizeof(ccname),
-	    "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid());
-	if (ret < 0 || (size_t)ret >= sizeof(ccname))
-		return ENOMEM;
-
-	old_umask = umask(0177);
-	tmpfd = mkstemp(ccname + strlen("FILE:"));
-	oerrno = errno;
-	umask(old_umask);
-	if (tmpfd == -1) {
-		logit("mkstemp(): %.100s", strerror(oerrno));
-		return oerrno;
-	}
+#endif
+
+	ret = ssh_krb5_get_cctemplate(ctx, &ccname);
 
-	if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
+	if (ret) {
+		ret = xasprintf(&ccname, cctemplate, geteuid());
+		old_umask = umask(0177);
+		tmpfd = mkstemp(ccname + strlen("FILE:"));
 		oerrno = errno;
-		logit("fchmod(): %.100s", strerror(oerrno));
+		umask(old_umask);
+		if (tmpfd == -1) {
+			logit("mkstemp(): %.100s", strerror(oerrno));
+			return oerrno;
+		}
+
+		if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
+			oerrno = errno;
+			logit("fchmod(): %.100s", strerror(oerrno));
+			close(tmpfd);
+			return oerrno;
+		}
 		close(tmpfd);
-		return oerrno;
 	}
-	close(tmpfd);
+	debug("%s: Setting ccname to %s", __func__, ccname);
 
 	return (krb5_cc_resolve(ctx, ccname, ccache));
 }
diff --git a/configure.ac b/configure.ac
index 5f5905b..7df6fc3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4025,7 +4025,7 @@ AC_ARG_WITH([kerberos5],
 		]])
 		saved_LIBS="$LIBS"
 		LIBS="$LIBS $K5LIBS"
-		AC_CHECK_FUNCS([krb5_cc_new_unique krb5_get_error_message krb5_free_error_message])
+		AC_CHECK_FUNCS([krb5_cc_new_unique krb5_get_error_message krb5_free_error_message krb5_cc_get_type])
 		LIBS="$saved_LIBS"
 
 	fi
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
index 795992d..27e6642 100644
--- a/gss-serv-krb5.c
+++ b/gss-serv-krb5.c
@@ -121,8 +121,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
 	krb5_error_code problem;
 	krb5_principal princ;
 	OM_uint32 maj_status, min_status;
-	int len;
-	const char *errmsg;
+	const char *ccache_name, *ccache_type, *errmsg;
 
 	if (client->creds == NULL) {
 		debug("No credentials stored");
@@ -181,11 +180,23 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
 		return;
 	}
 
-	client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache));
+	ccache_name = krb5_cc_get_name(krb_context, ccache);
+	ccache_type = krb5_cc_get_type(krb_context, ccache);
+	if (ccache_name == NULL || ccache_type == NULL) {
+		logit("can't get krb5 cache name or type");
+		krb5_cc_destroy(krb_context, ccache);
+		return;
+	}
+
+	if (ccache_name[0] == ':')
+		/* for DIR::pathname form ccache_name begins with : */
+		client->store.filename = xstrdup(ccache_name + 1);
+	else
+		client->store.filename = xstrdup(ccache_name);
+
+
 	client->store.envvar = "KRB5CCNAME";
-	len = strlen(client->store.filename) + 6;
-	client->store.envval = xmalloc(len);
-	snprintf(client->store.envval, len, "FILE:%s", client->store.filename);
+	xasprintf(&client->store.envval, "%s:%s", ccache_type, ccache_name);
 
 #ifdef USE_PAM
 	if (options.use_pam)
diff --git a/gss-serv.c b/gss-serv.c
index 5c59924..7237db7 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -311,12 +311,19 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
 void
 ssh_gssapi_cleanup_creds(void)
 {
-	if (gssapi_client.store.filename != NULL) {
-		/* Unlink probably isn't sufficient */
-		debug("removing gssapi cred file\"%s\"",
-		    gssapi_client.store.filename);
-		unlink(gssapi_client.store.filename);
-	}
+	krb5_ccache ccache;
+	krb5_context ctx;
+
+	if (gssapi_client.creds == NULL)
+		return;
+
+	if (krb5_init_context(&ctx))
+		return;
+
+	krb5_cc_resolve(ctx, gssapi_client.store.envval, &ccache);
+	krb5_cc_destroy(ctx, ccache);
+
+	krb5_free_context(ctx);
 }
 
 /* As user */
-- 
2.2.0

_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@xxxxxxxxxxx
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux