From ae349ad4e95e221abf32f3f7360ca26ae4e69c9b Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 13 Apr 2010 22:57:34 +0000
Subject: [PATCH 109/120] Validate and renew should work on non-TGT creds

The validate and renew APIs were using get_cred_from_kdc, which
always presents a TGT to get credentials.  Instead, they should
present the ticket they are trying to validate or renew.  This is
most easily done with krb5_get_cred_via_tkt().  Move the relevant
code into a new file since it now has nothing in common with the
other APIs implemented in get_creds.c.

ticket: 6699

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23891 dc483132-0cff-0310-8789-dd5450dbe970
---
 src/lib/krb5/krb/Makefile.in |   3 +
 src/lib/krb5/krb/get_creds.c | 151 ---------------------------------
 src/lib/krb5/krb/val_renew.c | 194 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 197 insertions(+), 151 deletions(-)
 create mode 100644 src/lib/krb5/krb/val_renew.c

diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in
index abab3f0..deeb43b 100644
--- a/src/lib/krb5/krb/Makefile.in
+++ b/src/lib/krb5/krb/Makefile.in
@@ -101,6 +101,7 @@ STLIBOBJS= \
 	str_conv.o	\
 	tgtname.o	\
 	unparse.o	\
+	val_renew.o	\
 	valid_times.o	\
 	vfy_increds.o	\
 	vic_opt.o	\
@@ -195,6 +196,7 @@ OBJS=	$(OUTPRE)addr_comp.$(OBJEXT)	\
 	$(OUTPRE)str_conv.$(OBJEXT)	\
 	$(OUTPRE)tgtname.$(OBJEXT)	\
 	$(OUTPRE)unparse.$(OBJEXT)	\
+	$(OUTPRE)val_renew.$(OBJEXT)	\
 	$(OUTPRE)valid_times.$(OBJEXT)	\
 	$(OUTPRE)vfy_increds.$(OBJEXT)	\
 	$(OUTPRE)vic_opt.$(OBJEXT)	\
@@ -291,6 +293,7 @@ SRCS=	$(srcdir)/addr_comp.c	\
 	$(srcdir)/t_ad_fx_armor.c \
 	$(srcdir)/tgtname.c	\
 	$(srcdir)/unparse.c	\
+	$(srcdir)/val_renew.c	\
 	$(srcdir)/valid_times.c	\
 	$(srcdir)/vfy_increds.c \
 	$(srcdir)/vic_opt.c	\
diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c
index 5b2c1c2..396ea79 100644
--- a/src/lib/krb5/krb/get_creds.c
+++ b/src/lib/krb5/krb/get_creds.c
@@ -209,154 +209,3 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
     *out_creds = ncreds;
     return 0;
 }
-
-#define INT_GC_VALIDATE 1
-#define INT_GC_RENEW 2
-
-static krb5_error_code
-get_credentials_val_renew_core(krb5_context context, krb5_flags options,
-                               krb5_ccache ccache, krb5_creds *in_creds,
-                               krb5_creds **out_creds, int which)
-{
-    krb5_error_code retval;
-    krb5_principal tmp;
-    krb5_creds **tgts = 0;
-
-    switch(which) {
-    case INT_GC_VALIDATE:
-        retval = krb5_get_cred_from_kdc_validate(context, ccache,
-                                                 in_creds, out_creds, &tgts);
-        break;
-    case INT_GC_RENEW:
-        retval = krb5_get_cred_from_kdc_renew(context, ccache,
-                                              in_creds, out_creds, &tgts);
-        break;
-    default:
-        /* Should never happen */
-        retval = 255;
-        break;
-    }
-    /*
-     * Callers to krb5_get_cred_blah... must free up tgts even in
-     * error cases.
-     */
-    if (tgts) krb5_free_tgt_creds(context, tgts);
-    if (retval) return retval;
-
-    retval = krb5_cc_get_principal(context, ccache, &tmp);
-    if (retval) return retval;
-
-    retval = krb5_cc_initialize(context, ccache, tmp);
-    if (retval) return retval;
-
-    retval = krb5_cc_store_cred(context, ccache, *out_creds);
-    return retval;
-}
-
-krb5_error_code KRB5_CALLCONV
-krb5_get_credentials_validate(krb5_context context, krb5_flags options,
-                              krb5_ccache ccache, krb5_creds *in_creds,
-                              krb5_creds **out_creds)
-{
-    return(get_credentials_val_renew_core(context, options, ccache,
-                                          in_creds, out_creds,
-                                          INT_GC_VALIDATE));
-}
-
-krb5_error_code KRB5_CALLCONV
-krb5_get_credentials_renew(krb5_context context, krb5_flags options,
-                           krb5_ccache ccache, krb5_creds *in_creds,
-                           krb5_creds **out_creds)
-{
-
-    return(get_credentials_val_renew_core(context, options, ccache,
-                                          in_creds, out_creds,
-                                          INT_GC_RENEW));
-}
-
-static krb5_error_code
-validate_or_renew_creds(krb5_context context, krb5_creds *creds,
-                        krb5_principal client, krb5_ccache ccache,
-                        char *in_tkt_service, int validate)
-{
-    krb5_error_code ret;
-    krb5_creds in_creds; /* only client and server need to be filled in */
-    krb5_creds *out_creds = 0; /* for check before dereferencing below */
-    krb5_creds **tgts;
-
-    memset(&in_creds, 0, sizeof(krb5_creds));
-
-    in_creds.server = NULL;
-    tgts = NULL;
-
-    in_creds.client = client;
-
-    if (in_tkt_service) {
-        /* this is ugly, because so are the data structures involved.  I'm
-           in the library, so I'm going to manipulate the data structures
-           directly, otherwise, it will be worse. */
-
-        if ((ret = krb5_parse_name(context, in_tkt_service, &in_creds.server)))
-            goto cleanup;
-
-        /* stuff the client realm into the server principal.
-           realloc if necessary */
-        if (in_creds.server->realm.length < in_creds.client->realm.length)
-            if ((in_creds.server->realm.data =
-                 (char *) realloc(in_creds.server->realm.data,
-                                  in_creds.client->realm.length)) == NULL) {
-                ret = ENOMEM;
-                goto cleanup;
-            }
-
-        in_creds.server->realm.length = in_creds.client->realm.length;
-        memcpy(in_creds.server->realm.data, in_creds.client->realm.data,
-               in_creds.client->realm.length);
-    } else {
-        if ((ret = krb5_build_principal_ext(context, &in_creds.server,
-                                            in_creds.client->realm.length,
-                                            in_creds.client->realm.data,
-                                            KRB5_TGS_NAME_SIZE,
-                                            KRB5_TGS_NAME,
-                                            in_creds.client->realm.length,
-                                            in_creds.client->realm.data,
-                                            0)))
-            goto cleanup;
-    }
-
-    if (validate)
-        ret = krb5_get_cred_from_kdc_validate(context, ccache,
-                                              &in_creds, &out_creds, &tgts);
-    else
-        ret = krb5_get_cred_from_kdc_renew(context, ccache,
-                                           &in_creds, &out_creds, &tgts);
-
-    /* ick.  copy the struct contents, free the container */
-    if (out_creds) {
-        *creds = *out_creds;
-        free(out_creds);
-    }
-
-cleanup:
-
-    if (in_creds.server)
-        krb5_free_principal(context, in_creds.server);
-    if (tgts)
-        krb5_free_tgt_creds(context, tgts);
-
-    return(ret);
-}
-
-krb5_error_code KRB5_CALLCONV
-krb5_get_validated_creds(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_ccache ccache, char *in_tkt_service)
-{
-    return(validate_or_renew_creds(context, creds, client, ccache,
-                                   in_tkt_service, 1));
-}
-
-krb5_error_code KRB5_CALLCONV
-krb5_get_renewed_creds(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_ccache ccache, char *in_tkt_service)
-{
-    return(validate_or_renew_creds(context, creds, client, ccache,
-                                   in_tkt_service, 0));
-}
diff --git a/src/lib/krb5/krb/val_renew.c b/src/lib/krb5/krb/val_renew.c
new file mode 100644
index 0000000..46eff99
--- /dev/null
+++ b/src/lib/krb5/krb/val_renew.c
@@ -0,0 +1,194 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/krb5/krb/val_renew.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Implements the following APIs:
+ *
+ *   krb5_get_credentials_validate
+ *   krb5_get_credentials_renew
+ *   krb5_get_validated_creds
+ *   krb5_get_renewed_creds
+ *
+ * The first two are old but not formally deprecated; the latter two are newer.
+ */
+
+#include "k5-int.h"
+#include "int-proto.h"
+
+/*
+ * Get a validated or renewed credential matching in_creds, by retrieving a
+ * matching credential from ccache and renewing or validating it with the
+ * credential's realm's KDC.  kdcopt specifies whether to validate or renew.
+ * The result is placed in *out_creds.
+ */
+static krb5_error_code
+get_new_creds(krb5_context context, krb5_ccache ccache, krb5_creds *in_creds,
+	      krb5_flags kdcopt, krb5_creds **out_creds)
+{
+    krb5_error_code code;
+    krb5_creds old_creds, *new_creds = NULL;
+
+    *out_creds = NULL;
+
+    /* Retrieve an existing cached credential matching in_creds. */
+    code = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_SUPPORTED_KTYPES,
+				 in_creds, &old_creds);
+    if (code != 0)
+	return code;
+
+    /* Use it to get a new credential from the KDC. */
+    code = krb5_get_cred_via_tkt(context, &old_creds, kdcopt,
+				 old_creds.addresses, in_creds, &new_creds);
+    krb5_free_cred_contents(context, &old_creds);
+    if (code != 0)
+	return code;
+
+    *out_creds = new_creds;
+    return code;
+}
+
+/*
+ * Core of the older pair of APIs: get a validated or renewed credential
+ * matching in_creds and reinitialize ccache so that it contains only the new
+ * credential.
+ */
+static krb5_error_code
+gc_valrenew(krb5_context context, krb5_ccache ccache, krb5_creds *in_creds,
+	    krb5_flags kdcopt, krb5_creds **out_creds)
+{
+    krb5_error_code code;
+    krb5_creds *new_creds = NULL;
+    krb5_principal default_princ = NULL;
+
+    /* Get the validated or renewed credential. */
+    code = get_new_creds(context, ccache, in_creds, kdcopt, &new_creds);
+    if (code != 0)
+	goto cleanup;
+
+    /* Reinitialize the cache without changing its default principal. */
+    code = krb5_cc_get_principal(context, ccache, &default_princ);
+    if (code != 0)
+        goto cleanup;
+    code = krb5_cc_initialize(context, ccache, default_princ);
+    if (code != 0)
+        goto cleanup;
+
+    /* Store the validated or renewed cred in the now-empty cache. */
+    code = krb5_cc_store_cred(context, ccache, new_creds);
+    if (code != 0)
+	goto cleanup;
+
+    *out_creds = new_creds;
+    new_creds = NULL;
+
+cleanup:
+    krb5_free_principal(context, default_princ);
+    krb5_free_creds(context, new_creds);
+    return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_credentials_validate(krb5_context context, krb5_flags options,
+                              krb5_ccache ccache, krb5_creds *in_creds,
+                              krb5_creds **out_creds)
+{
+    return gc_valrenew(context, ccache, in_creds, KDC_OPT_VALIDATE, out_creds);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_credentials_renew(krb5_context context, krb5_flags options,
+                           krb5_ccache ccache, krb5_creds *in_creds,
+                           krb5_creds **out_creds)
+{
+    return gc_valrenew(context, ccache, in_creds, KDC_OPT_RENEW, out_creds);
+}
+
+/*
+ * Core of the newer pair of APIs: get new credentials for in_tkt_service
+ * (defaults to the TGT of the client's realm) and store them into *out_creds.
+ */
+static krb5_error_code
+get_valrenewed_creds(krb5_context context, krb5_creds *out_creds,
+		     krb5_principal client, krb5_ccache ccache,
+		     char *in_tkt_service, int kdcopt)
+{
+    krb5_error_code code;
+    krb5_creds in_creds, *new_creds;
+    krb5_principal server = NULL;
+
+    if (in_tkt_service != NULL) {
+	/* Parse in_tkt_service, but use the client's realm. */
+	code = krb5_parse_name(context, in_tkt_service, &server);
+	if (code != 0)
+	    goto cleanup;
+	krb5_free_data_contents(context, &server->realm);
+	code = krb5int_copy_data_contents(context, &client->realm,
+					  &server->realm);
+	if (code != 0)
+	    goto cleanup;
+    } else {
+	/* Use the TGT name for the client's realm. */
+	code = krb5int_tgtname(context, &client->realm, &client->realm,
+			       &server);
+	if (code != 0)
+	    goto cleanup;
+    }
+
+    memset(&in_creds, 0, sizeof(krb5_creds));
+    in_creds.client = client;
+    in_creds.server = server;
+
+    /* Get the validated or renewed credential from the KDC. */
+    code = get_new_creds(context, ccache, &in_creds, kdcopt, &new_creds);
+    if (code != 0)
+	goto cleanup;
+
+    /* Fill in *out_creds and free the unwanted new_creds container. */
+    *out_creds = *new_creds;
+    free(new_creds);
+
+cleanup:
+    krb5_free_principal(context, server);
+    return code;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_validated_creds(krb5_context context, krb5_creds *creds,
+			 krb5_principal client, krb5_ccache ccache,
+			 char *in_tkt_service)
+{
+    return get_valrenewed_creds(context, creds, client, ccache,
+				in_tkt_service, KDC_OPT_VALIDATE);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_renewed_creds(krb5_context context, krb5_creds *creds,
+		       krb5_principal client, krb5_ccache ccache,
+		       char *in_tkt_service)
+{
+    return get_valrenewed_creds(context, creds, client, ccache,
+				in_tkt_service, KDC_OPT_RENEW);
+}
-- 
2.6.6

