Home | History | Annotate | Line # | Download | only in kuser
      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