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 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 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 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 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 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 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 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 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