summaryrefslogtreecommitdiffstats
path: root/krb5-1.12-pwdch-fast.patch
blob: 4751c9fc93ef13364e6fbb58e6e072740fbdf903 (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
177
From 0c00989f5bdaf9adf6d243455fbc7056bd0013f5 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
  * backport from 1.12 to 1.11
---
 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 9fdb8934a0765395ebc2b875d31b393d954bcbb4..c6c3161ca777698f0a7cbb789df34e51fa85b12c 100644
--- a/src/lib/krb5/krb/gic_pwd.c
+++ b/src/lib/krb5/krb/gic_pwd.c
@@ -241,6 +241,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,
+                  krb5_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 (krb5_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 (krb5_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,
@@ -258,6 +306,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;
+    krb5_gic_opt_private storage2;
     struct gak_password gakpw;
     krb5_data pw0, pw1;
     char banner[1024], pw0array[1024], pw1array[1024];
@@ -347,16 +397,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);
     if ((ret = krb5int_get_init_creds(context, &chpw_creds, client,
                                       prompter, data,
                                       start_time, "kadmin/changepw", chpw_opts,
@@ -473,8 +514,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 9f76e9a7fad23e03b37e067d58c75fd7eaa8e673..74a6bdcff95618b6c9c77d5b50a243790af557e3 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -71,6 +71,7 @@ check-pytests:: hist t_init_creds
 	$(RUNPYTEST) $(srcdir)/t_iprop.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_anonpkinit.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_lockout.py $(PYTESTFLAGS)
+	$(RUNPYTEST) $(srcdir)/t_changepw.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_kadm5_hook.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_kdb_locking.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_keyrollover.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