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