xref: /illumos-gate/usr/src/cmd/cmd-inet/common/kcmd.c (revision 1fa2a66491e7d8ae0be84e7da4da8e812480c710)
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 }
796