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