2 * appl/telnet/libtelnet/kerberos5.c
6 * Copyright (c) 1991, 1993
7 * The Regents of the University of California. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the University of
20 * California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 /* based on @(#)kerberos5.c 8.1 (Berkeley) 6/4/93 */
41 * Copyright (C) 1990 by the Massachusetts Institute of Technology
43 * Export of this software from the United States of America may
44 * require a specific license from the United States Government.
45 * It is the responsibility of any person or organization contemplating
46 * export to obtain such a license before exporting.
48 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
49 * distribute this software and its documentation for any purpose and
50 * without fee is hereby granted, provided that the above copyright
51 * notice appear in all copies and that both that copyright notice and
52 * this permission notice appear in supporting documentation, and that
53 * the name of M.I.T. not be used in advertising or publicity pertaining
54 * to distribution of the software without specific, written prior
55 * permission. Furthermore if you modify this software you must label
56 * your software as modified software and not distribute it in such a
57 * fashion that it might be confused with the original M.I.T. software.
58 * M.I.T. makes no representations about the suitability of
59 * this software for any purpose. It is provided "as is" without express
60 * or implied warranty.
65 #include <arpa/telnet.h>
67 #define KRB5_DEPRECATED 1 /* krb5_auth_con_getremotesubkey */
73 #include <sys/errno.h>
78 extern char *malloc();
90 extern int auth_debug_mode
;
94 int forward_flags
= 0; /* Flags get set in telnet/main.c on -f and -F */
96 void kerberos5_forward();
98 rd_and_store_for_creds(krb5_context context
, krb5_auth_context auth_context
, krb5_data
*inbuf
, krb5_ticket
*ticket
);
102 static unsigned char str_data
[8192] = {IAC
, SB
, TELOPT_AUTHENTICATION
, 0,
103 AUTHTYPE_KERBEROS_V5
, };
104 /*static unsigned char str_name[1024] = { IAC, SB, TELOPT_AUTHENTICATION,
107 #define AUTH_ENCRYPT_OFF 0
108 #define AUTH_ENCRYPT_ON 4
109 #define AUTH_ENCRYPT_MASK 4
111 #define KRB_AUTH 0 /* Authentication data follows */
112 #define KRB_REJECT 1 /* Rejected (reason might follow) */
113 #define KRB_ACCEPT 2 /* Accepted */
114 #define KRB_RESPONSE 3 /* Response for mutual auth. */
117 #define KRB_FORWARD 4 /* Forwarded credentials follow */
118 #define KRB_FORWARD_ACCEPT 5 /* Forwarded credentials accepted */
119 #define KRB_FORWARD_REJECT 6 /* Forwarded credentials rejected */
122 krb5_auth_context auth_context
= 0;
124 static krb5_data auth
;
125 /* telnetd gets session key from here */
126 static krb5_ticket
* ticket
= NULL
;
127 /* telnet matches the AP_REQ and AP_REP with this */
129 /* some compilers can't hack void *, so we use the Kerberos krb5_pointer,
130 which is either void * or char *, depending on the compiler. */
132 #define Voidptr krb5_pointer
134 krb5_keyblock
*session_key
= 0;
135 char * telnet_srvtab
= NULL
;
136 char * telnet_krb5_realm
= NULL
;
145 unsigned char *p
= str_data
+ 4;
146 unsigned char *cd
= (unsigned char *)d
;
147 size_t spaceleft
= sizeof(str_data
) - 4;
150 c
= strlen((char *)cd
);
152 if (auth_debug_mode
) {
153 printf("%s:%d: [%d] (%d)",
154 str_data
[3] == TELQUAL_IS
? ">>>IS" : ">>>REPLY",
165 if ((*p
++ = *cd
++) == IAC
) {
169 if ((--spaceleft
< 4) && c
) {
176 if (str_data
[3] == TELQUAL_IS
)
177 printsub('>', &str_data
[2], p
- &str_data
[2]);
178 return(net_write(str_data
, p
- str_data
));
181 krb5_context telnet_context
= 0;
183 kerberos5_init(ap
, server
)
187 krb5_error_code retval
;
190 str_data
[3] = TELQUAL_REPLY
;
192 str_data
[3] = TELQUAL_IS
;
193 if (telnet_context
== 0) {
194 retval
= krb5_init_context(&telnet_context
);
204 krb5_error_code retval
;
208 if (telnet_context
== 0)
211 ccname
= getenv("KRB5CCNAME");
213 retval
= krb5_cc_resolve(telnet_context
, ccname
, &ccache
);
215 retval
= krb5_cc_destroy(telnet_context
, ccache
);
218 krb5_free_context(telnet_context
);
229 krb5_creds creds
; /* telnet gets session key from here */
230 krb5_creds
* new_creds
= 0;
233 krb5_data check_data
;
236 krb5_keyblock
*newkey
= 0;
237 #endif /* ENCRYPTION */
239 if (!UserNameRequested
) {
240 if (auth_debug_mode
) {
242 "telnet: Kerberos V5: no user name supplied\r\n");
247 if ((r
= krb5_cc_default(telnet_context
, &ccache
))) {
248 if (auth_debug_mode
) {
250 "telnet: Kerberos V5: could not get default ccache\r\n");
255 memset((char *)&creds
, 0, sizeof(creds
));
256 if ((r
= krb5_sname_to_principal(telnet_context
, RemoteHostName
,
257 "host", KRB5_NT_SRV_HST
,
260 printf("telnet: Kerberos V5: error while constructing service name: %s\r\n", error_message(r
));
264 if (telnet_krb5_realm
!= NULL
) {
267 rdata
.length
= strlen(telnet_krb5_realm
);
268 rdata
.data
= (char *) malloc(rdata
.length
+ 1);
269 if (rdata
.data
== NULL
) {
270 fprintf(stderr
, "malloc failed\n");
273 strcpy(rdata
.data
, telnet_krb5_realm
);
274 krb5_princ_set_realm(telnet_context
, creds
.server
, &rdata
);
277 if ((r
= krb5_cc_get_principal(telnet_context
, ccache
,
279 if (auth_debug_mode
) {
281 "telnet: Kerberos V5: failure on principal (%s)\r\n",
284 krb5_free_cred_contents(telnet_context
, &creds
);
288 creds
.keyblock
.enctype
=ENCTYPE_DES_CBC_CRC
;
289 if ((r
= krb5_get_credentials(telnet_context
, 0,
290 ccache
, &creds
, &new_creds
))) {
291 if (auth_debug_mode
) {
293 "telnet: Kerberos V5: failure on credentials(%s)\r\n",
296 krb5_free_cred_contents(telnet_context
, &creds
);
300 if ((ap
->way
& AUTH_HOW_MASK
) == AUTH_HOW_MUTUAL
)
301 ap_opts
= AP_OPTS_MUTUAL_REQUIRED
;
306 ap_opts
|= AP_OPTS_USE_SUBKEY
;
307 #endif /* ENCRYPTION */
310 krb5_auth_con_free(telnet_context
, auth_context
);
313 if ((r
= krb5_auth_con_init(telnet_context
, &auth_context
))) {
314 if (auth_debug_mode
) {
315 printf("Kerberos V5: failed to init auth_context (%s)\r\n",
321 krb5_auth_con_setflags(telnet_context
, auth_context
,
322 KRB5_AUTH_CONTEXT_RET_TIME
);
324 type_check
[0] = ap
->type
;
325 type_check
[1] = ap
->way
;
326 check_data
.magic
= KV5M_DATA
;
327 check_data
.length
= 2;
328 check_data
.data
= (char *) &type_check
;
330 r
= krb5_mk_req_extended(telnet_context
, &auth_context
, ap_opts
,
331 &check_data
, new_creds
, &auth
);
334 krb5_auth_con_getlocalsubkey(telnet_context
, auth_context
, &newkey
);
336 krb5_free_keyblock(telnet_context
, session_key
);
341 /* keep the key in our private storage, but don't use it
342 yet---see kerberos5_reply() below */
343 if ((newkey
->enctype
!= ENCTYPE_DES_CBC_CRC
) &&
344 (newkey
-> enctype
!= ENCTYPE_DES_CBC_MD5
)) {
345 if ((new_creds
->keyblock
.enctype
== ENCTYPE_DES_CBC_CRC
) ||
346 (new_creds
->keyblock
.enctype
== ENCTYPE_DES_CBC_MD5
))
347 /* use the session key in credentials instead */
348 krb5_copy_keyblock(telnet_context
,&new_creds
->keyblock
,
353 krb5_copy_keyblock(telnet_context
, newkey
, &session_key
);
355 krb5_free_keyblock(telnet_context
, newkey
);
357 #endif /* ENCRYPTION */
358 krb5_free_cred_contents(telnet_context
, &creds
);
359 krb5_free_creds(telnet_context
, new_creds
);
361 if (auth_debug_mode
) {
362 printf("telnet: Kerberos V5: mk_req failed (%s)\r\n",
368 if (!auth_sendname(UserNameRequested
, strlen(UserNameRequested
))) {
370 printf("telnet: Not enough room for user name\r\n");
373 if (!Data(ap
, KRB_AUTH
, auth
.data
, auth
.length
)) {
376 "telnet: Not enough room for authentication data\r\n");
379 if (auth_debug_mode
) {
380 printf("telnet: Sent Kerberos V5 credentials to server\r\n");
386 kerberos5_is(ap
, data
, cnt
)
392 krb5_principal server
;
393 krb5_keyblock
*newkey
= NULL
;
394 krb5_keytab keytabid
= 0;
403 krb5_authenticator
*authenticator
;
409 auth
.data
= (char *)data
;
412 if (!r
&& !auth_context
)
413 r
= krb5_auth_con_init(telnet_context
, &auth_context
);
417 r
= krb5_auth_con_getrcache(telnet_context
, auth_context
,
420 r
= krb5_sname_to_principal(telnet_context
, 0, 0,
421 KRB5_NT_SRV_HST
, &server
);
423 r
= krb5_get_server_rcache(telnet_context
,
424 krb5_princ_component(telnet_context
,
427 krb5_free_principal(telnet_context
, server
);
431 r
= krb5_auth_con_setrcache(telnet_context
,
432 auth_context
, rcache
);
434 if (!r
&& telnet_srvtab
)
435 r
= krb5_kt_resolve(telnet_context
,
436 telnet_srvtab
, &keytabid
);
438 r
= krb5_rd_req(telnet_context
, &auth_context
, &auth
,
439 NULL
, keytabid
, NULL
, &ticket
);
441 (void) strcpy(errbuf
, "krb5_rd_req failed: ");
442 errbuf
[sizeof(errbuf
) - 1] = '\0';
443 (void) strncat(errbuf
, error_message(r
), sizeof(errbuf
) - 1 - strlen(errbuf
));
448 * 256 bytes should be much larger than any reasonable
449 * first component of a service name especially since
450 * the default is of length 4.
452 if (krb5_princ_component(telnet_context
,ticket
->server
,0)->length
< 256) {
455 krb5_princ_component(telnet_context
, ticket
->server
,0)->data
,
456 krb5_princ_component(telnet_context
, ticket
->server
,0)->length
);
457 princ
[krb5_princ_component(telnet_context
,
458 ticket
->server
,0)->length
] = '\0';
459 if (strcmp("host", princ
)) {
460 if(strlen(princ
) < sizeof(errbuf
) - 39) {
461 (void) sprintf(errbuf
, "incorrect service name: \"%s\" != \"host\"",
464 (void) sprintf(errbuf
, "incorrect service name: principal != \"host\"");
469 (void) strcpy(errbuf
, "service name too long");
473 r
= krb5_auth_con_getauthenticator(telnet_context
,
477 (void) strcpy(errbuf
,
478 "krb5_auth_con_getauthenticator failed: ");
479 errbuf
[sizeof(errbuf
) - 1] = '\0';
480 (void) strncat(errbuf
, error_message(r
), sizeof(errbuf
) - 1 - strlen(errbuf
));
483 if ((ap
->way
& AUTH_ENCRYPT_MASK
) == AUTH_ENCRYPT_ON
&&
484 !authenticator
->checksum
) {
485 (void) strcpy(errbuf
,
486 "authenticator is missing required checksum");
489 if (authenticator
->checksum
) {
491 krb5_checksum
*cksum
= authenticator
->checksum
;
494 type_check
[0] = ap
->type
;
495 type_check
[1] = ap
->way
;
497 r
= krb5_auth_con_getkey(telnet_context
, auth_context
,
500 (void) strcpy(errbuf
, "krb5_auth_con_getkey failed: ");
501 errbuf
[sizeof(errbuf
) - 1] = '\0';
502 (void) strncat(errbuf
, error_message(r
), sizeof(errbuf
) - 1 - strlen(errbuf
));
505 r
= krb5_verify_checksum(telnet_context
,
506 cksum
->checksum_type
, cksum
,
507 &type_check
, 2, key
->contents
,
510 * Note that krb5_verify_checksum() will fail if a pre-
511 * MIT Kerberos Beta 5 client is attempting to connect
512 * to this server (Beta 6 or later). There is not way to
513 * fix this without compromising encryption. It would be
514 * reasonable to add a -i option to telnetd to ignore
515 * checksums (like in klogind). Such an option is not
516 * present at this time.
519 (void) strcpy(errbuf
,
520 "checksum verification failed: ");
521 errbuf
[sizeof(errbuf
) - 1] = '\0';
522 (void) strncat(errbuf
, error_message(r
), sizeof(errbuf
) - 1 - strlen(errbuf
));
525 krb5_free_keyblock(telnet_context
, key
);
527 krb5_free_authenticator(telnet_context
, authenticator
);
528 if ((ap
->way
& AUTH_HOW_MASK
) == AUTH_HOW_MUTUAL
) {
529 /* do ap_rep stuff here */
530 if ((r
= krb5_mk_rep(telnet_context
, auth_context
,
532 (void) strcpy(errbuf
, "Make reply failed: ");
533 errbuf
[sizeof(errbuf
) - 1] = '\0';
534 (void) strncat(errbuf
, error_message(r
), sizeof(errbuf
) - 1 - strlen(errbuf
));
538 Data(ap
, KRB_RESPONSE
, outbuf
.data
, outbuf
.length
);
540 if (krb5_unparse_name(telnet_context
,
541 ticket
->enc_part2
->client
,
544 Data(ap
, KRB_ACCEPT
, name
, name
? -1 : 0);
545 if (auth_debug_mode
) {
547 "telnetd: Kerberos5 identifies him as ``%s''\r\n",
550 auth_finished(ap
, AUTH_USER
);
554 krb5_auth_con_getremotesubkey(telnet_context
, auth_context
,
557 krb5_free_keyblock(telnet_context
, session_key
);
561 krb5_copy_keyblock(telnet_context
, newkey
, &session_key
);
562 krb5_free_keyblock(telnet_context
, newkey
);
564 krb5_copy_keyblock(telnet_context
,
565 ticket
->enc_part2
->session
,
572 skey
.data
= session_key
->contents
;
573 encrypt_session_key(&skey
, 1);
579 inbuf
.data
= (char *)data
;
580 if ((r
= krb5_auth_con_genaddrs(telnet_context
, auth_context
,
581 net
, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR
)) ||
582 (r
= rd_and_store_for_creds(telnet_context
, auth_context
,
587 (void) strcpy(errbuf
, "Read forwarded creds failed: ");
588 errbuf
[sizeof(errbuf
) - 1] = '\0';
589 (void) strncat(errbuf
, error_message(r
), sizeof(errbuf
) - 1 - strlen(errbuf
));
590 Data(ap
, KRB_FORWARD_REJECT
, errbuf
, -1);
593 "telnetd: Could not read forwarded credentials\r\n");
596 Data(ap
, KRB_FORWARD_ACCEPT
, 0, 0);
598 printf("telnetd: Forwarded credentials obtained\r\n");
603 printf("telnetd: Unknown Kerberos option %d\r\n",
605 Data(ap
, KRB_REJECT
, 0, 0);
614 strcpy(eerrbuf
, "telnetd: ");
615 eerrbuf
[sizeof(eerrbuf
) - 1] = '\0';
616 strncat(eerrbuf
, errbuf
, sizeof(eerrbuf
) - 1 - strlen(eerrbuf
));
617 Data(ap
, KRB_REJECT
, eerrbuf
, -1);
620 printf("telnetd: %s\r\n", errbuf
);
621 syslog(LOG_ERR
, "%s", errbuf
);
623 krb5_auth_con_free(telnet_context
, auth_context
);
630 kerberos5_reply(ap
, data
, cnt
)
638 static int mutual_complete
= 0;
645 printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n",
648 printf("[ Kerberos V5 refuses authentication ]\r\n");
652 if (!mutual_complete
) {
653 if ((ap
->way
& AUTH_HOW_MASK
) == AUTH_HOW_MUTUAL
) {
654 printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\r\n");
662 skey
.data
= session_key
->contents
;
663 encrypt_session_key(&skey
, 0);
665 #endif /* ENCRYPTION */
668 printf("[ Kerberos V5 accepts you as ``%.*s'' ]\r\n", cnt
, data
);
670 printf("[ Kerberos V5 accepts you ]\r\n");
671 auth_finished(ap
, AUTH_USER
);
673 if (forward_flags
& OPTS_FORWARD_CREDS
)
674 kerberos5_forward(ap
);
678 if ((ap
->way
& AUTH_HOW_MASK
) == AUTH_HOW_MUTUAL
) {
679 /* the rest of the reply should contain a krb_ap_rep */
680 krb5_ap_rep_enc_part
*reply
;
685 inbuf
.data
= (char *)data
;
687 if ((r
= krb5_rd_rep(telnet_context
, auth_context
, &inbuf
,
689 printf("[ Mutual authentication failed: %s ]\r\n",
694 krb5_free_ap_rep_enc_part(telnet_context
, reply
);
699 skey
.data
= session_key
->contents
;
700 encrypt_session_key(&skey
, 0);
702 #endif /* ENCRYPTION */
707 case KRB_FORWARD_ACCEPT
:
708 printf("[ Kerberos V5 accepted forwarded credentials ]\r\n");
710 case KRB_FORWARD_REJECT
:
711 printf("[ Kerberos V5 refuses forwarded credentials because %.*s ]\r\n",
717 printf("Unknown Kerberos option %d\r\n", data
[-1]);
724 kerberos5_status(ap
, name
, level
)
729 if (level
< AUTH_USER
)
732 if (UserNameRequested
&&
733 krb5_kuserok(telnet_context
, ticket
->enc_part2
->client
,
736 /* the name buffer comes from telnetd/telnetd{-ktd}.c */
737 strncpy(name
, UserNameRequested
, 255);
744 #define BUMP(buf, len) while (*(buf)) {++(buf), --(len);}
745 #define ADDC(buf, len, c) if ((len) > 0) {*(buf)++ = (c); --(len);}
748 kerberos5_printsub(data
, cnt
, buf
, buflen
)
749 unsigned char *data
, *buf
;
755 buf
[buflen
-1] = '\0'; /* make sure its NULL terminated */
759 case KRB_REJECT
: /* Rejected (reason might follow) */
760 strncpy((char *)buf
, " REJECT ", buflen
);
763 case KRB_ACCEPT
: /* Accepted (name might follow) */
764 strncpy((char *)buf
, " ACCEPT ", buflen
);
769 ADDC(buf
, buflen
, '"');
770 for (i
= 4; i
< cnt
; i
++)
771 ADDC(buf
, buflen
, data
[i
]);
772 ADDC(buf
, buflen
, '"');
773 ADDC(buf
, buflen
, '\0');
777 case KRB_AUTH
: /* Authentication data follows */
778 strncpy((char *)buf
, " AUTH", buflen
);
782 strncpy((char *)buf
, " RESPONSE", buflen
);
786 case KRB_FORWARD
: /* Forwarded credentials follow */
787 strncpy((char *)buf
, " FORWARD", buflen
);
790 case KRB_FORWARD_ACCEPT
: /* Forwarded credentials accepted */
791 strncpy((char *)buf
, " FORWARD_ACCEPT", buflen
);
794 case KRB_FORWARD_REJECT
: /* Forwarded credentials rejected */
795 /* (reason might follow) */
796 strncpy((char *)buf
, " FORWARD_REJECT", buflen
);
801 sprintf(lbuf
, " %d (unknown)", data
[3]);
802 strncpy((char *)buf
, lbuf
, buflen
);
805 for (i
= 4; i
< cnt
; i
++) {
806 sprintf(lbuf
, " %d", data
[i
]);
807 strncpy((char *)buf
, lbuf
, buflen
);
817 kerberos5_forward(ap
)
822 krb5_principal client
= 0;
823 krb5_principal server
= 0;
824 krb5_data forw_creds
;
828 if ((r
= krb5_cc_default(telnet_context
, &ccache
))) {
830 printf("Kerberos V5: could not get default ccache - %s\r\n",
835 if ((r
= krb5_cc_get_principal(telnet_context
, ccache
, &client
))) {
837 printf("Kerberos V5: could not get default principal - %s\r\n",
842 if ((r
= krb5_sname_to_principal(telnet_context
, RemoteHostName
, "host",
843 KRB5_NT_SRV_HST
, &server
))) {
845 printf("Kerberos V5: could not make server principal - %s\r\n",
850 if ((r
= krb5_auth_con_genaddrs(telnet_context
, auth_context
, net
,
851 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR
))) {
853 printf("Kerberos V5: could not gen local full address - %s\r\n",
858 if ((r
= krb5_fwd_tgt_creds(telnet_context
, auth_context
, 0, client
,
860 forward_flags
& OPTS_FORWARDABLE_CREDS
,
863 printf("Kerberos V5: error getting forwarded creds - %s\r\n",
868 /* Send forwarded credentials */
869 if (!Data(ap
, KRB_FORWARD
, forw_creds
.data
, forw_creds
.length
)) {
871 printf("Not enough room for authentication data\r\n");
874 printf("Forwarded local Kerberos V5 credentials to server\r\n");
879 krb5_free_principal(telnet_context
, client
);
881 krb5_free_principal(telnet_context
, server
);
883 free(forw_creds
.data
);
884 krb5_cc_close(telnet_context
, ccache
);