1 /* $NetBSD: kimpersonate.c,v 1.3 2019/12/15 22:50:46 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2000 - 2007 Kungliga Tekniska Hgskolan 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 ret = copy_PrincipalName(&cred->client->name, &et.cname); 88 if (ret) 89 krb5_err(context, 1, ret, "copy_PrincipalName"); 90 { 91 krb5_data empty_string; 92 93 krb5_data_zero(&empty_string); 94 et.transited.tr_type = DOMAIN_X500_COMPRESS; 95 et.transited.contents = empty_string; 96 } 97 et.authtime = cred->times.authtime; 98 et.starttime = NULL; 99 et.endtime = cred->times.endtime; 100 et.renew_till = NULL; 101 et.caddr = &cred->addresses; 102 et.authorization_data = NULL; /* XXX allow random authorization_data */ 103 104 /* 105 * Encrypt `enc_part' of ticket with service key 106 */ 107 108 ASN1_MALLOC_ENCODE(EncTicketPart, buf, len, &et, &size, ret); 109 if (ret) 110 krb5_err(context, 1, ret, "EncTicketPart"); 111 112 ret = krb5_crypto_init(context, skey, etype, &crypto); 113 if (ret) 114 krb5_err(context, 1, ret, "krb5_crypto_init"); 115 ret = krb5_encrypt_EncryptedData(context, 116 crypto, 117 KRB5_KU_TICKET, 118 buf, 119 len, 120 skvno, 121 &ticket.enc_part); 122 if (ret) 123 krb5_err(context, 1, ret, "krb5_encrypt_EncryptedData"); 124 125 free(buf); 126 krb5_crypto_destroy(context, crypto); 127 128 /* 129 * Encode ticket 130 */ 131 132 ticket.tkt_vno = 5; 133 ticket.realm = cred->server->realm; 134 ret = copy_PrincipalName(&cred->server->name, &ticket.sname); 135 if (ret) 136 krb5_err(context, 1, ret, "copy_PrincipalName"); 137 138 ASN1_MALLOC_ENCODE(Ticket, buf, len, &ticket, &size, ret); 139 if(ret) 140 krb5_err(context, 1, ret, "encode_Ticket"); 141 142 krb5_data_copy(&cred->ticket, buf, len); 143 free(buf); 144 } 145 146 /* 147 * 148 */ 149 150 static int 151 create_krb5_tickets(krb5_context context, krb5_keytab kt) 152 { 153 krb5_error_code ret; 154 krb5_keytab_entry entry; 155 krb5_creds cred; 156 krb5_enctype etype; 157 krb5_enctype session_etype; 158 krb5_ccache ccache; 159 160 memset(&cred, 0, sizeof(cred)); 161 162 ret = krb5_string_to_enctype(context, enc_type, &etype); 163 if (ret) 164 krb5_err (context, 1, ret, "krb5_string_to_enctype (enc-type)"); 165 ret = krb5_string_to_enctype(context, session_enc_type, &session_etype); 166 if (ret) 167 krb5_err (context, 1, ret, "krb5_string_to_enctype (session-enc-type)"); 168 ret = krb5_kt_get_entry(context, kt, server_principal, 0, etype, &entry); 169 if (ret) 170 krb5_err(context, 1, ret, "krb5_kt_get_entry (perhaps use different --enc-type)"); 171 172 /* 173 * setup cred 174 */ 175 176 177 ret = krb5_copy_principal(context, client_principal, &cred.client); 178 if (ret) 179 krb5_err(context, 1, ret, "krb5_copy_principal"); 180 ret = krb5_copy_principal(context, server_principal, &cred.server); 181 if (ret) 182 krb5_err(context, 1, ret, "krb5_copy_principal"); 183 krb5_generate_random_keyblock(context, session_etype, &cred.session); 184 185 cred.times.authtime = time(NULL); 186 cred.times.starttime = time(NULL); 187 cred.times.endtime = time(NULL) + expiration_time; 188 cred.times.renew_till = 0; 189 krb5_data_zero(&cred.second_ticket); 190 191 ret = krb5_get_all_client_addrs(context, &cred.addresses); 192 if (ret) 193 krb5_err(context, 1, ret, "krb5_get_all_client_addrs"); 194 cred.flags.b = ticket_flags; 195 196 197 /* 198 * Encode encrypted part of ticket 199 */ 200 201 encode_ticket(context, &entry.keyblock, etype, entry.vno, &cred); 202 krb5_kt_free_entry(context, &entry); 203 204 /* 205 * Write to cc 206 */ 207 208 if (ccache_str) { 209 ret = krb5_cc_resolve(context, ccache_str, &ccache); 210 if (ret) 211 krb5_err(context, 1, ret, "krb5_cc_resolve"); 212 } else { 213 ret = krb5_cc_default(context, &ccache); 214 if (ret) 215 krb5_err(context, 1, ret, "krb5_cc_default"); 216 } 217 218 if (add_to_ccache) { 219 krb5_principal def_princ; 220 221 /* 222 * Force fcache to read the ccache header, otherwise the store 223 * will fail. 224 */ 225 ret = krb5_cc_get_principal(context, ccache, &def_princ); 226 if (ret) { 227 krb5_warn(context, ret, 228 "Given ccache appears not to exist; initializing it"); 229 ret = krb5_cc_initialize(context, ccache, cred.client); 230 if (ret) 231 krb5_err(context, 1, ret, "krb5_cc_initialize"); 232 } 233 krb5_free_principal(context, def_princ); 234 } else { 235 ret = krb5_cc_initialize(context, ccache, cred.client); 236 if (ret) 237 krb5_err(context, 1, ret, "krb5_cc_initialize"); 238 } 239 240 if (use_referral_realm && 241 strcmp(krb5_principal_get_realm(context, cred.server), "") != 0) { 242 krb5_free_principal(context, cred.server); 243 ret = krb5_copy_principal(context, server_principal, &cred.server); 244 if (ret) 245 krb5_err(context, 1, ret, "krb5_copy_principal"); 246 ret = krb5_principal_set_realm(context, cred.server, ""); 247 if (ret) 248 krb5_err(context, 1, ret, "krb5_principal_set_realm"); 249 ret = krb5_cc_store_cred(context, ccache, &cred); 250 if (ret) 251 krb5_err(context, 1, ret, "krb5_cc_store_cred"); 252 253 krb5_free_principal(context, cred.server); 254 ret = krb5_copy_principal(context, server_principal, &cred.server); 255 if (ret) 256 krb5_err(context, 1, ret, "krb5_copy_principal"); 257 } 258 ret = krb5_cc_store_cred(context, ccache, &cred); 259 if (ret) 260 krb5_err(context, 1, ret, "krb5_cc_store_cred"); 261 262 krb5_free_cred_contents(context, &cred); 263 krb5_cc_close(context, ccache); 264 265 return 0; 266 } 267 268 /* 269 * 270 */ 271 272 static void 273 setup_env(krb5_context context, krb5_keytab *kt) 274 { 275 krb5_error_code ret; 276 277 if (keytab_file) 278 ret = krb5_kt_resolve(context, keytab_file, kt); 279 else 280 ret = krb5_kt_default(context, kt); 281 if (ret) 282 krb5_err(context, 1, ret, "resolving keytab"); 283 284 if (client_principal_str == NULL) 285 krb5_errx(context, 1, "missing client principal"); 286 ret = krb5_parse_name(context, client_principal_str, &client_principal); 287 if (ret) 288 krb5_err(context, 1, ret, "resolvning client name"); 289 290 if (server_principal_str == NULL) 291 krb5_errx(context, 1, "missing server principal"); 292 ret = krb5_parse_name(context, server_principal_str, &server_principal); 293 if (ret) 294 krb5_err(context, 1, ret, "resolvning server name"); 295 296 /* If no session-enc-type specified on command line and this is an afs */ 297 /* service ticket, change default of session_enc_type to DES. */ 298 if (session_enctype_string == NULL 299 && strcmp("afs", *server_principal->name.name_string.val) == 0) 300 session_enc_type = "des-cbc-crc"; 301 302 if (ticket_flags_str) { 303 int ticket_flags_int; 304 305 ticket_flags_int = parse_flags(ticket_flags_str, 306 asn1_TicketFlags_units(), 0); 307 if (ticket_flags_int <= 0) { 308 krb5_warnx(context, "bad ticket flags: `%s'", ticket_flags_str); 309 print_flags_table(asn1_TicketFlags_units(), stderr); 310 exit(1); 311 } 312 if (ticket_flags_int) 313 ticket_flags = int2TicketFlags(ticket_flags_int); 314 } 315 } 316 317 /* 318 * 319 */ 320 321 struct getargs args[] = { 322 { "ccache", 0, arg_string, &ccache_str, 323 "name of kerberos 5 credential cache", "cache-name"}, 324 { "server", 's', arg_string, &server_principal_str, 325 "name of server principal", NULL }, 326 { "client", 'c', arg_string, &client_principal_str, 327 "name of client principal", NULL }, 328 { "keytab", 'k', arg_string, &keytab_file, 329 "name of keytab file", NULL }, 330 { "krb5", '5', arg_flag, &use_krb5, 331 "create a kerberos 5 ticket", NULL }, 332 { "add", 'A', arg_flag, &add_to_ccache, 333 "add to ccache without re-initializing it", NULL }, 334 { "referral", 'R', arg_flag, &use_referral_realm, 335 "store an additional entry for the service with the empty realm", NULL }, 336 { "expire-time", 'e', arg_integer, &expiration_time, 337 "lifetime of ticket in seconds", NULL }, 338 { "client-addresses", 'a', arg_strings, &client_addresses, 339 "addresses of client", NULL }, 340 { "enc-type", 't', arg_string, &enctype_string, 341 "encryption type", NULL }, 342 { "session-enc-type", 0, arg_string,&session_enctype_string, 343 "encryption type", NULL }, 344 { "ticket-flags", 'f', arg_string, &ticket_flags_str, 345 "ticket flags for krb5 ticket", NULL }, 346 { "version", 0, arg_flag, &version_flag, "Print version", 347 NULL }, 348 { "help", 0, arg_flag, &help_flag, NULL, 349 NULL } 350 }; 351 352 static void 353 usage(int ret) 354 { 355 arg_printusage(args, 356 sizeof(args) / sizeof(args[0]), 357 NULL, 358 ""); 359 exit(ret); 360 } 361 362 int 363 main(int argc, char **argv) 364 { 365 int optidx = 0; 366 krb5_error_code ret; 367 krb5_context context; 368 krb5_keytab kt; 369 370 setprogname(argv[0]); 371 372 ret = krb5_init_context(&context); 373 if (ret) 374 errx(1, "krb5_init_context failed: %u", ret); 375 376 if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) 377 usage(1); 378 379 if (help_flag) 380 usage(0); 381 382 if (version_flag) { 383 print_version(NULL); 384 return 0; 385 } 386 387 if (enctype_string) 388 enc_type = enctype_string; 389 if (session_enctype_string) 390 session_enc_type = session_enctype_string; 391 else 392 session_enc_type = enc_type; 393 394 setup_env(context, &kt); 395 396 if (use_krb5) 397 create_krb5_tickets(context, kt); 398 399 krb5_kt_close(context, kt); 400 401 return 0; 402 } 403