1 /* 2 * $Source: /afs/athena.mit.edu/astaff/project/krb5/src/appl/telnet/libtelnet/RCS/kerberos5.c,v $ 3 * $Author: jtkohl $ 4 * $Id: kerberos5.c,v 1.3 91/07/19 16:37:57 jtkohl Exp Locker: tytso $ 5 */ 6 7 #if !defined(lint) && !defined(SABER) 8 static 9 #ifdef __STDC__ 10 const 11 #endif 12 char rcsid_kerberos5_c[] = "$Id: kerberos5.c,v 1.3 91/07/19 16:37:57 jtkohl Exp Locker: tytso $"; 13 #endif /* lint */ 14 15 /*- 16 * Copyright (c) 1991, 1993 17 * The Regents of the University of California. All rights reserved. 18 * 19 * %sccs.include.redist.c% 20 */ 21 22 #ifndef lint 23 static char sccsid[] = "@(#)kerberos5.c 8.1 (Berkeley) 06/04/93"; 24 #endif /* not lint */ 25 26 /* 27 * Copyright (C) 1990 by the Massachusetts Institute of Technology 28 * 29 * Export of this software from the United States of America is assumed 30 * to require a specific license from the United States Government. 31 * It is the responsibility of any person or organization contemplating 32 * export to obtain such a license before exporting. 33 * 34 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 35 * distribute this software and its documentation for any purpose and 36 * without fee is hereby granted, provided that the above copyright 37 * notice appear in all copies and that both that copyright notice and 38 * this permission notice appear in supporting documentation, and that 39 * the name of M.I.T. not be used in advertising or publicity pertaining 40 * to distribution of the software without specific, written prior 41 * permission. M.I.T. makes no representations about the suitability of 42 * this software for any purpose. It is provided "as is" without express 43 * or implied warranty. 44 */ 45 46 47 #ifdef KRB5 48 #include <arpa/telnet.h> 49 #include <stdio.h> 50 #include <krb5/krb5.h> 51 #include <krb5/crc-32.h> 52 #include <krb5/los-proto.h> 53 #include <krb5/ext-proto.h> 54 #include <com_err.h> 55 #include <netdb.h> 56 #include <ctype.h> 57 58 59 /* kerberos 5 include files (ext-proto.h) will get an appropriate stdlib.h 60 and string.h/strings.h */ 61 62 #include "encrypt.h" 63 #include "auth.h" 64 #include "misc.h" 65 66 extern auth_debug_mode; 67 68 69 static unsigned char str_data[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 0, 70 AUTHTYPE_KERBEROS_V5, }; 71 /*static unsigned char str_name[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 72 TELQUAL_NAME, };*/ 73 74 #define KRB_AUTH 0 /* Authentication data follows */ 75 #define KRB_REJECT 1 /* Rejected (reason might follow) */ 76 #define KRB_ACCEPT 2 /* Accepted */ 77 #define KRB_RESPONSE 3 /* Response for mutual auth. */ 78 79 static krb5_data auth; 80 /* telnetd gets session key from here */ 81 static krb5_tkt_authent *authdat = NULL; 82 /* telnet matches the AP_REQ and AP_REP with this */ 83 static krb5_authenticator authenticator; 84 85 /* some compilers can't hack void *, so we use the Kerberos krb5_pointer, 86 which is either void * or char *, depending on the compiler. */ 87 88 #define Voidptr krb5_pointer 89 90 #ifdef ENCRYPTION 91 Block session_key; 92 #endif /* ENCRYPTION */ 93 static int 94 Data(ap, type, d, c) 95 Authenticator *ap; 96 int type; 97 Voidptr d; 98 int c; 99 { 100 unsigned char *p = str_data + 4; 101 unsigned char *cd = (unsigned char *)d; 102 103 if (c == -1) 104 c = strlen((char *)cd); 105 106 if (auth_debug_mode) { 107 printf("%s:%d: [%d] (%d)", 108 str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY", 109 str_data[3], 110 type, c); 111 printd(d, c); 112 printf("\r\n"); 113 } 114 *p++ = ap->type; 115 *p++ = ap->way; 116 *p++ = type; 117 while (c-- > 0) { 118 if ((*p++ = *cd++) == IAC) 119 *p++ = IAC; 120 } 121 *p++ = IAC; 122 *p++ = SE; 123 if (str_data[3] == TELQUAL_IS) 124 printsub('>', &str_data[2], p - &str_data[2]); 125 return(net_write(str_data, p - str_data)); 126 } 127 128 int 129 kerberos5_init(ap, server) 130 Authenticator *ap; 131 int server; 132 { 133 if (server) 134 str_data[3] = TELQUAL_REPLY; 135 else 136 str_data[3] = TELQUAL_IS; 137 krb5_init_ets(); 138 return(1); 139 } 140 141 int 142 kerberos5_send(ap) 143 Authenticator *ap; 144 { 145 char **realms; 146 char *name; 147 char *p1, *p2; 148 krb5_checksum ksum; 149 krb5_octet sum[CRC32_CKSUM_LENGTH]; 150 krb5_principal server; 151 krb5_error_code r; 152 krb5_ccache ccache; 153 krb5_creds creds; /* telnet gets session key from here */ 154 extern krb5_flags krb5_kdc_default_options; 155 int ap_opts; 156 157 #ifdef ENCRYPTION 158 krb5_keyblock *newkey = 0; 159 #endif /* ENCRYPTION */ 160 161 ksum.checksum_type = CKSUMTYPE_CRC32; 162 ksum.contents = sum; 163 ksum.length = sizeof(sum); 164 bzero((Voidptr )sum, sizeof(sum)); 165 166 if (!UserNameRequested) { 167 if (auth_debug_mode) { 168 printf("Kerberos V5: no user name supplied\r\n"); 169 } 170 return(0); 171 } 172 173 if (r = krb5_cc_default(&ccache)) { 174 if (auth_debug_mode) { 175 printf("Kerberos V5: could not get default ccache\r\n"); 176 } 177 return(0); 178 } 179 180 if ((name = malloc(strlen(RemoteHostName)+1)) == NULL) { 181 if (auth_debug_mode) 182 printf("Out of memory for hostname in Kerberos V5\r\n"); 183 return(0); 184 } 185 186 if (r = krb5_get_host_realm(RemoteHostName, &realms)) { 187 if (auth_debug_mode) 188 printf("Kerberos V5: no realm for %s\r\n", RemoteHostName); 189 free(name); 190 return(0); 191 } 192 193 p1 = RemoteHostName; 194 p2 = name; 195 196 while (*p2 = *p1++) { 197 if (isupper(*p2)) 198 *p2 |= 040; 199 ++p2; 200 } 201 202 if (r = krb5_build_principal_ext(&server, 203 strlen(realms[0]), realms[0], 204 4, "rcmd", 205 p2 - name, name, 206 0)) { 207 if (auth_debug_mode) { 208 printf("Kerberos V5: failure setting up principal (%s)\r\n", 209 error_message(r)); 210 } 211 free(name); 212 krb5_free_host_realm(realms); 213 return(0); 214 } 215 216 217 bzero((char *)&creds, sizeof(creds)); 218 creds.server = server; 219 220 if (r = krb5_cc_get_principal(ccache, &creds.client)) { 221 if (auth_debug_mode) { 222 printf("Kerberos V5: failure on principal (%s)\r\n", 223 error_message(r)); 224 } 225 free(name); 226 krb5_free_principal(server); 227 krb5_free_host_realm(realms); 228 return(0); 229 } 230 231 if (r = krb5_get_credentials(krb5_kdc_default_options, ccache, &creds)) { 232 if (auth_debug_mode) { 233 printf("Kerberos V5: failure on credentials(%d)\r\n",r); 234 } 235 free(name); 236 krb5_free_host_realm(realms); 237 krb5_free_principal(server); 238 return(0); 239 } 240 241 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) 242 ap_opts = AP_OPTS_MUTUAL_REQUIRED; 243 else 244 ap_opts = 0; 245 246 r = krb5_mk_req_extended(ap_opts, &ksum, krb5_kdc_default_options, 0, 247 #ifdef ENCRYPTION 248 &newkey, 249 #else /* ENCRYPTION */ 250 0, 251 #endif /* ENCRYPTION */ 252 ccache, &creds, &authenticator, &auth); 253 /* don't let the key get freed if we clean up the authenticator */ 254 authenticator.subkey = 0; 255 256 free(name); 257 krb5_free_host_realm(realms); 258 krb5_free_principal(server); 259 #ifdef ENCRYPTION 260 if (newkey) { 261 /* keep the key in our private storage, but don't use it 262 yet---see kerberos5_reply() below */ 263 if (newkey->keytype != KEYTYPE_DES) { 264 if (creds.keyblock.keytype == KEYTYPE_DES) 265 /* use the session key in credentials instead */ 266 memcpy((char *)session_key, 267 (char *)creds.keyblock.contents, sizeof(Block)); 268 else 269 /* XXX ? */; 270 } else { 271 memcpy((char *)session_key, (char *)newkey->contents, 272 sizeof(Block)); 273 } 274 krb5_free_keyblock(newkey); 275 } 276 #endif /* ENCRYPTION */ 277 if (r) { 278 if (auth_debug_mode) { 279 printf("Kerberos V5: mk_req failed (%s)\r\n", 280 error_message(r)); 281 } 282 return(0); 283 } 284 285 if (!auth_sendname(UserNameRequested, strlen(UserNameRequested))) { 286 if (auth_debug_mode) 287 printf("Not enough room for user name\r\n"); 288 return(0); 289 } 290 if (!Data(ap, KRB_AUTH, auth.data, auth.length)) { 291 if (auth_debug_mode) 292 printf("Not enough room for authentication data\r\n"); 293 return(0); 294 } 295 if (auth_debug_mode) { 296 printf("Sent Kerberos V5 credentials to server\r\n"); 297 } 298 return(1); 299 } 300 301 void 302 kerberos5_is(ap, data, cnt) 303 Authenticator *ap; 304 unsigned char *data; 305 int cnt; 306 { 307 int r; 308 struct hostent *hp; 309 char *p1, *p2; 310 static char *realm = NULL; 311 krb5_principal server; 312 krb5_ap_rep_enc_part reply; 313 krb5_data outbuf; 314 Session_Key skey; 315 char *name; 316 char *getenv(); 317 318 if (cnt-- < 1) 319 return; 320 switch (*data++) { 321 case KRB_AUTH: 322 auth.data = (char *)data; 323 auth.length = cnt; 324 325 if (!(hp = gethostbyname(LocalHostName))) { 326 if (auth_debug_mode) 327 printf("Cannot resolve local host name\r\n"); 328 Data(ap, KRB_REJECT, "Unknown local hostname.", -1); 329 auth_finished(ap, AUTH_REJECT); 330 return; 331 } 332 333 if (!realm && (krb5_get_default_realm(&realm))) { 334 if (auth_debug_mode) 335 printf("Could not get default realm\r\n"); 336 Data(ap, KRB_REJECT, "Could not get default realm.", -1); 337 auth_finished(ap, AUTH_REJECT); 338 return; 339 } 340 341 if ((name = malloc(strlen(hp->h_name)+1)) == NULL) { 342 if (auth_debug_mode) 343 printf("Out of memory for hostname in Kerberos V5\r\n"); 344 Data(ap, KRB_REJECT, "Out of memory.", -1); 345 auth_finished(ap, AUTH_REJECT); 346 return; 347 } 348 349 p1 = hp->h_name; 350 p2 = name; 351 352 while (*p2 = *p1++) { 353 if (isupper(*p2)) 354 *p2 |= 040; 355 ++p2; 356 } 357 358 if (authdat) 359 krb5_free_tkt_authent(authdat); 360 361 r = krb5_build_principal_ext(&server, 362 strlen(realm), realm, 363 4, "rcmd", 364 p2 - name, name, 365 0); 366 if (!r) { 367 r = krb5_rd_req_simple(&auth, server, 0, &authdat); 368 krb5_free_principal(server); 369 } 370 if (r) { 371 char errbuf[128]; 372 373 errout: 374 authdat = 0; 375 (void) strcpy(errbuf, "Read req failed: "); 376 (void) strcat(errbuf, error_message(r)); 377 Data(ap, KRB_REJECT, errbuf, -1); 378 if (auth_debug_mode) 379 printf("%s\r\n", errbuf); 380 return; 381 } 382 free(name); 383 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { 384 /* do ap_rep stuff here */ 385 reply.ctime = authdat->authenticator->ctime; 386 reply.cusec = authdat->authenticator->cusec; 387 reply.subkey = 0; /* use the one he gave us, so don't 388 need to return one here */ 389 reply.seq_number = 0; /* we don't do seq #'s. */ 390 391 if (r = krb5_mk_rep(&reply, 392 authdat->authenticator->subkey ? 393 authdat->authenticator->subkey : 394 authdat->ticket->enc_part2->session, 395 &outbuf)) { 396 goto errout; 397 } 398 Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length); 399 } 400 if (krb5_unparse_name(authdat->ticket->enc_part2 ->client, 401 &name)) 402 name = 0; 403 Data(ap, KRB_ACCEPT, name, name ? -1 : 0); 404 if (auth_debug_mode) { 405 printf("Kerberos5 identifies him as ``%s''\r\n", 406 name ? name : ""); 407 } 408 auth_finished(ap, AUTH_USER); 409 410 free(name); 411 if (authdat->authenticator->subkey && 412 authdat->authenticator->subkey->keytype == KEYTYPE_DES) { 413 bcopy((Voidptr )authdat->authenticator->subkey->contents, 414 (Voidptr )session_key, sizeof(Block)); 415 } else if (authdat->ticket->enc_part2->session->keytype == 416 KEYTYPE_DES) { 417 bcopy((Voidptr )authdat->ticket->enc_part2->session->contents, 418 (Voidptr )session_key, sizeof(Block)); 419 } else 420 break; 421 422 skey.type = SK_DES; 423 skey.length = 8; 424 skey.data = session_key; 425 encrypt_session_key(&skey, 1); 426 break; 427 default: 428 if (auth_debug_mode) 429 printf("Unknown Kerberos option %d\r\n", data[-1]); 430 Data(ap, KRB_REJECT, 0, 0); 431 break; 432 } 433 } 434 435 void 436 kerberos5_reply(ap, data, cnt) 437 Authenticator *ap; 438 unsigned char *data; 439 int cnt; 440 { 441 Session_Key skey; 442 static int mutual_complete = 0; 443 444 if (cnt-- < 1) 445 return; 446 switch (*data++) { 447 case KRB_REJECT: 448 if (cnt > 0) { 449 printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n", 450 cnt, data); 451 } else 452 printf("[ Kerberos V5 refuses authentication ]\r\n"); 453 auth_send_retry(); 454 return; 455 case KRB_ACCEPT: 456 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && 457 !mutual_complete) { 458 printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\n"); 459 auth_send_retry(); 460 return; 461 } 462 if (cnt) 463 printf("[ Kerberos V5 accepts you as ``%.*s'' ]\n", cnt, data); 464 else 465 printf("[ Kerberos V5 accepts you ]\n"); 466 auth_finished(ap, AUTH_USER); 467 break; 468 case KRB_RESPONSE: 469 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { 470 /* the rest of the reply should contain a krb_ap_rep */ 471 krb5_ap_rep_enc_part *reply; 472 krb5_data inbuf; 473 krb5_error_code r; 474 krb5_keyblock tmpkey; 475 476 inbuf.length = cnt; 477 inbuf.data = (char *)data; 478 479 tmpkey.keytype = KEYTYPE_DES; 480 tmpkey.contents = session_key; 481 tmpkey.length = sizeof(Block); 482 483 if (r = krb5_rd_rep(&inbuf, &tmpkey, &reply)) { 484 printf("[ Mutual authentication failed: %s ]\n", 485 error_message(r)); 486 auth_send_retry(); 487 return; 488 } 489 if (reply->ctime != authenticator.ctime || 490 reply->cusec != authenticator.cusec) { 491 printf("[ Mutual authentication failed (mismatched KRB_AP_REP) ]\n"); 492 auth_send_retry(); 493 return; 494 } 495 krb5_free_ap_rep_enc_part(reply); 496 #ifdef ENCRYPTION 497 skey.type = SK_DES; 498 skey.length = 8; 499 skey.data = session_key; 500 encrypt_session_key(&skey, 0); 501 #endif /* ENCRYPTION */ 502 mutual_complete = 1; 503 } 504 return; 505 default: 506 if (auth_debug_mode) 507 printf("Unknown Kerberos option %d\r\n", data[-1]); 508 return; 509 } 510 } 511 512 int 513 kerberos5_status(ap, name, level) 514 Authenticator *ap; 515 char *name; 516 int level; 517 { 518 if (level < AUTH_USER) 519 return(level); 520 521 if (UserNameRequested && 522 krb5_kuserok(authdat->ticket->enc_part2->client, UserNameRequested)) 523 { 524 strcpy(name, UserNameRequested); 525 return(AUTH_VALID); 526 } else 527 return(AUTH_USER); 528 } 529 530 #define BUMP(buf, len) while (*(buf)) {++(buf), --(len);} 531 #define ADDC(buf, len, c) if ((len) > 0) {*(buf)++ = (c); --(len);} 532 533 void 534 kerberos5_printsub(data, cnt, buf, buflen) 535 unsigned char *data, *buf; 536 int cnt, buflen; 537 { 538 char lbuf[32]; 539 register int i; 540 541 buf[buflen-1] = '\0'; /* make sure its NULL terminated */ 542 buflen -= 1; 543 544 switch(data[3]) { 545 case KRB_REJECT: /* Rejected (reason might follow) */ 546 strncpy((char *)buf, " REJECT ", buflen); 547 goto common; 548 549 case KRB_ACCEPT: /* Accepted (name might follow) */ 550 strncpy((char *)buf, " ACCEPT ", buflen); 551 common: 552 BUMP(buf, buflen); 553 if (cnt <= 4) 554 break; 555 ADDC(buf, buflen, '"'); 556 for (i = 4; i < cnt; i++) 557 ADDC(buf, buflen, data[i]); 558 ADDC(buf, buflen, '"'); 559 ADDC(buf, buflen, '\0'); 560 break; 561 562 563 case KRB_AUTH: /* Authentication data follows */ 564 strncpy((char *)buf, " AUTH", buflen); 565 goto common2; 566 567 case KRB_RESPONSE: 568 strncpy((char *)buf, " RESPONSE", buflen); 569 goto common2; 570 571 default: 572 sprintf(lbuf, " %d (unknown)", data[3]); 573 strncpy((char *)buf, lbuf, buflen); 574 common2: 575 BUMP(buf, buflen); 576 for (i = 4; i < cnt; i++) { 577 sprintf(lbuf, " %d", data[i]); 578 strncpy((char *)buf, lbuf, buflen); 579 BUMP(buf, buflen); 580 } 581 break; 582 } 583 } 584 #endif 585