xref: /original-bsd/lib/libtelnet/forward.c (revision c4f3b704)
1 /*
2  *    $Source: /mit/krb5/.cvsroot/src/appl/bsd/forward.c,v $
3  *    $Id: forward.c,v 1.4 1993/10/15 16:41:29 tytso Exp $
4  */
5 
6 #ifndef lint
7 static char *rcsid_forward_c =
8   "$Id: forward.c,v 1.4 1993/10/15 16:41:29 tytso Exp $";
9 #endif /* lint */
10 #define LIBC_SCCS
11 
12 /*-
13  * Copyright (c) 1993
14  *	The Regents of the University of California.  All rights reserved.
15  *
16  * %sccs.include.redist.c%
17  */
18 
19 #ifndef lint
20 static char sccsid[] = "@(#)forward.c	8.2 (Berkeley) 05/30/95";
21 #endif /* not lint */
22 
23 
24 /* General-purpose forwarding routines. These routines may be put into */
25 /* libkrb5.a to allow widespread use */
26 
27 #if defined(KRB5) && defined(FORWARD)
28 #include <stdio.h>
29 #include <pwd.h>
30 #include <netdb.h>
31 
32 #include <krb5/krb5.h>
33 #include <krb5/asn1.h>
34 #include <krb5/crc-32.h>
35 #include <krb5/los-proto.h>
36 #include <krb5/ext-proto.h>
37 
38 #define KRB5_DEFAULT_LIFE 60*60*8   /* 8 hours */
39 /* helper function: convert flags to necessary KDC options */
40 #define flags2options(flags) (flags & KDC_TKT_COMMON_MASK)
41 
42 
43 
44 /* Get a TGT for use at the remote host */
45 krb5_error_code
get_for_creds(etype,sumtype,rhost,client,enc_key,forwardable,outbuf)46 get_for_creds(etype, sumtype, rhost, client, enc_key, forwardable, outbuf)
47      const krb5_enctype etype;
48      const krb5_cksumtype sumtype;
49      char *rhost;
50      krb5_principal client;
51      krb5_keyblock *enc_key;
52      int forwardable;      /* Should forwarded TGT also be forwardable? */
53      krb5_data *outbuf;
54 {
55     struct hostent *hp;
56     krb5_address **addrs;
57     krb5_error_code retval;
58     krb5_data *scratch;
59     krb5_kdc_rep *dec_rep;
60     krb5_error *err_reply;
61     krb5_response tgsrep;
62     krb5_creds creds, tgt;
63     krb5_ccache cc;
64     krb5_flags kdcoptions;
65     krb5_timestamp now;
66     char *remote_host;
67     char **hrealms;
68     int i;
69 
70     if (!rhost || !(hp = gethostbyname(rhost)))
71       return KRB5_ERR_BAD_HOSTNAME;
72 
73     remote_host = (char *) malloc(strlen(hp->h_name)+1);
74     if (!remote_host)
75       return ENOMEM;
76     strcpy(remote_host, hp->h_name);
77 
78     if (retval = krb5_get_host_realm(remote_host, &hrealms)) {
79 	free(remote_host);
80 	return retval;
81     }
82     if (!hrealms[0]) {
83 	free(remote_host);
84 	krb5_xfree(hrealms);
85 	return KRB5_ERR_HOST_REALM_UNKNOWN;
86     }
87 
88     /* Count elements */
89     for(i=0; hp->h_addr_list[i]; i++);
90 
91     addrs = (krb5_address **) malloc ((i+1)*sizeof(*addrs));
92     if (!addrs)
93       return ENOMEM;
94 
95     for(i=0; hp->h_addr_list[i]; i++) {
96 	addrs[i] = (krb5_address *) malloc(sizeof(krb5_address));
97 	if (addrs[i]) {
98 	    addrs[i]->addrtype = hp->h_addrtype;
99 	    addrs[i]->length   = hp->h_length;
100 	    addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
101 	    if (!addrs[i]->contents) {
102 		krb5_free_addresses(addrs);
103 		return ENOMEM;
104 	    }
105 	    else
106 	      memmove ((char *)addrs[i]->contents, hp->h_addr_list[i],
107 		      addrs[i]->length);
108 	}
109 	else {
110 	    return ENOMEM;
111 	}
112     }
113     addrs[i] = 0;
114 
115     memset((char *)&creds, 0, sizeof(creds));
116     if (retval = krb5_copy_principal(client, &creds.client))
117       return retval;
118 
119     if (retval = krb5_build_principal_ext(&creds.server,
120 					  strlen(hrealms[0]),
121 					  hrealms[0],
122 					  KRB5_TGS_NAME_SIZE,
123 					  KRB5_TGS_NAME,
124 					  client->realm.length,
125 					  client->realm.data,
126 					  0))
127       return retval;
128 
129     creds.times.starttime = 0;
130     if (retval = krb5_timeofday(&now)) {
131 	return retval;
132     }
133     creds.times.endtime = now + KRB5_DEFAULT_LIFE;
134     creds.times.renew_till = 0;
135 
136     if (retval = krb5_cc_default(&cc)) {
137 	return retval;
138     }
139 
140     /* fetch tgt directly from cache */
141     if (retval = krb5_cc_retrieve_cred (cc,
142 					KRB5_TC_MATCH_SRV_NAMEONLY,
143 					&creds,
144 					&tgt)) {
145 	return retval;
146     }
147 
148     /* tgt->client must be equal to creds.client */
149     if (!krb5_principal_compare(tgt.client, creds.client))
150 	return KRB5_PRINC_NOMATCH;
151 
152     if (!tgt.ticket.length)
153 	return(KRB5_NO_TKT_SUPPLIED);
154 
155     kdcoptions = flags2options(tgt.ticket_flags)|KDC_OPT_FORWARDED;
156 
157     if (!forwardable) /* Reset KDC_OPT_FORWARDABLE */
158       kdcoptions &= ~(KDC_OPT_FORWARDABLE);
159 
160     if (retval = krb5_send_tgs(kdcoptions, &creds.times, etype, sumtype,
161 			       creds.server,
162 			       addrs,
163 			       creds.authdata,
164 			       0,		/* no padata */
165 			       0,		/* no second ticket */
166 			       &tgt, &tgsrep))
167 	return retval;
168 
169 #undef cleanup
170 #define cleanup() free(tgsrep.response.data)
171 
172     switch (tgsrep.message_type) {
173     case KRB5_TGS_REP:
174 	break;
175     case KRB5_ERROR:
176     default:
177 	if (!krb5_is_krb_error(&tgsrep.response)) {
178 	    retval = KRB5KRB_AP_ERR_MSG_TYPE;
179 	} else
180 	    retval = decode_krb5_error(&tgsrep.response, &err_reply);
181 	if (retval) {
182 	    cleanup();
183 	    return retval;		/* neither proper reply nor error! */
184 	}
185 
186 	retval = err_reply->error + ERROR_TABLE_BASE_krb5;
187 
188 	krb5_free_error(err_reply);
189 	cleanup();
190 	return retval;
191     }
192     retval = krb5_decode_kdc_rep(&tgsrep.response,
193 				 &tgt.keyblock,
194 				 etype, /* enctype */
195 				 &dec_rep);
196 
197     cleanup();
198     if (retval)
199 	return retval;
200 #undef cleanup
201 #define cleanup() {\
202 	memset((char *)dec_rep->enc_part2->session->contents, 0,\
203 	      dec_rep->enc_part2->session->length);\
204 		  krb5_free_kdc_rep(dec_rep); }
205 
206     if (dec_rep->msg_type != KRB5_TGS_REP) {
207 	retval = KRB5KRB_AP_ERR_MSG_TYPE;
208 	cleanup();
209 	return retval;
210     }
211 
212     /* now it's decrypted and ready for prime time */
213 
214     if (!krb5_principal_compare(dec_rep->client, tgt.client)) {
215 	cleanup();
216 	return KRB5_KDCREP_MODIFIED;
217     }
218 
219     if (retval = mk_cred(dec_rep,
220 			 etype,
221 			 enc_key,
222 			 0,
223 			 0,
224 			 outbuf))
225       return retval;
226 
227     krb5_free_kdc_rep(dec_rep);
228 
229     return retval;
230 #undef cleanup
231 }
232 
233 
234 
235 /* Create asn.1 encoded KRB-CRED message from the kdc reply. */
236 krb5_error_code
mk_cred(dec_rep,etype,key,sender_addr,recv_addr,outbuf)237 mk_cred(dec_rep, etype, key, sender_addr, recv_addr, outbuf)
238 krb5_kdc_rep *dec_rep;
239 krb5_enctype etype;
240 krb5_keyblock *key;
241 krb5_address *sender_addr;
242 krb5_address *recv_addr;
243 krb5_data *outbuf;
244 {
245     krb5_error_code retval;
246     krb5_encrypt_block eblock;
247     krb5_cred ret_cred;
248     krb5_cred_enc_part cred_enc_part;
249     krb5_data *scratch;
250 
251     if (!valid_etype(etype))
252       return KRB5_PROG_ETYPE_NOSUPP;
253 
254     ret_cred.tickets = (krb5_ticket **) calloc(2, sizeof(*ret_cred.tickets));
255     if (!ret_cred.tickets)
256       return ENOMEM;
257     ret_cred.tickets[0] = dec_rep->ticket;
258     ret_cred.tickets[1] = 0;
259 
260     ret_cred.enc_part.etype = etype;
261     ret_cred.enc_part.kvno = 0;
262 
263     cred_enc_part.ticket_info = (krb5_cred_info **)
264       calloc(2, sizeof(*cred_enc_part.ticket_info));
265     if (!cred_enc_part.ticket_info) {
266 	krb5_free_tickets(ret_cred.tickets);
267 	return ENOMEM;
268     }
269     cred_enc_part.ticket_info[0] = (krb5_cred_info *)
270       malloc(sizeof(*cred_enc_part.ticket_info[0]));
271     if (!cred_enc_part.ticket_info[0]) {
272 	krb5_free_tickets(ret_cred.tickets);
273 	krb5_free_cred_enc_part(cred_enc_part);
274 	return ENOMEM;
275     }
276     cred_enc_part.nonce = 0;
277 
278     if (retval = krb5_us_timeofday(&cred_enc_part.timestamp,
279 				   &cred_enc_part.usec))
280       return retval;
281 
282     cred_enc_part.s_address = (krb5_address *)sender_addr;
283     cred_enc_part.r_address = (krb5_address *)recv_addr;
284 
285     cred_enc_part.ticket_info[0]->session = dec_rep->enc_part2->session;
286     cred_enc_part.ticket_info[0]->client = dec_rep->client;
287     cred_enc_part.ticket_info[0]->server = dec_rep->enc_part2->server;
288     cred_enc_part.ticket_info[0]->flags  = dec_rep->enc_part2->flags;
289     cred_enc_part.ticket_info[0]->times  = dec_rep->enc_part2->times;
290     cred_enc_part.ticket_info[0]->caddrs = dec_rep->enc_part2->caddrs;
291 
292     cred_enc_part.ticket_info[1] = 0;
293 
294     /* start by encoding to-be-encrypted part of the message */
295 
296     if (retval = encode_krb5_enc_cred_part(&cred_enc_part, &scratch))
297       return retval;
298 
299 #define cleanup_scratch() { (void) memset(scratch->data, 0, scratch->length); krb5_free_data(scratch); }
300 
301     /* put together an eblock for this encryption */
302 
303     krb5_use_cstype(&eblock, etype);
304     ret_cred.enc_part.ciphertext.length = krb5_encrypt_size(scratch->length,
305 						eblock.crypto_entry);
306     /* add padding area, and zero it */
307     if (!(scratch->data = realloc(scratch->data,
308 				  ret_cred.enc_part.ciphertext.length))) {
309 	/* may destroy scratch->data */
310 	krb5_xfree(scratch);
311 	return ENOMEM;
312     }
313     memset(scratch->data + scratch->length, 0,
314 	  ret_cred.enc_part.ciphertext.length - scratch->length);
315     if (!(ret_cred.enc_part.ciphertext.data =
316 	  malloc(ret_cred.enc_part.ciphertext.length))) {
317 	retval = ENOMEM;
318 	goto clean_scratch;
319     }
320 
321 #define cleanup_encpart() {\
322 	(void) memset(ret_cred.enc_part.ciphertext.data, 0, \
323 	     ret_cred.enc_part.ciphertext.length); \
324 	free(ret_cred.enc_part.ciphertext.data); \
325 	ret_cred.enc_part.ciphertext.length = 0; \
326 	ret_cred.enc_part.ciphertext.data = 0;}
327 
328     /* do any necessary key pre-processing */
329     if (retval = krb5_process_key(&eblock, key)) {
330 	goto clean_encpart;
331     }
332 
333 #define cleanup_prockey() {(void) krb5_finish_key(&eblock);}
334 
335     /* call the encryption routine */
336     if (retval = krb5_encrypt((krb5_pointer) scratch->data,
337 			      (krb5_pointer)
338 			      ret_cred.enc_part.ciphertext.data,
339 			      scratch->length, &eblock,
340 			      0)) {
341 	goto clean_prockey;
342     }
343 
344     /* private message is now assembled-- do some cleanup */
345     cleanup_scratch();
346 
347     if (retval = krb5_finish_key(&eblock)) {
348 	cleanup_encpart();
349 	return retval;
350     }
351     /* encode private message */
352     if (retval = encode_krb5_cred(&ret_cred, &scratch))  {
353 	cleanup_encpart();
354 	return retval;
355     }
356 
357     cleanup_encpart();
358 
359     *outbuf = *scratch;
360     krb5_xfree(scratch);
361     return 0;
362 
363  clean_prockey:
364     cleanup_prockey();
365  clean_encpart:
366     cleanup_encpart();
367  clean_scratch:
368     cleanup_scratch();
369 
370     return retval;
371 #undef cleanup_prockey
372 #undef cleanup_encpart
373 #undef cleanup_scratch
374 }
375 
376 
377 
378 /* Decode, decrypt and store the forwarded creds in the local ccache. */
379 krb5_error_code
rd_and_store_for_creds(inbuf,ticket,lusername)380 rd_and_store_for_creds(inbuf, ticket, lusername)
381      krb5_data *inbuf;
382      krb5_ticket *ticket;
383      char *lusername;
384 {
385     krb5_encrypt_block eblock;
386     krb5_creds creds;
387     krb5_error_code retval;
388     char ccname[35];
389     krb5_ccache ccache = NULL;
390     struct passwd *pwd;
391 
392     if (retval = rd_cred(inbuf, ticket->enc_part2->session,
393 			 &creds, 0, 0)) {
394 	return(retval);
395     }
396 
397     if (!(pwd = (struct passwd *) getpwnam(lusername))) {
398 	return -1;
399     }
400 
401     sprintf(ccname, "FILE:/tmp/krb5cc_%d", pwd->pw_uid);
402 
403     if (retval = krb5_cc_resolve(ccname, &ccache)) {
404 	return(retval);
405     }
406 
407     if (retval = krb5_cc_initialize(ccache,
408 				    ticket->enc_part2->client)) {
409 	return(retval);
410     }
411 
412     if (retval = krb5_cc_store_cred(ccache, &creds)) {
413 	return(retval);
414     }
415 
416     if (retval = chown(ccname+5, pwd->pw_uid, -1)) {
417 	return(retval);
418     }
419 
420     return retval;
421 }
422 
423 
424 
425 extern krb5_deltat krb5_clockskew;
426 #define in_clock_skew(date) (abs((date)-currenttime) < krb5_clockskew)
427 
428 /* Decode the KRB-CRED message, and return creds */
429 krb5_error_code
rd_cred(inbuf,key,creds,sender_addr,recv_addr)430 rd_cred(inbuf, key, creds, sender_addr, recv_addr)
431 const krb5_data *inbuf;
432 const krb5_keyblock *key;
433 krb5_creds *creds;		  /* Filled in */
434 const krb5_address *sender_addr;  /* optional */
435 const krb5_address *recv_addr;	  /* optional */
436 {
437     krb5_error_code retval;
438     krb5_encrypt_block eblock;
439     krb5_cred *credmsg;
440     krb5_cred_enc_part *credmsg_enc_part;
441     krb5_data *scratch;
442     krb5_timestamp currenttime;
443 
444     if (!krb5_is_krb_cred(inbuf))
445 	return KRB5KRB_AP_ERR_MSG_TYPE;
446 
447     /* decode private message */
448     if (retval = decode_krb5_cred(inbuf, &credmsg))  {
449 	return retval;
450     }
451 
452 #define cleanup_credmsg() {(void)krb5_xfree(credmsg->enc_part.ciphertext.data); (void)krb5_xfree(credmsg);}
453 
454     if (!(scratch = (krb5_data *) malloc(sizeof(*scratch)))) {
455 	cleanup_credmsg();
456 	return ENOMEM;
457     }
458 
459 #define cleanup_scratch() {(void)memset(scratch->data, 0, scratch->length); (void)krb5_xfree(scratch->data);}
460 
461     if (retval = encode_krb5_ticket(credmsg->tickets[0], &scratch)) {
462 	cleanup_credmsg();
463 	cleanup_scratch();
464 	return(retval);
465     }
466 
467     creds->ticket = *scratch;
468     if (!(creds->ticket.data = malloc(scratch->length))) {
469 	krb5_xfree(creds->ticket.data);
470 	return ENOMEM;
471     }
472     memmove((char *)creds->ticket.data, (char *) scratch->data, scratch->length);
473 
474     cleanup_scratch();
475 
476     if (!valid_etype(credmsg->enc_part.etype)) {
477 	cleanup_credmsg();
478 	return KRB5_PROG_ETYPE_NOSUPP;
479     }
480 
481     /* put together an eblock for this decryption */
482 
483     krb5_use_cstype(&eblock, credmsg->enc_part.etype);
484     scratch->length = credmsg->enc_part.ciphertext.length;
485 
486     if (!(scratch->data = malloc(scratch->length))) {
487 	cleanup_credmsg();
488 	return ENOMEM;
489     }
490 
491     /* do any necessary key pre-processing */
492     if (retval = krb5_process_key(&eblock, key)) {
493 	cleanup_credmsg();
494 	cleanup_scratch();
495 	return retval;
496     }
497 
498 #define cleanup_prockey() {(void) krb5_finish_key(&eblock);}
499 
500     /* call the decryption routine */
501     if (retval = krb5_decrypt((krb5_pointer) credmsg->enc_part.ciphertext.data,
502 			      (krb5_pointer) scratch->data,
503 			      scratch->length, &eblock,
504 			      0)) {
505 	cleanup_credmsg();
506 	cleanup_scratch();
507 	cleanup_prockey();
508 	return retval;
509     }
510 
511     /* cred message is now decrypted -- do some cleanup */
512 
513     cleanup_credmsg();
514 
515     if (retval = krb5_finish_key(&eblock)) {
516 	cleanup_scratch();
517 	return retval;
518     }
519 
520     /*  now decode the decrypted stuff */
521     if (retval = decode_krb5_enc_cred_part(scratch, &credmsg_enc_part)) {
522 	cleanup_scratch();
523 	return retval;
524     }
525     cleanup_scratch();
526 
527 #define cleanup_mesg() {(void)krb5_xfree(credmsg_enc_part);}
528 
529     if (retval = krb5_timeofday(&currenttime)) {
530 	cleanup_mesg();
531 	return retval;
532     }
533     if (!in_clock_skew(credmsg_enc_part->timestamp)) {
534 	cleanup_mesg();
535 	return KRB5KRB_AP_ERR_SKEW;
536     }
537 
538     if (sender_addr && credmsg_enc_part->s_address &&
539 	!krb5_address_compare(sender_addr,
540 			      credmsg_enc_part->s_address)) {
541 	cleanup_mesg();
542 	return KRB5KRB_AP_ERR_BADADDR;
543     }
544     if (recv_addr && credmsg_enc_part->r_address &&
545 	!krb5_address_compare(recv_addr,
546 			      credmsg_enc_part->r_address)) {
547 	cleanup_mesg();
548 	return KRB5KRB_AP_ERR_BADADDR;
549     }
550 
551     if (credmsg_enc_part->r_address) {
552 	krb5_address **our_addrs;
553 
554 	if (retval = krb5_os_localaddr(&our_addrs)) {
555 	    cleanup_mesg();
556 	    return retval;
557 	}
558 	if (!krb5_address_search(credmsg_enc_part->r_address,
559 				 our_addrs)) {
560 	    krb5_free_addresses(our_addrs);
561 	    cleanup_mesg();
562 	    return KRB5KRB_AP_ERR_BADADDR;
563 	}
564 	krb5_free_addresses(our_addrs);
565     }
566 
567     if (retval = krb5_copy_principal(credmsg_enc_part->ticket_info[0]->client,
568 				     &creds->client)) {
569 	return(retval);
570     }
571 
572     if (retval = krb5_copy_principal(credmsg_enc_part->ticket_info[0]->server,
573 				     &creds->server)) {
574 	return(retval);
575     }
576 
577     if (retval =
578 	krb5_copy_keyblock_contents(credmsg_enc_part->ticket_info[0]->session,
579 				    &creds->keyblock)) {
580 	return(retval);
581     }
582 
583 #undef clean
584 #define clean() {\
585 	memset((char *)creds->keyblock.contents, 0, creds->keyblock.length);}
586 
587     creds->times = credmsg_enc_part->ticket_info[0]->times;
588     creds->is_skey = FALSE;
589     creds->ticket_flags = credmsg_enc_part->ticket_info[0]->flags;
590 
591     if (retval = krb5_copy_addresses(credmsg_enc_part->ticket_info[0]->caddrs,
592 				     &creds->addresses)) {
593 	clean();
594 	return(retval);
595     }
596 
597     creds->second_ticket.length = 0;
598 
599     creds->authdata = 0;
600 
601     cleanup_mesg();
602     return 0;
603 #undef clean
604 #undef cleanup_credmsg
605 #undef cleanup_scratch
606 #undef cleanup_prockey
607 #undef cleanup_mesg
608 }
609 
610 #endif /* defined(KRB5) && defined(FORWARD) */
611