1 /*
   2  * Copyright 2010 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 /*
  27  * remote login server:
  28  *      remuser\0
  29  *      locuser\0
  30  *      terminal info\0
  31  *      data
  32  */
  33 
  34 #include <time.h>
  35 #include <sys/types.h>
  36 #include <sys/stat.h>
  37 #include <sys/socket.h>
  38 #include <sys/wait.h>
  39 
  40 #include <netinet/in.h>
  41 
  42 #include <errno.h>
  43 #include <signal.h>
  44 #include <fcntl.h>
  45 #include <stdio.h>
  46 #include <netdb.h>
  47 #include <syslog.h>
  48 #include <string.h>
  49 #include <unistd.h>
  50 #include <stdlib.h>
  51 #include <alloca.h>
  52 #include <stropts.h>
  53 #include <sac.h>  /* for SC_WILDC */
  54 #include <utmpx.h>
  55 #include <sys/filio.h>
  56 #include <sys/logindmux.h>
  57 #include <sys/rlioctl.h>
  58 #include <sys/termios.h>
  59 #include <sys/tihdr.h>
  60 #include <arpa/inet.h>
  61 #include <security/pam_appl.h>
  62 #include <strings.h>
  63 #include <com_err.h>
  64 #include <k5-int.h>
  65 #include <kcmd.h>
  66 #include <krb5_repository.h>
  67 #include <sys/cryptmod.h>
  68 #include <bsm/adt.h>
  69 #include <addr_match.h>
  70 #include <store_forw_creds.h>
  71 
  72 #define KRB5_RECVAUTH_V5 5
  73 #define UT_NAMESIZE     sizeof (((struct utmpx *)0)->ut_name)
  74 
  75 static char lusername[UT_NAMESIZE+1];
  76 static char rusername[UT_NAMESIZE+1];
  77 static char *krusername = NULL;
  78 static char term[64];
  79 
  80 static krb5_ccache ccache = NULL;
  81 static krb5_keyblock *session_key = NULL;
  82 static int chksum_flag = 0;
  83 static int use_auth = 0;
  84 static enum kcmd_proto kcmd_protocol;
  85 #ifdef ALLOW_KCMD_V2
  86 static krb5_data encr_iv = { NULL, 0 };
  87 static krb5_data decr_iv = { NULL, 0 };
  88 #endif /* ALLOW_KCMD_V2 */
  89 
  90 #define CHKSUM_REQUIRED 0x01
  91 #define CHKSUM_IGNORED  0x02
  92 #define VALID_CHKSUM(x) ((x) == 0 || (x) == CHKSUM_REQUIRED ||\
  93                         (x) == CHKSUM_IGNORED)
  94 
  95 #define PWD_IF_FAIL  0x01
  96 #define PWD_REQUIRED 0x02
  97 
  98 #define AUTH_NONE 0x00
  99 
 100 #define ARGSTR "k5exEXciM:s:S:D:"
 101 #define DEFAULT_TOS 16
 102 
 103 #define KRB5_PROG_NAME "krlogin"
 104 
 105 #define SECURE_MSG "This rlogin session is using encryption " \
 106         "for all data transmissions.\r\n"
 107 
 108 #define KRB_V5_SENDAUTH_VERS    "KRB5_SENDAUTH_V1.0"
 109 #define KRB5_RECVAUTH_V5        5
 110 
 111 static krb5_error_code krb5_compat_recvauth(krb5_context context,
 112                                             krb5_auth_context *auth_context,
 113                                             krb5_pointer fdp,
 114                                             krb5_principal server,
 115                                             krb5_int32 flags,
 116                                             krb5_keytab keytab,
 117                                             krb5_ticket **ticket,
 118                                             krb5_int32 *auth_sys,
 119                                             krb5_data *version);
 120 
 121 static void do_krb_login(int, char *, char *, krb5_context, int, krb5_keytab);
 122 static int configure_stream(int, krb5_keyblock *, int, krb5_data *, uint_t);
 123 
 124 extern krb5_error_code krb5_read_message(krb5_context, krb5_pointer,
 125                                         krb5_data *);
 126 extern krb5_error_code krb5_net_read(krb5_context, int, char *, int);
 127 
 128 #define LOGIN_PROGRAM "/bin/login"
 129 
 130 #define DEFAULT_PROG_NAME       "rlogin"
 131 
 132 static const char *pam_prog_name = DEFAULT_PROG_NAME;
 133 static void     rmut(void);
 134 static void     doit(int,  struct sockaddr_storage *, krb5_context, int,
 135                     krb5_keytab);
 136 static void     protocol(int, int, int);
 137 
 138 static int      readstream(int, char *, int);
 139 static void     fatal(int, const char *);
 140 static void     fatalperror(int, const char *);
 141 static int      send_oob(int fd, void *ptr, size_t count);
 142 static int      removemod(int f, char *modname);
 143 
 144 static int
 145 issock(int fd)
 146 {
 147         struct stat stats;
 148 
 149         if (fstat(fd, &stats) == -1)
 150                 return (0);
 151         return (S_ISSOCK(stats.st_mode));
 152 }
 153 
 154 /*
 155  * audit_rlogin_settid stores the terminal id while it is still
 156  * available.  Subsequent calls to adt_load_hostname() return
 157  * the id which is stored here.
 158  */
 159 static int
 160 audit_rlogin_settid(int fd) {
 161         adt_session_data_t      *ah;
 162         adt_termid_t            *termid;
 163         int                     rc;
 164 
 165         if ((rc = adt_start_session(&ah, NULL, 0)) == 0) {
 166                 if ((rc = adt_load_termid(fd, &termid)) == 0) {
 167                         if ((rc = adt_set_user(ah, ADT_NO_AUDIT,
 168                             ADT_NO_AUDIT, 0, ADT_NO_AUDIT,
 169                             termid, ADT_SETTID)) == 0)
 170                                 (void) adt_set_proc(ah);
 171                         free(termid);
 172                 }
 173                 (void) adt_end_session(ah);
 174         }
 175         return (rc);
 176 }
 177 
 178 
 179 /* ARGSUSED */
 180 int
 181 main(int argc, char *argv[])
 182 {
 183         int on = 1;
 184         socklen_t fromlen;
 185         struct sockaddr_storage from;
 186         int fd = -1;
 187 
 188         extern char *optarg;
 189         char c;
 190         int tos = -1;
 191         krb5_context krb_context;
 192         krb5_keytab keytab = NULL;
 193         krb5_error_code status;
 194         char *realm = NULL;
 195         char *keytab_file = NULL;
 196         int encr_flag = 0;
 197         struct sockaddr_storage ouraddr;
 198         socklen_t ourlen;
 199 #ifdef DEBUG
 200         int debug_port = 0;
 201 #endif /* DEBUG */
 202         openlog("rlogind", LOG_PID | LOG_ODELAY, LOG_DAEMON);
 203 
 204         while ((c = getopt(argc, argv, ARGSTR)) != -1) {
 205                 switch (c) {
 206                 case 'k':
 207                 case '5':
 208                         use_auth = KRB5_RECVAUTH_V5;
 209                         break;
 210                 case 'e':
 211                 case 'E':
 212                 case 'x':
 213                 case 'X':
 214                         encr_flag = 1;
 215                         break;
 216                 case 'M':
 217                         realm = (char *)strdup(optarg);
 218                         break;
 219                 case 'S':
 220                         keytab_file = (char *)strdup(optarg);
 221                         break;
 222                 case 'c':
 223                         chksum_flag |= CHKSUM_REQUIRED;
 224                         break;
 225                 case 'i':
 226                         chksum_flag |= CHKSUM_IGNORED;
 227                         break;
 228                 case 's':
 229                         if (optarg == NULL || (tos = atoi(optarg)) < 0 ||
 230                             tos > 255) {
 231                                 syslog(LOG_ERR, "%s: illegal tos value: "
 232                                     "%s\n", argv[0], optarg);
 233                         } else {
 234                                 if (tos < 0)
 235                                         tos = DEFAULT_TOS;
 236                         }
 237                         break;
 238 #ifdef DEBUG
 239                 case 'D':
 240                         debug_port = atoi(optarg);
 241                         break;
 242 #endif /* DEBUG */
 243                 default:
 244                         syslog(LOG_ERR, "Unrecognized command line option "
 245                             "(-%c), exiting", optopt);
 246                         exit(EXIT_FAILURE);
 247                 }
 248         }
 249         if (use_auth == KRB5_RECVAUTH_V5) {
 250                 status = krb5_init_context(&krb_context);
 251                 if (status) {
 252                         syslog(LOG_ERR, "Error initializing krb5: %s",
 253                             error_message(status));
 254                         exit(EXIT_FAILURE);
 255                 }
 256                 if (realm != NULL)
 257                         (void) krb5_set_default_realm(krb_context, realm);
 258                 if (keytab_file != NULL) {
 259                         if ((status = krb5_kt_resolve(krb_context,
 260                                                     keytab_file,
 261                                                     &keytab))) {
 262                                 com_err(argv[0],
 263                                         status,
 264                                         "while resolving srvtab file %s",
 265                                         keytab_file);
 266                                 exit(EXIT_FAILURE);
 267                         }
 268                 }
 269         }
 270 
 271 #ifdef DEBUG
 272         if (debug_port) {
 273                 int s;
 274                 struct sockaddr_in sin;
 275 
 276                 if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
 277                         fatalperror(STDERR_FILENO, "Error in socket");
 278                 }
 279 
 280                 (void) memset((char *)&sin, 0, sizeof (sin));
 281                 sin.sin_family = AF_INET;
 282                 sin.sin_port = htons(debug_port);
 283                 sin.sin_addr.s_addr = INADDR_ANY;
 284 
 285                 (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
 286                                         (char *)&on, sizeof (on));
 287 
 288                 if ((bind(s, (struct sockaddr *)&sin, sizeof (sin))) < 0) {
 289                         fatalperror(STDERR_FILENO, "bind error");
 290                 }
 291 
 292                 if ((listen(s, 5)) < 0) {
 293                         fatalperror(STDERR_FILENO, "listen error");
 294                 }
 295 
 296                 fromlen = sizeof (from);
 297                 if ((fd = accept(s, (struct sockaddr *)&from, &fromlen)) < 0) {
 298                         fatalperror(STDERR_FILENO, "accept error");
 299                 }
 300 
 301                 (void) close(s);
 302         } else
 303 #endif /* DEBUG */
 304         {
 305                 if (!issock(STDIN_FILENO))
 306                         fatal(STDIN_FILENO,
 307                                 "stdin is not a socket file descriptor");
 308                 fd = STDIN_FILENO;
 309         }
 310 
 311         fromlen = sizeof (from);
 312         if (getpeername(fd, (struct sockaddr *)&from, &fromlen) < 0)
 313                 fatalperror(STDERR_FILENO, "getpeername");
 314 
 315         if (audit_rlogin_settid(fd))    /* set terminal ID */
 316                 fatalperror(STDERR_FILENO, "audit");
 317 
 318         if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
 319             sizeof (on)) < 0)
 320                 syslog(LOG_WARNING, "setsockopt(SO_KEEPALIVE): %m");
 321 
 322         if (!VALID_CHKSUM(chksum_flag)) {
 323                 syslog(LOG_ERR, "Configuration error: mutually exclusive "
 324                     "options specified (-c and -i)");
 325                 fatal(fd, "Checksums are required and ignored (-c and -i);"
 326                     "these options are mutually exclusive - check "
 327                     "the documentation.");
 328         }
 329         ourlen = sizeof (ouraddr);
 330         if (getsockname(fd, (struct sockaddr *)&ouraddr, &ourlen) == -1) {
 331                 syslog(LOG_ERR, "getsockname error: %m");
 332                 exit(EXIT_FAILURE);
 333         }
 334 
 335         if (tos != -1 &&
 336             ouraddr.ss_family != AF_INET6 &&
 337             setsockopt(fd, IPPROTO_IP, IP_TOS, (char *)&tos,
 338                                         sizeof (tos)) < 0 &&
 339                                         errno != ENOPROTOOPT) {
 340                 syslog(LOG_ERR, "setsockopt(IP_TOS %d): %m", tos);
 341         }
 342         doit(fd, &from, krb_context, encr_flag, keytab);
 343         return (0);
 344 }
 345 
 346 static void     cleanup(int);
 347 static int      nsize = 0;      /* bytes read prior to pushing rlmod */
 348 static char     *rlbuf;         /* buffer where nbytes are read to */
 349 static char     *line;
 350 
 351 static struct winsize win = { 0, 0, 0, 0 };
 352 static pid_t pid;
 353 static char hostname[MAXHOSTNAMELEN + 1];
 354 
 355 static void
 356 getstr(int f, char *buf, int cnt, char *err)
 357 {
 358         char c;
 359         do {
 360                 if (read(f, &c, 1) != 1 || (--cnt < 0)) {
 361                         syslog(LOG_ERR, "Error reading \'%s\' field", err);
 362                         exit(EXIT_FAILURE);
 363                 }
 364                 *buf++ = c;
 365         } while (c != '\0');
 366 }
 367 
 368 static krb5_error_code
 369 recvauth(int f,
 370         krb5_context krb_context,
 371         unsigned int *valid_checksum,
 372         krb5_ticket **ticket,
 373         int *auth_type,
 374         krb5_principal *client,
 375         int encr_flag,
 376         krb5_keytab keytab)
 377 {
 378         krb5_error_code status = 0;
 379         krb5_auth_context auth_context = NULL;
 380         krb5_rcache rcache;
 381         krb5_authenticator *authenticator;
 382         krb5_data inbuf;
 383         krb5_data auth_version;
 384 
 385         *valid_checksum = 0;
 386 
 387         if ((status = krb5_auth_con_init(krb_context, &auth_context)))
 388                 return (status);
 389 
 390         /* Only need remote address for rd_cred() to verify client */
 391         if ((status = krb5_auth_con_genaddrs(krb_context, auth_context, f,
 392                         KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)))
 393                 return (status);
 394 
 395         status = krb5_auth_con_getrcache(krb_context, auth_context, &rcache);
 396         if (status)
 397                 return (status);
 398 
 399         if (!rcache) {
 400                 krb5_principal server;
 401 
 402                 status = krb5_sname_to_principal(krb_context, 0, 0,
 403                                                 KRB5_NT_SRV_HST, &server);
 404                 if (status)
 405                         return (status);
 406 
 407                 status = krb5_get_server_rcache(krb_context,
 408                                 krb5_princ_component(krb_context, server, 0),
 409                                 &rcache);
 410                 krb5_free_principal(krb_context, server);
 411                 if (status)
 412                         return (status);
 413 
 414                 status = krb5_auth_con_setrcache(krb_context, auth_context,
 415                                                 rcache);
 416                 if (status)
 417                         return (status);
 418         }
 419         if ((status = krb5_compat_recvauth(krb_context,
 420                                         &auth_context,
 421                                         &f,
 422                                         NULL,   /* Specify daemon principal */
 423                                         0,      /* no flags */
 424                                         keytab, /* NULL to use v5srvtab */
 425                                         ticket, /* return ticket */
 426                                         auth_type, /* authentication system */
 427                                         &auth_version))) {
 428                 if (*auth_type == KRB5_RECVAUTH_V5) {
 429                         /*
 430                          * clean up before exiting
 431                          */
 432                         getstr(f, rusername, sizeof (rusername), "remuser");
 433                         getstr(f, lusername, sizeof (lusername), "locuser");
 434                         getstr(f, term, sizeof (term), "Terminal type");
 435                 }
 436                 return (status);
 437         }
 438 
 439         getstr(f, lusername, sizeof (lusername), "locuser");
 440         getstr(f, term, sizeof (term), "Terminal type");
 441 
 442         kcmd_protocol = KCMD_UNKNOWN_PROTOCOL;
 443         if (auth_version.length != 9 || auth_version.data == NULL) {
 444                 syslog(LOG_ERR, "Bad application protocol version length in "
 445                     "KRB5 exchange, exiting");
 446                 fatal(f, "Bad application version length, exiting.");
 447         }
 448         /*
 449          * Determine which Kerberos CMD protocol was used.
 450          */
 451         if (strncmp(auth_version.data, "KCMDV0.1", 9) == 0) {
 452                 kcmd_protocol = KCMD_OLD_PROTOCOL;
 453         } else if (strncmp(auth_version.data, "KCMDV0.2", 9) == 0) {
 454                 kcmd_protocol = KCMD_NEW_PROTOCOL;
 455         } else {
 456                 syslog(LOG_ERR, "Unrecognized KCMD protocol (%s), exiting",
 457                         (char *)auth_version.data);
 458                 fatal(f, "Unrecognized KCMD protocol, exiting");
 459         }
 460 
 461         if ((*auth_type == KRB5_RECVAUTH_V5) && chksum_flag &&
 462                 kcmd_protocol == KCMD_OLD_PROTOCOL) {
 463                 if ((status = krb5_auth_con_getauthenticator(krb_context,
 464                                                             auth_context,
 465                                                             &authenticator)))
 466                         return (status);
 467                 if (authenticator->checksum) {
 468                         struct sockaddr_storage adr;
 469                         int adr_length = sizeof (adr);
 470                         int buflen;
 471                         krb5_data input;
 472                         krb5_keyblock key;
 473                         char *chksumbuf;
 474 
 475                         /*
 476                          * Define the lenght of the chksum buffer.
 477                          * chksum string = "[portnum]:termstr:username"
 478                          * The extra 32 is to hold a integer string for
 479                          * the portnumber.
 480                          */
 481                         buflen = strlen(term) + strlen(lusername) + 32;
 482                         chksumbuf = (char *)malloc(buflen);
 483                         if (chksumbuf == 0) {
 484                                 krb5_free_authenticator(krb_context,
 485                                                         authenticator);
 486                                 fatal(f, "Out of memory error");
 487                         }
 488 
 489                         if (getsockname(f, (struct sockaddr *)&adr,
 490                                                         &adr_length) != 0) {
 491                                 krb5_free_authenticator(krb_context,
 492                                                         authenticator);
 493                                 fatal(f, "getsockname error");
 494                         }
 495 
 496                         (void) snprintf(chksumbuf, buflen,
 497                                         "%u:%s%s",
 498                                         ntohs(SOCK_PORT(adr)),
 499                                         term, lusername);
 500 
 501                         input.data = chksumbuf;
 502                         input.length = strlen(chksumbuf);
 503                         key.contents = (*ticket)->enc_part2->session->contents;
 504                         key.length = (*ticket)->enc_part2->session->length;
 505                         status = krb5_c_verify_checksum(krb_context,
 506                                                 &key, 0,
 507                                                 &input,
 508                                                 authenticator->checksum,
 509                                                 valid_checksum);
 510 
 511                         if (status == 0 && *valid_checksum == 0)
 512                                 status = KRB5KRB_AP_ERR_BAD_INTEGRITY;
 513 
 514                         if (chksumbuf)
 515                                 krb5_xfree(chksumbuf);
 516                         if (status) {
 517                                 krb5_free_authenticator(krb_context,
 518                                                         authenticator);
 519                                 return (status);
 520                         }
 521                 }
 522                 krb5_free_authenticator(krb_context, authenticator);
 523         }
 524 
 525         if ((status = krb5_copy_principal(krb_context,
 526                                         (*ticket)->enc_part2->client,
 527                                         client)))
 528                 return (status);
 529 
 530         /* Get the Unix username of the remote user */
 531         getstr(f, rusername, sizeof (rusername), "remuser");
 532 
 533         /* Get the Kerberos principal name string of the remote user */
 534         if ((status = krb5_unparse_name(krb_context, *client, &krusername)))
 535                 return (status);
 536 
 537 #ifdef DEBUG
 538         syslog(LOG_DEBUG | LOG_AUTH, "rlogind: got krb5 credentials for %s",
 539             (krusername != NULL ? krusername : "<unknown>"));
 540 #endif
 541 
 542         if (encr_flag) {
 543                 status = krb5_auth_con_getremotesubkey(krb_context,
 544                                                     auth_context,
 545                                                     &session_key);
 546                 if (status) {
 547                         syslog(LOG_ERR, "Error getting KRB5 session "
 548                             "subkey, exiting");
 549                         fatal(f, "Error getting KRB5 session subkey, exiting");
 550                 }
 551                 /*
 552                  * The "new" protocol requires that a subkey be sent.
 553                  */
 554                 if (session_key == NULL &&
 555                     kcmd_protocol == KCMD_NEW_PROTOCOL) {
 556                         syslog(LOG_ERR, "No KRB5 session subkey sent, exiting");
 557                         fatal(f, "No KRB5 session subkey sent, exiting");
 558                 }
 559                 /*
 560                  * The "old" protocol does not permit an authenticator subkey.
 561                  * The key is taken from the ticket instead (see below).
 562                  */
 563                 if (session_key != NULL &&
 564                     kcmd_protocol == KCMD_OLD_PROTOCOL) {
 565                         syslog(LOG_ERR, "KRB5 session subkey not permitted "
 566                             "with old KCMD protocol, exiting");
 567 
 568                         fatal(f, "KRB5 session subkey not permitted "
 569                             "with old KCMD protocol, exiting");
 570                 }
 571                 /*
 572                  * If no key at this point, use the session key from
 573                  * the ticket.
 574                  */
 575                 if (session_key == NULL) {
 576                         /*
 577                          * Save the session key so we can configure the crypto
 578                          * module later.
 579                          */
 580                         status = krb5_copy_keyblock(krb_context,
 581                                             (*ticket)->enc_part2->session,
 582                                             &session_key);
 583                         if (status) {
 584                                 syslog(LOG_ERR, "krb5_copy_keyblock failed");
 585                                 fatal(f, "krb5_copy_keyblock failed");
 586                         }
 587                 }
 588                 /*
 589                  * If session key still cannot be found, we must
 590                  * exit because encryption is required here
 591                  * when encr_flag (-x) is set.
 592                  */
 593                 if (session_key == NULL) {
 594                         syslog(LOG_ERR, "Could not find an encryption key,"
 595                                     "exiting");
 596                         fatal(f, "Encryption required but key not found, "
 597                             "exiting");
 598                 }
 599         }
 600         /*
 601          * Use krb5_read_message to read the principal stuff.
 602          */
 603         if ((status = krb5_read_message(krb_context, (krb5_pointer)&f,
 604                                         &inbuf)))
 605                 fatal(f, "Error reading krb5 message");
 606 
 607         if (inbuf.length) { /* Forwarding being done, read creds */
 608                 krb5_creds **creds = NULL;
 609 
 610                 if (status = krb5_rd_cred(krb_context, auth_context, &inbuf,
 611                                             &creds, NULL)) {
 612                         if (rcache)
 613                                 (void) krb5_rc_close(krb_context, rcache);
 614                         krb5_free_creds(krb_context, *creds);
 615                         fatal(f, "Can't get forwarded credentials");
 616                 }
 617 
 618                 /* Store the forwarded creds in the ccache */
 619                 if (status = store_forw_creds(krb_context,
 620                                             creds, *ticket, lusername,
 621                                             &ccache)) {
 622                         if (rcache)
 623                                 (void) krb5_rc_close(krb_context, rcache);
 624                         krb5_free_creds(krb_context, *creds);
 625                         fatal(f, "Can't store forwarded credentials");
 626                 }
 627                 krb5_free_creds(krb_context, *creds);
 628         }
 629 
 630         if (rcache)
 631                 (void) krb5_rc_close(krb_context, rcache);
 632 
 633         return (status);
 634 }
 635 
 636 static void
 637 do_krb_login(int f, char *host_addr, char *hostname,
 638             krb5_context krb_context, int encr_flag,
 639             krb5_keytab keytab)
 640 {
 641         krb5_error_code status;
 642         uint_t valid_checksum;
 643         krb5_ticket     *ticket = NULL;
 644         int auth_sys = 0;
 645         int auth_sent = 0;
 646         krb5_principal client = NULL;
 647 
 648         if (getuid())
 649                 fatal(f, "Error authorizing KRB5 connection, "
 650                         "server lacks privilege");
 651 
 652         status = recvauth(f, krb_context, &valid_checksum, &ticket,
 653                         &auth_sys, &client, encr_flag, keytab);
 654         if (status) {
 655                 if (ticket)
 656                         krb5_free_ticket(krb_context, ticket);
 657                 if (status != 255)
 658                         syslog(LOG_ERR,
 659                             "Authentication failed from %s(%s): %s\n",
 660                             host_addr, hostname, error_message(status));
 661                 fatal(f, "Kerberos authentication failed, exiting");
 662         }
 663 
 664         if (auth_sys != KRB5_RECVAUTH_V5) {
 665                 fatal(f, "This server only supports Kerberos V5");
 666         } else {
 667                 /*
 668                  * Authenticated OK, now check authorization.
 669                  */
 670                 if (client && krb5_kuserok(krb_context, client, lusername))
 671                     auth_sent = KRB5_RECVAUTH_V5;
 672         }
 673 
 674         if (auth_sent == KRB5_RECVAUTH_V5 &&
 675             kcmd_protocol == KCMD_OLD_PROTOCOL &&
 676             chksum_flag == CHKSUM_REQUIRED && !valid_checksum) {
 677                 syslog(LOG_ERR, "Client did not supply required checksum, "
 678                     "connection rejected.");
 679                 fatal(f, "Client did not supply required checksum, "
 680                     "connection rejected.");
 681         }
 682 
 683         if (auth_sys != auth_sent) {
 684                 char *msg_fail = NULL;
 685                 int msgsize = 0;
 686 
 687                 if (ticket)
 688                         krb5_free_ticket(krb_context, ticket);
 689 
 690                 if (krusername != NULL) {
 691                         /*
 692                          * msgsize must be enough to hold
 693                          * krusername, lusername and a brief
 694                          * message describing the failure.
 695                          */
 696                         msgsize = strlen(krusername) +
 697                                 strlen(lusername) + 80;
 698                         msg_fail = (char *)malloc(msgsize);
 699                 }
 700                 if (msg_fail == NULL) {
 701                         syslog(LOG_ERR, "User is not authorized to login to "
 702                             "specified account");
 703 
 704                         fatal(f, "User is not authorized to login to "
 705                             "specified account");
 706                 }
 707                 if (auth_sent != 0)
 708                         (void) snprintf(msg_fail, msgsize,
 709                                         "Access denied because of improper "
 710                                         "KRB5 credentials");
 711                 else
 712                         (void) snprintf(msg_fail, msgsize,
 713                                         "User %s is not authorized to login "
 714                                         "to account %s",
 715                                         krusername, lusername);
 716                 syslog(LOG_ERR, "%s", msg_fail);
 717                 fatal(f, msg_fail);
 718         }
 719 }
 720 
 721 /*
 722  * stop_stream
 723  *
 724  * Utility routine to send a CRYPTIOCSTOP ioctl to the
 725  * crypto module(cryptmod).
 726  */
 727 static void
 728 stop_stream(int fd, int dir)
 729 {
 730         struct strioctl  crioc;
 731         uint32_t stopdir = dir;
 732 
 733         crioc.ic_cmd = CRYPTIOCSTOP;
 734         crioc.ic_timout = -1;
 735         crioc.ic_len = sizeof (stopdir);
 736         crioc.ic_dp = (char *)&stopdir;
 737 
 738         if (ioctl(fd, I_STR, &crioc))
 739                 syslog(LOG_ERR, "Error sending CRYPTIOCSTOP ioctl: %m");
 740 }
 741 
 742 /*
 743  * start_stream
 744  *
 745  * Utility routine to send a CRYPTIOCSTART ioctl to the
 746  * crypto module(cryptmod).  This routine may contain optional
 747  * payload data that the cryptmod will interpret as bytes that
 748  * need to be decrypted and sent back up to the application
 749  * via the data stream.
 750  */
 751 static void
 752 start_stream(int fd, int dir)
 753 {
 754         struct strioctl crioc;
 755         uint32_t iocval;
 756         size_t datalen = 0;
 757         char *data = NULL;
 758 
 759         if (dir == CRYPT_DECRYPT) {
 760                 iocval = CRYPTIOCSTARTDEC;
 761 
 762                 /* Look for data not yet processed */
 763                 if (ioctl(fd, I_NREAD, &datalen) < 0) {
 764                         syslog(LOG_ERR, "I_NREAD returned error %m");
 765                         datalen = 0;
 766                 } else {
 767                         if (datalen > 0) {
 768                                 data = malloc(datalen);
 769                                 if (data != NULL) {
 770                                         int nbytes = read(fd, data, datalen);
 771                                         datalen = nbytes;
 772                                 } else {
 773                                         syslog(LOG_ERR,
 774                                                 "malloc error (%d bytes)",
 775                                                 datalen);
 776                                         datalen = 0;
 777                                 }
 778                         } else {
 779                                 datalen = 0;
 780                         }
 781                 }
 782         } else {
 783                 iocval = CRYPTIOCSTARTENC;
 784         }
 785 
 786         crioc.ic_cmd = iocval;
 787         crioc.ic_timout = -1;
 788         crioc.ic_len = datalen;
 789         crioc.ic_dp = data;
 790 
 791         if (ioctl(fd, I_STR, &crioc))
 792                 syslog(LOG_ERR, "Error sending CRYPTIOCSTART ioctl: %m");
 793 
 794         if (data != NULL)
 795                 free(data);
 796 }
 797 
 798 static int
 799 configure_stream(int fd, krb5_keyblock *skey, int dir, krb5_data *ivec,
 800                 uint_t iv_usage)
 801 {
 802         struct cr_info_t setup_info;
 803         struct strioctl crioc;
 804         int retval = 0;
 805 
 806         switch (skey->enctype) {
 807         case ENCTYPE_DES_CBC_CRC:
 808                 setup_info.crypto_method = CRYPT_METHOD_DES_CBC_CRC;
 809                 break;
 810         case ENCTYPE_DES_CBC_MD5:
 811                 setup_info.crypto_method = CRYPT_METHOD_DES_CBC_MD5;
 812                 break;
 813         case ENCTYPE_DES_CBC_RAW:
 814                 setup_info.crypto_method = CRYPT_METHOD_DES_CBC_NULL;
 815                 break;
 816         case ENCTYPE_DES3_CBC_SHA1:
 817                 setup_info.crypto_method = CRYPT_METHOD_DES3_CBC_SHA1;
 818                 break;
 819         case ENCTYPE_ARCFOUR_HMAC:
 820                 setup_info.crypto_method = CRYPT_METHOD_ARCFOUR_HMAC_MD5;
 821                 break;
 822         case ENCTYPE_ARCFOUR_HMAC_EXP:
 823                 setup_info.crypto_method = CRYPT_METHOD_ARCFOUR_HMAC_MD5_EXP;
 824                 break;
 825         case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
 826                 setup_info.crypto_method = CRYPT_METHOD_AES128;
 827                 break;
 828         case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
 829                 setup_info.crypto_method = CRYPT_METHOD_AES256;
 830                 break;
 831         default:
 832                 syslog(LOG_ERR, "Enctype in kerberos session key "
 833                     "is not supported by crypto module(%d)",
 834                     skey->enctype);
 835                 return (-1);
 836         }
 837         if (ivec == NULL || ivec->length == 0) {
 838                 (void) memset(&setup_info.ivec, 0, sizeof (setup_info.ivec));
 839 
 840                 if (skey->enctype != ENCTYPE_ARCFOUR_HMAC &&
 841                     skey->enctype != ENCTYPE_ARCFOUR_HMAC_EXP)
 842                         /* Kerberos IVs are 8 bytes long for DES keys */
 843                         setup_info.iveclen = KRB5_MIT_DES_KEYSIZE;
 844                 else
 845                         setup_info.iveclen = 0;
 846         } else {
 847                 (void) memcpy(&setup_info.ivec, ivec->data, ivec->length);
 848                 setup_info.iveclen = ivec->length;
 849         }
 850 
 851         setup_info.ivec_usage = iv_usage;
 852         (void) memcpy(&setup_info.key, skey->contents, skey->length);
 853 
 854         setup_info.keylen = skey->length;
 855         setup_info.direction_mask = dir;
 856         /*
 857          * R* commands get special handling by crypto module -
 858          * 4 byte length field is used before each encrypted block
 859          * of data.
 860          */
 861         setup_info.option_mask = (kcmd_protocol == KCMD_OLD_PROTOCOL ?
 862                                 CRYPTOPT_RCMD_MODE_V1 :
 863                                 CRYPTOPT_RCMD_MODE_V2);
 864 
 865         crioc.ic_cmd = CRYPTIOCSETUP;
 866         crioc.ic_timout = -1;
 867         crioc.ic_len = sizeof (setup_info);
 868         crioc.ic_dp = (char *)&setup_info;
 869 
 870         if (ioctl(fd, I_STR, &crioc)) {
 871                 syslog(LOG_ERR, "Error sending CRYPTIOCSETUP ioctl: %m");
 872                 retval = -1;
 873         }
 874         return (retval);
 875 }
 876 
 877 static krb5_error_code
 878 krb5_compat_recvauth(krb5_context context,
 879                     krb5_auth_context *auth_context,
 880                     krb5_pointer fdp,   /* IN */
 881                     krb5_principal server,      /* IN */
 882                     krb5_int32 flags,   /* IN */
 883                     krb5_keytab keytab, /* IN */
 884                     krb5_ticket **ticket, /* OUT */
 885                     krb5_int32 *auth_sys, /* OUT */
 886                     krb5_data *version)   /* OUT */
 887 {
 888         krb5_int32 vlen;
 889         char    *buf;
 890         int     len, length;
 891         krb5_int32      retval;
 892         int             fd = *((int *)fdp);
 893 
 894         if ((retval = krb5_net_read(context, fd, (char *)&vlen, 4)) != 4)
 895                 return ((retval < 0) ? errno : ECONNABORTED);
 896 
 897         /*
 898          * Assume that we're talking to a V5 recvauth; read in the
 899          * the version string, and make sure it matches.
 900          */
 901         len = (int)ntohl(vlen);
 902 
 903         if (len < 0 || len > 255)
 904                 return (KRB5_SENDAUTH_BADAUTHVERS);
 905 
 906         buf = malloc(len);
 907         if (buf == NULL)
 908                 return (ENOMEM);
 909 
 910         length = krb5_net_read(context, fd, buf, len);
 911         if (len != length) {
 912                 krb5_xfree(buf);
 913                 return ((len < 0) ? errno : ECONNABORTED);
 914         }
 915 
 916         if (strcmp(buf, KRB_V5_SENDAUTH_VERS) != 0) {
 917                 krb5_xfree(buf);
 918                 return (KRB5_SENDAUTH_BADAUTHVERS);
 919         }
 920         krb5_xfree(buf);
 921 
 922         *auth_sys = KRB5_RECVAUTH_V5;
 923 
 924         retval = krb5_recvauth_version(context, auth_context, fdp,
 925                                     server, flags | KRB5_RECVAUTH_SKIP_VERSION,
 926                                     keytab, ticket, version);
 927 
 928         return (retval);
 929 }
 930 
 931 
 932 static void
 933 doit(int f,
 934         struct sockaddr_storage *fromp,
 935         krb5_context krb_context,
 936         int encr_flag,
 937         krb5_keytab keytab)
 938 {
 939         int p, t, on = 1;
 940         char c;
 941         char abuf[INET6_ADDRSTRLEN];
 942         struct sockaddr_in *sin;
 943         struct sockaddr_in6 *sin6;
 944         int fromplen;
 945         in_port_t port;
 946         struct termios tp;
 947         boolean_t bad_port;
 948         boolean_t no_name;
 949         char rhost_addra[INET6_ADDRSTRLEN];
 950 
 951         if (!(rlbuf = malloc(BUFSIZ))) {
 952                 syslog(LOG_ERR, "rlbuf malloc failed\n");
 953                 exit(EXIT_FAILURE);
 954         }
 955         (void) alarm(60);
 956         if (read(f, &c, 1) != 1 || c != 0) {
 957                 syslog(LOG_ERR, "failed to receive protocol zero byte\n");
 958                 exit(EXIT_FAILURE);
 959         }
 960         (void) alarm(0);
 961         if (fromp->ss_family == AF_INET) {
 962                 sin = (struct sockaddr_in *)fromp;
 963                 port = sin->sin_port = ntohs((ushort_t)sin->sin_port);
 964                 fromplen = sizeof (struct sockaddr_in);
 965 
 966                 if (!inet_ntop(AF_INET, &sin->sin_addr,
 967                             rhost_addra, sizeof (rhost_addra)))
 968                         goto badconversion;
 969         } else if (fromp->ss_family == AF_INET6) {
 970                 sin6 = (struct sockaddr_in6 *)fromp;
 971                 port = sin6->sin6_port = ntohs((ushort_t)sin6->sin6_port);
 972                 fromplen = sizeof (struct sockaddr_in6);
 973 
 974                 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
 975                         struct in_addr ipv4_addr;
 976 
 977                         IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
 978                                             &ipv4_addr);
 979                         if (!inet_ntop(AF_INET, &ipv4_addr, rhost_addra,
 980                                     sizeof (rhost_addra)))
 981                                 goto badconversion;
 982                 } else {
 983                         if (!inet_ntop(AF_INET6, &sin6->sin6_addr,
 984                                     rhost_addra, sizeof (rhost_addra)))
 985                                 goto badconversion;
 986                 }
 987         } else {
 988                 syslog(LOG_ERR, "unknown address family %d\n",
 989                     fromp->ss_family);
 990                 fatal(f, "Permission denied");
 991         }
 992 
 993         /*
 994          * Allow connections only from the "ephemeral" reserved
 995          * ports(ports 512 - 1023) by checking the remote port
 996          * because other utilities(e.g. in.ftpd) can be used to
 997          * allow a unprivileged user to originate a connection
 998          * from a privileged port and provide untrustworthy
 999          * authentication.
1000          */
1001         bad_port = (use_auth != KRB5_RECVAUTH_V5 &&
1002                     (port >= (in_port_t)IPPORT_RESERVED) ||
1003                     (port < (in_port_t)(IPPORT_RESERVED/2)));
1004         no_name = getnameinfo((const struct sockaddr *) fromp,
1005                             fromplen, hostname, sizeof (hostname),
1006                             NULL, 0, 0) != 0;
1007 
1008         if (no_name || bad_port) {
1009                 (void) strlcpy(abuf, rhost_addra, sizeof (abuf));
1010                 /* If no host name, use IP address for name later on. */
1011                 if (no_name)
1012                         (void) strlcpy(hostname, abuf, sizeof (hostname));
1013         }
1014 
1015         if (!no_name) {
1016                 /*
1017                  * Even if getnameinfo() succeeded, we still have to check
1018                  * for spoofing.
1019                  */
1020                 check_address("rlogind", fromp, sin, sin6, rhost_addra,
1021                     hostname, sizeof (hostname));
1022         }
1023 
1024         if (bad_port) {
1025                 if (no_name)
1026                         syslog(LOG_NOTICE,
1027                             "connection from %s - bad port\n",
1028                             abuf);
1029                 else
1030                         syslog(LOG_NOTICE,
1031                             "connection from %s(%s) - bad port\n",
1032                             hostname, abuf);
1033                 fatal(f, "Permission denied");
1034         }
1035 
1036         if (use_auth == KRB5_RECVAUTH_V5) {
1037                 do_krb_login(f, rhost_addra, hostname,
1038                             krb_context, encr_flag, keytab);
1039                 if (krusername != NULL && strlen(krusername)) {
1040                         /*
1041                          * Kerberos Authentication succeeded,
1042                          * so set the proper program name to use
1043                          * with pam (important during 'cleanup'
1044                          * routine later).
1045                          */
1046                         pam_prog_name = KRB5_PROG_NAME;
1047                 }
1048         }
1049 
1050         if (write(f, "", 1) != 1) {
1051                 syslog(LOG_NOTICE,
1052                     "send of the zero byte(to %s) failed:"
1053                     " cannot start data transfer mode\n",
1054                     (no_name ? abuf : hostname));
1055                 exit(EXIT_FAILURE);
1056         }
1057         if ((p = open("/dev/ptmx", O_RDWR)) == -1)
1058                 fatalperror(f, "cannot open /dev/ptmx");
1059         if (grantpt(p) == -1)
1060                 fatal(f, "could not grant slave pty");
1061         if (unlockpt(p) == -1)
1062                 fatal(f, "could not unlock slave pty");
1063         if ((line = ptsname(p)) == NULL)
1064                 fatal(f, "could not enable slave pty");
1065         if ((t = open(line, O_RDWR)) == -1)
1066                 fatal(f, "could not open slave pty");
1067         if (ioctl(t, I_PUSH, "ptem") == -1)
1068                 fatalperror(f, "ioctl I_PUSH ptem");
1069         if (ioctl(t, I_PUSH, "ldterm") == -1)
1070                 fatalperror(f, "ioctl I_PUSH ldterm");
1071         if (ioctl(t, I_PUSH, "ttcompat") == -1)
1072                 fatalperror(f, "ioctl I_PUSH ttcompat");
1073         /*
1074          * POP the sockmod and push the rlmod module.
1075          *
1076          * Note that sockmod has to be removed since readstream assumes
1077          * a "raw" TPI endpoint(e.g. it uses getmsg).
1078          */
1079         if (removemod(f, "sockmod") < 0)
1080                 fatalperror(f, "couldn't remove sockmod");
1081 
1082         if (encr_flag) {
1083                 if (ioctl(f, I_PUSH, "cryptmod") < 0)
1084                     fatalperror(f, "ioctl I_PUSH rlmod");
1085 
1086         }
1087 
1088         if (ioctl(f, I_PUSH, "rlmod") < 0)
1089                 fatalperror(f, "ioctl I_PUSH rlmod");
1090 
1091         if (encr_flag) {
1092                 /*
1093                  * Make sure rlmod will pass unrecognized IOCTLs to cryptmod
1094                  */
1095                 uchar_t passthru = 1;
1096                 struct strioctl rlmodctl;
1097 
1098                 rlmodctl.ic_cmd = CRYPTPASSTHRU;
1099                 rlmodctl.ic_timout = -1;
1100                 rlmodctl.ic_len = sizeof (uchar_t);
1101                 rlmodctl.ic_dp = (char *)&passthru;
1102 
1103                 if (ioctl(f, I_STR, &rlmodctl) < 0)
1104                         fatal(f, "ioctl CRYPTPASSTHRU failed\n");
1105         }
1106 
1107         /*
1108          * readstream will do a getmsg till it receives
1109          * M_PROTO type T_DATA_REQ from rloginmodopen()
1110          * indicating all data on the stream prior to pushing rlmod has
1111          * been drained at the stream head.
1112          */
1113         if ((nsize = readstream(f, rlbuf, BUFSIZ)) < 0)
1114                 fatalperror(f, "readstream failed");
1115         /*
1116          * Make sure the pty doesn't modify the strings passed
1117          * to login as part of the "rlogin protocol."  The login
1118          * program should set these flags to apropriate values
1119          * after it has read the strings.
1120          */
1121         if (ioctl(t, TCGETS, &tp) == -1)
1122                 fatalperror(f, "ioctl TCGETS");
1123         tp.c_lflag &= ~(ECHO|ICANON);
1124         tp.c_oflag &= ~(XTABS|OCRNL);
1125         tp.c_iflag &= ~(IGNPAR|ICRNL);
1126         if (ioctl(t, TCSETS, &tp) == -1)
1127                 fatalperror(f, "ioctl TCSETS");
1128 
1129         /*
1130          * System V ptys allow the TIOC{SG}WINSZ ioctl to be
1131          * issued on the master side of the pty.  Luckily, that's
1132          * the only tty ioctl we need to do do, so we can close the
1133          * slave side in the parent process after the fork.
1134          */
1135         (void) ioctl(p, TIOCSWINSZ, &win);
1136 
1137         pid = fork();
1138         if (pid < 0)
1139                 fatalperror(f, "fork");
1140         if (pid == 0) {
1141                 int tt;
1142                 struct utmpx ut;
1143 
1144                 /* System V login expects a utmp entry to already be there */
1145                 (void) memset(&ut, 0, sizeof (ut));
1146                 (void) strncpy(ut.ut_user, ".rlogin", sizeof (ut.ut_user));
1147                 (void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
1148                 ut.ut_pid = getpid();
1149                 ut.ut_id[0] = 'r';
1150                 ut.ut_id[1] = (char)SC_WILDC;
1151                 ut.ut_id[2] = (char)SC_WILDC;
1152                 ut.ut_id[3] = (char)SC_WILDC;
1153                 ut.ut_type = LOGIN_PROCESS;
1154                 ut.ut_exit.e_termination = 0;
1155                 ut.ut_exit.e_exit = 0;
1156                 (void) time(&ut.ut_tv.tv_sec);
1157                 if (makeutx(&ut) == NULL)
1158                         syslog(LOG_INFO, "in.rlogind:\tmakeutx failed");
1159 
1160                 /* controlling tty */
1161                 if (setsid() == -1)
1162                         fatalperror(f, "setsid");
1163                 if ((tt = open(line, O_RDWR)) == -1)
1164                         fatalperror(f, "could not re-open slave pty");
1165 
1166                 if (close(p) == -1)
1167                         fatalperror(f, "error closing pty master");
1168                 if (close(t) == -1)
1169                         fatalperror(f, "error closing pty slave"
1170                                     " opened before session established");
1171                 /*
1172                  * If this fails we may or may not be able to output an
1173                  * error message.
1174                  */
1175                 if (close(f) == -1)
1176                         fatalperror(f, "error closing deamon stdout");
1177                 if (dup2(tt, STDIN_FILENO) == -1 ||
1178                     dup2(tt, STDOUT_FILENO) == -1 ||
1179                     dup2(tt, STDERR_FILENO) == -1)
1180                         exit(EXIT_FAILURE);     /* Disaster! No stderr! */
1181 
1182                 (void) close(tt);
1183 
1184                 if (use_auth == KRB5_RECVAUTH_V5 &&
1185                     krusername != NULL && strlen(krusername)) {
1186                         (void) execl(LOGIN_PROGRAM, "login",
1187                                     "-d", line,
1188                                     "-r", hostname,
1189                                     "-u", krusername, /* KRB5 principal name */
1190                                     "-s", pam_prog_name,
1191                                     "-t", term, /* Remote Terminal */
1192                                     "-U", rusername,    /* Remote User */
1193                                     "-R", KRB5_REPOSITORY_NAME,
1194                                     lusername,  /* local user */
1195                                     NULL);
1196                 } else {
1197                         (void) execl(LOGIN_PROGRAM, "login",
1198                                 "-d", line,
1199                                 "-r", hostname,
1200                                 NULL);
1201                 }
1202 
1203                 fatalperror(STDERR_FILENO, "/bin/login");
1204                 /*NOTREACHED*/
1205         }
1206         (void) close(t);
1207         (void) ioctl(f, FIONBIO, &on);
1208         (void) ioctl(p, FIONBIO, &on);
1209 
1210         /*
1211          * Must ignore SIGTTOU, otherwise we'll stop
1212          * when we try and set slave pty's window shape
1213          * (our controlling tty is the master pty).
1214          * Likewise, we don't want any of the tty-generated
1215          * signals from chars passing through.
1216          */
1217         (void) sigset(SIGTSTP, SIG_IGN);
1218         (void) sigset(SIGINT, SIG_IGN);
1219         (void) sigset(SIGQUIT, SIG_IGN);
1220         (void) sigset(SIGTTOU, SIG_IGN);
1221         (void) sigset(SIGTTIN, SIG_IGN);
1222         (void) sigset(SIGCHLD, cleanup);
1223         (void) setpgrp();
1224 
1225         if (encr_flag) {
1226                 krb5_data ivec, *ivptr;
1227                 uint_t ivec_usage;
1228                 stop_stream(f, CRYPT_ENCRYPT|CRYPT_DECRYPT);
1229 
1230                 /*
1231                  * Configure the STREAMS crypto module.  For now,
1232                  * don't use any IV parameter.  KCMDV0.2 support
1233                  * will require the use of Initialization Vectors
1234                  * for both encrypt and decrypt modes.
1235                  */
1236                 if (kcmd_protocol == KCMD_OLD_PROTOCOL) {
1237                         if (session_key->enctype == ENCTYPE_DES_CBC_CRC) {
1238                                 /*
1239                                  * This is gross but necessary for MIT compat.
1240                                  */
1241                                 ivec.length = session_key->length;
1242                                 ivec.data = (char *)session_key->contents;
1243                                 ivec_usage = IVEC_REUSE;
1244                                 ivptr = &ivec;
1245                         } else {
1246                                 ivptr = NULL; /* defaults to all 0's */
1247                                 ivec_usage = IVEC_NEVER;
1248                         }
1249                         /*
1250                          * configure both sides of stream together
1251                          * since they share the same IV.
1252                          * This is what makes the OLD KCMD protocol
1253                          * less secure than the newer one - Bad ivecs.
1254                          */
1255                         if (configure_stream(f, session_key,
1256                                 CRYPT_ENCRYPT|CRYPT_DECRYPT,
1257                                 ivptr, ivec_usage) != 0)
1258                                 fatal(f, "Cannot initialize encryption -"
1259                                         " exiting.\n");
1260                 } else {
1261                         size_t blocksize;
1262                         if (session_key->enctype == ENCTYPE_ARCFOUR_HMAC ||
1263                             session_key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
1264                                 if (configure_stream(f, session_key,
1265                                         CRYPT_ENCRYPT|CRYPT_DECRYPT,
1266                                         NULL, IVEC_NEVER) != 0)
1267                                         fatal(f,
1268                                         "Cannot initialize encryption -"
1269                                         " exiting.\n");
1270                                 goto startcrypto;
1271                         }
1272                         if (krb5_c_block_size(krb_context,
1273                                             session_key->enctype,
1274                                             &blocksize)) {
1275                                 syslog(LOG_ERR, "Cannot determine blocksize "
1276                                     "for encryption type %d",
1277                                     session_key->enctype);
1278                                 fatal(f, "Cannot determine blocksize "
1279                                     "for encryption - exiting.\n");
1280                         }
1281                         ivec.data = (char *)malloc(blocksize);
1282                         ivec.length = blocksize;
1283                         if (ivec.data == NULL)
1284                                 fatal(f, "memory error - exiting\n");
1285                         /*
1286                          * Following MIT convention -
1287                          *   encrypt IV = 0x01 x blocksize
1288                          *   decrypt IV = 0x00 x blocksize
1289                          *   ivec_usage = IVEC_ONETIME
1290                          *
1291                          * configure_stream separately for encrypt and
1292                          * decrypt because there are 2 different IVs.
1293                          *
1294                          * AES uses 0's for IV.
1295                          */
1296                         if (session_key->enctype ==
1297                                 ENCTYPE_AES128_CTS_HMAC_SHA1_96 ||
1298                             session_key->enctype ==
1299                                 ENCTYPE_AES256_CTS_HMAC_SHA1_96)
1300                                 (void) memset(ivec.data, 0x00, blocksize);
1301                         else
1302                                 (void) memset(ivec.data, 0x01, blocksize);
1303                         if (configure_stream(f, session_key, CRYPT_ENCRYPT,
1304                                 &ivec, IVEC_ONETIME) != 0)
1305                                 fatal(f, "Cannot initialize encryption -"
1306                                         " exiting.\n");
1307                         (void) memset(ivec.data, 0x00, blocksize);
1308                         if (configure_stream(f, session_key, CRYPT_DECRYPT,
1309                                 &ivec, IVEC_ONETIME) != 0)
1310                                 fatal(f, "Cannot initialize encryption -"
1311                                         " exiting.\n");
1312 
1313                         (void) free(ivec.data);
1314                 }
1315 startcrypto:
1316                 start_stream(f, CRYPT_ENCRYPT);
1317                 start_stream(f, CRYPT_DECRYPT);
1318         }
1319         protocol(f, p, encr_flag);
1320         cleanup(0);
1321         /*NOTREACHED*/
1322 
1323 badconversion:
1324         fatalperror(f, "address conversion");
1325         /*NOTREACHED*/
1326 }
1327 
1328 /*
1329  * rlogin "protocol" machine.
1330  */
1331 static void
1332 protocol(int f, int p, int encr_flag)
1333 {
1334         struct  stat    buf;
1335         struct  protocol_arg    rloginp;
1336         struct  strioctl        rloginmod;
1337         int     ptmfd;  /* fd of logindmux coneected to ptmx */
1338         int     netfd;  /* fd of logindmux connected to netf */
1339         static uchar_t  oobdata[] = {TIOCPKT_WINDOW};
1340 
1341         /* indicate new rlogin */
1342         if (send_oob(f, oobdata, 1) < 0)
1343                 fatalperror(f, "send_oob");
1344         /*
1345          * We cannot send the SECURE_MSG until after the
1346          * client has been signaled with the oobdata (above).
1347          */
1348         if (encr_flag) {
1349                 if (write(f, SECURE_MSG, strlen(SECURE_MSG)) < 0)
1350                         fatalperror(f, "Error writing SECURE message");
1351         }
1352 
1353         /*
1354          * Open logindmux driver and link netf and ptmx
1355          * underneath logindmux.
1356          */
1357         if ((ptmfd = open("/dev/logindmux", O_RDWR)) == -1)
1358                 fatalperror(f, "open /dev/logindmux");
1359 
1360         if ((netfd = open("/dev/logindmux", O_RDWR)) == -1)
1361                 fatalperror(f, "open /dev/logindmux");
1362 
1363         if (ioctl(ptmfd, I_LINK, p) < 0)
1364                 fatal(f, "ioctl I_LINK of /dev/ptmx failed\n");
1365 
1366         if (ioctl(netfd, I_LINK, f) < 0)
1367                 fatal(f, "ioctl I_LINK of tcp connection failed\n");
1368 
1369         /*
1370          * Figure out the device number of the ptm's mux fd, and pass that
1371          * to the net's mux.
1372          */
1373         if (fstat(ptmfd, &buf) < 0)
1374                 fatalperror(f, "cannot determine device number"
1375                     " of pty side of /dev/logindmux");
1376         rloginp.dev = buf.st_rdev;
1377         rloginp.flag = 0;
1378 
1379         rloginmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
1380         rloginmod.ic_timout = -1;
1381         rloginmod.ic_len = sizeof (struct protocol_arg);
1382         rloginmod.ic_dp = (char *)&rloginp;
1383 
1384         if (ioctl(netfd, I_STR, &rloginmod) < 0)
1385                 fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of netfd failed\n");
1386 
1387         /*
1388          * Figure out the device number of the net's mux fd, and pass that
1389          * to the ptm's mux.
1390          */
1391         if (fstat(netfd, &buf))
1392                 fatalperror(f, "cannot determine device number"
1393                     " of network side of /dev/logindmux");
1394         rloginp.dev = buf.st_rdev;
1395         rloginp.flag = 1;
1396 
1397         rloginmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
1398         rloginmod.ic_timout = -1;
1399         rloginmod.ic_len = sizeof (struct protocol_arg);
1400         rloginmod.ic_dp = (char *)&rloginp;
1401 
1402         if (ioctl(ptmfd, I_STR, &rloginmod) < 0)
1403                 fatal(netfd, "ioctl LOGDMXZ_IOC_QEXCHANGE of ptmfd failed\n");
1404         /*
1405          * Send an ioctl type RL_IOC_ENABLE to reenable the
1406          * message queue and reinsert the data read from streamhead
1407          * at the time of pushing rloginmod module.
1408          * We need to send this ioctl even if no data was read earlier
1409          * since we need to reenable the message queue of rloginmod module.
1410          */
1411         rloginmod.ic_cmd = RL_IOC_ENABLE;
1412         rloginmod.ic_timout = -1;
1413         if (nsize) {
1414                 rloginmod.ic_len = nsize;
1415                 rloginmod.ic_dp = rlbuf;
1416         } else {
1417                 rloginmod.ic_len = 0;
1418                 rloginmod.ic_dp = NULL;
1419         }
1420 
1421         if (ioctl(netfd, I_STR, &rloginmod) < 0)
1422                 fatal(netfd, "ioctl RL_IOC_ENABLE of netfd failed\n");
1423 
1424         /*
1425          * User level daemon now pauses till the shell exits.
1426          */
1427         (void) pause();
1428 }
1429 
1430 /* This is a signal handler, hence the dummy argument */
1431 /*ARGSUSED*/
1432 static void
1433 cleanup(int dummy)
1434 {
1435         rmut();
1436         exit(EXIT_FAILURE);
1437         /*NOTREACHED*/
1438 }
1439 
1440 /*
1441  * TPI style replacement for socket send() primitive, so we don't require
1442  * sockmod to be on the stream.
1443  */
1444 static int
1445 send_oob(int fd, void *ptr, size_t count)
1446 {
1447         struct T_exdata_req exd_req;
1448         struct strbuf hdr, dat;
1449         int ret;
1450 
1451         exd_req.PRIM_type = T_EXDATA_REQ;
1452         exd_req.MORE_flag = 0;
1453 
1454         hdr.buf = (char *)&exd_req;
1455         hdr.len = sizeof (exd_req);
1456 
1457         dat.buf = ptr;
1458         dat.len = count;
1459 
1460         ret = putmsg(fd, &hdr, &dat, 0);
1461         if (ret == 0)
1462                 ret = count;
1463         return (ret);
1464 }
1465 
1466 static void
1467 fatal(int fd, const char *msg)
1468 {
1469         char *bufp;
1470         size_t len = strlen(msg) + 16;          /* enough for our wrapper */
1471 
1472         bufp = alloca(len);
1473         /* ASCII 001 is the error indicator */
1474         len = snprintf(bufp, len, "\01rlogind: %s.\r\n", msg);
1475         (void) write(fd, bufp, len);
1476         exit(EXIT_FAILURE);
1477         /*NOTREACHED*/
1478 }
1479 
1480 /*PRINTFLIKE2*/
1481 static void
1482 fatalperror(int fd, const char *msg)
1483 {
1484         char *bufp;
1485         const char *errstr;
1486         int save_errno = errno;
1487         size_t len = strlen(msg);
1488 
1489         if ((errstr = strerror(save_errno))) {
1490                 len += strlen(errstr) + 3;      /* 3 for ": " and \0 below */
1491                 bufp = alloca(len);
1492                 (void) snprintf(bufp, len, "%s: %s", msg, errstr);
1493         } else {
1494                 const char fmt[] = "%s: Error %d";
1495 
1496                 /* -4 for %s & %d. "*8/3" is bytes->decimal, pessimistically */
1497                 len += sizeof (fmt) -4 + (sizeof (save_errno) *8 /3);
1498                 bufp = alloca(len);
1499                 (void) snprintf(bufp, len, fmt, msg, save_errno);
1500         }
1501         fatal(fd, bufp);
1502         /*NOTREACHED*/
1503 }
1504 
1505 static void
1506 rmut(void)
1507 {
1508         pam_handle_t *pamh;
1509         struct utmpx *up;
1510         char user[sizeof (up->ut_user) + 1];
1511         char ttyn[sizeof (up->ut_line) + 1];
1512         char rhost[sizeof (up->ut_host) + 1];
1513 
1514         /* while cleaning up dont allow disruption */
1515         (void) sigset(SIGCHLD, SIG_IGN);
1516 
1517         setutxent();
1518         while (up = getutxent()) {
1519                 if (up->ut_pid == pid) {
1520                         if (up->ut_type == DEAD_PROCESS)
1521                                 break;          /* Cleaned up elsewhere. */
1522 
1523                         /*
1524                          * call pam_close_session if login changed
1525                          * the utmpx user entry from type LOGIN_PROCESS
1526                          * to type USER_PROCESS, which happens
1527                          * after pam_open_session is called.
1528                          */
1529                         if (up->ut_type == USER_PROCESS) {
1530                                 (void) strlcpy(user, up->ut_user,
1531                                             sizeof (user));
1532                                 (void) strlcpy(ttyn, up->ut_line,
1533                                             sizeof (ttyn));
1534                                 (void) strlcpy(rhost, up->ut_host,
1535                                             sizeof (rhost));
1536 
1537                                 /*
1538                                  * Use the same pam_prog_name that
1539                                  * 'login' used.
1540                                  */
1541                                 if ((pam_start(pam_prog_name, user,  NULL,
1542                                             &pamh))
1543                                     == PAM_SUCCESS) {
1544                                         (void) pam_set_item(pamh, PAM_TTY,
1545                                                             ttyn);
1546                                         (void) pam_set_item(pamh, PAM_RHOST,
1547                                                             rhost);
1548                                         (void) pam_close_session(pamh, 0);
1549                                         (void) pam_end(pamh, PAM_SUCCESS);
1550                                 }
1551                         }
1552 
1553                         up->ut_type = DEAD_PROCESS;
1554                         up->ut_exit.e_termination = WTERMSIG(0);
1555                         up->ut_exit.e_exit = WEXITSTATUS(0);
1556                         (void) time(&up->ut_tv.tv_sec);
1557 
1558                         if (modutx(up) == NULL) {
1559                                 /*
1560                                  * Since modutx failed we'll
1561                                  * write out the new entry
1562                                  * ourselves.
1563                                  */
1564                                 (void) pututxline(up);
1565                                 updwtmpx("wtmpx", up);
1566                         }
1567                         break;
1568                 }
1569         }
1570 
1571         endutxent();
1572 
1573         (void) sigset(SIGCHLD, cleanup);
1574 }
1575 
1576 static int
1577 readstream(int fd, char *buf, int size)
1578 {
1579         struct strbuf ctlbuf, datbuf;
1580         union T_primitives tpi;
1581         int     nbytes = 0;
1582         int     ret = 0;
1583         int     flags = 0;
1584         int     bufsize = size;
1585         int     nread;
1586 
1587         (void) memset(&ctlbuf, 0, sizeof (ctlbuf));
1588         (void) memset(&datbuf, 0, sizeof (datbuf));
1589 
1590         ctlbuf.buf = (char *)&tpi;
1591         ctlbuf.maxlen = sizeof (tpi);
1592         datbuf.buf = buf;
1593         datbuf.maxlen = size;
1594 
1595         for (;;) {
1596                 if (ioctl(fd, I_NREAD, &nread) < 0) {
1597                         syslog(LOG_ERR, "I_NREAD returned error %m");
1598                         return (-1);
1599                 }
1600                 if (nread + nbytes > bufsize) {
1601                         buf = (char *)realloc(buf, (unsigned)(bufsize + nread));
1602                         if (buf == NULL) {
1603                                 syslog(LOG_WARNING,
1604                                     "buffer allocation failed\n");
1605                                 return (-1);
1606                         }
1607                         bufsize += nread;
1608                         rlbuf = buf;
1609                         datbuf.buf = buf + nbytes;
1610                 }
1611                 datbuf.maxlen = bufsize - nbytes;
1612                 ret = getmsg(fd, &ctlbuf, &datbuf, &flags);
1613                 if (ret < 0) {
1614                         syslog(LOG_ERR, "getmsg failed error %m");
1615                         return (-1);
1616                 }
1617                 if ((ctlbuf.len == 0) && (datbuf.len == 0)) {
1618                         /*
1619                          * getmsg() returned no data - this indicates
1620                          * that the connection is closing down.
1621                          */
1622                         cleanup(0);
1623                 }
1624                 if (ctlbuf.len <= 0) {
1625                         nbytes += datbuf.len;
1626                         datbuf.buf += datbuf.len;
1627                         continue;
1628                 }
1629                 if (tpi.type == T_DATA_REQ) {
1630                         return (nbytes);
1631                 }
1632                 if ((tpi.type == T_ORDREL_IND) || (tpi.type == T_DISCON_IND))
1633                         cleanup(0);
1634         }
1635 }
1636 
1637 /*
1638  * Verify that the named module is at the top of the stream
1639  * and then pop it off.
1640  */
1641 static int
1642 removemod(int f, char *modname)
1643 {
1644         char topmodname[BUFSIZ];
1645 
1646         if (ioctl(f, I_LOOK, topmodname) < 0)
1647                 return (-1);
1648         if (strcmp(modname, topmodname) != 0) {
1649                 errno = ENXIO;
1650                 return (-1);
1651         }
1652         if (ioctl(f, I_POP, 0) < 0)
1653                 return (-1);
1654         return (0);
1655 }