Blame SOURCES/krb5-1.12-pwdch-fast.patch

4be148
From 230858394d2dded001ef3d2029daa6c468aca097 Mon Sep 17 00:00:00 2001
7d335d
From: Greg Hudson <ghudson@mit.edu>
7d335d
Date: Fri, 28 Feb 2014 14:49:35 -0500
7d335d
Subject: [PATCH] Use preauth options when changing password
7d335d
7d335d
If we try to change the password in rb5_get_init_creds_password, we
7d335d
must use all application-specified gic options which affect
7d335d
preauthentication when getting the kadmin/changepw ticket.  Create a
7d335d
helper function make_chpw_options which copies the application's
7d335d
options, unsets the options we don't want, and sets options
7d335d
appropriate for a temporary ticket.
7d335d
7d335d
ticket: 7868
7d335d
7d335d
npmccallum:
7d335d
  * include tests from 06817686bfdef99523f300464bcbb0c8b037a27d
7d335d
---
7d335d
 src/lib/krb5/krb/gic_pwd.c | 63 +++++++++++++++++++++++++++++++++++++---------
7d335d
 src/tests/Makefile.in      |  1 +
7d335d
 src/tests/t_changepw.py    | 37 +++++++++++++++++++++++++++
7d335d
 3 files changed, 89 insertions(+), 12 deletions(-)
7d335d
 create mode 100644 src/tests/t_changepw.py
7d335d
7d335d
diff --git a/src/lib/krb5/krb/gic_pwd.c b/src/lib/krb5/krb/gic_pwd.c
4be148
index a97823f6b51b7393755e82f36612c30b64096754..6aec7c3a71f99d2194b09374b296327174e6d4b8 100644
7d335d
--- a/src/lib/krb5/krb/gic_pwd.c
7d335d
+++ b/src/lib/krb5/krb/gic_pwd.c
4be148
@@ -242,6 +242,54 @@ warn_pw_expiry(krb5_context context, krb5_get_init_creds_opt *options,
7d335d
     (*prompter)(context, data, 0, banner, 0, 0);
7d335d
 }
7d335d
 
7d335d
+/*
7d335d
+ * Create a temporary options structure for getting a kadmin/changepw ticket,
7d335d
+ * based on the appplication-specified options.  Propagate all application
7d335d
+ * options which affect preauthentication, but not options which affect the
7d335d
+ * resulting ticket or how it is stored.  Set lifetime and flags appropriate
7d335d
+ * for a ticket which we will use immediately and then discard.
7d335d
+ *
7d335d
+ * storage1 and storage2 will be used to hold the temporary options.  The
7d335d
+ * caller must not free the result, as it will contain aliases into the
7d335d
+ * application options.
7d335d
+ */
7d335d
+static krb5_get_init_creds_opt *
7d335d
+make_chpw_options(krb5_get_init_creds_opt *in, krb5_gic_opt_ext *storage1,
4be148
+                  gic_opt_private *storage2)
7d335d
+{
7d335d
+    krb5_gic_opt_ext *in_ext;
7d335d
+    krb5_get_init_creds_opt *opt;
7d335d
+
7d335d
+    /* Copy the application's options to storage. */
7d335d
+    if (in == NULL) {
7d335d
+        storage1->flags = 0;
4be148
+    } else if (gic_opt_is_extended(in)) {
7d335d
+        in_ext = (krb5_gic_opt_ext *)in;
7d335d
+        *storage1 = *in_ext;
7d335d
+        *storage2 = *in_ext->opt_private;
7d335d
+        storage1->opt_private = storage2;
7d335d
+    } else {
7d335d
+        *(krb5_get_init_creds_opt *)storage1 = *in;
7d335d
+    }
7d335d
+
7d335d
+    /* Get a non-forwardable, non-proxiable, short-lifetime ticket. */
7d335d
+    opt = (krb5_get_init_creds_opt *)storage1;
7d335d
+    krb5_get_init_creds_opt_set_tkt_life(opt, 5 * 60);
7d335d
+    krb5_get_init_creds_opt_set_renew_life(opt, 0);
7d335d
+    krb5_get_init_creds_opt_set_forwardable(opt, 0);
7d335d
+    krb5_get_init_creds_opt_set_proxiable(opt, 0);
7d335d
+
7d335d
+    /* Unset options which should only apply to the actual ticket. */
7d335d
+    opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST;
7d335d
+    opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_ANONYMOUS;
7d335d
+
7d335d
+    /* The output ccache should only be used for the actual ticket. */
4be148
+    if (gic_opt_is_extended(opt))
7d335d
+        storage2->out_ccache = NULL;
7d335d
+
7d335d
+    return opt;
7d335d
+}
7d335d
+
7d335d
 krb5_error_code KRB5_CALLCONV
7d335d
 krb5_get_init_creds_password(krb5_context context,
7d335d
                              krb5_creds *creds,
4be148
@@ -259,6 +307,8 @@ krb5_get_init_creds_password(krb5_context context,
7d335d
     int tries;
7d335d
     krb5_creds chpw_creds;
7d335d
     krb5_get_init_creds_opt *chpw_opts = NULL;
7d335d
+    krb5_gic_opt_ext storage1;
4be148
+    gic_opt_private storage2;
7d335d
     struct gak_password gakpw;
7d335d
     krb5_data pw0, pw1;
7d335d
     char banner[1024], pw0array[1024], pw1array[1024];
4be148
@@ -345,16 +395,7 @@ krb5_get_init_creds_password(krb5_context context,
7d335d
     /* ok, we have an expired password.  Give the user a few chances
7d335d
        to change it */
7d335d
 
7d335d
-    /* use a minimal set of options */
7d335d
-
7d335d
-    ret = krb5_get_init_creds_opt_alloc(context, &chpw_opts);
7d335d
-    if (ret)
7d335d
-        goto cleanup;
7d335d
-    krb5_get_init_creds_opt_set_tkt_life(chpw_opts, 5*60);
7d335d
-    krb5_get_init_creds_opt_set_renew_life(chpw_opts, 0);
7d335d
-    krb5_get_init_creds_opt_set_forwardable(chpw_opts, 0);
7d335d
-    krb5_get_init_creds_opt_set_proxiable(chpw_opts, 0);
7d335d
-
7d335d
+    chpw_opts = make_chpw_options(options, &storage1, &storage2);
4be148
     ret = k5_get_init_creds(context, &chpw_creds, client, prompter, data,
4be148
                             start_time, "kadmin/changepw", chpw_opts,
4be148
                             krb5_get_as_key_password, &gakpw, &use_master,
4be148
@@ -471,8 +512,6 @@ cleanup:
7d335d
         warn_pw_expiry(context, options, prompter, data, in_tkt_service,
7d335d
                        as_reply);
7d335d
 
7d335d
-    if (chpw_opts)
7d335d
-        krb5_get_init_creds_opt_free(context, chpw_opts);
7d335d
     zapfree(gakpw.storage.data, gakpw.storage.length);
7d335d
     memset(pw0array, 0, sizeof(pw0array));
7d335d
     memset(pw1array, 0, sizeof(pw1array));
7d335d
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
4be148
index 62523895d53da24844141a6ada6cab23e77dd9e6..55f1d6419f8d924a6f9a2971d36f1eac6d293d32 100644
7d335d
--- a/src/tests/Makefile.in
7d335d
+++ b/src/tests/Makefile.in
4be148
@@ -94,6 +94,7 @@ check-pytests:: t_init_creds t_localauth
7d335d
 	$(RUNPYTEST) $(srcdir)/t_iprop.py $(PYTESTFLAGS)
4be148
 	$(RUNPYTEST) $(srcdir)/t_kprop.py $(PYTESTFLAGS)
4be148
 	$(RUNPYTEST) $(srcdir)/t_policy.py $(PYTESTFLAGS)
7d335d
+	$(RUNPYTEST) $(srcdir)/t_changepw.py $(PYTESTFLAGS)
4be148
 	$(RUNPYTEST) $(srcdir)/t_pkinit.py $(PYTESTFLAGS)
4be148
 	$(RUNPYTEST) $(srcdir)/t_otp.py $(PYTESTFLAGS)
4be148
 	$(RUNPYTEST) $(srcdir)/t_localauth.py $(PYTESTFLAGS)
7d335d
diff --git a/src/tests/t_changepw.py b/src/tests/t_changepw.py
7d335d
new file mode 100644
7d335d
index 0000000000000000000000000000000000000000..0b9832668e618b3db8d88cf388ec918898bb4df3
7d335d
--- /dev/null
7d335d
+++ b/src/tests/t_changepw.py
7d335d
@@ -0,0 +1,37 @@
7d335d
+#!/usr/bin/python
7d335d
+from k5test import *
7d335d
+
7d335d
+# This file is intended to cover any password-changing mechanism.  For
7d335d
+# now it only contains a regression test for #7868.
7d335d
+
7d335d
+realm = K5Realm(create_host=False, get_creds=False, start_kadmind=True)
7d335d
+
7d335d
+# Mark a principal as expired and change its password through kinit.
7d335d
+realm.run_kadminl('modprinc -pwexpire "1 day ago" user')
7d335d
+pwinput = password('user') + '\nabcd\nabcd\n'
7d335d
+realm.run([kinit, realm.user_princ], input=pwinput)
7d335d
+
7d335d
+# Do the same thing with FAST, with tracing turned on.
7d335d
+realm.run_kadminl('modprinc -pwexpire "1 day ago" user')
7d335d
+pwinput = 'abcd\nefgh\nefgh\n'
7d335d
+tracefile = os.path.join(realm.testdir, 'trace')
7d335d
+realm.run(['env', 'KRB5_TRACE=' + tracefile, kinit, '-T', realm.ccache,
7d335d
+           realm.user_princ], input=pwinput)
7d335d
+
7d335d
+# Read the trace and check that FAST was used when getting the
7d335d
+# kadmin/changepw ticket.
7d335d
+f = open(tracefile, 'r')
7d335d
+trace = f.read()
7d335d
+f.close()
7d335d
+getting_changepw = fast_used_for_changepw = False
7d335d
+for line in trace.splitlines():
7d335d
+    if 'Getting initial credentials for user@' in line:
7d335d
+        getting_changepw_ticket = False
7d335d
+    if 'Setting initial creds service to kadmin/changepw' in line:
7d335d
+        getting_changepw_ticket = True
7d335d
+    if getting_changepw_ticket and 'Using FAST' in line:
7d335d
+        fast_used_for_changepw = True
7d335d
+if not fast_used_for_changepw:
7d335d
+    fail('FAST was not used to get kadmin/changepw ticket')
7d335d
+
7d335d
+success('Password change tests')
7d335d
-- 
7d335d
1.8.5.3
7d335d