1 /* $NetBSD: kimpersonate.c,v 1.2 2017/01/28 21:31:45 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2000 - 2007 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "kuser_locl.h" 37 #include <krb5/parse_units.h> 38 39 static char *client_principal_str = NULL; 40 static krb5_principal client_principal; 41 static char *server_principal_str = NULL; 42 static krb5_principal server_principal; 43 44 static char *ccache_str = NULL; 45 46 static char *ticket_flags_str = NULL; 47 static TicketFlags ticket_flags; 48 static char *keytab_file = NULL; 49 static char *enctype_string = NULL; 50 static char *session_enctype_string = NULL; 51 static int expiration_time = 3600; 52 static struct getarg_strings client_addresses; 53 static int version_flag = 0; 54 static int help_flag = 0; 55 static int use_krb5 = 1; 56 static int add_to_ccache = 0; 57 static int use_referral_realm = 0; 58 59 static const char *enc_type = "aes256-cts-hmac-sha1-96"; 60 static const char *session_enc_type = NULL; 61 62 static void 63 encode_ticket(krb5_context context, 64 EncryptionKey *skey, 65 krb5_enctype etype, 66 int skvno, 67 krb5_creds *cred) 68 { 69 size_t len, size; 70 char *buf; 71 krb5_error_code ret; 72 krb5_crypto crypto; 73 EncryptedData enc_part; 74 EncTicketPart et; 75 Ticket ticket; 76 77 memset(&enc_part, 0, sizeof(enc_part)); 78 memset(&ticket, 0, sizeof(ticket)); 79 80 /* 81 * Set up `enc_part' 82 */ 83 84 et.flags = cred->flags.b; 85 et.key = cred->session; 86 et.crealm = cred->client->realm; 87 copy_PrincipalName(&cred->client->name, &et.cname); 88 { 89 krb5_data empty_string; 90 91 krb5_data_zero(&empty_string); 92 et.transited.tr_type = DOMAIN_X500_COMPRESS; 93 et.transited.contents = empty_string; 94 } 95 et.authtime = cred->times.authtime; 96 et.starttime = NULL; 97 et.endtime = cred->times.endtime; 98 et.renew_till = NULL; 99 et.caddr = &cred->addresses; 100 et.authorization_data = NULL; /* XXX allow random authorization_data */ 101 102 /* 103 * Encrypt `enc_part' of ticket with service key 104 */ 105 106 ASN1_MALLOC_ENCODE(EncTicketPart, buf, len, &et, &size, ret); 107 if (ret) 108 krb5_err(context, 1, ret, "EncTicketPart"); 109 110 ret = krb5_crypto_init(context, skey, etype, &crypto); 111 if (ret) 112 krb5_err(context, 1, ret, "krb5_crypto_init"); 113 ret = krb5_encrypt_EncryptedData(context, 114 crypto, 115 KRB5_KU_TICKET, 116 buf, 117 len, 118 skvno, 119 &ticket.enc_part); 120 if (ret) 121 krb5_err(context, 1, ret, "krb5_encrypt_EncryptedData"); 122 123 free(buf); 124 krb5_crypto_destroy(context, crypto); 125 126 /* 127 * Encode ticket 128 */ 129 130 ticket.tkt_vno = 5; 131 ticket.realm = cred->server->realm; 132 copy_PrincipalName(&cred->server->name, &ticket.sname); 133 134 ASN1_MALLOC_ENCODE(Ticket, buf, len, &ticket, &size, ret); 135 if(ret) 136 krb5_err(context, 1, ret, "encode_Ticket"); 137 138 krb5_data_copy(&cred->ticket, buf, len); 139 free(buf); 140 } 141 142 /* 143 * 144 */ 145 146 static int 147 create_krb5_tickets(krb5_context context, krb5_keytab kt) 148 { 149 krb5_error_code ret; 150 krb5_keytab_entry entry; 151 krb5_creds cred; 152 krb5_enctype etype; 153 krb5_enctype session_etype; 154 krb5_ccache ccache; 155 156 memset(&cred, 0, sizeof(cred)); 157 158 ret = krb5_string_to_enctype(context, enc_type, &etype); 159 if (ret) 160 krb5_err (context, 1, ret, "krb5_string_to_enctype (enc-type)"); 161 ret = krb5_string_to_enctype(context, session_enc_type, &session_etype); 162 if (ret) 163 krb5_err (context, 1, ret, "krb5_string_to_enctype (session-enc-type)"); 164 ret = krb5_kt_get_entry(context, kt, server_principal, 0, etype, &entry); 165 if (ret) 166 krb5_err(context, 1, ret, "krb5_kt_get_entry (perhaps use different --enc-type)"); 167 168 /* 169 * setup cred 170 */ 171 172 173 ret = krb5_copy_principal(context, client_principal, &cred.client); 174 if (ret) 175 krb5_err(context, 1, ret, "krb5_copy_principal"); 176 ret = krb5_copy_principal(context, server_principal, &cred.server); 177 if (ret) 178 krb5_err(context, 1, ret, "krb5_copy_principal"); 179 krb5_generate_random_keyblock(context, session_etype, &cred.session); 180 181 cred.times.authtime = time(NULL); 182 cred.times.starttime = time(NULL); 183 cred.times.endtime = time(NULL) + expiration_time; 184 cred.times.renew_till = 0; 185 krb5_data_zero(&cred.second_ticket); 186 187 ret = krb5_get_all_client_addrs(context, &cred.addresses); 188 if (ret) 189 krb5_err(context, 1, ret, "krb5_get_all_client_addrs"); 190 cred.flags.b = ticket_flags; 191 192 193 /* 194 * Encode encrypted part of ticket 195 */ 196 197 encode_ticket(context, &entry.keyblock, etype, entry.vno, &cred); 198 krb5_kt_free_entry(context, &entry); 199 200 /* 201 * Write to cc 202 */ 203 204 if (ccache_str) { 205 ret = krb5_cc_resolve(context, ccache_str, &ccache); 206 if (ret) 207 krb5_err(context, 1, ret, "krb5_cc_resolve"); 208 } else { 209 ret = krb5_cc_default(context, &ccache); 210 if (ret) 211 krb5_err(context, 1, ret, "krb5_cc_default"); 212 } 213 214 if (add_to_ccache) { 215 krb5_principal def_princ; 216 217 /* 218 * Force fcache to read the ccache header, otherwise the store 219 * will fail. 220 */ 221 ret = krb5_cc_get_principal(context, ccache, &def_princ); 222 if (ret) { 223 krb5_warn(context, ret, 224 "Given ccache appears not to exist; initializing it"); 225 ret = krb5_cc_initialize(context, ccache, cred.client); 226 if (ret) 227 krb5_err(context, 1, ret, "krb5_cc_initialize"); 228 } 229 krb5_free_principal(context, def_princ); 230 } else { 231 ret = krb5_cc_initialize(context, ccache, cred.client); 232 if (ret) 233 krb5_err(context, 1, ret, "krb5_cc_initialize"); 234 } 235 236 if (use_referral_realm && 237 strcmp(krb5_principal_get_realm(context, cred.server), "") != 0) { 238 krb5_free_principal(context, cred.server); 239 ret = krb5_copy_principal(context, server_principal, &cred.server); 240 if (ret) 241 krb5_err(context, 1, ret, "krb5_copy_principal"); 242 ret = krb5_principal_set_realm(context, cred.server, ""); 243 if (ret) 244 krb5_err(context, 1, ret, "krb5_principal_set_realm"); 245 ret = krb5_cc_store_cred(context, ccache, &cred); 246 if (ret) 247 krb5_err(context, 1, ret, "krb5_cc_store_cred"); 248 249 krb5_free_principal(context, cred.server); 250 ret = krb5_copy_principal(context, server_principal, &cred.server); 251 if (ret) 252 krb5_err(context, 1, ret, "krb5_copy_principal"); 253 } 254 ret = krb5_cc_store_cred(context, ccache, &cred); 255 if (ret) 256 krb5_err(context, 1, ret, "krb5_cc_store_cred"); 257 258 krb5_free_cred_contents(context, &cred); 259 krb5_cc_close(context, ccache); 260 261 return 0; 262 } 263 264 /* 265 * 266 */ 267 268 static void 269 setup_env(krb5_context context, krb5_keytab *kt) 270 { 271 krb5_error_code ret; 272 273 if (keytab_file) 274 ret = krb5_kt_resolve(context, keytab_file, kt); 275 else 276 ret = krb5_kt_default(context, kt); 277 if (ret) 278 krb5_err(context, 1, ret, "resolving keytab"); 279 280 if (client_principal_str == NULL) 281 krb5_errx(context, 1, "missing client principal"); 282 ret = krb5_parse_name(context, client_principal_str, &client_principal); 283 if (ret) 284 krb5_err(context, 1, ret, "resolvning client name"); 285 286 if (server_principal_str == NULL) 287 krb5_errx(context, 1, "missing server principal"); 288 ret = krb5_parse_name(context, server_principal_str, &server_principal); 289 if (ret) 290 krb5_err(context, 1, ret, "resolvning server name"); 291 292 /* If no session-enc-type specified on command line and this is an afs */ 293 /* service ticket, change default of session_enc_type to DES. */ 294 if (session_enctype_string == NULL 295 && strcmp("afs", *server_principal->name.name_string.val) == 0) 296 session_enc_type = "des-cbc-crc"; 297 298 if (ticket_flags_str) { 299 int ticket_flags_int; 300 301 ticket_flags_int = parse_flags(ticket_flags_str, 302 asn1_TicketFlags_units(), 0); 303 if (ticket_flags_int <= 0) { 304 krb5_warnx(context, "bad ticket flags: `%s'", ticket_flags_str); 305 print_flags_table(asn1_TicketFlags_units(), stderr); 306 exit(1); 307 } 308 if (ticket_flags_int) 309 ticket_flags = int2TicketFlags(ticket_flags_int); 310 } 311 } 312 313 /* 314 * 315 */ 316 317 struct getargs args[] = { 318 { "ccache", 0, arg_string, &ccache_str, 319 "name of kerberos 5 credential cache", "cache-name"}, 320 { "server", 's', arg_string, &server_principal_str, 321 "name of server principal", NULL }, 322 { "client", 'c', arg_string, &client_principal_str, 323 "name of client principal", NULL }, 324 { "keytab", 'k', arg_string, &keytab_file, 325 "name of keytab file", NULL }, 326 { "krb5", '5', arg_flag, &use_krb5, 327 "create a kerberos 5 ticket", NULL }, 328 { "add", 'A', arg_flag, &add_to_ccache, 329 "add to ccache without re-initializing it", NULL }, 330 { "referral", 'R', arg_flag, &use_referral_realm, 331 "store an additional entry for the service with the empty realm", NULL }, 332 { "expire-time", 'e', arg_integer, &expiration_time, 333 "lifetime of ticket in seconds", NULL }, 334 { "client-addresses", 'a', arg_strings, &client_addresses, 335 "addresses of client", NULL }, 336 { "enc-type", 't', arg_string, &enctype_string, 337 "encryption type", NULL }, 338 { "session-enc-type", 0, arg_string,&session_enctype_string, 339 "encryption type", NULL }, 340 { "ticket-flags", 'f', arg_string, &ticket_flags_str, 341 "ticket flags for krb5 ticket", NULL }, 342 { "version", 0, arg_flag, &version_flag, "Print version", 343 NULL }, 344 { "help", 0, arg_flag, &help_flag, NULL, 345 NULL } 346 }; 347 348 static void 349 usage(int ret) 350 { 351 arg_printusage(args, 352 sizeof(args) / sizeof(args[0]), 353 NULL, 354 ""); 355 exit(ret); 356 } 357 358 int 359 main(int argc, char **argv) 360 { 361 int optidx = 0; 362 krb5_error_code ret; 363 krb5_context context; 364 krb5_keytab kt; 365 366 setprogname(argv[0]); 367 368 ret = krb5_init_context(&context); 369 if (ret) 370 errx(1, "krb5_init_context failed: %u", ret); 371 372 if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) 373 usage(1); 374 375 if (help_flag) 376 usage(0); 377 378 if (version_flag) { 379 print_version(NULL); 380 return 0; 381 } 382 383 if (enctype_string) 384 enc_type = enctype_string; 385 if (session_enctype_string) 386 session_enc_type = session_enctype_string; 387 else 388 session_enc_type = enc_type; 389 390 setup_env(context, &kt); 391 392 if (use_krb5) 393 create_krb5_tickets(context, kt); 394 395 krb5_kt_close(context, kt); 396 397 return 0; 398 } 399