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