1 /* 2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * usr/src/cmd/cmd-inet/usr.bin/telnet/kerberos5.c 8 * 9 * Copyright (c) 1991, 1993 10 * The Regents of the University of California. All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 */ 40 41 /* based on @(#)kerberos5.c 8.1 (Berkeley) 6/4/93 */ 42 43 /* 44 * Copyright (C) 1990 by the Massachusetts Institute of Technology 45 * 46 * Export of this software from the United States of America may 47 * require a specific license from the United States Government. 48 * It is the responsibility of any person or organization contemplating 49 * export to obtain such a license before exporting. 50 * 51 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 52 * distribute this software and its documentation for any purpose and 53 * without fee is hereby granted, provided that the above copyright 54 * notice appear in all copies and that both that copyright notice and 55 * this permission notice appear in supporting documentation, and that 56 * the name of M.I.T. not be used in advertising or publicity pertaining 57 * to distribution of the software without specific, written prior 58 * permission. Furthermore if you modify this software you must label 59 * your software as modified software and not distribute it in such a 60 * fashion that it might be confused with the original M.I.T. software. 61 * M.I.T. makes no representations about the suitability of 62 * this software for any purpose. It is provided "as is" without express 63 * or implied warranty. 64 */ 65 66 67 #include <arpa/telnet.h> 68 #include <stdio.h> 69 #include <ctype.h> 70 #include <syslog.h> 71 #include <stdlib.h> 72 73 /* the following are from the kerberos tree */ 74 #include <k5-int.h> 75 #include <com_err.h> 76 #include <netdb.h> 77 #include <profile/prof_int.h> 78 #include <sys/param.h> 79 #include "externs.h" 80 81 extern char *RemoteHostName; 82 extern boolean_t auth_debug_mode; 83 extern int net; 84 85 #define ACCEPTED_ENCTYPE(a) \ 86 (a == ENCTYPE_DES_CBC_CRC || a == ENCTYPE_DES_CBC_MD5) 87 /* for comapatibility with non-Solaris KDC's, this has to be big enough */ 88 #define KERBEROS_BUFSIZ 8192 89 90 int forward_flags = 0; /* Flags get set in telnet/main.c on -f and -F */ 91 static void kerberos5_forward(Authenticator *); 92 93 static unsigned char str_data[KERBEROS_BUFSIZ] = { IAC, SB, 94 TELOPT_AUTHENTICATION, 0, AUTHTYPE_KERBEROS_V5, }; 95 static char *appdef[] = { "appdefaults", "telnet", NULL }; 96 static char *realmdef[] = { "realms", NULL, "telnet", NULL }; 97 98 static krb5_auth_context auth_context = 0; 99 100 static krb5_data auth; /* telnetd gets session key from here */ 101 static krb5_ticket *ticket = NULL; 102 /* telnet matches the AP_REQ and AP_REP with this */ 103 104 static krb5_keyblock *session_key = 0; 105 char *telnet_krb5_realm = NULL; 106 107 /* 108 * Change the kerberos realm 109 */ 110 void 111 set_krb5_realm(char *name) 112 { 113 if (name == NULL) { 114 (void) fprintf(stderr, gettext("Could not set Kerberos realm, " 115 "no realm provided.\n")); 116 return; 117 } 118 119 if (telnet_krb5_realm) 120 free(telnet_krb5_realm); 121 122 telnet_krb5_realm = (char *)strdup(name); 123 124 if (telnet_krb5_realm == NULL) 125 (void) fprintf(stderr, gettext( 126 "Could not set Kerberos realm, malloc failed\n")); 127 } 128 129 #define RETURN_NOMEM { errno = ENOMEM; return (-1); } 130 131 static int 132 krb5_send_data(Authenticator *ap, int type, krb5_pointer d, int c) 133 { 134 /* the first 3 bytes are control chars */ 135 unsigned char *p = str_data + 4; 136 unsigned char *cd = (unsigned char *)d; 137 /* spaceleft is incremented whenever p is decremented */ 138 size_t spaceleft = sizeof (str_data) - 4; 139 140 if (c == -1) 141 c = strlen((char *)cd); 142 143 if (auth_debug_mode) { 144 (void) printf("%s:%d: [%d] (%d)", 145 str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY", 146 str_data[3], type, c); 147 printd(d, c); 148 (void) printf("\r\n"); 149 } 150 151 if (spaceleft < 3) 152 RETURN_NOMEM; 153 *p++ = ap->type; 154 *p++ = ap->way; 155 *p++ = type; 156 spaceleft -= 3; 157 158 while (c-- > 0) { 159 if (spaceleft < 2) 160 RETURN_NOMEM; 161 if ((*p++ = *cd++) == IAC) { 162 *p++ = IAC; 163 spaceleft -= 2; 164 } 165 } 166 167 if (spaceleft < 2) 168 RETURN_NOMEM; 169 *p++ = IAC; 170 *p++ = SE; 171 if (str_data[3] == TELQUAL_IS) 172 printsub('>', &str_data[2], p - &str_data[2]); 173 return (net_write(str_data, p - str_data)); 174 } 175 176 krb5_context telnet_context = 0; 177 178 /* ARGSUSED */ 179 int 180 kerberos5_init(Authenticator *ap) 181 { 182 krb5_error_code retval; 183 184 str_data[3] = TELQUAL_IS; 185 if (krb5auth_flag && (telnet_context == 0)) { 186 retval = krb5_init_context(&telnet_context); 187 if (retval) 188 return (0); 189 } 190 return (1); 191 } 192 193 int 194 kerberos5_send(Authenticator *ap) 195 { 196 krb5_error_code retval; 197 krb5_ccache ccache; 198 krb5_creds creds; /* telnet gets session key from here */ 199 krb5_creds *new_creds = 0; 200 int ap_opts; 201 char type_check[2]; 202 krb5_data check_data; 203 204 krb5_keyblock *newkey = 0; 205 206 int i; 207 krb5_enctype *ktypes; 208 209 if (!UserNameRequested) { 210 if (auth_debug_mode) 211 (void) printf(gettext("telnet: Kerberos V5: " 212 "no user name supplied\r\n")); 213 return (0); 214 } 215 216 if ((retval = krb5_cc_default(telnet_context, &ccache))) { 217 if (auth_debug_mode) 218 (void) printf(gettext("telnet: Kerberos V5: " 219 "could not get default ccache\r\n")); 220 return (0); 221 } 222 223 (void) memset((char *)&creds, 0, sizeof (creds)); 224 if (auth_debug_mode) 225 printf("telnet: calling krb5_sname_to_principal\n"); 226 if ((retval = krb5_sname_to_principal(telnet_context, RemoteHostName, 227 "host", KRB5_NT_SRV_HST, &creds.server))) { 228 if (auth_debug_mode) 229 (void) printf(gettext("telnet: Kerberos V5: error " 230 "while constructing service name: %s\r\n"), 231 error_message(retval)); 232 return (0); 233 } 234 if (auth_debug_mode) 235 printf("telnet: done calling krb5_sname_to_principal\n"); 236 237 if (telnet_krb5_realm != NULL) { 238 krb5_data rdata; 239 240 rdata.magic = 0; 241 rdata.length = strlen(telnet_krb5_realm); 242 rdata.data = (char *)malloc(rdata.length + 1); 243 if (rdata.data == NULL) { 244 (void) fprintf(stderr, gettext("malloc failed\n")); 245 return (0); 246 } 247 (void) strcpy(rdata.data, telnet_krb5_realm); 248 krb5_princ_set_realm(telnet_context, creds.server, &rdata); 249 if (auth_debug_mode) 250 (void) printf(gettext( 251 "telnet: Kerberos V5: set kerberos realm to %s\r\n"), 252 telnet_krb5_realm); 253 } 254 255 if ((retval = krb5_cc_get_principal(telnet_context, ccache, 256 &creds.client)) != 0) { 257 if (auth_debug_mode) { 258 (void) printf(gettext( 259 "telnet: Kerberos V5: failure on principal " 260 "(%s)\r\n"), error_message(retval)); 261 } 262 krb5_free_cred_contents(telnet_context, &creds); 263 return (0); 264 } 265 /* 266 * Check to to confirm that at least one of the supported 267 * encryption types (des-cbc-md5, des-cbc-crc is available. If 268 * one is available then use it to obtain credentials. 269 */ 270 271 if ((retval = krb5_get_tgs_ktypes(telnet_context, creds.server, 272 &ktypes))) { 273 if (auth_debug_mode) { 274 (void) printf(gettext( 275 "telnet: Kerberos V5: could not determine " 276 "TGS encryption types " 277 "(see default_tgs_enctypes in krb5.conf) " 278 "(%s)\r\n"), error_message(retval)); 279 } 280 krb5_free_cred_contents(telnet_context, &creds); 281 return (0); 282 } 283 284 for (i = 0; ktypes[i]; i++) { 285 if (ACCEPTED_ENCTYPE(ktypes[i])) 286 break; 287 } 288 289 if (ktypes[i] == 0) { 290 if (auth_debug_mode) { 291 (void) printf(gettext( 292 "telnet: Kerberos V5: " 293 "failure on encryption types. " 294 "Cannot find des-cbc-md5 or des-cbc-crc " 295 "in list of TGS encryption types " 296 "(see default_tgs_enctypes in krb5.conf)\n")); 297 } 298 krb5_free_cred_contents(telnet_context, &creds); 299 return (0); 300 } 301 302 creds.keyblock.enctype = ktypes[i]; 303 if ((retval = krb5_get_credentials(telnet_context, 0, 304 ccache, &creds, &new_creds))) { 305 if (auth_debug_mode) { 306 (void) printf(gettext( 307 "telnet: Kerberos V5: failure on credentials " 308 "(%s)\r\n"), error_message(retval)); 309 } 310 krb5_free_cred_contents(telnet_context, &creds); 311 return (0); 312 } 313 314 ap_opts = ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? 315 AP_OPTS_MUTUAL_REQUIRED : 0; 316 317 ap_opts |= AP_OPTS_USE_SUBKEY; 318 319 if (auth_context) { 320 krb5_auth_con_free(telnet_context, auth_context); 321 auth_context = 0; 322 } 323 if ((retval = krb5_auth_con_init(telnet_context, &auth_context))) { 324 if (auth_debug_mode) { 325 (void) printf(gettext( 326 "Kerberos V5: failed to init auth_context " 327 "(%s)\r\n"), error_message(retval)); 328 } 329 return (0); 330 } 331 332 krb5_auth_con_setflags(telnet_context, auth_context, 333 KRB5_AUTH_CONTEXT_RET_TIME); 334 335 type_check[0] = ap->type; 336 type_check[1] = ap->way; 337 check_data.magic = KV5M_DATA; 338 check_data.length = 2; 339 check_data.data = (char *)&type_check; 340 341 retval = krb5_mk_req_extended(telnet_context, &auth_context, ap_opts, 342 &check_data, new_creds, &auth); 343 344 krb5_auth_con_getlocalsubkey(telnet_context, auth_context, &newkey); 345 if (session_key) { 346 krb5_free_keyblock(telnet_context, session_key); 347 session_key = 0; 348 } 349 350 if (newkey) { 351 /* 352 * keep the key in our private storage, but don't use it 353 * yet---see kerberos5_reply() below 354 */ 355 if (!(ACCEPTED_ENCTYPE(newkey->enctype))) { 356 if (!(ACCEPTED_ENCTYPE(new_creds->keyblock.enctype))) 357 /* use the session key in credentials instead */ 358 krb5_copy_keyblock(telnet_context, 359 &new_creds->keyblock, &session_key); 360 } else 361 krb5_copy_keyblock(telnet_context, 362 newkey, &session_key); 363 364 krb5_free_keyblock(telnet_context, newkey); 365 } 366 367 krb5_free_cred_contents(telnet_context, &creds); 368 krb5_free_creds(telnet_context, new_creds); 369 370 if (retval) { 371 if (auth_debug_mode) 372 (void) printf(gettext( 373 "telnet: Kerberos V5: mk_req failed (%s)\r\n"), 374 error_message(retval)); 375 return (0); 376 } 377 378 if ((auth_sendname((uchar_t *)UserNameRequested, 379 strlen(UserNameRequested))) == 0) { 380 if (auth_debug_mode) 381 (void) printf(gettext( 382 "telnet: Not enough room for user name\r\n")); 383 return (0); 384 } 385 retval = krb5_send_data(ap, KRB_AUTH, auth.data, auth.length); 386 if (auth_debug_mode && retval) { 387 (void) printf(gettext( 388 "telnet: Sent Kerberos V5 credentials to server\r\n")); 389 } else if (auth_debug_mode) { 390 (void) printf(gettext( 391 "telnet: Not enough room for authentication data\r\n")); 392 return (0); 393 } 394 return (1); 395 } 396 397 void 398 kerberos5_reply(Authenticator *ap, unsigned char *data, int cnt) 399 { 400 Session_Key skey; 401 static boolean_t mutual_complete = B_FALSE; 402 403 if (cnt-- < 1) 404 return; 405 switch (*data++) { 406 case KRB_REJECT: 407 if (cnt > 0) 408 (void) printf(gettext( 409 "[ Kerberos V5 refuses authentication because " 410 "%.*s ]\r\n"), cnt, data); 411 else 412 (void) printf(gettext( 413 "[ Kerberos V5 refuses authentication ]\r\n")); 414 auth_send_retry(); 415 return; 416 case KRB_ACCEPT: 417 if (!mutual_complete) { 418 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { 419 (void) printf(gettext( 420 "[ Kerberos V5 accepted you, but didn't " 421 "provide mutual authentication! ]\r\n")); 422 auth_send_retry(); 423 return; 424 } 425 426 if (session_key) { 427 skey.type = SK_DES; 428 skey.length = 8; 429 skey.data = session_key->contents; 430 encrypt_session_key(&skey); 431 } 432 } 433 if (cnt) 434 (void) printf(gettext( 435 "[ Kerberos V5 accepts you as ``%.*s'' ]\r\n"), 436 cnt, data); 437 else 438 (void) printf(gettext( 439 "[ Kerberos V5 accepts you ]\r\n")); 440 auth_finished(ap, AUTH_USER); 441 442 if (forward_flags & OPTS_FORWARD_CREDS) 443 kerberos5_forward(ap); 444 445 break; 446 case KRB_RESPONSE: 447 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { 448 /* the rest of the reply should contain a krb_ap_rep */ 449 krb5_ap_rep_enc_part *reply; 450 krb5_data inbuf; 451 krb5_error_code retval; 452 453 inbuf.length = cnt; 454 inbuf.data = (char *)data; 455 456 retval = krb5_rd_rep(telnet_context, auth_context, 457 &inbuf, &reply); 458 if (retval) { 459 (void) printf(gettext( 460 "[ Mutual authentication failed: " 461 "%s ]\r\n"), error_message(retval)); 462 auth_send_retry(); 463 return; 464 } 465 krb5_free_ap_rep_enc_part(telnet_context, reply); 466 467 if (session_key) { 468 skey.type = SK_DES; 469 skey.length = 8; 470 skey.data = session_key->contents; 471 encrypt_session_key(&skey); 472 } 473 mutual_complete = B_TRUE; 474 } 475 return; 476 case KRB_FORWARD_ACCEPT: 477 (void) printf(gettext( 478 "[ Kerberos V5 accepted forwarded credentials ]\r\n")); 479 return; 480 case KRB_FORWARD_REJECT: 481 (void) printf(gettext( 482 "[ Kerberos V5 refuses forwarded credentials because " 483 "%.*s ]\r\n"), cnt, data); 484 return; 485 default: 486 if (auth_debug_mode) 487 (void) printf(gettext( 488 "Unknown Kerberos option %d\r\n"), data[-1]); 489 return; 490 } 491 } 492 493 /* ARGSUSED */ 494 int 495 kerberos5_status(Authenticator *ap, char *name, int level) 496 { 497 if (level < AUTH_USER) 498 return (level); 499 500 if (UserNameRequested && krb5_kuserok(telnet_context, 501 ticket->enc_part2->client, UserNameRequested)) { 502 503 /* the name buffer comes from telnetd/telnetd{-ktd}.c */ 504 (void) strncpy(name, UserNameRequested, MAXNAMELEN); 505 name[MAXNAMELEN-1] = '\0'; 506 return (AUTH_VALID); 507 } else 508 return (AUTH_USER); 509 } 510 511 #define BUMP(buf, len) while (*(buf)) {++(buf), --(len); } 512 #define ADDC(buf, len, c) if ((len) > 0) {*(buf)++ = (c); --(len); } 513 514 /* 515 * Used with the set opt command to print suboptions 516 */ 517 void 518 kerberos5_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen) 519 { 520 char lbuf[AUTH_LBUF_BUFSIZ]; 521 register int i; 522 523 buf[buflen-1] = '\0'; /* make sure its NULL terminated */ 524 buflen -= 1; 525 526 switch (data[3]) { 527 case KRB_REJECT: /* Rejected (reason might follow) */ 528 (void) strncpy((char *)buf, " REJECT ", buflen); 529 goto common; 530 531 case KRB_ACCEPT: /* Accepted (name might follow) */ 532 (void) strncpy((char *)buf, " ACCEPT ", buflen); 533 common: 534 BUMP(buf, buflen); 535 if (cnt <= 4) 536 break; 537 ADDC(buf, buflen, '"'); 538 for (i = 4; i < cnt; i++) 539 ADDC(buf, buflen, data[i]); 540 ADDC(buf, buflen, '"'); 541 ADDC(buf, buflen, '\0'); 542 break; 543 544 case KRB_AUTH: /* Authentication data follows */ 545 (void) strncpy((char *)buf, " AUTH", buflen); 546 goto common2; 547 548 case KRB_RESPONSE: 549 (void) strncpy((char *)buf, " RESPONSE", buflen); 550 goto common2; 551 552 case KRB_FORWARD: /* Forwarded credentials follow */ 553 (void) strncpy((char *)buf, " FORWARD", buflen); 554 goto common2; 555 556 case KRB_FORWARD_ACCEPT: /* Forwarded credentials accepted */ 557 (void) strncpy((char *)buf, " FORWARD_ACCEPT", buflen); 558 goto common2; 559 560 case KRB_FORWARD_REJECT: /* Forwarded credentials rejected */ 561 /* (reason might follow) */ 562 (void) strncpy((char *)buf, " FORWARD_REJECT", buflen); 563 goto common2; 564 565 default: 566 (void) snprintf(lbuf, AUTH_LBUF_BUFSIZ, 567 gettext(" %d (unknown)"), 568 data[3]); 569 (void) strncpy((char *)buf, lbuf, buflen); 570 common2: 571 BUMP(buf, buflen); 572 for (i = 4; i < cnt; i++) { 573 (void) snprintf(lbuf, AUTH_LBUF_BUFSIZ, " %d", data[i]); 574 (void) strncpy((char *)buf, lbuf, buflen); 575 BUMP(buf, buflen); 576 } 577 break; 578 } 579 } 580 581 void 582 krb5_profile_get_options(char *host, char *realm, 583 profile_options_boolean *optionsp) 584 { 585 char **realms = NULL; 586 krb5_error_code err = 0; 587 588 if (!telnet_context) { 589 err = krb5_init_context(&telnet_context); 590 if (err) { 591 (void) fprintf(stderr, gettext( 592 "Error initializing Kerberos 5 library: %s\n"), 593 error_message(err)); 594 return; 595 } 596 } 597 598 if ((realmdef[1] = realm) == NULL) { 599 err = krb5_get_host_realm(telnet_context, host, &realms); 600 if (err) { 601 (void) fprintf(stderr, gettext( 602 "Error getting Kerberos 5 realms for: %s (%s)\n"), 603 host, error_message(err)); 604 return; 605 } 606 realmdef[1] = realms[0]; 607 } 608 609 profile_get_options_boolean(telnet_context->profile, 610 realmdef, optionsp); 611 profile_get_options_boolean(telnet_context->profile, 612 appdef, optionsp); 613 } 614 615 static void 616 kerberos5_forward(Authenticator *ap) 617 { 618 krb5_error_code retval; 619 krb5_ccache ccache; 620 krb5_principal client = 0; 621 krb5_principal server = 0; 622 krb5_data forw_creds; 623 624 forw_creds.data = 0; 625 626 if ((retval = krb5_cc_default(telnet_context, &ccache))) { 627 if (auth_debug_mode) 628 (void) printf(gettext( 629 "Kerberos V5: could not get default ccache - %s\r\n"), 630 error_message(retval)); 631 return; 632 } 633 634 retval = krb5_cc_get_principal(telnet_context, ccache, &client); 635 if (retval) { 636 if (auth_debug_mode) 637 (void) printf(gettext( 638 "Kerberos V5: could not get default " 639 "principal - %s\r\n"), error_message(retval)); 640 goto cleanup; 641 } 642 643 retval = krb5_sname_to_principal(telnet_context, RemoteHostName, 644 "host", KRB5_NT_SRV_HST, &server); 645 if (retval) { 646 if (auth_debug_mode) 647 (void) printf(gettext( 648 "Kerberos V5: could not make server " 649 "principal - %s\r\n"), error_message(retval)); 650 goto cleanup; 651 } 652 653 retval = krb5_auth_con_genaddrs(telnet_context, auth_context, net, 654 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR); 655 if (retval) { 656 if (auth_debug_mode) 657 (void) printf(gettext( 658 "Kerberos V5: could not gen local full " 659 "address - %s\r\n"), error_message(retval)); 660 goto cleanup; 661 } 662 663 retval = krb5_fwd_tgt_creds(telnet_context, auth_context, 0, client, 664 server, ccache, forward_flags & OPTS_FORWARDABLE_CREDS, 665 &forw_creds); 666 if (retval) { 667 if (auth_debug_mode) 668 (void) printf(gettext( 669 "Kerberos V5: error getting forwarded " 670 "creds - %s\r\n"), error_message(retval)); 671 goto cleanup; 672 } 673 674 /* Send forwarded credentials */ 675 if (!krb5_send_data(ap, KRB_FORWARD, forw_creds.data, 676 forw_creds.length)) { 677 if (auth_debug_mode) 678 (void) printf(gettext( 679 "Not enough room for authentication data\r\n")); 680 } else if (auth_debug_mode) 681 (void) printf(gettext( 682 "Forwarded local Kerberos V5 credentials to server\r\n")); 683 cleanup: 684 if (client) 685 krb5_free_principal(telnet_context, client); 686 if (server) 687 krb5_free_principal(telnet_context, server); 688 if (forw_creds.data) 689 free(forw_creds.data); 690 /* LINTED */ 691 krb5_cc_close(telnet_context, ccache); 692 } 693