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