2 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * Copyright (c) 1983 Regents of the University of California.
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.
23 /* derived from @(#)rcmd.c 5.17 (Berkeley) 6/27/88 */
31 #include <sys/param.h>
32 #include <sys/types.h>
37 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
51 static char *default_service
= "host";
53 #define KCMD_BUFSIZ 102400
54 #define KCMD8_BUFSIZ (4096 - 256)
56 * For compatibility with earlier versions of Solaris and other OS
57 * (kerborized rsh uses 4KB of RSH_BUFSIZE)- 256 to make sure
60 static int deswrite_compat(int, char *, int, int);
62 #define KCMD_KEYUSAGE 1026
64 static char storage
[KCMD_BUFSIZ
];
65 static int nstored
= 0;
66 static int MAXSIZE
= (KCMD_BUFSIZ
+ 8);
67 static char *store_ptr
= storage
;
68 static krb5_data desinbuf
, desoutbuf
;
70 static boolean_t encrypt_flag
= B_FALSE
;
71 static krb5_context kcmd_context
;
73 /* XXX Overloaded: use_ivecs!=0 -> new protocol, inband signalling, etc. */
74 static boolean_t use_ivecs
= B_FALSE
;
75 static krb5_data encivec_i
[2], encivec_o
[2];
76 static krb5_keyusage enc_keyusage_i
[2], enc_keyusage_o
[2];
77 static krb5_enctype final_enctype
;
78 static krb5_keyblock
*skey
;
82 kcmd(int *sock
, char **ahost
, ushort_t rport
,
83 char *locuser
, char *remuser
,
84 char *cmd
, int *fd2p
, char *service
, char *realm
,
85 krb5_context bsd_context
, krb5_auth_context
*authconp
,
86 krb5_creds
**cred
, krb5_int32
*seqno
, krb5_int32
*server_seqno
,
88 int anyport
, enum kcmd_proto
*protonump
)
91 sigset_t oldmask
, urgmask
;
92 struct sockaddr_in sin
;
93 struct sockaddr_storage from
;
94 krb5_creds
*get_cred
= NULL
;
95 krb5_creds
*ret_cred
= NULL
;
99 char *host_save
= NULL
;
100 krb5_error_code status
;
101 krb5_ap_rep_enc_part
*rep_ret
;
102 krb5_error
*error
= 0;
105 krb5_flags options
= authopts
;
106 krb5_auth_context auth_context
= NULL
;
111 enum kcmd_proto protonum
= *protonump
;
113 bsize
= strlen(cmd
) + strlen(remuser
) + 64;
114 if ((cksumbuf
= malloc(bsize
)) == 0) {
115 (void) fprintf(stderr
, gettext("Unable to allocate"
116 " memory for checksum buffer.\n"));
119 (void) snprintf(cksumbuf
, bsize
, "%u:", ntohs(rport
));
120 if (strlcat(cksumbuf
, cmd
, bsize
) >= bsize
) {
121 (void) fprintf(stderr
, gettext("cmd buffer too long.\n"));
125 if (strlcat(cksumbuf
, remuser
, bsize
) >= bsize
) {
126 (void) fprintf(stderr
, gettext("remuser too long.\n"));
130 cksumdat
.data
= cksumbuf
;
131 cksumdat
.length
= strlen(cksumbuf
);
133 hp
= gethostbyname(*ahost
);
135 (void) fprintf(stderr
,
136 gettext("%s: unknown host\n"), *ahost
);
140 if ((host_save
= (char *)strdup(hp
->h_name
)) == NULL
) {
141 (void) fprintf(stderr
, gettext("kcmd: no memory\n"));
145 /* If no service is given set to the default service */
146 if (!service
) service
= default_service
;
148 if (!(get_cred
= (krb5_creds
*)calloc(1, sizeof (krb5_creds
)))) {
149 (void) fprintf(stderr
, gettext("kcmd: no memory\n"));
152 (void) sigemptyset(&urgmask
);
153 (void) sigaddset(&urgmask
, SIGURG
);
154 (void) sigprocmask(SIG_BLOCK
, &urgmask
, &oldmask
);
156 status
= krb5_sname_to_principal(bsd_context
, host_save
, service
,
157 KRB5_NT_SRV_HST
, &get_cred
->server
);
159 (void) fprintf(stderr
,
161 "krb5_sname_to_principal failed: %s\n"),
162 error_message(status
));
167 if (realm
&& *realm
) {
169 krb5_princ_realm(bsd_context
, get_cred
->server
)->data
);
170 krb5_princ_set_realm_length(bsd_context
, get_cred
->server
,
172 krb5_princ_set_realm_data(bsd_context
, get_cred
->server
,
176 s
= socket(AF_INET
, SOCK_STREAM
, 0);
178 perror(gettext("Error creating socket"));
183 * Kerberos only supports IPv4 addresses for now.
185 if (hp
->h_addrtype
== AF_INET
) {
186 sin
.sin_family
= hp
->h_addrtype
;
187 (void) memcpy((void *)&sin
.sin_addr
,
188 hp
->h_addr
, hp
->h_length
);
189 sin
.sin_port
= rport
;
191 syslog(LOG_ERR
, "Address type %d not supported for "
192 "Kerberos", hp
->h_addrtype
);
197 if (connect(s
, (struct sockaddr
*)&sin
, sizeof (sin
)) < 0) {
204 (void) write(s
, "", 1);
209 struct sockaddr_storage sname
;
210 struct sockaddr_in
*sp
;
211 int len
= sizeof (struct sockaddr_storage
);
213 s2
= socket(AF_INET
, SOCK_STREAM
, 0);
218 (void) memset((char *)&sin
, 0, sizeof (sin
));
219 sin
.sin_family
= AF_INET
;
220 sin
.sin_addr
.s_addr
= INADDR_ANY
;
223 if (bind(s2
, (struct sockaddr
*)&sin
, sizeof (sin
)) < 0) {
224 perror(gettext("error binding socket"));
229 if (getsockname(s2
, (struct sockaddr
*)&sname
, &len
) < 0) {
230 perror(gettext("getsockname error"));
235 sp
= (struct sockaddr_in
*)&sname
;
236 (void) listen(s2
, 1);
237 (void) snprintf(num
, sizeof (num
), "%d",
238 htons((ushort_t
)sp
->sin_port
));
239 if (write(s
, num
, strlen(num
)+1) != strlen(num
)+1) {
240 perror(gettext("write: error setting up stderr"));
246 s3
= accept(s2
, (struct sockaddr
*)&from
, &len
);
249 perror(gettext("accept"));
254 if (SOCK_FAMILY(from
) == AF_INET
) {
255 if (!anyport
&& SOCK_PORT(from
) >= IPPORT_RESERVED
) {
256 (void) fprintf(stderr
,
257 gettext("socket: protocol "
258 "failure in circuit setup.\n"));
263 (void) fprintf(stderr
,
264 gettext("Kerberos does not support "
265 "address type %d\n"),
272 if (status
= krb5_cc_default(bsd_context
, &cc
))
275 status
= krb5_cc_get_principal(bsd_context
, cc
, &get_cred
->client
);
277 (void) krb5_cc_close(bsd_context
, cc
);
281 /* Get ticket from credentials cache or kdc */
282 status
= krb5_get_credentials(bsd_context
, 0, cc
, get_cred
, &ret_cred
);
283 (void) krb5_cc_close(bsd_context
, cc
);
284 if (status
) goto bad2
;
286 /* Reset internal flags; these should not be sent. */
287 authopts
&= (~OPTS_FORWARD_CREDS
);
288 authopts
&= (~OPTS_FORWARDABLE_CREDS
);
290 if ((status
= krb5_auth_con_init(bsd_context
, &auth_context
)))
293 if ((status
= krb5_auth_con_setflags(bsd_context
, auth_context
,
294 KRB5_AUTH_CONTEXT_RET_TIME
)))
297 /* Only need local address for mk_cred() to send to krlogind */
298 if ((status
= krb5_auth_con_genaddrs(bsd_context
, auth_context
, s
,
299 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR
)))
302 if (protonum
== KCMD_PROTOCOL_COMPAT_HACK
) {
304 status
= krb5_c_enctype_compare(bsd_context
,
306 ret_cred
->keyblock
.enctype
,
310 protonum
= is_des
? KCMD_OLD_PROTOCOL
: KCMD_NEW_PROTOCOL
;
314 case KCMD_NEW_PROTOCOL
:
315 authopts
|= AP_OPTS_USE_SUBKEY
;
316 kcmd_version
= "KCMDV0.2";
318 case KCMD_OLD_PROTOCOL
:
319 kcmd_version
= "KCMDV0.1";
327 * Call the Kerberos library routine to obtain an authenticator,
328 * pass it over the socket to the server, and obtain mutual
331 status
= krb5_sendauth(bsd_context
, &auth_context
, (krb5_pointer
) &s
,
332 kcmd_version
, ret_cred
->client
, ret_cred
->server
,
333 authopts
, &cksumdat
, ret_cred
, 0, &error
,
335 krb5_xfree(cksumdat
.data
);
337 (void) fprintf(stderr
, gettext("Couldn't authenticate"
339 error_message(status
));
341 (void) fprintf(stderr
, gettext("Server returned error"
344 error_message(ERROR_TABLE_BASE_krb5
+
346 if (error
->text
.length
)
347 (void) fprintf(stderr
,
349 " sent from server: %s\n"),
353 krb5_free_error(bsd_context
, error
);
358 if (rep_ret
&& server_seqno
) {
359 *server_seqno
= rep_ret
->seq_number
;
360 krb5_free_ap_rep_enc_part(bsd_context
, rep_ret
);
363 (void) write(s
, remuser
, strlen(remuser
)+1);
364 (void) write(s
, cmd
, strlen(cmd
)+1);
366 (void) write(s
, locuser
, strlen(locuser
)+1);
368 (void) write(s
, "", 1);
370 if (options
& OPTS_FORWARD_CREDS
) { /* Forward credentials */
371 if (status
= krb5_fwd_tgt_creds(bsd_context
, auth_context
,
373 ret_cred
->client
, ret_cred
->server
,
374 0, options
& OPTS_FORWARDABLE_CREDS
,
376 (void) fprintf(stderr
,
377 gettext("kcmd: Error getting"
378 " forwarded creds\n"));
381 /* Send forwarded credentials */
382 if (status
= krb5_write_message(bsd_context
, (krb5_pointer
)&s
,
385 } else { /* Dummy write to signal no forwarding */
387 if (status
= krb5_write_message(bsd_context
,
388 (krb5_pointer
)&s
, &outbuf
))
392 if ((rc
= read(s
, &c
, 1)) != 1) {
396 (void) fprintf(stderr
, gettext("kcmd: bad connection "
397 "with remote host\n"));
403 while (read(s
, &c
, 1) == 1) {
404 (void) write(2, &c
, 1);
411 (void) sigprocmask(SIG_SETMASK
, &oldmask
, (sigset_t
*)0);
414 /* pass back credentials if wanted */
415 if (cred
) (void) krb5_copy_creds(bsd_context
, ret_cred
, cred
);
416 krb5_free_creds(bsd_context
, ret_cred
);
418 * Initialize *authconp to auth_context, so
419 * that the clients can make use of it
421 *authconp
= auth_context
;
431 krb5_free_creds(bsd_context
, get_cred
);
433 krb5_free_creds(bsd_context
, ret_cred
);
436 (void) sigprocmask(SIG_SETMASK
, &oldmask
, (sigset_t
*)0);
441 * Strsave was a routine in the version 4 krb library: we put it here
442 * for compatablilty with version 5 krb library, since kcmd.o is linked
451 if ((ret
= (char *)strdup(sp
)) == NULL
) {
452 (void) fprintf(stderr
, gettext("no memory for saving args\n"));
460 * This routine is to initialize the desinbuf, desoutbuf and the session key
461 * structures to carry out desread()'s and deswrite()'s successfully
464 init_encrypt(int enc
, krb5_context ctxt
, enum kcmd_proto protonum
,
465 krb5_data
*inbuf
, krb5_data
*outbuf
,
466 int amclient
, krb5_encrypt_block
*block
)
468 krb5_error_code statuscode
;
476 desinbuf
.data
= inbuf
->data
;
477 desoutbuf
.data
= outbuf
->data
+ 4;
478 desinbuf
.length
= inbuf
->length
;
479 desoutbuf
.length
= outbuf
->length
+ 4;
480 encrypt_flag
= B_TRUE
;
482 encrypt_flag
= B_FALSE
;
487 final_enctype
= skey
->enctype
;
489 enc_keyusage_i
[0] = KCMD_KEYUSAGE
;
490 enc_keyusage_i
[1] = KCMD_KEYUSAGE
;
491 enc_keyusage_o
[0] = KCMD_KEYUSAGE
;
492 enc_keyusage_o
[1] = KCMD_KEYUSAGE
;
494 if (protonum
== KCMD_OLD_PROTOCOL
) {
500 switch (skey
->enctype
) {
502 * For the DES-based enctypes and the 3DES enctype we
503 * want to use a non-zero IV because that's what we did.
504 * In the future we use different keyusage for each
505 * channel and direction and a fresh cipher state.
507 case ENCTYPE_DES_CBC_CRC
:
508 case ENCTYPE_DES_CBC_MD4
:
509 case ENCTYPE_DES_CBC_MD5
:
510 case ENCTYPE_DES3_CBC_SHA1
:
511 statuscode
= krb5_c_block_size(kcmd_context
, final_enctype
,
514 /* XXX what do I do? */
518 encivec_i
[0].length
= encivec_i
[1].length
=
519 encivec_o
[0].length
= encivec_o
[1].length
= blocksize
;
521 if ((encivec_i
[0].data
= malloc(encivec_i
[0].length
* 4))
523 /* XXX what do I do? */
526 encivec_i
[1].data
= encivec_i
[0].data
+ encivec_i
[0].length
;
527 encivec_o
[0].data
= encivec_i
[1].data
+ encivec_i
[0].length
;
528 encivec_o
[1].data
= encivec_o
[0].data
+ encivec_i
[0].length
;
530 /* is there a better way to initialize this? */
531 (void) memset(encivec_i
[0].data
, amclient
, blocksize
);
532 (void) memset(encivec_o
[0].data
, 1 - amclient
, blocksize
);
533 (void) memset(encivec_i
[1].data
, 2 | amclient
, blocksize
);
534 (void) memset(encivec_o
[1].data
, 2 | (1 - amclient
), blocksize
);
538 enc_keyusage_i
[0] = 1028;
539 enc_keyusage_i
[1] = 1030;
540 enc_keyusage_o
[0] = 1032;
541 enc_keyusage_o
[1] = 1034;
542 } else { /* amclient */
543 enc_keyusage_i
[0] = 1032;
544 enc_keyusage_i
[1] = 1034;
545 enc_keyusage_o
[0] = 1028;
546 enc_keyusage_o
[1] = 1030;
548 for (i
= 0; i
< 2; i
++) {
549 ret
= krb5_c_init_state(ctxt
,
550 skey
, enc_keyusage_i
[i
],
554 ret
= krb5_c_init_state(ctxt
,
555 skey
, enc_keyusage_o
[i
],
568 desread(int fd
, char *buf
, int len
, int secondary
)
571 long net_len
, rd_len
;
574 unsigned char len_buf
[4];
575 krb5_enc_data inputd
;
579 return (read(fd
, buf
, len
));
582 * If there is stored data from a previous read,
583 * put it into the output buffer and return it now.
585 if (nstored
>= len
) {
586 (void) memcpy(buf
, store_ptr
, len
);
590 } else if (nstored
) {
591 (void) memcpy(buf
, store_ptr
, nstored
);
592 nreturned
+= nstored
;
598 if ((cc
= krb5_net_read(kcmd_context
, fd
, (char *)len_buf
, 4)) != 4) {
599 if ((cc
< 0) && ((errno
== EWOULDBLOCK
) || (errno
== EAGAIN
)))
601 /* XXX can't read enough, pipe must have closed */
604 rd_len
= ((len_buf
[0] << 24) | (len_buf
[1] << 16) |
605 (len_buf
[2] << 8) | len_buf
[3]);
607 if (krb5_c_encrypt_length(kcmd_context
, final_enctype
,
608 use_ivecs
? (size_t)rd_len
+ 4 : (size_t)rd_len
,
610 net_len
= ((size_t)-1);
614 if ((net_len
<= 0) || (net_len
> desinbuf
.length
)) {
616 * preposterous length; assume out-of-sync; only recourse
617 * is to close connection, so return 0
619 (void) fprintf(stderr
, gettext("Read size problem.\n"));
623 if ((cc
= krb5_net_read(kcmd_context
, fd
, desinbuf
.data
, net_len
))
625 /* pipe must have closed, return 0 */
626 (void) fprintf(stderr
,
627 gettext("Read error: length received %d "
628 "!= expected %d.\n"),
634 * Decrypt information
636 inputd
.enctype
= ENCTYPE_UNKNOWN
;
637 inputd
.ciphertext
.length
= net_len
;
638 inputd
.ciphertext
.data
= (krb5_pointer
)desinbuf
.data
;
640 outputd
.length
= sizeof (storage
);
641 outputd
.data
= (krb5_pointer
)storage
;
644 * data is decrypted into the "storage" buffer, which
645 * had better be large enough!
647 cc
= krb5_c_decrypt(kcmd_context
, skey
,
648 enc_keyusage_i
[secondary
],
649 use_ivecs
? encivec_i
+ secondary
: 0,
652 (void) fprintf(stderr
, gettext("Cannot decrypt data "
659 if (use_ivecs
== B_TRUE
) {
661 rd_len2
= storage
[0] & 0xff;
662 rd_len2
<<= 8; rd_len2
|= storage
[1] & 0xff;
663 rd_len2
<<= 8; rd_len2
|= storage
[2] & 0xff;
664 rd_len2
<<= 8; rd_len2
|= storage
[3] & 0xff;
665 if (rd_len2
!= rd_len
) {
666 /* cleartext length trashed? */
673 * Copy only as much data as the input buffer will allow.
674 * The rest is kept in the 'storage' pointer for the next
678 (void) memcpy(buf
, store_ptr
, len
);
683 (void) memcpy(buf
, store_ptr
, nstored
);
684 nreturned
+= nstored
;
691 deswrite(int fd
, char *buf
, int len
, int secondary
)
698 return (write(fd
, buf
, len
));
702 p
= buf
+ bytes_written
;
703 if (len
> KCMD8_BUFSIZ
)
704 outlen
= KCMD8_BUFSIZ
;
707 r
= deswrite_compat(fd
, p
, outlen
, secondary
);
713 return (bytes_written
);
716 deswrite_compat(int fd
, char *buf
, int len
, int secondary
)
721 krb5_enc_data outputd
;
722 char tmpbuf
[KCMD_BUFSIZ
+ 8];
723 char encrbuf
[KCMD_BUFSIZ
+ 8];
724 unsigned char *len_buf
= (unsigned char *)tmpbuf
;
726 if (use_ivecs
== B_TRUE
) {
727 unsigned char *lenbuf2
= (unsigned char *)tmpbuf
;
728 if (len
+ 4 > sizeof (tmpbuf
))
730 lenbuf2
[0] = (len
& 0xff000000) >> 24;
731 lenbuf2
[1] = (len
& 0xff0000) >> 16;
732 lenbuf2
[2] = (len
& 0xff00) >> 8;
733 lenbuf2
[3] = (len
& 0xff);
734 (void) memcpy(tmpbuf
+ 4, buf
, len
);
736 inputd
.data
= (krb5_pointer
)tmpbuf
;
737 inputd
.length
= len
+ 4;
739 inputd
.data
= (krb5_pointer
)buf
;
743 desoutbuf
.data
= encrbuf
;
745 if (krb5_c_encrypt_length(kcmd_context
, final_enctype
,
746 use_ivecs
? (size_t)len
+ 4 : (size_t)len
, &ret
)) {
747 desoutbuf
.length
= ((size_t)-1);
750 desoutbuf
.length
= ret
;
753 if (desoutbuf
.length
> MAXSIZE
) {
754 (void) fprintf(stderr
, gettext("Write size problem.\n"));
759 * Encrypt information
761 outputd
.ciphertext
.length
= desoutbuf
.length
;
762 outputd
.ciphertext
.data
= (krb5_pointer
)desoutbuf
.data
;
764 cc
= krb5_c_encrypt(kcmd_context
, skey
,
765 enc_keyusage_o
[secondary
],
766 use_ivecs
? encivec_o
+ secondary
: 0,
771 (void) fprintf(stderr
, gettext("Write encrypt problem.\n"));
775 len_buf
[0] = (len
& 0xff000000) >> 24;
776 len_buf
[1] = (len
& 0xff0000) >> 16;
777 len_buf
[2] = (len
& 0xff00) >> 8;
778 len_buf
[3] = (len
& 0xff);
779 (void) write(fd
, len_buf
, 4);
781 if (write(fd
, desoutbuf
.data
, desoutbuf
.length
) != desoutbuf
.length
) {
782 (void) fprintf(stderr
, gettext("Could not write "