1 /* 2 * Copyright 2002 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 DEFAULT_ENCTYPE ENCTYPE_DES_CBC_CRC 88 #define ACCEPT_ENCTYPES (ENCTYPE_DES_CBC_CRC | 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 if (!UserNameRequested) { 209 if (auth_debug_mode) 210 (void) printf(gettext("telnet: Kerberos V5: " 211 "no user name supplied\r\n")); 212 return (0); 213 } 214 215 if ((retval = krb5_cc_default(telnet_context, &ccache))) { 216 if (auth_debug_mode) 217 (void) printf(gettext("telnet: Kerberos V5: " 218 "could not get default ccache\r\n")); 219 return (0); 220 } 221 222 (void) memset((char *)&creds, 0, sizeof (creds)); 223 if ((retval = krb5_sname_to_principal(telnet_context, RemoteHostName, 224 "host", KRB5_NT_SRV_HST, &creds.server))) { 225 if (auth_debug_mode) 226 (void) printf(gettext("telnet: Kerberos V5: error " 227 "while constructing service name: %s\r\n"), 228 error_message(retval)); 229 return (0); 230 } 231 232 if (telnet_krb5_realm != NULL) { 233 krb5_data rdata; 234 235 rdata.length = strlen(telnet_krb5_realm); 236 rdata.data = (char *)malloc(rdata.length + 1); 237 if (rdata.data == NULL) { 238 (void) fprintf(stderr, gettext("malloc failed\n")); 239 return (0); 240 } 241 (void) strcpy(rdata.data, telnet_krb5_realm); 242 krb5_princ_set_realm(telnet_context, creds.server, &rdata); 243 if (auth_debug_mode) 244 (void) printf(gettext( 245 "telnet: Kerberos V5: set kerberos realm to %s\r\n"), 246 telnet_krb5_realm); 247 } 248 249 if ((retval = krb5_cc_get_principal(telnet_context, ccache, 250 &creds.client)) != NULL) { 251 if (auth_debug_mode) { 252 (void) printf(gettext( 253 "telnet: Kerberos V5: failure on principal " 254 "(%s)\r\n"), error_message(retval)); 255 } 256 krb5_free_cred_contents(telnet_context, &creds); 257 return (0); 258 } 259 260 creds.keyblock.enctype = DEFAULT_ENCTYPE; 261 if ((retval = krb5_get_credentials(telnet_context, 0, 262 ccache, &creds, &new_creds))) { 263 if (auth_debug_mode) { 264 (void) printf(gettext( 265 "telnet: Kerberos V5: failure on credentials " 266 "(%s)\r\n"), error_message(retval)); 267 } 268 krb5_free_cred_contents(telnet_context, &creds); 269 return (0); 270 } 271 272 ap_opts = ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? 273 AP_OPTS_MUTUAL_REQUIRED : 0; 274 275 ap_opts |= AP_OPTS_USE_SUBKEY; 276 277 if (auth_context) { 278 krb5_auth_con_free(telnet_context, auth_context); 279 auth_context = 0; 280 } 281 if ((retval = krb5_auth_con_init(telnet_context, &auth_context))) { 282 if (auth_debug_mode) { 283 (void) printf(gettext( 284 "Kerberos V5: failed to init auth_context " 285 "(%s)\r\n"), error_message(retval)); 286 } 287 return (0); 288 } 289 290 krb5_auth_con_setflags(telnet_context, auth_context, 291 KRB5_AUTH_CONTEXT_RET_TIME); 292 293 type_check[0] = ap->type; 294 type_check[1] = ap->way; 295 check_data.magic = KV5M_DATA; 296 check_data.length = 2; 297 check_data.data = (char *)&type_check; 298 299 retval = krb5_mk_req_extended(telnet_context, &auth_context, ap_opts, 300 &check_data, new_creds, &auth); 301 302 krb5_auth_con_getlocalsubkey(telnet_context, auth_context, &newkey); 303 if (session_key) { 304 krb5_free_keyblock(telnet_context, session_key); 305 session_key = 0; 306 } 307 308 if (newkey) { 309 /* 310 * keep the key in our private storage, but don't use it 311 * yet---see kerberos5_reply() below 312 */ 313 if (!(newkey->enctype & ACCEPT_ENCTYPES)) { 314 if (!(new_creds->keyblock.enctype & ACCEPT_ENCTYPES)) 315 /* use the session key in credentials instead */ 316 krb5_copy_keyblock(telnet_context, 317 &new_creds->keyblock, &session_key); 318 } else 319 krb5_copy_keyblock(telnet_context, 320 newkey, &session_key); 321 322 krb5_free_keyblock(telnet_context, newkey); 323 } 324 325 krb5_free_cred_contents(telnet_context, &creds); 326 krb5_free_creds(telnet_context, new_creds); 327 328 if (retval) { 329 if (auth_debug_mode) 330 (void) printf(gettext( 331 "telnet: Kerberos V5: mk_req failed (%s)\r\n"), 332 error_message(retval)); 333 return (0); 334 } 335 336 if ((auth_sendname((uchar_t *)UserNameRequested, 337 strlen(UserNameRequested))) == NULL) { 338 if (auth_debug_mode) 339 (void) printf(gettext( 340 "telnet: Not enough room for user name\r\n")); 341 return (0); 342 } 343 retval = krb5_send_data(ap, KRB_AUTH, auth.data, auth.length); 344 if (auth_debug_mode && retval) { 345 (void) printf(gettext( 346 "telnet: Sent Kerberos V5 credentials to server\r\n")); 347 } else if (auth_debug_mode) { 348 (void) printf(gettext( 349 "telnet: Not enough room for authentication data\r\n")); 350 return (0); 351 } 352 return (1); 353 } 354 355 void 356 kerberos5_reply(Authenticator *ap, unsigned char *data, int cnt) 357 { 358 Session_Key skey; 359 static boolean_t mutual_complete = B_FALSE; 360 361 if (cnt-- < 1) 362 return; 363 switch (*data++) { 364 case KRB_REJECT: 365 if (cnt > 0) 366 (void) printf(gettext( 367 "[ Kerberos V5 refuses authentication because " 368 "%.*s ]\r\n"), cnt, data); 369 else 370 (void) printf(gettext( 371 "[ Kerberos V5 refuses authentication ]\r\n")); 372 auth_send_retry(); 373 return; 374 case KRB_ACCEPT: 375 if (!mutual_complete) { 376 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { 377 (void) printf(gettext( 378 "[ Kerberos V5 accepted you, but didn't " 379 "provide mutual authentication! ]\r\n")); 380 auth_send_retry(); 381 return; 382 } 383 384 if (session_key) { 385 skey.type = SK_DES; 386 skey.length = 8; 387 skey.data = session_key->contents; 388 encrypt_session_key(&skey); 389 } 390 } 391 if (cnt) 392 (void) printf(gettext( 393 "[ Kerberos V5 accepts you as ``%.*s'' ]\r\n"), 394 cnt, data); 395 else 396 (void) printf(gettext( 397 "[ Kerberos V5 accepts you ]\r\n")); 398 auth_finished(ap, AUTH_USER); 399 400 if (forward_flags & OPTS_FORWARD_CREDS) 401 kerberos5_forward(ap); 402 403 break; 404 case KRB_RESPONSE: 405 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { 406 /* the rest of the reply should contain a krb_ap_rep */ 407 krb5_ap_rep_enc_part *reply; 408 krb5_data inbuf; 409 krb5_error_code retval; 410 411 inbuf.length = cnt; 412 inbuf.data = (char *)data; 413 414 retval = krb5_rd_rep(telnet_context, auth_context, 415 &inbuf, &reply); 416 if (retval) { 417 (void) printf(gettext( 418 "[ Mutual authentication failed: " 419 "%s ]\r\n"), error_message(retval)); 420 auth_send_retry(); 421 return; 422 } 423 krb5_free_ap_rep_enc_part(telnet_context, reply); 424 425 if (session_key) { 426 skey.type = SK_DES; 427 skey.length = 8; 428 skey.data = session_key->contents; 429 encrypt_session_key(&skey); 430 } 431 mutual_complete = B_TRUE; 432 } 433 return; 434 case KRB_FORWARD_ACCEPT: 435 (void) printf(gettext( 436 "[ Kerberos V5 accepted forwarded credentials ]\r\n")); 437 return; 438 case KRB_FORWARD_REJECT: 439 (void) printf(gettext( 440 "[ Kerberos V5 refuses forwarded credentials because " 441 "%.*s ]\r\n"), cnt, data); 442 return; 443 default: 444 if (auth_debug_mode) 445 (void) printf(gettext( 446 "Unknown Kerberos option %d\r\n"), data[-1]); 447 return; 448 } 449 } 450 451 /* ARGSUSED */ 452 int 453 kerberos5_status(Authenticator *ap, char *name, int level) 454 { 455 if (level < AUTH_USER) 456 return (level); 457 458 if (UserNameRequested && krb5_kuserok(telnet_context, 459 ticket->enc_part2->client, UserNameRequested)) { 460 461 /* the name buffer comes from telnetd/telnetd{-ktd}.c */ 462 (void) strncpy(name, UserNameRequested, MAXNAMELEN); 463 name[MAXNAMELEN-1] = '\0'; 464 return (AUTH_VALID); 465 } else 466 return (AUTH_USER); 467 } 468 469 #define BUMP(buf, len) while (*(buf)) {++(buf), --(len); } 470 #define ADDC(buf, len, c) if ((len) > 0) {*(buf)++ = (c); --(len); } 471 472 /* 473 * Used with the set opt command to print suboptions 474 */ 475 void 476 kerberos5_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen) 477 { 478 char lbuf[AUTH_LBUF_BUFSIZ]; 479 register int i; 480 481 buf[buflen-1] = '\0'; /* make sure its NULL terminated */ 482 buflen -= 1; 483 484 switch (data[3]) { 485 case KRB_REJECT: /* Rejected (reason might follow) */ 486 (void) strncpy((char *)buf, " REJECT ", buflen); 487 goto common; 488 489 case KRB_ACCEPT: /* Accepted (name might follow) */ 490 (void) strncpy((char *)buf, " ACCEPT ", buflen); 491 common: 492 BUMP(buf, buflen); 493 if (cnt <= 4) 494 break; 495 ADDC(buf, buflen, '"'); 496 for (i = 4; i < cnt; i++) 497 ADDC(buf, buflen, data[i]); 498 ADDC(buf, buflen, '"'); 499 ADDC(buf, buflen, '\0'); 500 break; 501 502 case KRB_AUTH: /* Authentication data follows */ 503 (void) strncpy((char *)buf, " AUTH", buflen); 504 goto common2; 505 506 case KRB_RESPONSE: 507 (void) strncpy((char *)buf, " RESPONSE", buflen); 508 goto common2; 509 510 case KRB_FORWARD: /* Forwarded credentials follow */ 511 (void) strncpy((char *)buf, " FORWARD", buflen); 512 goto common2; 513 514 case KRB_FORWARD_ACCEPT: /* Forwarded credentials accepted */ 515 (void) strncpy((char *)buf, " FORWARD_ACCEPT", buflen); 516 goto common2; 517 518 case KRB_FORWARD_REJECT: /* Forwarded credentials rejected */ 519 /* (reason might follow) */ 520 (void) strncpy((char *)buf, " FORWARD_REJECT", buflen); 521 goto common2; 522 523 default: 524 (void) snprintf(lbuf, AUTH_LBUF_BUFSIZ, 525 gettext(" %d (unknown)"), 526 data[3]); 527 (void) strncpy((char *)buf, lbuf, buflen); 528 common2: 529 BUMP(buf, buflen); 530 for (i = 4; i < cnt; i++) { 531 (void) snprintf(lbuf, AUTH_LBUF_BUFSIZ, " %d", data[i]); 532 (void) strncpy((char *)buf, lbuf, buflen); 533 BUMP(buf, buflen); 534 } 535 break; 536 } 537 } 538 539 void 540 krb5_profile_get_options(char *host, char *realm, 541 profile_options_boolean *optionsp) 542 { 543 char **realms = NULL; 544 krb5_error_code err = 0; 545 546 if (!telnet_context) { 547 err = krb5_init_context(&telnet_context); 548 if (err) { 549 (void) fprintf(stderr, gettext( 550 "Error initializing Kerberos 5 library: %s\n"), 551 error_message(err)); 552 return; 553 } 554 } 555 556 if ((realmdef[1] = realm) == NULL) { 557 err = krb5_get_host_realm(telnet_context, host, &realms); 558 if (err) { 559 (void) fprintf(stderr, gettext( 560 "Error getting Kerberos 5 realms for: %s (%s)\n"), 561 host, error_message(err)); 562 return; 563 } 564 realmdef[1] = realms[0]; 565 } 566 567 profile_get_options_boolean(telnet_context->profile, 568 realmdef, optionsp); 569 profile_get_options_boolean(telnet_context->profile, 570 appdef, optionsp); 571 } 572 573 static void 574 kerberos5_forward(Authenticator *ap) 575 { 576 krb5_error_code retval; 577 krb5_ccache ccache; 578 krb5_principal client = 0; 579 krb5_principal server = 0; 580 krb5_data forw_creds; 581 582 forw_creds.data = 0; 583 584 if ((retval = krb5_cc_default(telnet_context, &ccache))) { 585 if (auth_debug_mode) 586 (void) printf(gettext( 587 "Kerberos V5: could not get default ccache - %s\r\n"), 588 error_message(retval)); 589 return; 590 } 591 592 retval = krb5_cc_get_principal(telnet_context, ccache, &client); 593 if (retval) { 594 if (auth_debug_mode) 595 (void) printf(gettext( 596 "Kerberos V5: could not get default " 597 "principal - %s\r\n"), error_message(retval)); 598 goto cleanup; 599 } 600 601 retval = krb5_sname_to_principal(telnet_context, RemoteHostName, 602 "host", KRB5_NT_SRV_HST, &server); 603 if (retval) { 604 if (auth_debug_mode) 605 (void) printf(gettext( 606 "Kerberos V5: could not make server " 607 "principal - %s\r\n"), error_message(retval)); 608 goto cleanup; 609 } 610 611 retval = krb5_auth_con_genaddrs(telnet_context, auth_context, net, 612 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR); 613 if (retval) { 614 if (auth_debug_mode) 615 (void) printf(gettext( 616 "Kerberos V5: could not gen local full " 617 "address - %s\r\n"), error_message(retval)); 618 goto cleanup; 619 } 620 621 retval = krb5_fwd_tgt_creds(telnet_context, auth_context, 0, client, 622 server, ccache, forward_flags & OPTS_FORWARDABLE_CREDS, 623 &forw_creds); 624 if (retval) { 625 if (auth_debug_mode) 626 (void) printf(gettext( 627 "Kerberos V5: error getting forwarded " 628 "creds - %s\r\n"), error_message(retval)); 629 goto cleanup; 630 } 631 632 /* Send forwarded credentials */ 633 if (!krb5_send_data(ap, KRB_FORWARD, forw_creds.data, 634 forw_creds.length)) { 635 if (auth_debug_mode) 636 (void) printf(gettext( 637 "Not enough room for authentication data\r\n")); 638 } else if (auth_debug_mode) 639 (void) printf(gettext( 640 "Forwarded local Kerberos V5 credentials to server\r\n")); 641 cleanup: 642 if (client) 643 krb5_free_principal(telnet_context, client); 644 if (server) 645 krb5_free_principal(telnet_context, server); 646 if (forw_creds.data) 647 free(forw_creds.data); 648 /* LINTED */ 649 krb5_cc_close(telnet_context, ccache); 650 } 651