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
kcmd(int * sock,char ** ahost,ushort_t rport,char * locuser,char * remuser,char * cmd,int * fd2p,char * service,char * realm,krb5_context bsd_context,krb5_auth_context * authconp,krb5_creds ** cred,krb5_int32 * seqno,krb5_int32 * server_seqno,krb5_flags authopts,int anyport,enum kcmd_proto * protonump)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 *
strsave(char * sp)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
init_encrypt(int enc,krb5_context ctxt,enum kcmd_proto protonum,krb5_data * inbuf,krb5_data * outbuf,int amclient,krb5_encrypt_block * block)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
desread(int fd,char * buf,int len,int secondary)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
deswrite(int fd,char * buf,int len,int secondary)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
deswrite_compat(int fd,char * buf,int len,int secondary)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