1 /*- 2 * Copyright (c) 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 /*static char sccsid[] = "from: @(#)kerberos5.c 5.2 (Berkeley) 3/22/91";*/ 36 static char rcsid[] = "$Id: kerberos5.c,v 1.2 1993/08/01 18:32:34 mycroft Exp $"; 37 #endif /* not lint */ 38 39 /* 40 * Copyright (C) 1990 by the Massachusetts Institute of Technology 41 * 42 * Export of this software from the United States of America is assumed 43 * to require a specific license from the United States Government. 44 * It is the responsibility of any person or organization contemplating 45 * export to obtain such a license before exporting. 46 * 47 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 48 * distribute this software and its documentation for any purpose and 49 * without fee is hereby granted, provided that the above copyright 50 * notice appear in all copies and that both that copyright notice and 51 * this permission notice appear in supporting documentation, and that 52 * the name of M.I.T. not be used in advertising or publicity pertaining 53 * to distribution of the software without specific, written prior 54 * permission. M.I.T. makes no representations about the suitability of 55 * this software for any purpose. It is provided "as is" without express 56 * or implied warranty. 57 */ 58 59 60 #ifdef KRB5 61 #include <arpa/telnet.h> 62 #include <stdio.h> 63 #include <krb5/krb5.h> 64 #include <krb5/crc-32.h> 65 #include <krb5/libos-proto.h> 66 #include <netdb.h> 67 #include <ctype.h> 68 69 #ifdef __STDC__ 70 #include <stdlib.h> 71 #endif 72 #ifdef NO_STRING_H 73 #include <strings.h> 74 #else 75 #include <string.h> 76 #endif 77 78 #include "encrypt.h" 79 #include "auth.h" 80 #include "misc.h" 81 82 extern auth_debug_mode; 83 84 char *malloc(); 85 86 static unsigned char str_data[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 0, 87 AUTHTYPE_KERBEROS_V5, }; 88 static unsigned char str_name[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 89 TELQUAL_NAME, }; 90 91 #define KRB_AUTH 0 /* Authentication data follows */ 92 #define KRB_REJECT 1 /* Rejected (reason might follow) */ 93 #define KRB_ACCEPT 2 /* Accepted */ 94 #define KRB_CHALLANGE 3 /* Challange for mutual auth. */ 95 #define KRB_RESPONSE 4 /* Response for mutual auth. */ 96 97 static krb5_data auth; 98 /* telnetd gets session key from here */ 99 static krb5_tkt_authent *authdat = NULL; 100 101 #if defined(ENCRYPT) 102 Block session_key; 103 #endif 104 static Schedule sched; 105 static Block challange; 106 107 static int 108 Data(ap, type, d, c) 109 Authenticator *ap; 110 int type; 111 void *d; 112 int c; 113 { 114 unsigned char *p = str_data + 4; 115 unsigned char *cd = (unsigned char *)d; 116 117 if (c == -1) 118 c = strlen((char *)cd); 119 120 if (auth_debug_mode) { 121 printf("%s:%d: [%d] (%d)", 122 str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY", 123 str_data[3], 124 type, c); 125 printd(d, c); 126 printf("\r\n"); 127 } 128 *p++ = ap->type; 129 *p++ = ap->way; 130 *p++ = type; 131 while (c-- > 0) { 132 if ((*p++ = *cd++) == IAC) 133 *p++ = IAC; 134 } 135 *p++ = IAC; 136 *p++ = SE; 137 if (str_data[3] == TELQUAL_IS) 138 printsub('>', &str_data[2], p - &str_data[2]); 139 return(net_write(str_data, p - str_data)); 140 } 141 142 int 143 kerberos5_init(ap, server) 144 Authenticator *ap; 145 int server; 146 { 147 if (server) 148 str_data[3] = TELQUAL_REPLY; 149 else 150 str_data[3] = TELQUAL_IS; 151 krb5_init_ets(); 152 return(1); 153 } 154 155 int 156 kerberos5_send(ap) 157 Authenticator *ap; 158 { 159 char **realms; 160 char *name; 161 char *p1, *p2; 162 krb5_checksum ksum; 163 krb5_octet sum[CRC32_CKSUM_LENGTH]; 164 krb5_data *server[4]; 165 krb5_data srvdata[3]; 166 krb5_error_code r; 167 krb5_ccache ccache; 168 krb5_creds creds; /* telnet gets session key from here */ 169 extern krb5_flags krb5_kdc_default_options; 170 171 ksum.checksum_type = CKSUMTYPE_CRC32; 172 ksum.contents = sum; 173 ksum.length = sizeof(sum); 174 bzero((void *)sum, sizeof(sum)); 175 176 if (!UserNameRequested) { 177 if (auth_debug_mode) { 178 printf("Kerberos V5: no user name supplied\r\n"); 179 } 180 return(0); 181 } 182 183 if (r = krb5_cc_default(&ccache)) { 184 if (auth_debug_mode) { 185 printf("Kerberos V5: could not get default ccache\r\n"); 186 } 187 return(0); 188 } 189 190 if ((name = malloc(strlen(RemoteHostName)+1)) == NULL) { 191 if (auth_debug_mode) 192 printf("Out of memory for hostname in Kerberos V5\r\n"); 193 return(0); 194 } 195 196 if (r = krb5_get_host_realm(RemoteHostName, &realms)) { 197 if (auth_debug_mode) 198 printf("Kerberos V5: no realm for %s\r\n", RemoteHostName); 199 free(name); 200 return(0); 201 } 202 203 p1 = RemoteHostName; 204 p2 = name; 205 206 while (*p2 = *p1++) { 207 if (isupper(*p2)) 208 *p2 |= 040; 209 ++p2; 210 } 211 212 srvdata[0].data = realms[0]; 213 srvdata[0].length = strlen(realms[0]); 214 srvdata[1].data = "rcmd"; 215 srvdata[1].length = 4; 216 srvdata[2].data = name; 217 srvdata[2].length = p2 - name; 218 219 server[0] = &srvdata[0]; 220 server[1] = &srvdata[1]; 221 server[2] = &srvdata[2]; 222 server[3] = 0; 223 224 bzero((char *)&creds, sizeof(creds)); 225 creds.server = (krb5_principal)server; 226 227 if (r = krb5_cc_get_principal(ccache, &creds.client)) { 228 if (auth_debug_mode) { 229 printf("Keberos V5: failure on principal (%d)\r\n", 230 error_message(r)); 231 } 232 free(name); 233 krb5_free_host_realm(realms); 234 return(0); 235 } 236 237 if (r = krb5_get_credentials(krb5_kdc_default_options, ccache, &creds)) { 238 if (auth_debug_mode) { 239 printf("Keberos V5: failure on credentials(%d)\r\n",r); 240 } 241 free(name); 242 krb5_free_host_realm(realms); 243 return(0); 244 } 245 246 r = krb5_mk_req_extended(0, &ksum, &creds.times, 247 krb5_kdc_default_options, 248 ccache, &creds, 0, &auth); 249 250 free(name); 251 krb5_free_host_realm(realms); 252 if (r) { 253 if (auth_debug_mode) { 254 printf("Keberos V5: mk_req failed\r\n"); 255 } 256 return(0); 257 } 258 259 if (!auth_sendname(UserNameRequested, strlen(UserNameRequested))) { 260 if (auth_debug_mode) 261 printf("Not enough room for user name\r\n"); 262 return(0); 263 } 264 if (!Data(ap, KRB_AUTH, auth.data, auth.length)) { 265 if (auth_debug_mode) 266 printf("Not enough room for authentication data\r\n"); 267 return(0); 268 } 269 /* 270 * If we are doing mutual authentication, get set up to send 271 * the challange, and verify it when the response comes back. 272 */ 273 if (((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) 274 && (creds.keyblock.keytype == KEYTYPE_DES)) { 275 register int i; 276 277 des_key_sched(creds.keyblock.contents, sched); 278 des_set_random_generator_seed(creds.keyblock.contents); 279 des_new_random_key(challange); 280 des_ecb_encrypt(challange, session_key, sched, 1); 281 /* 282 * Increment the challange by 1, and encrypt it for 283 * later comparison. 284 */ 285 for (i = 7; i >= 0; --i) { 286 register int x; 287 x = (unsigned int)challange[i] + 1; 288 challange[i] = x; /* ignore overflow */ 289 if (x < 256) /* if no overflow, all done */ 290 break; 291 } 292 des_ecb_encrypt(challange, challange, sched, 1); 293 } 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_data *server[4]; 312 krb5_data srvdata[3]; 313 Session_Key skey; 314 char *name; 315 char *getenv(); 316 317 if (cnt-- < 1) 318 return; 319 switch (*data++) { 320 case KRB_AUTH: 321 auth.data = (char *)data; 322 auth.length = cnt; 323 324 if (!(hp = gethostbyname(LocalHostName))) { 325 if (auth_debug_mode) 326 printf("Cannot resolve local host name\r\n"); 327 Data(ap, KRB_REJECT, "Unknown local hostname.", -1); 328 auth_finished(ap, AUTH_REJECT); 329 return; 330 } 331 332 if (!realm && (krb5_get_default_realm(&realm))) { 333 if (auth_debug_mode) 334 printf("Could not get defualt realm\r\n"); 335 Data(ap, KRB_REJECT, "Could not get default realm.", -1); 336 auth_finished(ap, AUTH_REJECT); 337 return; 338 } 339 340 if ((name = malloc(strlen(hp->h_name)+1)) == NULL) { 341 if (auth_debug_mode) 342 printf("Out of memory for hostname in Kerberos V5\r\n"); 343 Data(ap, KRB_REJECT, "Out of memory.", -1); 344 auth_finished(ap, AUTH_REJECT); 345 return; 346 } 347 348 p1 = hp->h_name; 349 p2 = name; 350 351 while (*p2 = *p1++) { 352 if (isupper(*p2)) 353 *p2 |= 040; 354 ++p2; 355 } 356 357 srvdata[0].data = realm; 358 srvdata[0].length = strlen(realm); 359 srvdata[1].data = "rcmd"; 360 srvdata[1].length = 4; 361 srvdata[2].data = name; 362 srvdata[2].length = p2 - name; 363 364 server[0] = &srvdata[0]; 365 server[1] = &srvdata[1]; 366 server[2] = &srvdata[2]; 367 server[3] = 0; 368 369 if (authdat) 370 krb5_free_tkt_authent(authdat); 371 if (r = krb5_rd_req_simple(&auth, server, 0, &authdat)) { 372 char errbuf[128]; 373 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 (krb5_unparse_name(authdat->ticket->enc_part2 ->client, 384 &name)) 385 name = 0; 386 Data(ap, KRB_ACCEPT, name, name ? -1 : 0); 387 if (auth_debug_mode) { 388 printf("Kerberos5 accepting him as ``%s''\r\n", 389 name ? name : ""); 390 } 391 auth_finished(ap, AUTH_USER); 392 if (authdat->ticket->enc_part2->session->keytype != KEYTYPE_DES) 393 break; 394 bcopy((void *)authdat->ticket->enc_part2->session->contents, 395 (void *)session_key, sizeof(Block)); 396 break; 397 398 case KRB_CHALLANGE: 399 if (!VALIDKEY(session_key)) { 400 /* 401 * We don't have a valid session key, so just 402 * send back a response with an empty session 403 * key. 404 */ 405 Data(ap, KRB_RESPONSE, (void *)0, 0); 406 break; 407 } 408 409 des_key_sched(session_key, sched); 410 bcopy((void *)data, (void *)datablock, sizeof(Block)); 411 /* 412 * Take the received encrypted challange, and encrypt 413 * it again to get a unique session_key for the 414 * ENCRYPT option. 415 */ 416 des_ecb_encrypt(datablock, session_key, sched, 1); 417 skey.type = SK_DES; 418 skey.length = 8; 419 skey.data = session_key; 420 encrypt_session_key(&skey, 1); 421 /* 422 * Now decrypt the received encrypted challange, 423 * increment by one, re-encrypt it and send it back. 424 */ 425 des_ecb_encrypt(datablock, challange, sched, 0); 426 for (r = 7; r >= 0; r++) { 427 register int t; 428 t = (unsigned int)challange[r] + 1; 429 challange[r] = t; /* ignore overflow */ 430 if (t < 256) /* if no overflow, all done */ 431 break; 432 } 433 des_ecb_encrypt(challange, challange, sched, 1); 434 Data(ap, KRB_RESPONSE, (void *)challange, sizeof(challange)); 435 break; 436 437 default: 438 if (auth_debug_mode) 439 printf("Unknown Kerberos option %d\r\n", data[-1]); 440 Data(ap, KRB_REJECT, 0, 0); 441 break; 442 } 443 } 444 445 void 446 kerberos5_reply(ap, data, cnt) 447 Authenticator *ap; 448 unsigned char *data; 449 int cnt; 450 { 451 Session_Key skey; 452 453 if (cnt-- < 1) 454 return; 455 switch (*data++) { 456 case KRB_REJECT: 457 if (cnt > 0) { 458 printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n", 459 cnt, data); 460 } else 461 printf("[ Kerberos V5 refuses authentication ]\r\n"); 462 auth_send_retry(); 463 return; 464 case KRB_ACCEPT: 465 printf("[ Kerberos V5 accepts you ]\n", cnt, data); 466 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { 467 /* 468 * Send over the encrypted challange. 469 */ 470 Data(ap, KRB_CHALLANGE, (void *)session_key, 471 sizeof(session_key)); 472 #if defined(ENCRYPT) 473 des_ecb_encrypt(session_key, session_key, sched, 1); 474 skey.type = SK_DES; 475 skey.length = 8; 476 skey.data = session_key; 477 encrypt_session_key(&skey, 0); 478 #endif 479 return; 480 } 481 auth_finished(ap, AUTH_USER); 482 return; 483 case KRB_RESPONSE: 484 /* 485 * Verify that the response to the challange is correct. 486 */ 487 if ((cnt != sizeof(Block)) || 488 (0 != memcmp((void *)data, (void *)challange, 489 sizeof(challange)))) 490 { 491 printf("[ Kerberos V5 challange failed!!! ]\r\n"); 492 auth_send_retry(); 493 return; 494 } 495 printf("[ Kerberos V5 challange successful ]\r\n"); 496 auth_finished(ap, AUTH_USER); 497 break; 498 default: 499 if (auth_debug_mode) 500 printf("Unknown Kerberos option %d\r\n", data[-1]); 501 return; 502 } 503 } 504 505 int 506 kerberos5_status(ap, name, level) 507 Authenticator *ap; 508 char *name; 509 int level; 510 { 511 if (level < AUTH_USER) 512 return(level); 513 514 if (UserNameRequested && 515 krb5_kuserok(authdat->ticket->enc_part2->client, UserNameRequested)) 516 { 517 strcpy(name, UserNameRequested); 518 return(AUTH_VALID); 519 } else 520 return(AUTH_USER); 521 } 522 523 #define BUMP(buf, len) while (*(buf)) {++(buf), --(len);} 524 #define ADDC(buf, len, c) if ((len) > 0) {*(buf)++ = (c); --(len));} 525 526 void 527 kerberos5_printsub(data, cnt, buf, buflen) 528 unsigned char *data, *buf; 529 int cnt, buflen; 530 { 531 char lbuf[32]; 532 register int i; 533 534 buf[buflen-1] = '\0'; /* make sure its NULL terminated */ 535 buflen -= 1; 536 537 switch(data[3]) { 538 case KRB_REJECT: /* Rejected (reason might follow) */ 539 strncpy((char *)buf, " REJECT ", buflen); 540 goto common; 541 542 case KRB_ACCEPT: /* Accepted (name might follow) */ 543 strncpy((char *)buf, " ACCEPT ", buflen); 544 common: 545 BUMP(buf, buflen); 546 if (cnt <= 4) 547 break; 548 ADDC(buf, buflen, '"'); 549 for (i = 4; i < cnt; i++) 550 ADDC(buf, buflen, data[i]); 551 ADDC(buf, buflen, '"'); 552 ADDC(buf, buflen, '\0'); 553 break; 554 555 case KRB_AUTH: /* Authentication data follows */ 556 strncpy((char *)buf, " AUTH", buflen); 557 goto common2; 558 559 case KRB_CHALLANGE: 560 strncpy((char *)buf, " CHALLANGE", buflen); 561 goto common2; 562 563 case KRB_RESPONSE: 564 strncpy((char *)buf, " RESPONSE", buflen); 565 goto common2; 566 567 default: 568 sprintf(lbuf, " %d (unknown)", data[3]); 569 strncpy((char *)buf, lbuf, buflen); 570 common2: 571 BUMP(buf, buflen); 572 for (i = 4; i < cnt; i++) { 573 sprintf(lbuf, " %d", data[i]); 574 strncpy((char *)buf, lbuf, buflen); 575 BUMP(buf, buflen); 576 } 577 break; 578 } 579 } 580 #endif 581