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, 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. 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 * - If the "err" attribute is set on the client principal, the verify method 44 * returns an KDC_ERR_ETYPE_NOSUPP error on the first try, with the contents 45 * of the err attribute as pa-data. If the client tries again with the 46 * padata value "tryagain", the verify method preuthenticates successfully 47 * with no additional processing. 48 * 49 * - If the "failopt" attribute is set on the client principal, the verify 50 * method returns KDC_ERR_PREAUTH_FAILED on optimistic preauth attempts. 51 * 52 * - If the "2rt" attribute is set on client principal, the verify method sends 53 * the client a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error with the contents of 54 * the 2rt attribute as pa-data, and sets a cookie containing "more". If the 55 * "fail2rt" attribute is set on the client principal, the client's second 56 * try results in a KDC_ERR_PREAUTH_FAILED error. 57 * 58 * - It receives a space-separated list from the clpreauth module and asserts 59 * each string as an authentication indicator. It always succeeds in 60 * pre-authenticating the request. 61 */ 62 63 #include "k5-int.h" 64 #include <krb5/kdcpreauth_plugin.h> 65 #include "common.h" 66 67 #define TEST_PA_TYPE -123 68 69 static krb5_preauthtype pa_types[] = { TEST_PA_TYPE, 0 }; 70 71 static void 72 test_edata(krb5_context context, krb5_kdc_req *req, 73 krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, 74 krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, 75 krb5_kdcpreauth_edata_respond_fn respond, void *arg) 76 { 77 krb5_error_code ret; 78 const krb5_keyblock *k = cb->client_keyblock(context, rock); 79 krb5_pa_data *pa; 80 size_t enclen; 81 krb5_enc_data enc; 82 krb5_data d; 83 char *attr; 84 85 ret = cb->get_string(context, rock, "teststring", &attr); 86 assert(!ret); 87 if (k != NULL) { 88 d = string2data((attr != NULL) ? attr : "no attr"); 89 ret = krb5_c_encrypt_length(context, k->enctype, d.length, &enclen); 90 assert(!ret); 91 ret = alloc_data(&enc.ciphertext, enclen); 92 assert(!ret); 93 ret = krb5_c_encrypt(context, k, 1024, NULL, &d, &enc); 94 assert(!ret); 95 pa = make_pa(enc.ciphertext.data, enc.ciphertext.length); 96 free(enc.ciphertext.data); 97 } else { 98 pa = make_pa("no key", 6); 99 } 100 101 /* Exercise setting a cookie information from the edata method. */ 102 d = string2data("method-data"); 103 ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d); 104 assert(!ret); 105 106 cb->free_string(context, rock, attr); 107 (*respond)(arg, 0, pa); 108 } 109 110 static void 111 test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, 112 krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data, 113 krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, 114 krb5_kdcpreauth_moddata moddata, 115 krb5_kdcpreauth_verify_respond_fn respond, void *arg) 116 { 117 krb5_error_code ret; 118 krb5_boolean second_round_trip = FALSE, optimistic = FALSE; 119 krb5_pa_data **list = NULL; 120 krb5_data cookie_data, d; 121 char *str, *ind, *toksave = NULL; 122 char *attr_err, *attr_2rt, *attr_fail2rt, *attr_failopt; 123 124 ret = cb->get_string(context, rock, "err", &attr_err); 125 assert(!ret); 126 ret = cb->get_string(context, rock, "2rt", &attr_2rt); 127 assert(!ret); 128 ret = cb->get_string(context, rock, "fail2rt", &attr_fail2rt); 129 assert(!ret); 130 ret = cb->get_string(context, rock, "failopt", &attr_failopt); 131 assert(!ret); 132 133 /* Check the incoming cookie value. */ 134 if (!cb->get_cookie(context, rock, TEST_PA_TYPE, &cookie_data)) { 135 /* Make sure we are seeing optimistic preauth and not a lost cookie. */ 136 d = make_data(data->contents, data->length); 137 assert(data_eq_string(d, "optimistic")); 138 optimistic = TRUE; 139 } else if (data_eq_string(cookie_data, "more")) { 140 second_round_trip = TRUE; 141 } else { 142 assert(data_eq_string(cookie_data, "method-data") || 143 data_eq_string(cookie_data, "err")); 144 } 145 146 if (attr_err != NULL) { 147 d = make_data(data->contents, data->length); 148 if (data_eq_string(d, "tryagain")) { 149 /* Authenticate successfully. */ 150 enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; 151 } else { 152 d = string2data("err"); 153 ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d); 154 assert(!ret); 155 ret = KRB5KDC_ERR_ETYPE_NOSUPP; 156 list = make_pa_list(attr_err, strlen(attr_err)); 157 } 158 } else if (attr_2rt != NULL && !second_round_trip) { 159 d = string2data("more"); 160 ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d); 161 assert(!ret); 162 ret = KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED; 163 list = make_pa_list(attr_2rt, strlen(attr_2rt)); 164 } else if ((attr_fail2rt != NULL && second_round_trip) || 165 (attr_failopt != NULL && optimistic)) { 166 ret = KRB5KDC_ERR_PREAUTH_FAILED; 167 } else { 168 /* Parse and assert the indicators. */ 169 str = k5memdup0(data->contents, data->length, &ret); 170 if (ret) 171 abort(); 172 ind = strtok_r(str, " ", &toksave); 173 while (ind != NULL) { 174 cb->add_auth_indicator(context, rock, ind); 175 ind = strtok_r(NULL, " ", &toksave); 176 } 177 free(str); 178 enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; 179 } 180 181 cb->free_string(context, rock, attr_err); 182 cb->free_string(context, rock, attr_2rt); 183 cb->free_string(context, rock, attr_fail2rt); 184 cb->free_string(context, rock, attr_failopt); 185 (*respond)(arg, ret, NULL, list, NULL); 186 } 187 188 static krb5_error_code 189 test_return(krb5_context context, krb5_pa_data *padata, krb5_data *req_pkt, 190 krb5_kdc_req *request, krb5_kdc_rep *reply, 191 krb5_keyblock *encrypting_key, krb5_pa_data **send_pa_out, 192 krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, 193 krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq) 194 { 195 const krb5_keyblock *k = cb->client_keyblock(context, rock); 196 197 assert(k == encrypting_key || k == NULL); 198 return 0; 199 } 200 201 krb5_error_code 202 kdcpreauth_test_initvt(krb5_context context, int maj_ver, 203 int min_ver, krb5_plugin_vtable vtable); 204 205 krb5_error_code 206 kdcpreauth_test_initvt(krb5_context context, int maj_ver, 207 int min_ver, krb5_plugin_vtable vtable) 208 { 209 krb5_kdcpreauth_vtable vt; 210 211 if (maj_ver != 1) 212 return KRB5_PLUGIN_VER_NOTSUPP; 213 vt = (krb5_kdcpreauth_vtable)vtable; 214 vt->name = "test"; 215 vt->pa_type_list = pa_types; 216 vt->edata = test_edata; 217 vt->verify = test_verify; 218 vt->return_padata = test_return; 219 return 0; 220 } 221