Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / plugins / preauth / test / kdctest.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/preauth/test/kdctest.c - Test kdcpreauth module */
3 /*
4  * Copyright (C) 2015 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.  Currently, the
35  * kdcpreauth module does the following:
36  *
37  * - When generating initial method-data, it retrieves the "teststring"
38  *   attribute from the client principal and sends it to the client, encrypted
39  *   in the reply key.  (The plain text "no key" is sent if there is no reply
40  *   key; the encrypted message "no attr" is sent if there is no string
41  *   attribute.)  It also sets a cookie containing "method-data".
42  *
43  * - It retrieves the "2rt" attribute from the client principal.  If set, the
44  *   verify method sends the client a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error
45  *   with the contents of the 2rt attribute as pa-data, and sets a cookie
46  *   containing "more".
47  *
48  * - It receives a space-separated list from the clpreauth module and asserts
49  *   each string as an authentication indicator.  It always succeeds in
50  *   pre-authenticating the request.
51  */
52
53 #include "k5-int.h"
54 #include <krb5/kdcpreauth_plugin.h>
55
56 #define TEST_PA_TYPE -123
57
58 static krb5_preauthtype pa_types[] = { TEST_PA_TYPE, 0 };
59
60 static void
61 test_edata(krb5_context context, krb5_kdc_req *req,
62            krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
63            krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
64            krb5_kdcpreauth_edata_respond_fn respond, void *arg)
65 {
66     krb5_error_code ret;
67     const krb5_keyblock *k = cb->client_keyblock(context, rock);
68     krb5_pa_data *pa;
69     size_t enclen;
70     krb5_enc_data enc;
71     krb5_data d;
72     char *attr;
73
74     ret = cb->get_string(context, rock, "teststring", &attr);
75     assert(!ret);
76     pa = k5alloc(sizeof(*pa), &ret);
77     assert(!ret);
78     if (pa == NULL)
79         abort();
80     pa->pa_type = TEST_PA_TYPE;
81     if (k != NULL) {
82         d = string2data((attr != NULL) ? attr : "no attr");
83         ret = krb5_c_encrypt_length(context, k->enctype, d.length, &enclen);
84         assert(!ret);
85         ret = alloc_data(&enc.ciphertext, enclen);
86         assert(!ret);
87         ret = krb5_c_encrypt(context, k, 1024, NULL, &d, &enc);
88         assert(!ret);
89         pa->contents = (uint8_t *)enc.ciphertext.data;
90         pa->length = enc.ciphertext.length;
91     } else {
92         pa->contents = (uint8_t *)strdup("no key");
93         assert(pa->contents != NULL);
94         pa->length = 6;
95     }
96
97     /* Exercise setting a cookie information from the edata method. */
98     d = string2data("method-data");
99     ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
100     assert(!ret);
101
102     cb->free_string(context, rock, attr);
103     (*respond)(arg, 0, pa);
104 }
105
106 static void
107 test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
108             krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data,
109             krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
110             krb5_kdcpreauth_moddata moddata,
111             krb5_kdcpreauth_verify_respond_fn respond, void *arg)
112 {
113     krb5_error_code ret;
114     krb5_boolean second_round_trip = FALSE;
115     krb5_pa_data **list;
116     krb5_data cookie_data, d;
117     char *str, *ind, *attr, *toksave = NULL;
118
119     ret = cb->get_string(context, rock, "2rt", &attr);
120     assert(!ret);
121
122     /* Check the incoming cookie value. */
123     if (!cb->get_cookie(context, rock, TEST_PA_TYPE, &cookie_data)) {
124         /* Make sure we are seeing optimistic preauth and not a lost cookie. */
125         d = make_data(data->contents, data->length);
126         assert(data_eq_string(d, "optimistic"));
127     } else if (data_eq_string(cookie_data, "more")) {
128         second_round_trip = TRUE;
129     } else {
130         assert(data_eq_string(cookie_data, "method-data"));
131     }
132
133     if (attr == NULL || second_round_trip) {
134         /* Parse and assert the indicators. */
135         str = k5memdup0(data->contents, data->length, &ret);
136         if (ret)
137             abort();
138         ind = strtok_r(str, " ", &toksave);
139         while (ind != NULL) {
140             cb->add_auth_indicator(context, rock, ind);
141             ind = strtok_r(NULL, " ", &toksave);
142         }
143         free(str);
144         enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
145         cb->free_string(context, rock, attr);
146         (*respond)(arg, 0, NULL, NULL, NULL);
147     } else {
148         d = string2data("more");
149         ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
150         list = k5calloc(2, sizeof(*list), &ret);
151         assert(!ret);
152         list[0] = k5alloc(sizeof(*list[0]), &ret);
153         assert(!ret);
154         list[0]->pa_type = TEST_PA_TYPE;
155         list[0]->contents = (uint8_t *)attr;
156         list[0]->length = strlen(attr);
157         (*respond)(arg, KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED, NULL, list,
158                    NULL);
159     }
160 }
161
162 static krb5_error_code
163 test_return(krb5_context context, krb5_pa_data *padata, krb5_data *req_pkt,
164             krb5_kdc_req *request, krb5_kdc_rep *reply,
165             krb5_keyblock *encrypting_key, krb5_pa_data **send_pa_out,
166             krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
167             krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq)
168 {
169     const krb5_keyblock *k = cb->client_keyblock(context, rock);
170
171     assert(k == encrypting_key || k == NULL);
172     return 0;
173 }
174
175 krb5_error_code
176 kdcpreauth_test_initvt(krb5_context context, int maj_ver,
177                              int min_ver, krb5_plugin_vtable vtable);
178
179 krb5_error_code
180 kdcpreauth_test_initvt(krb5_context context, int maj_ver,
181                              int min_ver, krb5_plugin_vtable vtable)
182 {
183     krb5_kdcpreauth_vtable vt;
184
185     if (maj_ver != 1)
186         return KRB5_PLUGIN_VER_NOTSUPP;
187     vt = (krb5_kdcpreauth_vtable)vtable;
188     vt->name = "test";
189     vt->pa_type_list = pa_types;
190     vt->edata = test_edata;
191     vt->verify = test_verify;
192     vt->return_padata = test_return;
193     return 0;
194 }