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