1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright(c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1983 The Regents of the University of California. 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms are permitted 14 * provided that the above copyright notice and this paragraph are 15 * duplicated in all such forms and that any documentation, 16 * advertising materials, and other materials related to such 17 * distribution and use acknowledge that the software was developed 18 * by the University of California, Berkeley. The name of the 19 * University may not be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 23 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * remote login server: 30 * remuser\0 31 * locuser\0 32 * terminal info\0 33 * data 34 */ 35 36 #include <time.h> 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <sys/socket.h> 40 #include <sys/wait.h> 41 42 #include <netinet/in.h> 43 44 #include <errno.h> 45 #include <signal.h> 46 #include <fcntl.h> 47 #include <stdio.h> 48 #include <netdb.h> 49 #include <syslog.h> 50 #include <string.h> 51 #include <unistd.h> 52 #include <stdlib.h> 53 #include <alloca.h> 54 #include <stropts.h> 55 #include <sac.h> /* for SC_WILDC */ 56 #include <utmpx.h> 57 #include <sys/filio.h> 58 #include <sys/logindmux.h> 59 #include <sys/rlioctl.h> 60 #include <sys/termios.h> 61 #include <sys/tihdr.h> 62 #include <arpa/inet.h> 63 #include <security/pam_appl.h> 64 #include <strings.h> 65 #include <com_err.h> 66 #include <k5-int.h> 67 #include <kcmd.h> 68 #include <krb5_repository.h> 69 #include <sys/cryptmod.h> 70 #include <bsm/adt.h> 71 #include <addr_match.h> 72 #include <store_forw_creds.h> 73 74 #define KRB5_RECVAUTH_V5 5 75 #define UT_NAMESIZE sizeof (((struct utmpx *)0)->ut_name) 76 77 static char lusername[UT_NAMESIZE+1]; 78 static char rusername[UT_NAMESIZE+1]; 79 static char *krusername = NULL; 80 static char term[64]; 81 82 static krb5_ccache ccache = NULL; 83 static krb5_keyblock *session_key = NULL; 84 static int chksum_flag = 0; 85 static int use_auth = 0; 86 static enum kcmd_proto kcmd_protocol; 87 #ifdef ALLOW_KCMD_V2 88 static krb5_data encr_iv = { NULL, 0 }; 89 static krb5_data decr_iv = { NULL, 0 }; 90 #endif /* ALLOW_KCMD_V2 */ 91 92 #define CHKSUM_REQUIRED 0x01 93 #define CHKSUM_IGNORED 0x02 94 #define VALID_CHKSUM(x) ((x) == 0 || (x) == CHKSUM_REQUIRED ||\ 95 (x) == CHKSUM_IGNORED) 96 97 #define PWD_IF_FAIL 0x01 98 #define PWD_REQUIRED 0x02 99 100 #define AUTH_NONE 0x00 101 102 #define ARGSTR "k5exEXciM:s:S:D:" 103 #define DEFAULT_TOS 16 104 105 #define KRB5_PROG_NAME "krlogin" 106 107 #define SECURE_MSG "This rlogin session is using encryption " \ 108 "for all data transmissions.\r\n" 109 110 #define KRB_V5_SENDAUTH_VERS "KRB5_SENDAUTH_V1.0" 111 #define KRB5_RECVAUTH_V5 5 112 113 static krb5_error_code krb5_compat_recvauth(krb5_context context, 114 krb5_auth_context *auth_context, 115 krb5_pointer fdp, 116 krb5_principal server, 117 krb5_int32 flags, 118 krb5_keytab keytab, 119 krb5_ticket **ticket, 120 krb5_int32 *auth_sys, 121 krb5_data *version); 122 123 static void do_krb_login(int, char *, char *, krb5_context, int, krb5_keytab); 124 static int configure_stream(int, krb5_keyblock *, int, krb5_data *, uint_t); 125 126 extern krb5_error_code krb5_read_message(krb5_context, krb5_pointer, 127 krb5_data *); 128 extern krb5_error_code krb5_net_read(krb5_context, int, char *, int); 129 130 #define LOGIN_PROGRAM "/bin/login" 131 132 #define DEFAULT_PROG_NAME "rlogin" 133 134 static const char *pam_prog_name = DEFAULT_PROG_NAME; 135 static void rmut(void); 136 static void doit(int, struct sockaddr_storage *, krb5_context, int, 137 krb5_keytab); 138 static void protocol(int, int, int); 139 140 static int readstream(int, char *, int); 141 static void fatal(int, const char *); 142 static void fatalperror(int, const char *); 143 static int send_oob(int fd, void *ptr, size_t count); 144 static int removemod(int f, char *modname); 145 146 static int 147 issock(int fd) 148 { 149 struct stat stats; 150 151 if (fstat(fd, &stats) == -1) 152 return (0); 153 return (S_ISSOCK(stats.st_mode)); 154 } 155 156 /* 157 * audit_rlogin_settid stores the terminal id while it is still 158 * available. Subsequent calls to adt_load_hostname() return 159 * the id which is stored here. 160 */ 161 static int 162 audit_rlogin_settid(int fd) { 163 adt_session_data_t *ah; 164 adt_termid_t *termid; 165 int rc; 166 167 if ((rc = adt_start_session(&ah, NULL, 0)) == 0) { 168 if ((rc = adt_load_termid(fd, &termid)) == 0) { 169 if ((rc = adt_set_user(ah, ADT_NO_AUDIT, 170 ADT_NO_AUDIT, 0, ADT_NO_AUDIT, 171 termid, ADT_SETTID)) == 0) 172 (void) adt_set_proc(ah); 173 free(termid); 174 } 175 (void) adt_end_session(ah); 176 } 177 return (rc); 178 } 179 180 181 /* ARGSUSED */ 182 int 183 main(int argc, char *argv[]) 184 { 185 int on = 1; 186 socklen_t fromlen; 187 struct sockaddr_storage from; 188 int fd = -1; 189 190 extern char *optarg; 191 char c; 192 int tos = -1; 193 krb5_context krb_context; 194 krb5_keytab keytab = NULL; 195 krb5_error_code status; 196 char *realm = NULL; 197 char *keytab_file = NULL; 198 int encr_flag = 0; 199 struct sockaddr_storage ouraddr; 200 socklen_t ourlen; 201 #ifdef DEBUG 202 int debug_port = 0; 203 #endif /* DEBUG */ 204 openlog("rlogind", LOG_PID | LOG_ODELAY, LOG_DAEMON); 205 206 while ((c = getopt(argc, argv, ARGSTR)) != -1) { 207 switch (c) { 208 case 'k': 209 case '5': 210 use_auth = KRB5_RECVAUTH_V5; 211 break; 212 case 'e': 213 case 'E': 214 case 'x': 215 case 'X': 216 encr_flag = 1; 217 break; 218 case 'M': 219 realm = (char *)strdup(optarg); 220 break; 221 case 'S': 222 keytab_file = (char *)strdup(optarg); 223 break; 224 case 'c': 225 chksum_flag |= CHKSUM_REQUIRED; 226 break; 227 case 'i': 228 chksum_flag |= CHKSUM_IGNORED; 229 break; 230 case 's': 231 if (optarg == NULL || (tos = atoi(optarg)) < 0 || 232 tos > 255) { 233 syslog(LOG_ERR, "%s: illegal tos value: " 234 "%s\n", argv[0], optarg); 235 } else { 236 if (tos < 0) 237 tos = DEFAULT_TOS; 238 } 239 break; 240 #ifdef DEBUG 241 case 'D': 242 debug_port = atoi(optarg); 243 break; 244 #endif /* DEBUG */ 245 default: 246 syslog(LOG_ERR, "Unrecognized command line option " 247 "(%s), exiting", argv[optind]); 248 exit(EXIT_FAILURE); 249 } 250 } 251 if (use_auth == KRB5_RECVAUTH_V5) { 252 status = krb5_init_context(&krb_context); 253 if (status) { 254 syslog(LOG_ERR, "Error initializing krb5: %s", 255 error_message(status)); 256 exit(EXIT_FAILURE); 257 } 258 if (realm != NULL) 259 krb5_set_default_realm(krb_context, realm); 260 if (keytab_file != NULL) { 261 if ((status = krb5_kt_resolve(krb_context, 262 keytab_file, 263 &keytab))) { 264 com_err(argv[0], 265 status, 266 "while resolving srvtab file %s", 267 keytab_file); 268 exit(EXIT_FAILURE); 269 } 270 } 271 } 272 273 #ifdef DEBUG 274 if (debug_port) { 275 int s; 276 struct sockaddr_in sin; 277 278 if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) { 279 fatalperror(STDERR_FILENO, "Error in socket"); 280 } 281 282 (void) memset((char *)&sin, 0, sizeof (sin)); 283 sin.sin_family = AF_INET; 284 sin.sin_port = htons(debug_port); 285 sin.sin_addr.s_addr = INADDR_ANY; 286 287 (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 288 (char *)&on, sizeof (on)); 289 290 if ((bind(s, (struct sockaddr *)&sin, sizeof (sin))) < 0) { 291 fatalperror(STDERR_FILENO, "bind error"); 292 } 293 294 if ((listen(s, 5)) < 0) { 295 fatalperror(STDERR_FILENO, "listen error"); 296 } 297 298 fromlen = sizeof (from); 299 if ((fd = accept(s, (struct sockaddr *)&from, &fromlen)) < 0) { 300 fatalperror(STDERR_FILENO, "accept error"); 301 } 302 303 (void) close(s); 304 } else 305 #endif /* DEBUG */ 306 { 307 if (!issock(STDIN_FILENO)) 308 fatal(STDIN_FILENO, 309 "stdin is not a socket file descriptor"); 310 fd = STDIN_FILENO; 311 } 312 313 fromlen = sizeof (from); 314 if (getpeername(fd, (struct sockaddr *)&from, &fromlen) < 0) 315 fatalperror(STDERR_FILENO, "getpeername"); 316 317 if (audit_rlogin_settid(fd)) /* set terminal ID */ 318 fatalperror(STDERR_FILENO, "audit"); 319 320 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, 321 sizeof (on)) < 0) 322 syslog(LOG_WARNING, "setsockopt(SO_KEEPALIVE): %m"); 323 324 if (!VALID_CHKSUM(chksum_flag)) { 325 syslog(LOG_ERR, "Configuration error: mutually exclusive " 326 "options specified (-c and -i)"); 327 fatal(fd, "Checksums are required and ignored (-c and -i);" 328 "these options are mutually exclusive - check " 329 "the documentation."); 330 } 331 ourlen = sizeof (ouraddr); 332 if (getsockname(fd, (struct sockaddr *)&ouraddr, &ourlen) == -1) { 333 syslog(LOG_ERR, "getsockname error: %m"); 334 exit(EXIT_FAILURE); 335 } 336 337 if (tos != -1 && 338 ouraddr.ss_family != AF_INET6 && 339 setsockopt(fd, IPPROTO_IP, IP_TOS, (char *)&tos, 340 sizeof (tos)) < 0 && 341 errno != ENOPROTOOPT) { 342 syslog(LOG_ERR, "setsockopt(IP_TOS %d): %m", tos); 343 } 344 doit(fd, &from, krb_context, encr_flag, keytab); 345 return (0); 346 } 347 348 static void cleanup(int); 349 static int nsize = 0; /* bytes read prior to pushing rlmod */ 350 static char *rlbuf; /* buffer where nbytes are read to */ 351 static char *line; 352 353 static struct winsize win = { 0, 0, 0, 0 }; 354 static pid_t pid; 355 static char hostname[MAXHOSTNAMELEN + 1]; 356 357 static void 358 getstr(int f, char *buf, int cnt, char *err) 359 { 360 char c; 361 do { 362 if (read(f, &c, 1) != 1 || (--cnt < 0)) { 363 syslog(LOG_ERR, "Error reading \'%s\' field", err); 364 exit(EXIT_FAILURE); 365 } 366 *buf++ = c; 367 } while (c != '\0'); 368 } 369 370 static krb5_error_code 371 recvauth(int f, 372 krb5_context krb_context, 373 unsigned int *valid_checksum, 374 krb5_ticket **ticket, 375 int *auth_type, 376 krb5_principal *client, 377 int encr_flag, 378 krb5_keytab keytab) 379 { 380 krb5_error_code status = 0; 381 krb5_auth_context auth_context = NULL; 382 krb5_rcache rcache; 383 krb5_authenticator *authenticator; 384 krb5_data inbuf; 385 krb5_data auth_version; 386 387 *valid_checksum = 0; 388 389 if ((status = krb5_auth_con_init(krb_context, &auth_context))) 390 return (status); 391 392 /* Only need remote address for rd_cred() to verify client */ 393 if ((status = krb5_auth_con_genaddrs(krb_context, auth_context, f, 394 KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))) 395 return (status); 396 397 status = krb5_auth_con_getrcache(krb_context, auth_context, &rcache); 398 if (status) 399 return (status); 400 401 if (!rcache) { 402 krb5_principal server; 403 404 status = krb5_sname_to_principal(krb_context, 0, 0, 405 KRB5_NT_SRV_HST, &server); 406 if (status) 407 return (status); 408 409 status = krb5_get_server_rcache(krb_context, 410 krb5_princ_component(krb_context, server, 0), 411 &rcache); 412 krb5_free_principal(krb_context, server); 413 if (status) 414 return (status); 415 416 status = krb5_auth_con_setrcache(krb_context, auth_context, 417 rcache); 418 if (status) 419 return (status); 420 } 421 if ((status = krb5_compat_recvauth(krb_context, 422 &auth_context, 423 &f, 424 NULL, /* Specify daemon principal */ 425 0, /* no flags */ 426 keytab, /* NULL to use v5srvtab */ 427 ticket, /* return ticket */ 428 auth_type, /* authentication system */ 429 &auth_version))) { 430 if (*auth_type == KRB5_RECVAUTH_V5) { 431 /* 432 * clean up before exiting 433 */ 434 getstr(f, rusername, sizeof (rusername), "remuser"); 435 getstr(f, lusername, sizeof (lusername), "locuser"); 436 getstr(f, term, sizeof (term), "Terminal type"); 437 } 438 return (status); 439 } 440 441 getstr(f, lusername, sizeof (lusername), "locuser"); 442 getstr(f, term, sizeof (term), "Terminal type"); 443 444 kcmd_protocol = KCMD_UNKNOWN_PROTOCOL; 445 if (auth_version.length != 9 || auth_version.data == NULL) { 446 syslog(LOG_ERR, "Bad application protocol version length in " 447 "KRB5 exchange, exiting"); 448 fatal(f, "Bad application version length, exiting."); 449 } 450 /* 451 * Determine which Kerberos CMD protocol was used. 452 */ 453 if (strncmp(auth_version.data, "KCMDV0.1", 9) == 0) { 454 kcmd_protocol = KCMD_OLD_PROTOCOL; 455 } else if (strncmp(auth_version.data, "KCMDV0.2", 9) == 0) { 456 kcmd_protocol = KCMD_NEW_PROTOCOL; 457 } else { 458 syslog(LOG_ERR, "Unrecognized KCMD protocol (%s), exiting", 459 (char *)auth_version.data); 460 fatal(f, "Unrecognized KCMD protocol, exiting"); 461 } 462 463 if ((*auth_type == KRB5_RECVAUTH_V5) && chksum_flag && 464 kcmd_protocol == KCMD_OLD_PROTOCOL) { 465 if ((status = krb5_auth_con_getauthenticator(krb_context, 466 auth_context, 467 &authenticator))) 468 return (status); 469 if (authenticator->checksum) { 470 struct sockaddr_storage adr; 471 int adr_length = sizeof (adr); 472 int buflen; 473 krb5_data input; 474 krb5_keyblock key; 475 char *chksumbuf; 476 477 /* 478 * Define the lenght of the chksum buffer. 479 * chksum string = "[portnum]:termstr:username" 480 * The extra 32 is to hold a integer string for 481 * the portnumber. 482 */ 483 buflen = strlen(term) + strlen(lusername) + 32; 484 chksumbuf = (char *)malloc(buflen); 485 if (chksumbuf == 0) { 486 krb5_free_authenticator(krb_context, 487 authenticator); 488 fatal(f, "Out of memory error"); 489 } 490 491 if (getsockname(f, (struct sockaddr *)&adr, 492 &adr_length) != 0) { 493 krb5_free_authenticator(krb_context, 494 authenticator); 495 fatal(f, "getsockname error"); 496 } 497 498 (void) snprintf(chksumbuf, buflen, 499 "%u:%s%s", 500 ntohs(SOCK_PORT(adr)), 501 term, lusername); 502 503 input.data = chksumbuf; 504 input.length = strlen(chksumbuf); 505 key.contents = (*ticket)->enc_part2->session->contents; 506 key.length = (*ticket)->enc_part2->session->length; 507 status = krb5_c_verify_checksum(krb_context, 508 &key, 0, 509 &input, 510 authenticator->checksum, 511 valid_checksum); 512 513 if (status == 0 && *valid_checksum == 0) 514 status = KRB5KRB_AP_ERR_BAD_INTEGRITY; 515 516 if (chksumbuf) 517 krb5_xfree(chksumbuf); 518 if (status) { 519 krb5_free_authenticator(krb_context, 520 authenticator); 521 return (status); 522 } 523 } 524 krb5_free_authenticator(krb_context, authenticator); 525 } 526 527 if ((status = krb5_copy_principal(krb_context, 528 (*ticket)->enc_part2->client, 529 client))) 530 return (status); 531 532 /* Get the Unix username of the remote user */ 533 getstr(f, rusername, sizeof (rusername), "remuser"); 534 535 /* Get the Kerberos principal name string of the remote user */ 536 if ((status = krb5_unparse_name(krb_context, *client, &krusername))) 537 return (status); 538 539 #ifdef DEBUG 540 syslog(LOG_DEBUG | LOG_AUTH, "rlogind: got krb5 credentials for %s", 541 (krusername != NULL ? krusername : "<unknown>")); 542 #endif 543 544 if (encr_flag) { 545 status = krb5_auth_con_getremotesubkey(krb_context, 546 auth_context, 547 &session_key); 548 if (status) { 549 syslog(LOG_ERR, "Error getting KRB5 session " 550 "subkey, exiting"); 551 fatal(f, "Error getting KRB5 session subkey, exiting"); 552 } 553 /* 554 * The "new" protocol requires that a subkey be sent. 555 */ 556 if (session_key == NULL && 557 kcmd_protocol == KCMD_NEW_PROTOCOL) { 558 syslog(LOG_ERR, "No KRB5 session subkey sent, exiting"); 559 fatal(f, "No KRB5 session subkey sent, exiting"); 560 } 561 /* 562 * The "old" protocol does not permit an authenticator subkey. 563 * The key is taken from the ticket instead (see below). 564 */ 565 if (session_key != NULL && 566 kcmd_protocol == KCMD_OLD_PROTOCOL) { 567 syslog(LOG_ERR, "KRB5 session subkey not permitted " 568 "with old KCMD protocol, exiting"); 569 570 fatal(f, "KRB5 session subkey not permitted " 571 "with old KCMD protocol, exiting"); 572 } 573 /* 574 * If no key at this point, use the session key from 575 * the ticket. 576 */ 577 if (session_key == NULL) { 578 /* 579 * Save the session key so we can configure the crypto 580 * module later. 581 */ 582 status = krb5_copy_keyblock(krb_context, 583 (*ticket)->enc_part2->session, 584 &session_key); 585 if (status) { 586 syslog(LOG_ERR, "krb5_copy_keyblock failed"); 587 fatal(f, "krb5_copy_keyblock failed"); 588 } 589 } 590 /* 591 * If session key still cannot be found, we must 592 * exit because encryption is required here 593 * when encr_flag (-x) is set. 594 */ 595 if (session_key == NULL) { 596 syslog(LOG_ERR, "Could not find an encryption key," 597 "exiting"); 598 fatal(f, "Encryption required but key not found, " 599 "exiting"); 600 } 601 } 602 /* 603 * Use krb5_read_message to read the principal stuff. 604 */ 605 if ((status = krb5_read_message(krb_context, (krb5_pointer)&f, 606 &inbuf))) 607 fatal(f, "Error reading krb5 message"); 608 609 if (inbuf.length) { /* Forwarding being done, read creds */ 610 krb5_creds **creds = NULL; 611 612 if (status = krb5_rd_cred(krb_context, auth_context, &inbuf, 613 &creds, NULL)) { 614 if (rcache) 615 (void) krb5_rc_close(krb_context, rcache); 616 krb5_free_creds(krb_context, *creds); 617 fatal(f, "Can't get forwarded credentials"); 618 } 619 620 /* Store the forwarded creds in the ccache */ 621 if (status = store_forw_creds(krb_context, 622 creds, *ticket, lusername, 623 &ccache)) { 624 if (rcache) 625 (void) krb5_rc_close(krb_context, rcache); 626 krb5_free_creds(krb_context, *creds); 627 fatal(f, "Can't store forwarded credentials"); 628 } 629 krb5_free_creds(krb_context, *creds); 630 } 631 632 if (rcache) 633 (void) krb5_rc_close(krb_context, rcache); 634 635 return (status); 636 } 637 638 static void 639 do_krb_login(int f, char *host_addr, char *hostname, 640 krb5_context krb_context, int encr_flag, 641 krb5_keytab keytab) 642 { 643 krb5_error_code status; 644 uint_t valid_checksum; 645 krb5_ticket *ticket = NULL; 646 int auth_sys = 0; 647 int auth_sent = 0; 648 krb5_principal client = NULL; 649 650 if (getuid()) 651 fatal(f, "Error authorizing KRB5 connection, " 652 "server lacks privilege"); 653 654 status = recvauth(f, krb_context, &valid_checksum, &ticket, 655 &auth_sys, &client, encr_flag, keytab); 656 if (status) { 657 if (ticket) 658 krb5_free_ticket(krb_context, ticket); 659 if (status != 255) 660 syslog(LOG_ERR, 661 "Authentication failed from %s(%s): %s\n", 662 host_addr, hostname, error_message(status)); 663 fatal(f, "Kerberos authentication failed, exiting"); 664 } 665 666 if (auth_sys != KRB5_RECVAUTH_V5) { 667 fatal(f, "This server only supports Kerberos V5"); 668 } else { 669 /* 670 * Authenticated OK, now check authorization. 671 */ 672 if (client && krb5_kuserok(krb_context, client, lusername)) 673 auth_sent = KRB5_RECVAUTH_V5; 674 } 675 676 if (auth_sent == KRB5_RECVAUTH_V5 && 677 kcmd_protocol == KCMD_OLD_PROTOCOL && 678 chksum_flag == CHKSUM_REQUIRED && !valid_checksum) { 679 syslog(LOG_ERR, "Client did not supply required checksum, " 680 "connection rejected."); 681 fatal(f, "Client did not supply required checksum, " 682 "connection rejected."); 683 } 684 685 if (auth_sys != auth_sent) { 686 char *msg_fail = NULL; 687 int msgsize = 0; 688 689 if (ticket) 690 krb5_free_ticket(krb_context, ticket); 691 692 if (krusername != NULL) { 693 /* 694 * msgsize must be enough to hold 695 * krusername, lusername and a brief 696 * message describing the failure. 697 */ 698 msgsize = strlen(krusername) + 699 strlen(lusername) + 80; 700 msg_fail = (char *)malloc(msgsize); 701 } 702 if (msg_fail == NULL) { 703 syslog(LOG_ERR, "User is not authorized to login to " 704 "specified account"); 705 706 fatal(f, "User is not authorized to login to " 707 "specified account"); 708 } 709 if (auth_sent != 0) 710 (void) snprintf(msg_fail, msgsize, 711 "Access denied because of improper " 712 "KRB5 credentials"); 713 else 714 (void) snprintf(msg_fail, msgsize, 715 "User %s is not authorized to login " 716 "to account %s", 717 krusername, lusername); 718 syslog(LOG_ERR, "%s", msg_fail); 719 fatal(f, msg_fail); 720 } 721 } 722 723 /* 724 * stop_stream 725 * 726 * Utility routine to send a CRYPTIOCSTOP ioctl to the 727 * crypto module(cryptmod). 728 */ 729 static void 730 stop_stream(int fd, int dir) 731 { 732 struct strioctl crioc; 733 uint32_t stopdir = dir; 734 735 crioc.ic_cmd = CRYPTIOCSTOP; 736 crioc.ic_timout = -1; 737 crioc.ic_len = sizeof (stopdir); 738 crioc.ic_dp = (char *)&stopdir; 739 740 if (ioctl(fd, I_STR, &crioc)) 741 syslog(LOG_ERR, "Error sending CRYPTIOCSTOP ioctl: %m"); 742 } 743 744 /* 745 * start_stream 746 * 747 * Utility routine to send a CRYPTIOCSTART ioctl to the 748 * crypto module(cryptmod). This routine may contain optional 749 * payload data that the cryptmod will interpret as bytes that 750 * need to be decrypted and sent back up to the application 751 * via the data stream. 752 */ 753 static void 754 start_stream(int fd, int dir) 755 { 756 struct strioctl crioc; 757 uint32_t iocval; 758 size_t datalen = 0; 759 char *data = NULL; 760 761 if (dir == CRYPT_DECRYPT) { 762 iocval = CRYPTIOCSTARTDEC; 763 764 /* Look for data not yet processed */ 765 if (ioctl(fd, I_NREAD, &datalen) < 0) { 766 syslog(LOG_ERR, "I_NREAD returned error %m"); 767 datalen = 0; 768 } else { 769 if (datalen > 0) { 770 data = malloc(datalen); 771 if (data != NULL) { 772 int nbytes = read(fd, data, datalen); 773 datalen = nbytes; 774 } else { 775 syslog(LOG_ERR, 776 "malloc error (%d bytes)", 777 datalen); 778 datalen = 0; 779 } 780 } else { 781 datalen = 0; 782 } 783 } 784 } else { 785 iocval = CRYPTIOCSTARTENC; 786 } 787 788 crioc.ic_cmd = iocval; 789 crioc.ic_timout = -1; 790 crioc.ic_len = datalen; 791 crioc.ic_dp = data; 792 793 if (ioctl(fd, I_STR, &crioc)) 794 syslog(LOG_ERR, "Error sending CRYPTIOCSTART ioctl: %m"); 795 796 if (data != NULL) 797 free(data); 798 } 799 800 static int 801 configure_stream(int fd, krb5_keyblock *skey, int dir, krb5_data *ivec, 802 uint_t iv_usage) 803 { 804 struct cr_info_t setup_info; 805 struct strioctl crioc; 806 int retval = 0; 807 808 switch (skey->enctype) { 809 case ENCTYPE_DES_CBC_CRC: 810 setup_info.crypto_method = CRYPT_METHOD_DES_CBC_CRC; 811 break; 812 case ENCTYPE_DES_CBC_MD5: 813 setup_info.crypto_method = CRYPT_METHOD_DES_CBC_MD5; 814 break; 815 case ENCTYPE_DES_CBC_RAW: 816 setup_info.crypto_method = CRYPT_METHOD_DES_CBC_NULL; 817 break; 818 case ENCTYPE_DES3_CBC_SHA1: 819 setup_info.crypto_method = CRYPT_METHOD_DES3_CBC_SHA1; 820 break; 821 case ENCTYPE_ARCFOUR_HMAC: 822 setup_info.crypto_method = CRYPT_METHOD_ARCFOUR_HMAC_MD5; 823 break; 824 case ENCTYPE_ARCFOUR_HMAC_EXP: 825 setup_info.crypto_method = CRYPT_METHOD_ARCFOUR_HMAC_MD5_EXP; 826 break; 827 case ENCTYPE_AES128_CTS_HMAC_SHA1_96: 828 setup_info.crypto_method = CRYPT_METHOD_AES128; 829 break; 830 case ENCTYPE_AES256_CTS_HMAC_SHA1_96: 831 setup_info.crypto_method = CRYPT_METHOD_AES256; 832 break; 833 default: 834 syslog(LOG_ERR, "Enctype in kerberos session key " 835 "is not supported by crypto module(%d)", 836 skey->enctype); 837 return (-1); 838 } 839 if (ivec == NULL || ivec->length == 0) { 840 (void) memset(&setup_info.ivec, 0, sizeof (setup_info.ivec)); 841 842 if (skey->enctype != ENCTYPE_ARCFOUR_HMAC && 843 skey->enctype != ENCTYPE_ARCFOUR_HMAC_EXP) 844 /* Kerberos IVs are 8 bytes long for DES keys */ 845 setup_info.iveclen = KRB5_MIT_DES_KEYSIZE; 846 else 847 setup_info.iveclen = 0; 848 } else { 849 (void) memcpy(&setup_info.ivec, ivec->data, ivec->length); 850 setup_info.iveclen = ivec->length; 851 } 852 853 setup_info.ivec_usage = iv_usage; 854 (void) memcpy(&setup_info.key, skey->contents, skey->length); 855 856 setup_info.keylen = skey->length; 857 setup_info.direction_mask = dir; 858 /* 859 * R* commands get special handling by crypto module - 860 * 4 byte length field is used before each encrypted block 861 * of data. 862 */ 863 setup_info.option_mask = (kcmd_protocol == KCMD_OLD_PROTOCOL ? 864 CRYPTOPT_RCMD_MODE_V1 : 865 CRYPTOPT_RCMD_MODE_V2); 866 867 crioc.ic_cmd = CRYPTIOCSETUP; 868 crioc.ic_timout = -1; 869 crioc.ic_len = sizeof (setup_info); 870 crioc.ic_dp = (char *)&setup_info; 871 872 if (ioctl(fd, I_STR, &crioc)) { 873 syslog(LOG_ERR, "Error sending CRYPTIOCSETUP ioctl: %m"); 874 retval = -1; 875 } 876 return (retval); 877 } 878 879 static krb5_error_code 880 krb5_compat_recvauth(krb5_context context, 881 krb5_auth_context *auth_context, 882 krb5_pointer fdp, /* IN */ 883 krb5_principal server, /* IN */ 884 krb5_int32 flags, /* IN */ 885 krb5_keytab keytab, /* IN */ 886 krb5_ticket **ticket, /* OUT */ 887 krb5_int32 *auth_sys, /* OUT */ 888 krb5_data *version) /* OUT */ 889 { 890 krb5_int32 vlen; 891 char *buf; 892 int len, length; 893 krb5_int32 retval; 894 int fd = *((int *)fdp); 895 896 if ((retval = krb5_net_read(context, fd, (char *)&vlen, 4)) != 4) 897 return ((retval < 0) ? errno : ECONNABORTED); 898 899 /* 900 * Assume that we're talking to a V5 recvauth; read in the 901 * the version string, and make sure it matches. 902 */ 903 len = (int)ntohl(vlen); 904 905 if (len < 0 || len > 255) 906 return (KRB5_SENDAUTH_BADAUTHVERS); 907 908 buf = malloc(len); 909 if (buf == NULL) 910 return (ENOMEM); 911 912 length = krb5_net_read(context, fd, buf, len); 913 if (len != length) { 914 krb5_xfree(buf); 915 return ((len < 0) ? errno : ECONNABORTED); 916 } 917 918 if (strcmp(buf, KRB_V5_SENDAUTH_VERS) != 0) { 919 krb5_xfree(buf); 920 return (KRB5_SENDAUTH_BADAUTHVERS); 921 } 922 krb5_xfree(buf); 923 924 *auth_sys = KRB5_RECVAUTH_V5; 925 926 retval = krb5_recvauth_version(context, auth_context, fdp, 927 server, flags | KRB5_RECVAUTH_SKIP_VERSION, 928 keytab, ticket, version); 929 930 return (retval); 931 } 932 933 934 static void 935 doit(int f, 936 struct sockaddr_storage *fromp, 937 krb5_context krb_context, 938 int encr_flag, 939 krb5_keytab keytab) 940 { 941 int p, t, on = 1; 942 char c; 943 char abuf[INET6_ADDRSTRLEN]; 944 struct sockaddr_in *sin; 945 struct sockaddr_in6 *sin6; 946 int fromplen; 947 in_port_t port; 948 struct termios tp; 949 boolean_t bad_port; 950 boolean_t no_name; 951 char rhost_addra[INET6_ADDRSTRLEN]; 952 953 if (!(rlbuf = malloc(BUFSIZ))) { 954 syslog(LOG_ERR, "rlbuf malloc failed\n"); 955 exit(EXIT_FAILURE); 956 } 957 (void) alarm(60); 958 if (read(f, &c, 1) != 1 || c != 0) { 959 syslog(LOG_ERR, "failed to receive protocol zero byte\n"); 960 exit(EXIT_FAILURE); 961 } 962 (void) alarm(0); 963 if (fromp->ss_family == AF_INET) { 964 sin = (struct sockaddr_in *)fromp; 965 port = sin->sin_port = ntohs((ushort_t)sin->sin_port); 966 fromplen = sizeof (struct sockaddr_in); 967 968 if (!inet_ntop(AF_INET, &sin->sin_addr, 969 rhost_addra, sizeof (rhost_addra))) 970 goto badconversion; 971 } else if (fromp->ss_family == AF_INET6) { 972 sin6 = (struct sockaddr_in6 *)fromp; 973 port = sin6->sin6_port = ntohs((ushort_t)sin6->sin6_port); 974 fromplen = sizeof (struct sockaddr_in6); 975 976 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 977 struct in_addr ipv4_addr; 978 979 IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, 980 &ipv4_addr); 981 if (!inet_ntop(AF_INET, &ipv4_addr, rhost_addra, 982 sizeof (rhost_addra))) 983 goto badconversion; 984 } else { 985 if (!inet_ntop(AF_INET6, &sin6->sin6_addr, 986 rhost_addra, sizeof (rhost_addra))) 987 goto badconversion; 988 } 989 } else { 990 syslog(LOG_ERR, "unknown address family %d\n", 991 fromp->ss_family); 992 fatal(f, "Permission denied"); 993 } 994 995 /* 996 * Allow connections only from the "ephemeral" reserved 997 * ports(ports 512 - 1023) by checking the remote port 998 * because other utilities(e.g. in.ftpd) can be used to 999 * allow a unprivileged user to originate a connection 1000 * from a privileged port and provide untrustworthy 1001 * authentication. 1002 */ 1003 bad_port = (use_auth != KRB5_RECVAUTH_V5 && 1004 (port >= (in_port_t)IPPORT_RESERVED) || 1005 (port < (in_port_t)(IPPORT_RESERVED/2))); 1006 no_name = getnameinfo((const struct sockaddr *) fromp, 1007 fromplen, hostname, sizeof (hostname), 1008 NULL, 0, 0) != 0; 1009 1010 if (no_name || bad_port) { 1011 (void) strlcpy(abuf, rhost_addra, sizeof (abuf)); 1012 /* If no host name, use IP address for name later on. */ 1013 if (no_name) 1014 (void) strlcpy(hostname, abuf, sizeof (hostname)); 1015 } 1016 1017 if (!no_name) { 1018 /* 1019 * Even if getnameinfo() succeeded, we still have to check 1020 * for spoofing. 1021 */ 1022 check_address("rlogind", fromp, sin, sin6, rhost_addra, 1023 hostname, sizeof (hostname)); 1024 } 1025 1026 if (bad_port) { 1027 if (no_name) 1028 syslog(LOG_NOTICE, 1029 "connection from %s - bad port\n", 1030 abuf); 1031 else 1032 syslog(LOG_NOTICE, 1033 "connection from %s(%s) - bad port\n", 1034 hostname, abuf); 1035 fatal(f, "Permission denied"); 1036 } 1037 1038 if (use_auth == KRB5_RECVAUTH_V5) { 1039 do_krb_login(f, rhost_addra, hostname, 1040 krb_context, encr_flag, keytab); 1041 if (krusername != NULL && strlen(krusername)) { 1042 /* 1043 * Kerberos Authentication succeeded, 1044 * so set the proper program name to use 1045 * with pam (important during 'cleanup' 1046 * routine later). 1047 */ 1048 pam_prog_name = KRB5_PROG_NAME; 1049 } 1050 }