summaryrefslogtreecommitdiffstats
path: root/krb5-1.12-pwdch-fast.patch
blob: 3b7d4bb508f1a1fb69ba221908a4c93e7373ab9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
From 230858394d2dded001ef3d2029daa6c468aca097 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 28 Feb 2014 14:49:35 -0500
Subject: [PATCH] Use preauth options when changing password

If we try to change the password in rb5_get_init_creds_password, we
must use all application-specified gic options which affect
preauthentication when getting the kadmin/changepw ticket.  Create a
helper function make_chpw_options which copies the application's
options, unsets the options we don't want, and sets options
appropriate for a temporary ticket.

ticket: 7868

npmccallum:
  * include tests from 06817686bfdef99523f300464bcbb0c8b037a27d
---
 src/lib/krb5/krb/gic_pwd.c | 63 +++++++++++++++++++++++++++++++++++++---------
 src/tests/Makefile.in      |  1 +
 src/tests/t_changepw.py    | 37 +++++++++++++++++++++++++++
 3 files changed, 89 insertions(+), 12 deletions(-)
 create mode 100644 src/tests/t_changepw.py

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