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(¤ttime)) {
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