1 /*
   2  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 /*
   7  * Copyright (c) 1983 Regents of the University of California.
   8  * All rights reserved.
   9  *
  10  * Redistribution and use in source and binary forms are permitted
  11  * provided that the above copyright notice and this paragraph are
  12  * duplicated in all such forms and that any documentation,
  13  * advertising materials, and other materials related to such
  14  * distribution and use acknowledge that the software was developed
  15  * by the University of California, Berkeley.  The name of the
  16  * University may not be used to endorse or promote products derived
  17  * from this software without specific prior written permission.
  18  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  19  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  20  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  21  */
  22 
  23 /*
  24  * Copyright (c) 2018, Joyent, Inc.
  25  */
  26 
  27 /* derived from @(#)rcmd.c      5.17 (Berkeley) 6/27/88 */
  28 
  29 #include <unistd.h>
  30 #include <stdlib.h>
  31 #include <stdio.h>
  32 #include <ctype.h>
  33 #include <string.h>
  34 #include <pwd.h>
  35 #include <sys/param.h>
  36 #include <sys/types.h>
  37 #include <fcntl.h>
  38 
  39 #include <signal.h>
  40 #include <sys/file.h>
  41 #include <sys/socket.h>
  42 #include <sys/stat.h>
  43 
  44 #include <netinet/in.h>
  45 #include <arpa/inet.h>
  46 #include <netdb.h>
  47 #include <locale.h>
  48 #include <syslog.h>
  49 
  50 #include <errno.h>
  51 #include <com_err.h>
  52 #include <k5-int.h>
  53 #include <kcmd.h>
  54 
  55 static char *default_service = "host";
  56 
  57 #define KCMD_BUFSIZ     102400
  58 #define KCMD8_BUFSIZ    (4096 - 256)
  59 /*
  60  * For compatibility with earlier versions of Solaris and other OS
  61  * (kerborized rsh uses 4KB of RSH_BUFSIZE)- 256 to make sure
  62  * there is room
  63  */
  64 static int deswrite_compat(int, char *, int, int);
  65 
  66 #define KCMD_KEYUSAGE   1026
  67 
  68 static char storage[KCMD_BUFSIZ];
  69 static int nstored = 0;
  70 static int MAXSIZE = (KCMD_BUFSIZ + 8);
  71 static char *store_ptr = storage;
  72 static krb5_data desinbuf, desoutbuf;
  73 
  74 static boolean_t encrypt_flag = B_FALSE;
  75 static krb5_context kcmd_context;
  76 
  77 /* XXX Overloaded: use_ivecs!=0 -> new protocol, inband signalling, etc.  */
  78 static boolean_t use_ivecs = B_FALSE;
  79 static krb5_data encivec_i[2], encivec_o[2];
  80 static krb5_keyusage enc_keyusage_i[2], enc_keyusage_o[2];
  81 static krb5_enctype final_enctype;
  82 static krb5_keyblock *skey;
  83 
  84 /* ARGSUSED */
  85 int
  86 kcmd(int *sock, char **ahost, ushort_t rport,
  87         char *locuser, char *remuser,
  88         char *cmd, int *fd2p, char *service, char *realm,
  89         krb5_context bsd_context, krb5_auth_context *authconp,
  90         krb5_creds **cred, krb5_int32 *seqno, krb5_int32 *server_seqno,
  91         krb5_flags authopts,
  92         int anyport, enum kcmd_proto *protonump)
  93 {
  94         int s = -1;
  95         sigset_t oldmask, urgmask;
  96         struct sockaddr_in sin;
  97         struct sockaddr_storage from;
  98         krb5_creds *get_cred = NULL;
  99         krb5_creds *ret_cred = NULL;
 100         char c;
 101         struct hostent *hp;
 102         int rc;
 103         char *host_save = NULL;
 104         krb5_error_code status;
 105         krb5_ap_rep_enc_part *rep_ret;
 106         krb5_error      *error = 0;
 107         krb5_ccache cc;
 108         krb5_data outbuf;
 109         krb5_flags options = authopts;
 110         krb5_auth_context auth_context = NULL;
 111         char *cksumbuf;
 112         krb5_data cksumdat;
 113         int bsize = 0;
 114         char *kcmd_version;
 115         enum kcmd_proto protonum = *protonump;
 116 
 117         bsize = strlen(cmd) + strlen(remuser) + 64;
 118         if ((cksumbuf = malloc(bsize)) == 0) {
 119                 (void) fprintf(stderr, gettext("Unable to allocate"
 120                                             " memory for checksum buffer.\n"));
 121                 return (-1);
 122         }
 123         (void) snprintf(cksumbuf, bsize, "%u:", ntohs(rport));
 124         if (strlcat(cksumbuf, cmd, bsize) >= bsize) {
 125                 (void) fprintf(stderr, gettext("cmd buffer too long.\n"));
 126                 free(cksumbuf);
 127                 return (-1);
 128         }
 129         if (strlcat(cksumbuf, remuser, bsize) >= bsize) {
 130                 (void) fprintf(stderr, gettext("remuser too long.\n"));
 131                 free(cksumbuf);
 132                 return (-1);
 133         }
 134         cksumdat.data = cksumbuf;
 135         cksumdat.length = strlen(cksumbuf);
 136 
 137         hp = gethostbyname(*ahost);
 138         if (hp == 0) {
 139                 (void) fprintf(stderr,
 140                             gettext("%s: unknown host\n"), *ahost);
 141                 return (-1);
 142         }
 143 
 144         if ((host_save = (char *)strdup(hp->h_name)) == NULL) {
 145                 (void) fprintf(stderr, gettext("kcmd: no memory\n"));
 146                 return (-1);
 147         }
 148 
 149         /* If no service is given set to the default service */
 150         if (!service) service = default_service;
 151 
 152         if (!(get_cred = (krb5_creds *)calloc(1, sizeof (krb5_creds)))) {
 153                 (void) fprintf(stderr, gettext("kcmd: no memory\n"));
 154                 return (-1);
 155         }
 156         (void) sigemptyset(&urgmask);
 157         (void) sigaddset(&urgmask, SIGURG);
 158         (void) sigprocmask(SIG_BLOCK, &urgmask, &oldmask);
 159 
 160         status = krb5_sname_to_principal(bsd_context, host_save, service,
 161                                         KRB5_NT_SRV_HST, &get_cred->server);
 162         if (status) {
 163                 (void) fprintf(stderr,
 164                             gettext("kcmd: "
 165                                     "krb5_sname_to_principal failed: %s\n"),
 166                             error_message(status));
 167                 status = -1;
 168                 goto bad;
 169         }
 170 
 171         if (realm && *realm) {
 172                 (void) krb5_xfree(
 173                         krb5_princ_realm(bsd_context, get_cred->server)->data);
 174                 krb5_princ_set_realm_length(bsd_context, get_cred->server,
 175                                         strlen(realm));
 176                 krb5_princ_set_realm_data(bsd_context, get_cred->server,
 177                                                 strdup(realm));
 178         }
 179 
 180         s = socket(AF_INET, SOCK_STREAM, 0);
 181         if (s < 0) {
 182                 perror(gettext("Error creating socket"));
 183                 status = -1;
 184                 goto bad;
 185         }
 186         /*
 187          * Kerberos only supports IPv4 addresses for now.
 188          */
 189         if (hp->h_addrtype == AF_INET) {
 190                 sin.sin_family = hp->h_addrtype;
 191                 (void) memcpy((void *)&sin.sin_addr,
 192                             hp->h_addr, hp->h_length);
 193                 sin.sin_port = rport;
 194         } else {
 195                 syslog(LOG_ERR, "Address type %d not supported for "
 196                     "Kerberos", hp->h_addrtype);
 197                 status = -1;
 198                 goto bad;
 199         }
 200 
 201         if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
 202                 perror(host_save);
 203                 status = -1;
 204                 goto bad;
 205         }
 206 
 207         if (fd2p == 0) {
 208                 (void) write(s, "", 1);
 209         } else {
 210                 char num[16];
 211                 int s2;
 212                 int s3;
 213                 struct sockaddr_storage sname;
 214                 struct sockaddr_in *sp;
 215                 int len = sizeof (struct sockaddr_storage);
 216 
 217                 s2 = socket(AF_INET, SOCK_STREAM, 0);
 218                 if (s2 < 0) {
 219                         status = -1;
 220                         goto bad;
 221                 }
 222                 (void) memset((char *)&sin, 0, sizeof (sin));
 223                 sin.sin_family = AF_INET;
 224                 sin.sin_addr.s_addr = INADDR_ANY;
 225                 sin.sin_port = 0;
 226 
 227                 if (bind(s2, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
 228                         perror(gettext("error binding socket"));
 229                         (void) close(s2);
 230                         status = -1;
 231                         goto bad;
 232                 }
 233                 if (getsockname(s2, (struct sockaddr *)&sname, &len) < 0) {
 234                         perror(gettext("getsockname error"));
 235                         (void) close(s2);
 236                         status = -1;
 237                         goto bad;
 238                 }
 239                 sp = (struct sockaddr_in *)&sname;
 240                 (void) listen(s2, 1);
 241                 (void) snprintf(num, sizeof (num), "%d",
 242                             htons((ushort_t)sp->sin_port));
 243                 if (write(s, num, strlen(num)+1) != strlen(num)+1) {
 244                         perror(gettext("write: error setting up stderr"));
 245                         (void) close(s2);
 246                         status = -1;
 247                         goto bad;
 248                 }
 249 
 250                 s3 = accept(s2, (struct sockaddr *)&from, &len);
 251                 (void) close(s2);
 252                 if (s3 < 0) {
 253                         perror(gettext("accept"));
 254                         status = -1;
 255                         goto bad;
 256                 }
 257                 *fd2p = s3;
 258                 if (SOCK_FAMILY(from) == AF_INET) {
 259                         if (!anyport && SOCK_PORT(from) >= IPPORT_RESERVED) {
 260                                 (void) fprintf(stderr,
 261                                     gettext("socket: protocol "
 262                                             "failure in circuit setup.\n"));
 263                                 status = -1;
 264                                 goto bad2;
 265                         }
 266                 } else {
 267                         (void) fprintf(stderr,
 268                                     gettext("Kerberos does not support "
 269                                             "address type %d\n"),
 270                                     SOCK_FAMILY(from));
 271                         status = -1;
 272                         goto bad2;
 273                 }
 274         }
 275 
 276         if (status = krb5_cc_default(bsd_context, &cc))
 277                 goto bad2;
 278 
 279         status = krb5_cc_get_principal(bsd_context, cc, &get_cred->client);
 280         if (status) {
 281                 (void) krb5_cc_close(bsd_context, cc);
 282                 goto bad2;
 283         }
 284 
 285         /* Get ticket from credentials cache or kdc */
 286         status = krb5_get_credentials(bsd_context, 0, cc, get_cred, &ret_cred);
 287         (void) krb5_cc_close(bsd_context, cc);
 288         if (status) goto bad2;
 289 
 290         /* Reset internal flags; these should not be sent. */
 291         authopts &= (~OPTS_FORWARD_CREDS);
 292         authopts &= (~OPTS_FORWARDABLE_CREDS);
 293 
 294         if ((status = krb5_auth_con_init(bsd_context, &auth_context)))
 295                 goto bad2;
 296 
 297         if ((status = krb5_auth_con_setflags(bsd_context, auth_context,
 298                                         KRB5_AUTH_CONTEXT_RET_TIME)))
 299                 goto bad2;
 300 
 301         /* Only need local address for mk_cred() to send to krlogind */
 302         if ((status = krb5_auth_con_genaddrs(bsd_context, auth_context, s,
 303                         KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR)))
 304                 goto bad2;
 305 
 306         if (protonum == KCMD_PROTOCOL_COMPAT_HACK) {
 307                 krb5_boolean is_des;
 308                 status = krb5_c_enctype_compare(bsd_context,
 309                                                 ENCTYPE_DES_CBC_CRC,
 310                                                 ret_cred->keyblock.enctype,
 311                                                 &is_des);
 312                 if (status)
 313                         goto bad2;
 314                 protonum = is_des ? KCMD_OLD_PROTOCOL : KCMD_NEW_PROTOCOL;
 315         }
 316 
 317         switch (protonum) {
 318         case KCMD_NEW_PROTOCOL:
 319                 authopts |= AP_OPTS_USE_SUBKEY;
 320                 kcmd_version = "KCMDV0.2";
 321                 break;
 322         case KCMD_OLD_PROTOCOL:
 323                 kcmd_version = "KCMDV0.1";
 324                 break;
 325         default:
 326                 status = -1;
 327                 goto bad2;
 328         }
 329 
 330         /*
 331          * Call the Kerberos library routine to obtain an authenticator,
 332          * pass it over the socket to the server, and obtain mutual
 333          * authentication.
 334          */
 335         status = krb5_sendauth(bsd_context, &auth_context, (krb5_pointer) &s,
 336                         kcmd_version, ret_cred->client, ret_cred->server,
 337                         authopts, &cksumdat, ret_cred, 0, &error,
 338                         &rep_ret, NULL);
 339         krb5_xfree(cksumdat.data);
 340         if (status) {
 341                 (void) fprintf(stderr, gettext("Couldn't authenticate"
 342                                             " to server: %s\n"),
 343                             error_message(status));
 344                 if (error) {
 345                         (void) fprintf(stderr, gettext("Server returned error"
 346                                                 " code %d (%s)\n"),
 347                                 error->error,
 348                                 error_message(ERROR_TABLE_BASE_krb5 +
 349                                             error->error));
 350                         if (error->text.length)
 351                                 (void) fprintf(stderr,
 352                                             gettext("Error text"
 353                                                     " sent from server: %s\n"),
 354                                             error->text.data);
 355                 }
 356                 if (error) {
 357                         krb5_free_error(bsd_context, error);
 358                         error = 0;
 359                 }
 360                 goto bad2;
 361         }
 362         if (rep_ret && server_seqno) {
 363                 *server_seqno = rep_ret->seq_number;
 364                 krb5_free_ap_rep_enc_part(bsd_context, rep_ret);
 365         }
 366 
 367         (void) write(s, remuser, strlen(remuser)+1);
 368         (void) write(s, cmd, strlen(cmd)+1);
 369         if (locuser)
 370                 (void) write(s, locuser, strlen(locuser)+1);
 371         else
 372                 (void) write(s, "", 1);
 373 
 374         if (options & OPTS_FORWARD_CREDS) {   /* Forward credentials */
 375                 if (status = krb5_fwd_tgt_creds(bsd_context, auth_context,
 376                                         host_save,
 377                                         ret_cred->client, ret_cred->server,
 378                                         0, options & OPTS_FORWARDABLE_CREDS,
 379                                         &outbuf)) {
 380                         (void) fprintf(stderr,
 381                                     gettext("kcmd: Error getting"
 382                                             " forwarded creds\n"));
 383                         goto bad2;
 384                 }
 385                 /* Send forwarded credentials */
 386                 if (status = krb5_write_message(bsd_context, (krb5_pointer)&s,
 387                                                 &outbuf))
 388                         goto bad2;
 389         } else { /* Dummy write to signal no forwarding */
 390                 outbuf.length = 0;
 391                 if (status = krb5_write_message(bsd_context,
 392                                                 (krb5_pointer)&s, &outbuf))
 393                         goto bad2;
 394         }
 395 
 396         if ((rc = read(s, &c, 1)) != 1) {
 397                 if (rc == -1) {
 398                         perror(*ahost);
 399                 } else {
 400                         (void) fprintf(stderr, gettext("kcmd: bad connection "
 401                                         "with remote host\n"));
 402                 }
 403                 status = -1;
 404                 goto bad2;
 405         }
 406         if (c != 0) {
 407                 while (read(s, &c, 1) == 1) {
 408                         (void) write(2, &c, 1);
 409                         if (c == '\n')
 410                                 break;
 411                 }
 412                 status = -1;
 413                 goto bad2;
 414         }
 415         (void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
 416         *sock = s;
 417 
 418         /* pass back credentials if wanted */
 419         if (cred)
 420                 (void) krb5_copy_creds(bsd_context, ret_cred, cred);
 421 
 422         krb5_free_creds(bsd_context, ret_cred);
 423 
 424         /*
 425          * Initialize *authconp to auth_context, so
 426          * that the clients can make use of it
 427          */
 428         *authconp = auth_context;
 429 
 430         return (0);
 431 bad2:
 432         if (fd2p != NULL)
 433                 (void) close(*fd2p);
 434 bad:
 435         if (s > 0)
 436                 (void) close(s);
 437         if (get_cred)
 438                 krb5_free_creds(bsd_context, get_cred);
 439         if (ret_cred)
 440                 krb5_free_creds(bsd_context, ret_cred);
 441         if (host_save)
 442                 free(host_save);
 443         (void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
 444         return (status);
 445 }
 446 
 447 /*
 448  * Strsave was a routine in the version 4 krb library: we put it here
 449  * for compatablilty with version 5 krb library, since kcmd.o is linked
 450  * into all programs.
 451  */
 452 
 453 char *
 454 strsave(char *sp)
 455 {
 456         char *ret;
 457 
 458         if ((ret = (char *)strdup(sp)) == NULL) {
 459                 (void) fprintf(stderr, gettext("no memory for saving args\n"));
 460                 exit(1);
 461         }
 462         return (ret);
 463 }
 464 
 465 
 466 /*
 467  * This routine is to initialize the desinbuf, desoutbuf and the session key
 468  * structures to carry out desread()'s and deswrite()'s successfully
 469  */
 470 void
 471 init_encrypt(int enc, krb5_context ctxt, enum kcmd_proto protonum,
 472         krb5_data *inbuf, krb5_data *outbuf,
 473         int amclient, krb5_encrypt_block *block)
 474 {
 475         krb5_error_code statuscode;
 476         size_t blocksize;
 477         int i;
 478         krb5_error_code ret;
 479 
 480         kcmd_context = ctxt;
 481 
 482         if (enc > 0) {
 483                 desinbuf.data = inbuf->data;
 484                 desoutbuf.data = outbuf->data + 4;
 485                 desinbuf.length = inbuf->length;
 486                 desoutbuf.length = outbuf->length + 4;
 487                 encrypt_flag = B_TRUE;
 488         } else {
 489                 encrypt_flag = B_FALSE;
 490                 return;
 491         }
 492 
 493         skey = block->key;
 494         final_enctype = skey->enctype;
 495 
 496         enc_keyusage_i[0] = KCMD_KEYUSAGE;
 497         enc_keyusage_i[1] = KCMD_KEYUSAGE;
 498         enc_keyusage_o[0] = KCMD_KEYUSAGE;
 499         enc_keyusage_o[1] = KCMD_KEYUSAGE;
 500 
 501         if (protonum == KCMD_OLD_PROTOCOL) {
 502                 use_ivecs = B_FALSE;
 503                 return;
 504         }
 505 
 506         use_ivecs = B_TRUE;
 507         switch (skey->enctype) {
 508         /*
 509          * For the DES-based enctypes and the 3DES enctype we
 510          * want to use a non-zero  IV because that's what we did.
 511          * In the future we use different keyusage for each
 512          * channel and direction and a fresh cipher state.
 513          */
 514         case ENCTYPE_DES_CBC_CRC:
 515         case ENCTYPE_DES_CBC_MD4:
 516         case ENCTYPE_DES_CBC_MD5:
 517         case ENCTYPE_DES3_CBC_SHA1:
 518                 statuscode = krb5_c_block_size(kcmd_context, final_enctype,
 519                                 &blocksize);
 520                 if (statuscode) {
 521                         /* XXX what do I do? */
 522                         abort();
 523                 }
 524 
 525                 encivec_i[0].length = encivec_i[1].length =
 526                 encivec_o[0].length = encivec_o[1].length = blocksize;
 527 
 528                 if ((encivec_i[0].data = malloc(encivec_i[0].length * 4))
 529                         == NULL) {
 530                         /* XXX what do I do? */
 531                         abort();
 532                 }
 533                 encivec_i[1].data = encivec_i[0].data + encivec_i[0].length;
 534                 encivec_o[0].data = encivec_i[1].data + encivec_i[0].length;
 535                 encivec_o[1].data = encivec_o[0].data + encivec_i[0].length;
 536 
 537                 /* is there a better way to initialize this? */
 538                 (void) memset(encivec_i[0].data, amclient, blocksize);
 539                 (void) memset(encivec_o[0].data, 1 - amclient, blocksize);
 540                 (void) memset(encivec_i[1].data, 2 | amclient, blocksize);
 541                 (void) memset(encivec_o[1].data, 2 | (1 - amclient), blocksize);
 542                 break;
 543         default:
 544                 if (amclient) {
 545                         enc_keyusage_i[0] = 1028;
 546                         enc_keyusage_i[1] = 1030;
 547                         enc_keyusage_o[0] = 1032;
 548                         enc_keyusage_o[1] = 1034;
 549                 } else { /* amclient */
 550                         enc_keyusage_i[0] = 1032;
 551                         enc_keyusage_i[1] = 1034;
 552                         enc_keyusage_o[0] = 1028;
 553                         enc_keyusage_o[1] = 1030;
 554                 }
 555                 for (i = 0; i < 2; i++) {
 556                         ret = krb5_c_init_state(ctxt,
 557                                 skey, enc_keyusage_i[i],
 558                                 &encivec_i[i]);
 559                         if (ret)
 560                                 goto fail;
 561                         ret = krb5_c_init_state(ctxt,
 562                                 skey, enc_keyusage_o[i],
 563                                 &encivec_o[i]);
 564                         if (ret)
 565                                 goto fail;
 566                 }
 567                 break;
 568         }
 569         return;
 570 fail:
 571         abort();
 572 }
 573 
 574 int
 575 desread(int fd, char *buf, int len, int secondary)
 576 {
 577         int nreturned = 0;
 578         long net_len, rd_len;
 579         int cc;
 580         size_t ret = 0;
 581         unsigned char len_buf[4];
 582         krb5_enc_data inputd;
 583         krb5_data outputd;
 584 
 585         if (!encrypt_flag)
 586                 return (read(fd, buf, len));
 587 
 588         /*
 589          * If there is stored data from a previous read,
 590          * put it into the output buffer and return it now.
 591          */
 592         if (nstored >= len) {
 593                 (void) memcpy(buf, store_ptr, len);
 594                 store_ptr += len;
 595                 nstored -= len;
 596                 return (len);
 597         } else if (nstored) {
 598                 (void) memcpy(buf, store_ptr, nstored);
 599                 nreturned += nstored;
 600                 buf += nstored;
 601                 len -= nstored;
 602                 nstored = 0;
 603         }
 604 
 605         if ((cc = krb5_net_read(kcmd_context, fd, (char *)len_buf, 4)) != 4) {
 606                 if ((cc < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN)))
 607                         return (cc);
 608                 /* XXX can't read enough, pipe must have closed */
 609                 return (0);
 610         }
 611         rd_len = ((len_buf[0] << 24) | (len_buf[1] << 16) |
 612                     (len_buf[2] << 8) | len_buf[3]);
 613 
 614         if (krb5_c_encrypt_length(kcmd_context, final_enctype,
 615                                 use_ivecs ? (size_t)rd_len + 4 : (size_t)rd_len,
 616                                 &ret))
 617                 net_len = ((size_t)-1);
 618         else
 619                 net_len = ret;
 620 
 621         if ((net_len <= 0) || (net_len > desinbuf.length)) {
 622                 /*
 623                  * preposterous length; assume out-of-sync; only recourse
 624                  * is to close connection, so return 0
 625                  */
 626                 (void) fprintf(stderr, gettext("Read size problem.\n"));
 627                 return (0);
 628         }
 629 
 630         if ((cc = krb5_net_read(kcmd_context, fd, desinbuf.data, net_len))
 631             != net_len) {
 632                 /* pipe must have closed, return 0 */
 633                 (void) fprintf(stderr,
 634                             gettext("Read error: length received %d "
 635                                     "!= expected %d.\n"),
 636                             cc, net_len);
 637                 return (0);
 638         }
 639 
 640         /*
 641          * Decrypt information
 642          */
 643         inputd.enctype = ENCTYPE_UNKNOWN;
 644         inputd.ciphertext.length = net_len;
 645         inputd.ciphertext.data = (krb5_pointer)desinbuf.data;
 646 
 647         outputd.length = sizeof (storage);
 648         outputd.data = (krb5_pointer)storage;
 649 
 650         /*
 651          * data is decrypted into the "storage" buffer, which
 652          * had better be large enough!
 653          */
 654         cc = krb5_c_decrypt(kcmd_context, skey,
 655                                 enc_keyusage_i[secondary],
 656                                 use_ivecs ? encivec_i + secondary : 0,
 657                                 &inputd, &outputd);
 658         if (cc) {
 659                 (void) fprintf(stderr, gettext("Cannot decrypt data "
 660                         "from network\n"));
 661                 return (0);
 662         }
 663 
 664         store_ptr = storage;
 665         nstored = rd_len;
 666         if (use_ivecs == B_TRUE) {
 667                 int rd_len2;
 668                 rd_len2 = storage[0] & 0xff;
 669                 rd_len2 <<= 8; rd_len2 |= storage[1] & 0xff;
 670                 rd_len2 <<= 8; rd_len2 |= storage[2] & 0xff;
 671                 rd_len2 <<= 8; rd_len2 |= storage[3] & 0xff;
 672                 if (rd_len2 != rd_len) {
 673                         /* cleartext length trashed? */
 674                         errno = EIO;
 675                         return (-1);
 676                 }
 677                 store_ptr += 4;
 678         }
 679         /*
 680          * Copy only as much data as the input buffer will allow.
 681          * The rest is kept in the 'storage' pointer for the next
 682          * read.
 683          */
 684         if (nstored > len) {
 685                 (void) memcpy(buf, store_ptr, len);
 686                 nreturned += len;
 687                 store_ptr += len;
 688                 nstored -= len;
 689         } else {
 690                 (void) memcpy(buf, store_ptr, nstored);
 691                 nreturned += nstored;
 692                 nstored = 0;
 693         }
 694 
 695         return (nreturned);
 696 }
 697 int
 698 deswrite(int fd, char *buf, int len, int secondary)
 699 {
 700         int bytes_written;
 701         int r;
 702         int outlen;
 703         char *p;
 704         if (!encrypt_flag)
 705                 return (write(fd, buf, len));
 706 
 707         bytes_written = 0;
 708         while (len > 0) {
 709                 p = buf + bytes_written;
 710                 if (len > KCMD8_BUFSIZ)
 711                         outlen = KCMD8_BUFSIZ;
 712                 else
 713                         outlen = len;
 714                 r = deswrite_compat(fd, p, outlen, secondary);
 715                 if (r == -1)
 716                         return (r);
 717                 bytes_written += r;
 718                 len -= r;
 719         }
 720         return (bytes_written);
 721 }
 722 static int
 723 deswrite_compat(int fd, char *buf, int len, int secondary)
 724 {
 725         int cc;
 726         size_t ret = 0;
 727         krb5_data inputd;
 728         krb5_enc_data outputd;
 729         char tmpbuf[KCMD_BUFSIZ + 8];
 730         char encrbuf[KCMD_BUFSIZ + 8];
 731         unsigned char *len_buf = (unsigned char *)tmpbuf;
 732 
 733         if (use_ivecs == B_TRUE) {
 734                 unsigned char *lenbuf2 = (unsigned char *)tmpbuf;
 735                 if (len + 4 > sizeof (tmpbuf))
 736                         abort();
 737                 lenbuf2[0] = (len & 0xff000000) >> 24;
 738                 lenbuf2[1] = (len & 0xff0000) >> 16;
 739                 lenbuf2[2] = (len & 0xff00) >> 8;
 740                 lenbuf2[3] = (len & 0xff);
 741                 (void) memcpy(tmpbuf + 4, buf, len);
 742 
 743                 inputd.data = (krb5_pointer)tmpbuf;
 744                 inputd.length = len + 4;
 745         } else {
 746                 inputd.data = (krb5_pointer)buf;
 747                 inputd.length = len;
 748         }
 749 
 750         desoutbuf.data = encrbuf;
 751 
 752         if (krb5_c_encrypt_length(kcmd_context, final_enctype,
 753                         use_ivecs ? (size_t)len + 4 : (size_t)len, &ret)) {
 754                 desoutbuf.length = ((size_t)-1);
 755                 goto err;
 756         } else {
 757                 desoutbuf.length = ret;
 758         }
 759 
 760         if (desoutbuf.length > MAXSIZE) {
 761                 (void) fprintf(stderr, gettext("Write size problem.\n"));
 762                 return (-1);
 763         }
 764 
 765         /*
 766          * Encrypt information
 767          */
 768         outputd.ciphertext.length = desoutbuf.length;
 769         outputd.ciphertext.data = (krb5_pointer)desoutbuf.data;
 770 
 771         cc = krb5_c_encrypt(kcmd_context, skey,
 772                         enc_keyusage_o[secondary],
 773                         use_ivecs ? encivec_o + secondary : 0,
 774                         &inputd, &outputd);
 775 
 776         if (cc) {
 777 err:
 778                 (void) fprintf(stderr, gettext("Write encrypt problem.\n"));
 779                 return (-1);
 780         }
 781 
 782         len_buf[0] = (len & 0xff000000) >> 24;
 783         len_buf[1] = (len & 0xff0000) >> 16;
 784         len_buf[2] = (len & 0xff00) >> 8;
 785         len_buf[3] = (len & 0xff);
 786         (void) write(fd, len_buf, 4);
 787 
 788         if (write(fd, desoutbuf.data, desoutbuf.length) != desoutbuf.length) {
 789                 (void) fprintf(stderr, gettext("Could not write "
 790                         "out all data.\n"));
 791                 return (-1);
 792         } else {
 793                 return (len);
 794         }
 795 }