1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* plugins/preauth/test/kdctest.c - Test kdcpreauth module */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert * Copyright (C) 2015, 2017 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert * All rights reserved.
6*7f2fe78bSCy Schubert *
7*7f2fe78bSCy Schubert * Redistribution and use in source and binary forms, with or without
8*7f2fe78bSCy Schubert * modification, are permitted provided that the following conditions
9*7f2fe78bSCy Schubert * are met:
10*7f2fe78bSCy Schubert *
11*7f2fe78bSCy Schubert * * Redistributions of source code must retain the above copyright
12*7f2fe78bSCy Schubert * notice, this list of conditions and the following disclaimer.
13*7f2fe78bSCy Schubert *
14*7f2fe78bSCy Schubert * * Redistributions in binary form must reproduce the above copyright
15*7f2fe78bSCy Schubert * notice, this list of conditions and the following disclaimer in
16*7f2fe78bSCy Schubert * the documentation and/or other materials provided with the
17*7f2fe78bSCy Schubert * distribution.
18*7f2fe78bSCy Schubert *
19*7f2fe78bSCy Schubert * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*7f2fe78bSCy Schubert * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*7f2fe78bSCy Schubert * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22*7f2fe78bSCy Schubert * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23*7f2fe78bSCy Schubert * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24*7f2fe78bSCy Schubert * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*7f2fe78bSCy Schubert * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26*7f2fe78bSCy Schubert * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*7f2fe78bSCy Schubert * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28*7f2fe78bSCy Schubert * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*7f2fe78bSCy Schubert * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30*7f2fe78bSCy Schubert * OF THE POSSIBILITY OF SUCH DAMAGE.
31*7f2fe78bSCy Schubert */
32*7f2fe78bSCy Schubert
33*7f2fe78bSCy Schubert /*
34*7f2fe78bSCy Schubert * This module is used to test preauth interface features. Currently, the
35*7f2fe78bSCy Schubert * kdcpreauth module does the following:
36*7f2fe78bSCy Schubert *
37*7f2fe78bSCy Schubert * - When generating initial method-data, it retrieves the "teststring"
38*7f2fe78bSCy Schubert * attribute from the client principal and sends it to the client, encrypted
39*7f2fe78bSCy Schubert * in the reply key. (The plain text "no key" is sent if there is no reply
40*7f2fe78bSCy Schubert * key; the encrypted message "no attr" is sent if there is no string
41*7f2fe78bSCy Schubert * attribute.) It also sets a cookie containing "method-data".
42*7f2fe78bSCy Schubert *
43*7f2fe78bSCy Schubert * - If the "err" attribute is set on the client principal, the verify method
44*7f2fe78bSCy Schubert * returns an KDC_ERR_ETYPE_NOSUPP error on the first try, with the contents
45*7f2fe78bSCy Schubert * of the err attribute as pa-data. If the client tries again with the
46*7f2fe78bSCy Schubert * padata value "tryagain", the verify method preuthenticates successfully
47*7f2fe78bSCy Schubert * with no additional processing.
48*7f2fe78bSCy Schubert *
49*7f2fe78bSCy Schubert * - If the "failopt" attribute is set on the client principal, the verify
50*7f2fe78bSCy Schubert * method returns KDC_ERR_PREAUTH_FAILED on optimistic preauth attempts.
51*7f2fe78bSCy Schubert *
52*7f2fe78bSCy Schubert * - If the "2rt" attribute is set on client principal, the verify method sends
53*7f2fe78bSCy Schubert * the client a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error with the contents of
54*7f2fe78bSCy Schubert * the 2rt attribute as pa-data, and sets a cookie containing "more". If the
55*7f2fe78bSCy Schubert * "fail2rt" attribute is set on the client principal, the client's second
56*7f2fe78bSCy Schubert * try results in a KDC_ERR_PREAUTH_FAILED error.
57*7f2fe78bSCy Schubert *
58*7f2fe78bSCy Schubert * - It receives a space-separated list from the clpreauth module and asserts
59*7f2fe78bSCy Schubert * each string as an authentication indicator. It always succeeds in
60*7f2fe78bSCy Schubert * pre-authenticating the request.
61*7f2fe78bSCy Schubert */
62*7f2fe78bSCy Schubert
63*7f2fe78bSCy Schubert #include "k5-int.h"
64*7f2fe78bSCy Schubert #include <krb5/kdcpreauth_plugin.h>
65*7f2fe78bSCy Schubert #include "common.h"
66*7f2fe78bSCy Schubert
67*7f2fe78bSCy Schubert #define TEST_PA_TYPE -123
68*7f2fe78bSCy Schubert
69*7f2fe78bSCy Schubert static krb5_preauthtype pa_types[] = { TEST_PA_TYPE, 0 };
70*7f2fe78bSCy Schubert
71*7f2fe78bSCy Schubert static void
test_edata(krb5_context context,krb5_kdc_req * req,krb5_kdcpreauth_callbacks cb,krb5_kdcpreauth_rock rock,krb5_kdcpreauth_moddata moddata,krb5_preauthtype pa_type,krb5_kdcpreauth_edata_respond_fn respond,void * arg)72*7f2fe78bSCy Schubert test_edata(krb5_context context, krb5_kdc_req *req,
73*7f2fe78bSCy Schubert krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
74*7f2fe78bSCy Schubert krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
75*7f2fe78bSCy Schubert krb5_kdcpreauth_edata_respond_fn respond, void *arg)
76*7f2fe78bSCy Schubert {
77*7f2fe78bSCy Schubert krb5_error_code ret;
78*7f2fe78bSCy Schubert const krb5_keyblock *k = cb->client_keyblock(context, rock);
79*7f2fe78bSCy Schubert krb5_pa_data *pa;
80*7f2fe78bSCy Schubert size_t enclen;
81*7f2fe78bSCy Schubert krb5_enc_data enc;
82*7f2fe78bSCy Schubert krb5_data d;
83*7f2fe78bSCy Schubert char *attr;
84*7f2fe78bSCy Schubert
85*7f2fe78bSCy Schubert ret = cb->get_string(context, rock, "teststring", &attr);
86*7f2fe78bSCy Schubert assert(!ret);
87*7f2fe78bSCy Schubert if (k != NULL) {
88*7f2fe78bSCy Schubert d = string2data((attr != NULL) ? attr : "no attr");
89*7f2fe78bSCy Schubert ret = krb5_c_encrypt_length(context, k->enctype, d.length, &enclen);
90*7f2fe78bSCy Schubert assert(!ret);
91*7f2fe78bSCy Schubert ret = alloc_data(&enc.ciphertext, enclen);
92*7f2fe78bSCy Schubert assert(!ret);
93*7f2fe78bSCy Schubert ret = krb5_c_encrypt(context, k, 1024, NULL, &d, &enc);
94*7f2fe78bSCy Schubert assert(!ret);
95*7f2fe78bSCy Schubert pa = make_pa(enc.ciphertext.data, enc.ciphertext.length);
96*7f2fe78bSCy Schubert free(enc.ciphertext.data);
97*7f2fe78bSCy Schubert } else {
98*7f2fe78bSCy Schubert pa = make_pa("no key", 6);
99*7f2fe78bSCy Schubert }
100*7f2fe78bSCy Schubert
101*7f2fe78bSCy Schubert /* Exercise setting a cookie information from the edata method. */
102*7f2fe78bSCy Schubert d = string2data("method-data");
103*7f2fe78bSCy Schubert ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
104*7f2fe78bSCy Schubert assert(!ret);
105*7f2fe78bSCy Schubert
106*7f2fe78bSCy Schubert cb->free_string(context, rock, attr);
107*7f2fe78bSCy Schubert (*respond)(arg, 0, pa);
108*7f2fe78bSCy Schubert }
109*7f2fe78bSCy Schubert
110*7f2fe78bSCy Schubert static void
test_verify(krb5_context context,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,krb5_pa_data * data,krb5_kdcpreauth_callbacks cb,krb5_kdcpreauth_rock rock,krb5_kdcpreauth_moddata moddata,krb5_kdcpreauth_verify_respond_fn respond,void * arg)111*7f2fe78bSCy Schubert test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
112*7f2fe78bSCy Schubert krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data,
113*7f2fe78bSCy Schubert krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
114*7f2fe78bSCy Schubert krb5_kdcpreauth_moddata moddata,
115*7f2fe78bSCy Schubert krb5_kdcpreauth_verify_respond_fn respond, void *arg)
116*7f2fe78bSCy Schubert {
117*7f2fe78bSCy Schubert krb5_error_code ret;
118*7f2fe78bSCy Schubert krb5_boolean second_round_trip = FALSE, optimistic = FALSE;
119*7f2fe78bSCy Schubert krb5_pa_data **list = NULL;
120*7f2fe78bSCy Schubert krb5_data cookie_data, d;
121*7f2fe78bSCy Schubert char *str, *ind, *toksave = NULL;
122*7f2fe78bSCy Schubert char *attr_err, *attr_2rt, *attr_fail2rt, *attr_failopt;
123*7f2fe78bSCy Schubert
124*7f2fe78bSCy Schubert ret = cb->get_string(context, rock, "err", &attr_err);
125*7f2fe78bSCy Schubert assert(!ret);
126*7f2fe78bSCy Schubert ret = cb->get_string(context, rock, "2rt", &attr_2rt);
127*7f2fe78bSCy Schubert assert(!ret);
128*7f2fe78bSCy Schubert ret = cb->get_string(context, rock, "fail2rt", &attr_fail2rt);
129*7f2fe78bSCy Schubert assert(!ret);
130*7f2fe78bSCy Schubert ret = cb->get_string(context, rock, "failopt", &attr_failopt);
131*7f2fe78bSCy Schubert assert(!ret);
132*7f2fe78bSCy Schubert
133*7f2fe78bSCy Schubert /* Check the incoming cookie value. */
134*7f2fe78bSCy Schubert if (!cb->get_cookie(context, rock, TEST_PA_TYPE, &cookie_data)) {
135*7f2fe78bSCy Schubert /* Make sure we are seeing optimistic preauth and not a lost cookie. */
136*7f2fe78bSCy Schubert d = make_data(data->contents, data->length);
137*7f2fe78bSCy Schubert assert(data_eq_string(d, "optimistic"));
138*7f2fe78bSCy Schubert optimistic = TRUE;
139*7f2fe78bSCy Schubert } else if (data_eq_string(cookie_data, "more")) {
140*7f2fe78bSCy Schubert second_round_trip = TRUE;
141*7f2fe78bSCy Schubert } else {
142*7f2fe78bSCy Schubert assert(data_eq_string(cookie_data, "method-data") ||
143*7f2fe78bSCy Schubert data_eq_string(cookie_data, "err"));
144*7f2fe78bSCy Schubert }
145*7f2fe78bSCy Schubert
146*7f2fe78bSCy Schubert if (attr_err != NULL) {
147*7f2fe78bSCy Schubert d = make_data(data->contents, data->length);
148*7f2fe78bSCy Schubert if (data_eq_string(d, "tryagain")) {
149*7f2fe78bSCy Schubert /* Authenticate successfully. */
150*7f2fe78bSCy Schubert enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
151*7f2fe78bSCy Schubert } else {
152*7f2fe78bSCy Schubert d = string2data("err");
153*7f2fe78bSCy Schubert ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
154*7f2fe78bSCy Schubert assert(!ret);
155*7f2fe78bSCy Schubert ret = KRB5KDC_ERR_ETYPE_NOSUPP;
156*7f2fe78bSCy Schubert list = make_pa_list(attr_err, strlen(attr_err));
157*7f2fe78bSCy Schubert }
158*7f2fe78bSCy Schubert } else if (attr_2rt != NULL && !second_round_trip) {
159*7f2fe78bSCy Schubert d = string2data("more");
160*7f2fe78bSCy Schubert ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
161*7f2fe78bSCy Schubert assert(!ret);
162*7f2fe78bSCy Schubert ret = KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
163*7f2fe78bSCy Schubert list = make_pa_list(attr_2rt, strlen(attr_2rt));
164*7f2fe78bSCy Schubert } else if ((attr_fail2rt != NULL && second_round_trip) ||
165*7f2fe78bSCy Schubert (attr_failopt != NULL && optimistic)) {
166*7f2fe78bSCy Schubert ret = KRB5KDC_ERR_PREAUTH_FAILED;
167*7f2fe78bSCy Schubert } else {
168*7f2fe78bSCy Schubert /* Parse and assert the indicators. */
169*7f2fe78bSCy Schubert str = k5memdup0(data->contents, data->length, &ret);
170*7f2fe78bSCy Schubert if (ret)
171*7f2fe78bSCy Schubert abort();
172*7f2fe78bSCy Schubert ind = strtok_r(str, " ", &toksave);
173*7f2fe78bSCy Schubert while (ind != NULL) {
174*7f2fe78bSCy Schubert cb->add_auth_indicator(context, rock, ind);
175*7f2fe78bSCy Schubert ind = strtok_r(NULL, " ", &toksave);
176*7f2fe78bSCy Schubert }
177*7f2fe78bSCy Schubert free(str);
178*7f2fe78bSCy Schubert enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
179*7f2fe78bSCy Schubert }
180*7f2fe78bSCy Schubert
181*7f2fe78bSCy Schubert cb->free_string(context, rock, attr_err);
182*7f2fe78bSCy Schubert cb->free_string(context, rock, attr_2rt);
183*7f2fe78bSCy Schubert cb->free_string(context, rock, attr_fail2rt);
184*7f2fe78bSCy Schubert cb->free_string(context, rock, attr_failopt);
185*7f2fe78bSCy Schubert (*respond)(arg, ret, NULL, list, NULL);
186*7f2fe78bSCy Schubert }
187*7f2fe78bSCy Schubert
188*7f2fe78bSCy Schubert static krb5_error_code
test_return(krb5_context context,krb5_pa_data * padata,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_keyblock * encrypting_key,krb5_pa_data ** send_pa_out,krb5_kdcpreauth_callbacks cb,krb5_kdcpreauth_rock rock,krb5_kdcpreauth_moddata moddata,krb5_kdcpreauth_modreq modreq)189*7f2fe78bSCy Schubert test_return(krb5_context context, krb5_pa_data *padata, krb5_data *req_pkt,
190*7f2fe78bSCy Schubert krb5_kdc_req *request, krb5_kdc_rep *reply,
191*7f2fe78bSCy Schubert krb5_keyblock *encrypting_key, krb5_pa_data **send_pa_out,
192*7f2fe78bSCy Schubert krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
193*7f2fe78bSCy Schubert krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq)
194*7f2fe78bSCy Schubert {
195*7f2fe78bSCy Schubert const krb5_keyblock *k = cb->client_keyblock(context, rock);
196*7f2fe78bSCy Schubert
197*7f2fe78bSCy Schubert assert(k == encrypting_key || k == NULL);
198*7f2fe78bSCy Schubert return 0;
199*7f2fe78bSCy Schubert }
200*7f2fe78bSCy Schubert
201*7f2fe78bSCy Schubert krb5_error_code
202*7f2fe78bSCy Schubert kdcpreauth_test_initvt(krb5_context context, int maj_ver,
203*7f2fe78bSCy Schubert int min_ver, krb5_plugin_vtable vtable);
204*7f2fe78bSCy Schubert
205*7f2fe78bSCy Schubert krb5_error_code
kdcpreauth_test_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)206*7f2fe78bSCy Schubert kdcpreauth_test_initvt(krb5_context context, int maj_ver,
207*7f2fe78bSCy Schubert int min_ver, krb5_plugin_vtable vtable)
208*7f2fe78bSCy Schubert {
209*7f2fe78bSCy Schubert krb5_kdcpreauth_vtable vt;
210*7f2fe78bSCy Schubert
211*7f2fe78bSCy Schubert if (maj_ver != 1)
212*7f2fe78bSCy Schubert return KRB5_PLUGIN_VER_NOTSUPP;
213*7f2fe78bSCy Schubert vt = (krb5_kdcpreauth_vtable)vtable;
214*7f2fe78bSCy Schubert vt->name = "test";
215*7f2fe78bSCy Schubert vt->pa_type_list = pa_types;
216*7f2fe78bSCy Schubert vt->edata = test_edata;
217*7f2fe78bSCy Schubert vt->verify = test_verify;
218*7f2fe78bSCy Schubert vt->return_padata = test_return;
219*7f2fe78bSCy Schubert return 0;
220*7f2fe78bSCy Schubert }
221