1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/*
31 * Copyright (C) 1990 by the Massachusetts Institute of Technology
32 *
33 * Export of this software from the United States of America may
34 * require a specific license from the United States Government.
35 * It is the responsibility of any person or organization contemplating
36 * export to obtain such a license before exporting.
37 *
38 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
39 * distribute this software and its documentation for any purpose and
40 * without fee is hereby granted, provided that the above copyright
41 * notice appear in all copies and that both that copyright notice and
42 * this permission notice appear in supporting documentation, and that
43 * the name of M.I.T. not be used in advertising or publicity pertaining
44 * to distribution of the software without specific, written prior
45 * permission.  M.I.T. makes no representations about the suitability of
46 * this software for any purpose.  It is provided "as is" without express
47 * or implied warranty.
48 */
49
50#include <sys/cdefs.h>
51
52__FBSDID("$FreeBSD$");
53
54#ifdef	KRB5
55
56#include <arpa/telnet.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <unistd.h>
61#include <netdb.h>
62#include <ctype.h>
63#include <pwd.h>
64#define Authenticator k5_Authenticator
65#include <krb5.h>
66#undef Authenticator
67
68#include "encrypt.h"
69#include "auth.h"
70#include "misc.h"
71
72int forward_flags = 0;  /* Flags get set in telnet/main.c on -f and -F */
73
74/* These values need to be the same as those defined in telnet/main.c. */
75/* Either define them in both places, or put in some common header file. */
76#define OPTS_FORWARD_CREDS	0x00000002
77#define OPTS_FORWARDABLE_CREDS	0x00000001
78
79void kerberos5_forward (Authenticator *);
80
81static unsigned char str_data[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 0,
82			  		AUTHTYPE_KERBEROS_V5, };
83
84#define	KRB_AUTH		0	/* Authentication data follows */
85#define	KRB_REJECT		1	/* Rejected (reason might follow) */
86#define	KRB_ACCEPT		2	/* Accepted */
87#define	KRB_RESPONSE		3	/* Response for mutual auth. */
88
89#define KRB_FORWARD     	4       /* Forwarded credentials follow */
90#define KRB_FORWARD_ACCEPT     	5       /* Forwarded credentials accepted */
91#define KRB_FORWARD_REJECT     	6       /* Forwarded credentials rejected */
92
93static	krb5_data auth;
94static  krb5_ticket *ticket;
95
96static krb5_context context;
97static krb5_auth_context auth_context;
98
99static void
100print_krb5_error(krb5_context context, krb5_error_code code, const char *msg)
101{
102	const char *error_message;
103
104	error_message = krb5_get_error_message(context, code);
105	printf(msg, error_message);
106	krb5_free_error_message(context, error_message);
107}
108
109static int
110Data(Authenticator *ap, int type, const char *d, int c)
111{
112    unsigned char *p = str_data + 4;
113    const unsigned char *cd = d;
114
115    if (c == -1)
116	c = strlen(cd);
117
118    if (auth_debug_mode) {
119	printf("%s:%d: [%d] (%d)",
120	       str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
121	       str_data[3],
122	       type, c);
123	printd(d, c);
124	printf("\r\n");
125    }
126    *p++ = ap->type;
127    *p++ = ap->way;
128    *p++ = type;
129    while (c-- > 0) {
130	if ((*p++ = *cd++) == IAC)
131	    *p++ = IAC;
132    }
133    *p++ = IAC;
134    *p++ = SE;
135    if (str_data[3] == TELQUAL_IS)
136	printsub('>', &str_data[2], p - &str_data[2]);
137    return(net_write(str_data, p - str_data));
138}
139
140int
141kerberos5_init(Authenticator *ap __unused, int server)
142{
143    krb5_error_code ret;
144
145    ret = krb5_init_context(&context);
146    if (ret)
147	return 0;
148    if (server) {
149	krb5_keytab kt;
150	krb5_kt_cursor cursor;
151
152	ret = krb5_kt_default(context, &kt);
153	if (ret)
154	    return 0;
155
156	ret = krb5_kt_start_seq_get (context, kt, &cursor);
157	if (ret) {
158	    krb5_kt_close (context, kt);
159	    return 0;
160	}
161	krb5_kt_end_seq_get (context, kt, &cursor);
162	krb5_kt_close (context, kt);
163
164	str_data[3] = TELQUAL_REPLY;
165    } else
166	str_data[3] = TELQUAL_IS;
167    return(1);
168}
169
170extern int net;
171
172static int
173kerberos5_send(const char *name, Authenticator *ap)
174{
175    krb5_error_code ret;
176    krb5_ccache ccache;
177    int ap_opts;
178    krb5_data cksum_data;
179    char foo[2];
180
181    if (!UserNameRequested) {
182	if (auth_debug_mode) {
183	    printf("Kerberos V5: no user name supplied\r\n");
184	}
185	return(0);
186    }
187
188    ret = krb5_cc_default(context, &ccache);
189    if (ret) {
190	if (auth_debug_mode) {
191	    print_krb5_error(context, ret, "Kerberos V5: could not get default ccache: %s\r\n");
192	}
193	return 0;
194    }
195
196    if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL)
197	ap_opts = AP_OPTS_MUTUAL_REQUIRED;
198    else
199	ap_opts = 0;
200    ap_opts |= AP_OPTS_USE_SUBKEY;
201
202    ret = krb5_auth_con_init (context, &auth_context);
203    if (ret) {
204	if (auth_debug_mode) {
205	    print_krb5_error(context, ret, "Kerberos V5: krb5_auth_con_init failed (%s)\r\n");
206	}
207	return(0);
208    }
209
210    ret = krb5_auth_con_setaddrs_from_fd (context,
211					  auth_context,
212					  &net);
213    if (ret) {
214	if (auth_debug_mode) {
215	    print_krb5_error(context, ret, "Kerberos V5:"
216		    " krb5_auth_con_setaddrs_from_fd failed (%s)\r\n");
217	}
218	return(0);
219    }
220
221    krb5_auth_con_setkeytype (context, auth_context, KEYTYPE_DES);
222
223    foo[0] = ap->type;
224    foo[1] = ap->way;
225
226    cksum_data.length = sizeof(foo);
227    cksum_data.data   = foo;
228
229
230    {
231	krb5_principal service;
232	char sname[128];
233
234
235	ret = krb5_sname_to_principal (context,
236				       RemoteHostName,
237				       NULL,
238				       KRB5_NT_SRV_HST,
239				       &service);
240	if(ret) {
241	    if (auth_debug_mode) {
242		const char *err_str;
243
244		err_str = krb5_get_error_message(context, ret);
245		printf("Kerberosr V5:"
246			" krb5_sname_to_principal(%s) failed (%s)\r\n",
247			RemoteHostName, err_str);
248		krb5_free_error_message(context, err_str);
249	    }
250	    return 0;
251	}
252	ret = krb5_unparse_name_fixed(context, service, sname, sizeof(sname));
253	if(ret) {
254	    if (auth_debug_mode) {
255		print_krb5_error(context, ret, "Kerberos V5:"
256			" krb5_unparse_name_fixed failed (%s)\r\n");
257	    }
258	    return 0;
259	}
260	printf("[ Trying %s (%s)... ]\r\n", name, sname);
261	ret = krb5_mk_req_exact(context, &auth_context, ap_opts,
262				service,
263				&cksum_data, ccache, &auth);
264	krb5_free_principal (context, service);
265
266    }
267    if (ret) {
268	if (1 || auth_debug_mode) {
269	    print_krb5_error(context, ret, "Kerberos V5: mk_req failed (%s)\r\n");
270	}
271	return(0);
272    }
273
274    if (!auth_sendname((unsigned char *)UserNameRequested,
275		       strlen(UserNameRequested))) {
276	if (auth_debug_mode)
277	    printf("Not enough room for user name\r\n");
278	return(0);
279    }
280    if (!Data(ap, KRB_AUTH, auth.data, auth.length)) {
281	if (auth_debug_mode)
282	    printf("Not enough room for authentication data\r\n");
283	return(0);
284    }
285    if (auth_debug_mode) {
286	printf("Sent Kerberos V5 credentials to server\r\n");
287    }
288    return(1);
289}
290
291int
292kerberos5_send_mutual(Authenticator *ap)
293{
294    return kerberos5_send("mutual KERBEROS5", ap);
295}
296
297int
298kerberos5_send_oneway(Authenticator *ap)
299{
300    return kerberos5_send("KERBEROS5", ap);
301}
302
303void
304kerberos5_is(Authenticator *ap, unsigned char *data, int cnt)
305{
306    krb5_error_code ret;
307    krb5_data outbuf;
308    krb5_keyblock *key_block;
309    char *name;
310    krb5_principal server;
311    int zero = 0;
312
313    if (cnt-- < 1)
314	return;
315    switch (*data++) {
316    case KRB_AUTH:
317	auth.data = (char *)data;
318	auth.length = cnt;
319
320	auth_context = NULL;
321
322	ret = krb5_auth_con_init (context, &auth_context);
323	if (ret) {
324	    Data(ap, KRB_REJECT, "krb5_auth_con_init failed", -1);
325	    auth_finished(ap, AUTH_REJECT);
326	    if (auth_debug_mode)
327		print_krb5_error(context, ret, "Kerberos V5: krb5_auth_con_init failed (%s)\r\n");
328	    return;
329	}
330
331	ret = krb5_auth_con_setaddrs_from_fd (context,
332					      auth_context,
333					      &zero);
334	if (ret) {
335	    Data(ap, KRB_REJECT, "krb5_auth_con_setaddrs_from_fd failed", -1);
336	    auth_finished(ap, AUTH_REJECT);
337	    if (auth_debug_mode)
338		print_krb5_error(context, ret, "Kerberos V5: "
339		       "krb5_auth_con_setaddrs_from_fd failed (%s)\r\n");
340	    return;
341	}
342
343	ret = krb5_sock_to_principal (context,
344				      0,
345				      "host",
346				      KRB5_NT_SRV_HST,
347				      &server);
348	if (ret) {
349	    Data(ap, KRB_REJECT, "krb5_sock_to_principal failed", -1);
350	    auth_finished(ap, AUTH_REJECT);
351	    if (auth_debug_mode)
352		print_krb5_error(context, ret, "Kerberos V5: "
353		       "krb5_sock_to_principal failed (%s)\r\n");
354	    return;
355	}
356
357	ret = krb5_rd_req(context,
358			  &auth_context,
359			  &auth,
360			  server,
361			  NULL,
362			  NULL,
363			  &ticket);
364
365	krb5_free_principal (context, server);
366	if (ret) {
367	    char *errbuf;
368	    const char *err_str;
369
370	    err_str = krb5_get_error_message(context, ret);
371	    asprintf(&errbuf,
372		     "Read req failed: %s", err_str);
373	    krb5_free_error_message(context, err_str);
374	    Data(ap, KRB_REJECT, errbuf, -1);
375	    if (auth_debug_mode)
376		printf("%s\r\n", errbuf);
377	    free (errbuf);
378	    return;
379	}
380
381	{
382	    char foo[2];
383
384	    foo[0] = ap->type;
385	    foo[1] = ap->way;
386
387	    ret = krb5_verify_authenticator_checksum(context,
388						     auth_context,
389						     foo,
390						     sizeof(foo));
391
392	    if (ret) {
393		char *errbuf;
394		const char *err_str;
395
396		err_str = krb5_get_error_message(context, ret);
397		asprintf(&errbuf, "Bad checksum: %s", err_str);
398		krb5_free_error_message(context, err_str);
399		Data(ap, KRB_REJECT, errbuf, -1);
400		if (auth_debug_mode)
401		    printf ("%s\r\n", errbuf);
402		free(errbuf);
403		return;
404	    }
405	}
406	ret = krb5_auth_con_getremotesubkey (context,
407					     auth_context,
408					     &key_block);
409
410	if (ret) {
411	    Data(ap, KRB_REJECT, "krb5_auth_con_getremotesubkey failed", -1);
412	    auth_finished(ap, AUTH_REJECT);
413	    if (auth_debug_mode)
414		print_krb5_error(context, ret, "Kerberos V5: "
415		       "krb5_auth_con_getremotesubkey failed (%s)\r\n");
416	    return;
417	}
418
419	if (key_block == NULL) {
420	    ret = krb5_auth_con_getkey(context,
421				       auth_context,
422				       &key_block);
423	}
424	if (ret) {
425	    Data(ap, KRB_REJECT, "krb5_auth_con_getkey failed", -1);
426	    auth_finished(ap, AUTH_REJECT);
427	    if (auth_debug_mode)
428		print_krb5_error(context, ret, "Kerberos V5: "
429		       "krb5_auth_con_getkey failed (%s)\r\n");
430	    return;
431	}
432	if (key_block == NULL) {
433	    Data(ap, KRB_REJECT, "no subkey received", -1);
434	    auth_finished(ap, AUTH_REJECT);
435	    if (auth_debug_mode)
436		printf("Kerberos V5: "
437		       "krb5_auth_con_getremotesubkey returned NULL key\r\n");
438	    return;
439	}
440
441	if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
442	    ret = krb5_mk_rep(context, auth_context, &outbuf);
443	    if (ret) {
444		Data(ap, KRB_REJECT,
445		     "krb5_mk_rep failed", -1);
446		auth_finished(ap, AUTH_REJECT);
447		if (auth_debug_mode)
448		    print_krb5_error(context, ret, "Kerberos V5: "
449			   "krb5_mk_rep failed (%s)\r\n");
450		return;
451	    }
452	    Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length);
453	}
454	if (krb5_unparse_name(context, ticket->client, &name))
455	    name = 0;
456
457	if(UserNameRequested && krb5_kuserok(context,
458					     ticket->client,
459					     UserNameRequested)) {
460	    Data(ap, KRB_ACCEPT, name, name ? -1 : 0);
461	    if (auth_debug_mode) {
462		printf("Kerberos5 identifies him as ``%s''\r\n",
463		       name ? name : "");
464	    }
465
466	    if(key_block->keytype == ETYPE_DES_CBC_MD5 ||
467	       key_block->keytype == ETYPE_DES_CBC_MD4 ||
468	       key_block->keytype == ETYPE_DES_CBC_CRC) {
469		Session_Key skey;
470
471		skey.type = SK_DES;
472		skey.length = 8;
473		skey.data = key_block->keyvalue.data;
474		encrypt_session_key(&skey, 0);
475	    }
476
477	} else {
478	    char *msg;
479
480	    asprintf (&msg, "user `%s' is not authorized to "
481		      "login as `%s'",
482		      name ? name : "<unknown>",
483		      UserNameRequested ? UserNameRequested : "<nobody>");
484	    if (msg == NULL)
485		Data(ap, KRB_REJECT, NULL, 0);
486	    else {
487		Data(ap, KRB_REJECT, (void *)msg, -1);
488		free(msg);
489	    }
490	    auth_finished (ap, AUTH_REJECT);
491	    krb5_free_keyblock_contents(context, key_block);
492	    break;
493	}
494	auth_finished(ap, AUTH_USER);
495	krb5_free_keyblock_contents(context, key_block);
496
497	break;
498    case KRB_FORWARD: {
499	struct passwd *pwd;
500	char ccname[1024];	/* XXX */
501	krb5_data inbuf;
502	krb5_ccache ccache;
503	inbuf.data = (char *)data;
504	inbuf.length = cnt;
505
506	pwd = getpwnam (UserNameRequested);
507	if (pwd == NULL)
508	    break;
509
510	snprintf (ccname, sizeof(ccname),
511		  "FILE:/tmp/krb5cc_%u", pwd->pw_uid);
512
513	ret = krb5_cc_resolve (context, ccname, &ccache);
514	if (ret) {
515	    if (auth_debug_mode)
516		print_krb5_error(context, ret, "Kerberos V5: could not get ccache: %s\r\n");
517	    break;
518	}
519
520	ret = krb5_cc_initialize (context,
521				  ccache,
522				  ticket->client);
523	if (ret) {
524	    if (auth_debug_mode)
525		print_krb5_error(context, ret, "Kerberos V5: could not init ccache: %s\r\n");
526	    break;
527	}
528
529#if defined(DCE)
530	esetenv("KRB5CCNAME", ccname, 1);
531#endif
532	ret = krb5_rd_cred2 (context,
533			     auth_context,
534			     ccache,
535			     &inbuf);
536	if(ret) {
537	    char *errbuf;
538	    const char *err_str;
539
540	    err_str = krb5_get_error_message(context, ret);
541	    asprintf (&errbuf,
542		      "Read forwarded creds failed: %s", err_str);
543	    krb5_free_error_message(context, err_str);
544	    if(errbuf == NULL)
545		Data(ap, KRB_FORWARD_REJECT, NULL, 0);
546	    else
547		Data(ap, KRB_FORWARD_REJECT, errbuf, -1);
548	    if (auth_debug_mode)
549		printf("Could not read forwarded credentials: %s\r\n",
550		       errbuf);
551	    free (errbuf);
552	} else {
553	    Data(ap, KRB_FORWARD_ACCEPT, 0, 0);
554#if defined(DCE)
555	    dfsfwd = 1;
556#endif
557	}
558	chown (ccname + 5, pwd->pw_uid, -1);
559	if (auth_debug_mode)
560	    printf("Forwarded credentials obtained\r\n");
561	break;
562    }
563    default:
564	if (auth_debug_mode)
565	    printf("Unknown Kerberos option %d\r\n", data[-1]);
566	Data(ap, KRB_REJECT, 0, 0);
567	break;
568    }
569}
570
571void
572kerberos5_reply(Authenticator *ap, unsigned char *data, int cnt)
573{
574    static int mutual_complete = 0;
575
576    if (cnt-- < 1)
577	return;
578    switch (*data++) {
579    case KRB_REJECT:
580	if (cnt > 0) {
581	    printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n",
582		   cnt, data);
583	} else
584	    printf("[ Kerberos V5 refuses authentication ]\r\n");
585	auth_send_retry();
586	return;
587    case KRB_ACCEPT: {
588	krb5_error_code ret;
589	Session_Key skey;
590	krb5_keyblock *keyblock;
591
592	if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL &&
593	    !mutual_complete) {
594	    printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\r\n");
595	    auth_send_retry();
596	    return;
597	}
598	if (cnt)
599	    printf("[ Kerberos V5 accepts you as ``%.*s'' ]\r\n", cnt, data);
600	else
601	    printf("[ Kerberos V5 accepts you ]\r\n");
602
603	ret = krb5_auth_con_getlocalsubkey (context,
604					    auth_context,
605					    &keyblock);
606	if (ret)
607	    ret = krb5_auth_con_getkey (context,
608					auth_context,
609					&keyblock);
610	if(ret) {
611	    print_krb5_error(context, ret, "[ krb5_auth_con_getkey: %s ]\r\n");
612	    auth_send_retry();
613	    return;
614	}
615
616	skey.type = SK_DES;
617	skey.length = 8;
618	skey.data = keyblock->keyvalue.data;
619	encrypt_session_key(&skey, 0);
620	krb5_free_keyblock_contents (context, keyblock);
621	auth_finished(ap, AUTH_USER);
622	if (forward_flags & OPTS_FORWARD_CREDS)
623	    kerberos5_forward(ap);
624	break;
625    }
626    case KRB_RESPONSE:
627	if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
628	    /* the rest of the reply should contain a krb_ap_rep */
629	  krb5_ap_rep_enc_part *reply;
630	  krb5_data inbuf;
631	  krb5_error_code ret;
632
633	  inbuf.length = cnt;
634	  inbuf.data = (char *)data;
635
636	  ret = krb5_rd_rep(context, auth_context, &inbuf, &reply);
637	  if (ret) {
638	      print_krb5_error(context, ret, "[ Mutual authentication failed: %s ]\r\n");
639	      auth_send_retry();
640	      return;
641	  }
642	  krb5_free_ap_rep_enc_part(context, reply);
643	  mutual_complete = 1;
644	}
645	return;
646    case KRB_FORWARD_ACCEPT:
647	printf("[ Kerberos V5 accepted forwarded credentials ]\r\n");
648	return;
649    case KRB_FORWARD_REJECT:
650	printf("[ Kerberos V5 refuses forwarded credentials because %.*s ]\r\n",
651	       cnt, data);
652	return;
653    default:
654	if (auth_debug_mode)
655	    printf("Unknown Kerberos option %d\r\n", data[-1]);
656	return;
657    }
658}
659
660int
661kerberos5_status(Authenticator *ap __unused, char *name, int level)
662{
663    if (level < AUTH_USER)
664	return(level);
665
666    if (UserNameRequested &&
667	krb5_kuserok(context,
668		     ticket->client,
669		     UserNameRequested))
670	{
671	    strcpy(name, UserNameRequested);
672	    return(AUTH_VALID);
673	} else
674	    return(AUTH_USER);
675}
676
677#define	BUMP(buf, len)		while (*(buf)) {++(buf), --(len);}
678#define	ADDC(buf, len, c)	if ((len) > 0) {*(buf)++ = (c); --(len);}
679
680void
681kerberos5_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
682{
683    int i;
684
685    buf[buflen-1] = '\0';		/* make sure its NULL terminated */
686    buflen -= 1;
687
688    switch(data[3]) {
689    case KRB_REJECT:		/* Rejected (reason might follow) */
690	strlcpy((char *)buf, " REJECT ", buflen);
691	goto common;
692
693    case KRB_ACCEPT:		/* Accepted (name might follow) */
694	strlcpy((char *)buf, " ACCEPT ", buflen);
695    common:
696	BUMP(buf, buflen);
697	if (cnt <= 4)
698	    break;
699	ADDC(buf, buflen, '"');
700	for (i = 4; i < cnt; i++)
701	    ADDC(buf, buflen, data[i]);
702	ADDC(buf, buflen, '"');
703	ADDC(buf, buflen, '\0');
704	break;
705
706
707    case KRB_AUTH:			/* Authentication data follows */
708	strlcpy((char *)buf, " AUTH", buflen);
709	goto common2;
710
711    case KRB_RESPONSE:
712	strlcpy((char *)buf, " RESPONSE", buflen);
713	goto common2;
714
715    case KRB_FORWARD:		/* Forwarded credentials follow */
716	strlcpy((char *)buf, " FORWARD", buflen);
717	goto common2;
718
719    case KRB_FORWARD_ACCEPT:	/* Forwarded credentials accepted */
720	strlcpy((char *)buf, " FORWARD_ACCEPT", buflen);
721	goto common2;
722
723    case KRB_FORWARD_REJECT:	/* Forwarded credentials rejected */
724	/* (reason might follow) */
725	strlcpy((char *)buf, " FORWARD_REJECT", buflen);
726	goto common2;
727
728    default:
729	snprintf(buf, buflen, " %d (unknown)", data[3]);
730    common2:
731	BUMP(buf, buflen);
732	for (i = 4; i < cnt; i++) {
733	    snprintf(buf, buflen, " %d", data[i]);
734	    BUMP(buf, buflen);
735	}
736	break;
737    }
738}
739
740void
741kerberos5_forward(Authenticator *ap)
742{
743    krb5_error_code ret;
744    krb5_ccache     ccache;
745    krb5_creds      creds;
746    krb5_kdc_flags  flags;
747    krb5_data       out_data;
748    krb5_principal  principal;
749
750    ret = krb5_cc_default (context, &ccache);
751    if (ret) {
752	if (auth_debug_mode)
753	    print_krb5_error(context, ret, "KerberosV5: could not get default ccache: %s\r\n");
754	return;
755    }
756
757    ret = krb5_cc_get_principal (context, ccache, &principal);
758    if (ret) {
759	if (auth_debug_mode)
760	    print_krb5_error(context, ret, "KerberosV5: could not get principal: %s\r\n");
761	return;
762    }
763
764    memset (&creds, 0, sizeof(creds));
765
766    creds.client = principal;
767
768    ret = krb5_build_principal (context,
769				&creds.server,
770				strlen(principal->realm),
771				principal->realm,
772				"krbtgt",
773				principal->realm,
774				NULL);
775
776    if (ret) {
777	if (auth_debug_mode)
778	    print_krb5_error(context, ret, "KerberosV5: could not get principal: %s\r\n");
779	return;
780    }
781
782    creds.times.endtime = 0;
783
784    flags.i = 0;
785    flags.b.forwarded = 1;
786    if (forward_flags & OPTS_FORWARDABLE_CREDS)
787	flags.b.forwardable = 1;
788
789    ret = krb5_get_forwarded_creds (context,
790				    auth_context,
791				    ccache,
792				    flags.i,
793				    RemoteHostName,
794				    &creds,
795				    &out_data);
796    if (ret) {
797	if (auth_debug_mode)
798	    print_krb5_error(context, ret, "Kerberos V5: error getting forwarded creds: %s\r\n");
799	return;
800    }
801
802    if(!Data(ap, KRB_FORWARD, out_data.data, out_data.length)) {
803	if (auth_debug_mode)
804	    printf("Not enough room for authentication data\r\n");
805    } else {
806	if (auth_debug_mode)
807	    printf("Forwarded local Kerberos V5 credentials to server\r\n");
808    }
809}
810
811#if defined(DCE)
812/* if this was a K5 authentication try and join a PAG for the user. */
813void
814kerberos5_dfspag(void)
815{
816    if (dfsk5ok) {
817	dfspag = krb5_dfs_pag(context, dfsfwd, ticket->client,
818			      UserNameRequested);
819    }
820}
821#endif
822
823#endif /* KRB5 */
824