1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 2009 by the Massachusetts Institute of Technology. 4 * All Rights Reserved. 5 * 6 * Export of this software from the United States of America may 7 * require a specific license from the United States Government. 8 * It is the responsibility of any person or organization contemplating 9 * export to obtain such a license before exporting. 10 * 11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 12 * distribute this software and its documentation for any purpose and 13 * without fee is hereby granted, provided that the above copyright 14 * notice appear in all copies and that both that copyright notice and 15 * this permission notice appear in supporting documentation, and that 16 * the name of M.I.T. not be used in advertising or publicity pertaining 17 * to distribution of the software without specific, written prior 18 * permission. Furthermore if you modify this software you must label 19 * your software as modified software and not distribute it in such a 20 * fashion that it might be confused with the original M.I.T. software. 21 * M.I.T. makes no representations about the suitability of 22 * this software for any purpose. It is provided "as is" without express 23 * or implied warranty. 24 */ 25 26 /* 27 * Test program for protocol transition (S4U2Self) and constrained delegation 28 * (S4U2Proxy) 29 * 30 * Note: because of name canonicalization, the following tips may help 31 * when configuring with Active Directory: 32 * 33 * - Create a computer account FOO$ 34 * - Set the UPN to host/foo.domain (no suffix); this is necessary to 35 * be able to send an AS-REQ as this principal, otherwise you would 36 * need to use the canonical name (FOO$), which will cause principal 37 * comparison errors in gss_accept_sec_context(). 38 * - Add a SPN of host/foo.domain 39 * - Configure the computer account to support constrained delegation with 40 * protocol transition (Trust this computer for delegation to specified 41 * services only / Use any authentication protocol) 42 * - Add host/foo.domain to the keytab (possibly easiest to do this 43 * with ktadd) 44 * 45 * For S4U2Proxy to work the TGT must be forwardable too. 46 * 47 * Usage eg: 48 * 49 * kinit -k -t test.keytab -f 'host/test.win.mit.edu@WIN.MIT.EDU' 50 * ./t_s4u p:delegtest@WIN.MIT.EDU p:HOST/WIN-EQ7E4AA2WR8.win.mit.edu@WIN.MIT.EDU test.keytab 51 */ 52 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 57 #include "common.h" 58 59 static int use_spnego = 0; 60 61 static void 62 test_greet_authz_data(gss_name_t *name) 63 { 64 OM_uint32 major, minor; 65 gss_buffer_desc attr; 66 gss_buffer_desc value; 67 gss_name_t canon; 68 69 major = gss_canonicalize_name(&minor, *name, &mech_krb5, &canon); 70 check_gsserr("gss_canonicalize_name", major, minor); 71 72 attr.value = "greet:greeting"; 73 attr.length = strlen((char *)attr.value); 74 75 value.value = "Hello, acceptor world!"; 76 value.length = strlen((char *)value.value); 77 78 major = gss_set_name_attribute(&minor, canon, 1, &attr, &value); 79 if (major == GSS_S_UNAVAILABLE) { 80 (void)gss_release_name(&minor, &canon); 81 return; 82 } 83 check_gsserr("gss_set_name_attribute", major, minor); 84 gss_release_name(&minor, name); 85 *name = canon; 86 } 87 88 static void 89 init_accept_sec_context(gss_cred_id_t claimant_cred_handle, 90 gss_cred_id_t verifier_cred_handle, 91 gss_cred_id_t *deleg_cred_handle) 92 { 93 OM_uint32 major, minor, flags; 94 gss_name_t source_name = GSS_C_NO_NAME, target_name = GSS_C_NO_NAME; 95 gss_ctx_id_t initiator_context, acceptor_context; 96 gss_OID mech = GSS_C_NO_OID; 97 98 *deleg_cred_handle = GSS_C_NO_CREDENTIAL; 99 100 major = gss_inquire_cred(&minor, verifier_cred_handle, &target_name, NULL, 101 NULL, NULL); 102 check_gsserr("gss_inquire_cred", major, minor); 103 104 display_canon_name("Target name", target_name, &mech_krb5); 105 106 mech = use_spnego ? &mech_spnego : &mech_krb5; 107 display_oid("Target mech", mech); 108 109 flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; 110 establish_contexts(mech, claimant_cred_handle, verifier_cred_handle, 111 target_name, flags, &initiator_context, 112 &acceptor_context, &source_name, &mech, 113 deleg_cred_handle); 114 115 display_canon_name("Source name", source_name, &mech_krb5); 116 display_oid("Source mech", mech); 117 enumerate_attributes(source_name, 1); 118 119 (void)gss_release_name(&minor, &source_name); 120 (void)gss_release_name(&minor, &target_name); 121 (void)gss_delete_sec_context(&minor, &initiator_context, NULL); 122 (void)gss_delete_sec_context(&minor, &acceptor_context, NULL); 123 } 124 125 static void 126 check_ticket_count(gss_cred_id_t cred, int expected) 127 { 128 krb5_error_code ret; 129 krb5_context context = NULL; 130 krb5_creds kcred; 131 krb5_cc_cursor cur; 132 krb5_ccache ccache; 133 int count = 0; 134 gss_key_value_set_desc store; 135 gss_key_value_element_desc elem; 136 OM_uint32 major, minor; 137 const char *ccname = "MEMORY:count"; 138 139 store.count = 1; 140 store.elements = &elem; 141 elem.key = "ccache"; 142 elem.value = ccname; 143 major = gss_store_cred_into(&minor, cred, GSS_C_INITIATE, &mech_krb5, 1, 0, 144 &store, NULL, NULL); 145 check_gsserr("gss_store_cred_into", major, minor); 146 147 ret = krb5_init_context(&context); 148 check_k5err(context, "krb5_init_context", ret); 149 150 ret = krb5_cc_resolve(context, ccname, &ccache); 151 check_k5err(context, "krb5_cc_resolve", ret); 152 153 ret = krb5_cc_start_seq_get(context, ccache, &cur); 154 check_k5err(context, "krb5_cc_start_seq_get", ret); 155 156 while (!krb5_cc_next_cred(context, ccache, &cur, &kcred)) { 157 if (!krb5_is_config_principal(context, kcred.server)) 158 count++; 159 krb5_free_cred_contents(context, &kcred); 160 } 161 162 ret = krb5_cc_end_seq_get(context, ccache, &cur); 163 check_k5err(context, "krb5_cc_end_seq_get", ret); 164 165 if (expected != count) { 166 printf("Expected %d tickets but got %d\n", expected, count); 167 exit(1); 168 } 169 170 krb5_cc_destroy(context, ccache); 171 krb5_free_context(context); 172 } 173 174 static void 175 constrained_delegate(gss_OID_set desired_mechs, gss_name_t target, 176 gss_cred_id_t delegated_cred_handle, 177 gss_cred_id_t verifier_cred_handle) 178 { 179 OM_uint32 major, minor; 180 gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT; 181 gss_name_t cred_name = GSS_C_NO_NAME; 182 OM_uint32 time_rec, lifetime; 183 gss_cred_usage_t usage; 184 gss_buffer_desc token; 185 gss_OID_set mechs; 186 187 printf("Constrained delegation tests follow\n"); 188 printf("-----------------------------------\n\n"); 189 190 if (gss_inquire_cred(&minor, verifier_cred_handle, &cred_name, 191 &lifetime, &usage, NULL) == GSS_S_COMPLETE) { 192 display_canon_name("Proxy name", cred_name, &mech_krb5); 193 (void)gss_release_name(&minor, &cred_name); 194 } 195 display_canon_name("Target name", target, &mech_krb5); 196 if (gss_inquire_cred(&minor, delegated_cred_handle, &cred_name, 197 &lifetime, &usage, &mechs) == GSS_S_COMPLETE) { 198 display_canon_name("Delegated name", cred_name, &mech_krb5); 199 display_oid("Delegated mech", &mechs->elements[0]); 200 (void)gss_release_name(&minor, &cred_name); 201 } 202 203 printf("\n"); 204 205 major = gss_init_sec_context(&minor, delegated_cred_handle, 206 &initiator_context, target, 207 mechs ? &mechs->elements[0] : &mech_krb5, 208 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, 209 GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, 210 GSS_C_NO_BUFFER, NULL, &token, NULL, 211 &time_rec); 212 check_gsserr("gss_init_sec_context", major, minor); 213 214 (void)gss_release_buffer(&minor, &token); 215 (void)gss_delete_sec_context(&minor, &initiator_context, NULL); 216 217 /* Ensure a second call does not acquire new ticket. */ 218 major = gss_init_sec_context(&minor, delegated_cred_handle, 219 &initiator_context, target, 220 mechs ? &mechs->elements[0] : &mech_krb5, 221 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, 222 GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, 223 GSS_C_NO_BUFFER, NULL, &token, NULL, 224 &time_rec); 225 check_gsserr("gss_init_sec_context", major, minor); 226 227 (void)gss_release_buffer(&minor, &token); 228 (void)gss_delete_sec_context(&minor, &initiator_context, NULL); 229 (void)gss_release_oid_set(&minor, &mechs); 230 231 /* We expect three tickets: our TGT, the evidence ticket, and the ticket to 232 * the target service. */ 233 check_ticket_count(delegated_cred_handle, 3); 234 } 235 236 int 237 main(int argc, char *argv[]) 238 { 239 OM_uint32 minor, major; 240 gss_cred_id_t impersonator_cred_handle = GSS_C_NO_CREDENTIAL; 241 gss_cred_id_t user_cred_handle = GSS_C_NO_CREDENTIAL; 242 gss_cred_id_t delegated_cred_handle = GSS_C_NO_CREDENTIAL; 243 gss_name_t user = GSS_C_NO_NAME, target = GSS_C_NO_NAME; 244 gss_OID_set mechs; 245 gss_buffer_set_t bufset = GSS_C_NO_BUFFER_SET; 246 247 if (argc < 2 || argc > 5) { 248 fprintf(stderr, "Usage: %s [--spnego] [user] " 249 "[proxy-target] [keytab]\n", argv[0]); 250 fprintf(stderr, " proxy-target and keytab are optional\n"); 251 exit(1); 252 } 253 254 if (strcmp(argv[1], "--spnego") == 0) { 255 use_spnego++; 256 argc--; 257 argv++; 258 } 259 260 user = import_name(argv[1]); 261 262 if (argc > 2 && strcmp(argv[2], "-")) 263 target = import_name(argv[2]); 264 265 if (argc > 3) { 266 major = krb5_gss_register_acceptor_identity(argv[3]); 267 check_gsserr("krb5_gss_register_acceptor_identity", major, 0); 268 } 269 270 /* Get default cred. */ 271 mechs = use_spnego ? &mechset_spnego : &mechset_krb5; 272 major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE, mechs, 273 GSS_C_BOTH, &impersonator_cred_handle, NULL, 274 NULL); 275 check_gsserr("gss_acquire_cred", major, minor); 276 277 printf("Protocol transition tests follow\n"); 278 printf("-----------------------------------\n\n"); 279 280 test_greet_authz_data(&user); 281 282 /* Get S4U2Self cred. */ 283 major = gss_acquire_cred_impersonate_name(&minor, impersonator_cred_handle, 284 user, GSS_C_INDEFINITE, mechs, 285 GSS_C_INITIATE, 286 &user_cred_handle, NULL, NULL); 287 check_gsserr("gss_acquire_cred_impersonate_name", major, minor); 288 289 init_accept_sec_context(user_cred_handle, impersonator_cred_handle, 290 &delegated_cred_handle); 291 printf("\n"); 292 293 if (target != GSS_C_NO_NAME && 294 delegated_cred_handle != GSS_C_NO_CREDENTIAL) { 295 constrained_delegate(mechs, target, delegated_cred_handle, 296 impersonator_cred_handle); 297 } else if (target != GSS_C_NO_NAME) { 298 fprintf(stderr, "Warning: no delegated cred handle returned\n\n"); 299 fprintf(stderr, "Verify:\n\n"); 300 fprintf(stderr, " - The TGT for the impersonating service is " 301 "forwardable\n"); 302 fprintf(stderr, " - The T2A4D flag set on the impersonating service's " 303 "UAC\n"); 304 fprintf(stderr, " - The user is not marked sensitive and cannot be " 305 "delegated\n"); 306 fprintf(stderr, "\n"); 307 } 308 309 if (delegated_cred_handle != GSS_C_NO_CREDENTIAL) { 310 /* Inquire impersonator status. */ 311 major = gss_inquire_cred_by_oid(&minor, user_cred_handle, 312 GSS_KRB5_GET_CRED_IMPERSONATOR, 313 &bufset); 314 check_gsserr("gss_inquire_cred_by_oid", major, minor); 315 if (bufset->count == 0) 316 errout("gss_inquire_cred_by_oid(user) returned NO impersonator"); 317 (void)gss_release_buffer_set(&minor, &bufset); 318 319 major = gss_inquire_cred_by_oid(&minor, impersonator_cred_handle, 320 GSS_KRB5_GET_CRED_IMPERSONATOR, 321 &bufset); 322 check_gsserr("gss_inquire_cred_by_oid", major, minor); 323 if (bufset->count != 0) 324 errout("gss_inquire_cred_by_oid(svc) returned an impersonator"); 325 (void)gss_release_buffer_set(&minor, &bufset); 326 } 327 328 (void)gss_release_name(&minor, &user); 329 (void)gss_release_name(&minor, &target); 330 (void)gss_release_cred(&minor, &delegated_cred_handle); 331 (void)gss_release_cred(&minor, &impersonator_cred_handle); 332 (void)gss_release_cred(&minor, &user_cred_handle); 333 return 0; 334 } 335