xref: /netbsd-src/crypto/external/bsd/heimdal/dist/kuser/kimpersonate.c (revision 241bea01a19bbb306af27777a870b86d41cb3fda)
1 /*	$NetBSD: kimpersonate.c,v 1.3 2019/12/15 22:50:46 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
encode_ticket(krb5_context context,EncryptionKey * skey,krb5_enctype etype,int skvno,krb5_creds * cred)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
create_krb5_tickets(krb5_context context,krb5_keytab kt)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
setup_env(krb5_context context,krb5_keytab * kt)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
usage(int ret)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
main(int argc,char ** argv)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