1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/preauth/test/cltest.c - Test clpreauth module */
3 /*
4 * Copyright (C) 2015, 2017 by the Massachusetts Institute of Technology.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * This module is used to test preauth interface features. At this time, the
35 * clpreauth module does the following:
36 *
37 * - It decrypts a message from the initial KDC pa-data using the reply key and
38 * prints it to stdout. (The unencrypted message "no key" can also be
39 * displayed.)
40 *
41 * - If a second round trip is requested, it prints the pa-data contents
42 * accompanying the second round trip request.
43 *
44 * - It pulls an "indicators" attribute from the gic preauth options and sends
45 * it to the server, instructing the kdcpreauth module to assert one or more
46 * space-separated authentication indicators. (This string is sent on both
47 * round trips if a second round trip is requested.)
48 *
49 * - If a KDC_ERR_ENCTYPE_NOSUPP error with e-data is received, it prints the
50 * accompanying error padata and sends a follow-up request containing
51 * "tryagain".
52 *
53 * - If the "fail_optimistic", "fail_2rt", or "fail_tryagain" gic options are
54 * set, it fails with a recognizable error string at the requested point in
55 * processing.
56 *
57 * - If the "disable_fallback" gic option is set, fallback is disabled when a
58 * client message is generated.
59 */
60
61 #include "k5-int.h"
62 #include <krb5/clpreauth_plugin.h>
63 #include "common.h"
64
65 static krb5_preauthtype pa_types[] = { TEST_PA_TYPE, 0 };
66
67 struct client_state {
68 char *indicators;
69 krb5_boolean fail_optimistic;
70 krb5_boolean fail_2rt;
71 krb5_boolean fail_tryagain;
72 krb5_boolean disable_fallback;
73 };
74
75 struct client_request_state {
76 krb5_boolean second_round_trip;
77 };
78
79 static krb5_error_code
test_init(krb5_context context,krb5_clpreauth_moddata * moddata_out)80 test_init(krb5_context context, krb5_clpreauth_moddata *moddata_out)
81 {
82 struct client_state *st;
83
84 st = malloc(sizeof(*st));
85 assert(st != NULL);
86 st->indicators = NULL;
87 st->fail_optimistic = st->fail_2rt = st->fail_tryagain = FALSE;
88 st->disable_fallback = FALSE;
89 *moddata_out = (krb5_clpreauth_moddata)st;
90 return 0;
91 }
92
93 static void
test_fini(krb5_context context,krb5_clpreauth_moddata moddata)94 test_fini(krb5_context context, krb5_clpreauth_moddata moddata)
95 {
96 struct client_state *st = (struct client_state *)moddata;
97
98 free(st->indicators);
99 free(st);
100 }
101
102 static void
test_request_init(krb5_context context,krb5_clpreauth_moddata moddata,krb5_clpreauth_modreq * modreq_out)103 test_request_init(krb5_context context, krb5_clpreauth_moddata moddata,
104 krb5_clpreauth_modreq *modreq_out)
105 {
106 struct client_request_state *reqst;
107
108 reqst = malloc(sizeof(*reqst));
109 assert(reqst != NULL);
110 reqst->second_round_trip = FALSE;
111 *modreq_out = (krb5_clpreauth_modreq)reqst;
112 }
113
114 static void
test_request_fini(krb5_context context,krb5_clpreauth_moddata moddata,krb5_clpreauth_modreq modreq)115 test_request_fini(krb5_context context, krb5_clpreauth_moddata moddata,
116 krb5_clpreauth_modreq modreq)
117 {
118 free(modreq);
119 }
120
121 static krb5_error_code
test_process(krb5_context context,krb5_clpreauth_moddata moddata,krb5_clpreauth_modreq modreq,krb5_get_init_creds_opt * opt,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,krb5_kdc_req * request,krb5_data * encoded_request_body,krb5_data * encoded_previous_request,krb5_pa_data * pa_data,krb5_prompter_fct prompter,void * prompter_data,krb5_pa_data *** out_pa_data)122 test_process(krb5_context context, krb5_clpreauth_moddata moddata,
123 krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
124 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
125 krb5_kdc_req *request, krb5_data *encoded_request_body,
126 krb5_data *encoded_previous_request, krb5_pa_data *pa_data,
127 krb5_prompter_fct prompter, void *prompter_data,
128 krb5_pa_data ***out_pa_data)
129 {
130 struct client_state *st = (struct client_state *)moddata;
131 struct client_request_state *reqst = (struct client_request_state *)modreq;
132 krb5_error_code ret;
133 krb5_keyblock *k;
134 krb5_enc_data enc;
135 krb5_data plain;
136 const char *indstr;
137
138 if (pa_data->length == 0) {
139 /* This is an optimistic preauth test. Send a recognizable padata
140 * value so the KDC knows not to expect a cookie. */
141 if (st->fail_optimistic) {
142 k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced optimistic fail");
143 return KRB5_PREAUTH_FAILED;
144 }
145 *out_pa_data = make_pa_list("optimistic", 10);
146 if (st->disable_fallback)
147 cb->disable_fallback(context, rock);
148 return 0;
149 } else if (reqst->second_round_trip) {
150 printf("2rt: %.*s\n", pa_data->length, pa_data->contents);
151 if (st->fail_2rt) {
152 k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced 2rt fail");
153 return KRB5_PREAUTH_FAILED;
154 }
155 } else if (pa_data->length == 6 &&
156 memcmp(pa_data->contents, "no key", 6) == 0) {
157 printf("no key\n");
158 } else {
159 /* This fails during s4u_identify_user(), so don't assert. */
160 ret = cb->get_as_key(context, rock, &k);
161 if (ret)
162 return ret;
163 ret = alloc_data(&plain, pa_data->length);
164 assert(!ret);
165 enc.enctype = k->enctype;
166 enc.ciphertext = make_data(pa_data->contents, pa_data->length);
167 ret = krb5_c_decrypt(context, k, 1024, NULL, &enc, &plain);
168 assert(!ret);
169 printf("%.*s\n", plain.length, plain.data);
170 free(plain.data);
171 }
172 reqst->second_round_trip = TRUE;
173
174 indstr = (st->indicators != NULL) ? st->indicators : "";
175 *out_pa_data = make_pa_list(indstr, strlen(indstr));
176 if (st->disable_fallback)
177 cb->disable_fallback(context, rock);
178 return 0;
179 }
180
181 static krb5_error_code
test_tryagain(krb5_context context,krb5_clpreauth_moddata moddata,krb5_clpreauth_modreq modreq,krb5_get_init_creds_opt * opt,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,krb5_kdc_req * request,krb5_data * enc_req,krb5_data * enc_prev,krb5_preauthtype pa_type,krb5_error * error,krb5_pa_data ** padata,krb5_prompter_fct prompter,void * prompter_data,krb5_pa_data *** padata_out)182 test_tryagain(krb5_context context, krb5_clpreauth_moddata moddata,
183 krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
184 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
185 krb5_kdc_req *request, krb5_data *enc_req, krb5_data *enc_prev,
186 krb5_preauthtype pa_type, krb5_error *error,
187 krb5_pa_data **padata, krb5_prompter_fct prompter,
188 void *prompter_data, krb5_pa_data ***padata_out)
189 {
190 struct client_state *st = (struct client_state *)moddata;
191 int i;
192
193 *padata_out = NULL;
194 if (st->fail_tryagain) {
195 k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced tryagain fail");
196 return KRB5_PREAUTH_FAILED;
197 }
198 if (error->error != KDC_ERR_ENCTYPE_NOSUPP)
199 return KRB5_PREAUTH_FAILED;
200 for (i = 0; padata[i] != NULL; i++) {
201 if (padata[i]->pa_type == TEST_PA_TYPE)
202 printf("tryagain: %.*s\n", padata[i]->length, padata[i]->contents);
203 }
204 *padata_out = make_pa_list("tryagain", 8);
205 return 0;
206 }
207
208 static krb5_error_code
test_gic_opt(krb5_context kcontext,krb5_clpreauth_moddata moddata,krb5_get_init_creds_opt * opt,const char * attr,const char * value)209 test_gic_opt(krb5_context kcontext, krb5_clpreauth_moddata moddata,
210 krb5_get_init_creds_opt *opt, const char *attr, const char *value)
211 {
212 struct client_state *st = (struct client_state *)moddata;
213
214 if (strcmp(attr, "indicators") == 0) {
215 free(st->indicators);
216 st->indicators = strdup(value);
217 assert(st->indicators != NULL);
218 } else if (strcmp(attr, "fail_optimistic") == 0) {
219 st->fail_optimistic = TRUE;
220 } else if (strcmp(attr, "fail_2rt") == 0) {
221 st->fail_2rt = TRUE;
222 } else if (strcmp(attr, "fail_tryagain") == 0) {
223 st->fail_tryagain = TRUE;
224 } else if (strcmp(attr, "disable_fallback") == 0) {
225 st->disable_fallback = TRUE;
226 }
227 return 0;
228 }
229
230 krb5_error_code
231 clpreauth_test_initvt(krb5_context context, int maj_ver,
232 int min_ver, krb5_plugin_vtable vtable);
233
234 krb5_error_code
clpreauth_test_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)235 clpreauth_test_initvt(krb5_context context, int maj_ver,
236 int min_ver, krb5_plugin_vtable vtable)
237 {
238 krb5_clpreauth_vtable vt;
239
240 if (maj_ver != 1)
241 return KRB5_PLUGIN_VER_NOTSUPP;
242 vt = (krb5_clpreauth_vtable)vtable;
243 vt->name = "test";
244 vt->pa_type_list = pa_types;
245 vt->init = test_init;
246 vt->fini = test_fini;
247 vt->request_init = test_request_init;
248 vt->request_fini = test_request_fini;
249 vt->process = test_process;
250 vt->tryagain = test_tryagain;
251 vt->gic_opts = test_gic_opt;
252 return 0;
253 }
254