2 rsh.c - remote shell client
3 Copyright (C) 2003 Guus Sliepen <guus@sliepen.eu.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as published
7 by the Free Software Foundation.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <sys/types.h>
24 #include <sys/socket.h>
36 #define SERVICE "host"
41 #define BUFLEN 0x10000
51 "Usage: %s [-46vsx] [-l user] [-p port] [user@]host command...\n",
61 "Usage: %s [-46v] [-l user] [-p port] [user@]host command...\n",
67 /* Make sure everything gets written */
70 safewrite (int fd
, const void *buf
, size_t count
)
72 int written
= 0, result
;
76 result
= write (fd
, buf
, count
);
92 /* Safe and fast string building */
95 safecpy (char **dest
, int *len
, char *source
, bool terminate
)
97 while (*source
&& *len
)
99 *(*dest
)++ = *source
++;
103 if (terminate
&& *len
)
112 /* read encrypted data on socket */
114 readenc (Shishi
* h
, int sock
, char *buf
, int *len
, char *iv
, int *ivlen
,
124 int dlen
= 0, blocksize
, enctype
, hashsize
;
126 /* read size of message */
127 read (sock
, &dlen
, sizeof (int));
130 /* if 0 put read size to 0 */
137 /* convert size to encryption size */
138 enctype
= shishi_key_type (enckey
);
140 blocksize
= shishi_cipher_blocksize (enctype
);
142 shishi_checksum_cksumlen (shishi_cipher_defaultcksumtype (enctype
));
144 dlen
+= blocksize
- 1 + 4;
145 if (shishi_key_type (enckey
) != SHISHI_DES3_CBC_HMAC_SHA1_KD
)
153 if (shishi_key_type (enckey
) == SHISHI_DES3_CBC_HMAC_SHA1_KD
)
156 /* read encrypted data */
157 outbis
= malloc (dlen
);
160 printf ("Malloc error!\n");
164 rc
= read (sock
, outbis
, dlen
);
167 printf ("Error during read socket\n");
173 shishi_decrypt_ivupdate (h
, enckey
, 1026, iv
, *ivlen
, &iv2
, ivlen
, outbis
,
174 dlen
, &out
, &outlen
);
177 printf ("decryption error\n");
181 /* len = first 4 bytes of decrypted data */
182 *len
= ntohl (*((int *) out
));
185 memcpy (iv
, iv2
, *ivlen
);
187 /* Temp patch to remove 5 unidentified bytes data from server */
188 memset (buf
, 0, BUFLEN
);
189 if ((unsigned char) out
[4] == 255)
190 val
= 5 + sizeof (int);
194 /* copy decrypted data to output */
195 memcpy (buf
, out
+ val
, strlen (out
+ val
));
204 /* write encrypted data to socket */
206 writeenc (Shishi
* h
, int sock
, char *buf
, int wlen
, int *len
, char *iv
,
207 int *ivlen
, Shishi_key
* enckey
)
220 /* data to encrypt = size + data */
221 bufbis
= malloc (wlen
+ sizeof (int));
222 memcpy (bufbis
, (char *) &dlen
, sizeof (int));
223 memcpy (bufbis
+ sizeof (int), buf
, wlen
);
227 shishi_encrypt_ivupdate (h
, enckey
, 1026, iv
, *ivlen
, &iv2
, ivlen
, bufbis
,
228 wlen
+ sizeof (int), &out
, &outlen
);
231 printf ("decryption error\n");
237 /* data to send = original size + encrypted data */
238 bufbis
= malloc (outlen
+ sizeof (int));
241 printf ("Malloc error!\n");
244 memcpy (bufbis
, (char *) &dlen
, sizeof (int));
245 memcpy (bufbis
+ sizeof (int), out
, outlen
);
248 write (sock
, bufbis
, outlen
+ sizeof (int));
252 memcpy (iv
, iv2
, *ivlen
);
262 /* shishi authentication */
264 auth (Shishi
* h
, int verbose
, const char *cname
, const char *sname
, int sock
,
265 char *cmd
, char *port
, Shishi_key
** enckey
, Shishi_key
* deckey
)
269 Shishi_tkts_hint hint
;
276 /* KERBEROS 5 SENDAUTH MESSAGE */
277 char krb5sendauth
[] = "KRB5_SENDAUTH_V1.0";
278 /* PROTOCOL VERSION */
279 char krb5sendclient
[] = "KCMDV0.2";
280 /* to store error msg sent by server */
284 /* size of KRB5 auth message */
285 krb5len
= strlen (krb5sendauth
) + 1;
286 msglen
= htonl (krb5len
);
287 safewrite (sock
, &msglen
, sizeof (int));
288 /* KRB5 authentication message */
289 safewrite (sock
, krb5sendauth
, krb5len
);
290 /* size of client message */
291 krb5len
= strlen (krb5sendclient
) + 1;
292 msglen
= htonl (krb5len
);
293 safewrite (sock
, &msglen
, sizeof (int));
294 /* KRB5 client message */
295 safewrite (sock
, krb5sendclient
, krb5len
);
297 /* get answer from server 0 = ok, 1 = error with message */
298 read (sock
, &auth
, 1);
301 read (sock
, errormsg
, 100);
302 errormsg
[100] = '\0';
304 printf ("Error during server authentication : %s\n", errormsg
);
310 printf ("Client: %s\n", cname
);
311 printf ("Server: %s\n", sname
);
314 /* Get a ticket for the server. */
316 memset (&hint
, 0, sizeof (hint
));
318 hint
.client
= (char *) cname
;
319 hint
.server
= (char *) sname
;
321 tkt
= shishi_tkts_get (shishi_tkts_default (h
), &hint
);
324 printf ("cannot find ticket for \"%s\"\n", sname
);
329 shishi_tkt_pretty_print (tkt
, stderr
);
331 /* Create Authentication context */
333 rc
= shishi_ap_tktoptions (h
, &ap
, tkt
, SHISHI_APOPTIONS_MUTUAL_REQUIRED
);
336 printf ("cannot create authentication context\n");
341 /* checksum = port: terminal name */
343 snprintf (cksumdata
, 100, "%s:%s%s", port
, cmd
, cname
);
345 /* add checksum to authenticator */
347 shishi_ap_authenticator_cksumdata_set (ap
, cksumdata
, strlen (cksumdata
));
348 /* To be compatible with MIT rlogind */
349 shishi_ap_authenticator_cksumtype_set (ap
, SHISHI_RSA_MD5
);
351 /* create der encoded AP-REQ */
353 rc
= shishi_ap_req_der (ap
, &out
, &outlen
);
356 printf ("cannot build authentication request: %s\n",
357 shishi_strerror (rc
));
363 shishi_authenticator_print (h
, stderr
, shishi_ap_authenticator (ap
));
365 /* extract subkey if present from ap exchange for secure connection */
367 shishi_authenticator_get_subkey (h
, shishi_ap_authenticator (ap
), enckey
);
369 /* send size of AP-REQ to the server */
371 msglen
= htonl (outlen
);
372 safewrite (sock
, (char *) &msglen
, sizeof (int));
374 /* send AP-REQ to the server */
376 safewrite (sock
, out
, outlen
);
378 /* read a respond from server - what ? */
380 read (sock
, &auth
, sizeof (int));
382 /* For mutual authentication, wait for server reply. */
384 if (shishi_apreq_mutual_required_p (h
, shishi_ap_req (ap
)))
387 printf ("Waiting for server to authenticate itself...\n");
389 /* read size of the AP-REP */
391 read (sock
, (char *) &outlen
, sizeof (int));
394 outlen
= ntohl (outlen
);
395 outlen
= read (sock
, out
, outlen
);
397 rc
= shishi_ap_rep_verify_der (ap
, out
, outlen
);
401 printf ("AP-REP verification OK...\n");
405 if (rc
== SHISHI_APREP_VERIFY_FAILED
)
406 printf ("AP-REP verification failed...\n");
408 printf ("AP-REP verification error: %s\n", shishi_strerror (rc
));
412 /* The server is authenticated. */
414 printf ("Server authenticated.\n");
417 /* We are now authenticated. */
419 printf ("User authenticated.\n");
428 main (int argc
, char **argv
)
433 char *port
= "shell";
440 struct addrinfo hint
, *ai
, *aip
, *lai
;
441 struct sockaddr raddr
;
443 int err
, sock
= -1, lsock
= -1, esock
= -1, i
;
447 bool verbose
= false;
449 char hostaddr
[NI_MAXHOST
];
450 char portnr
[NI_MAXSERV
];
452 char buf
[3][BUFLEN
], *bufp
[3];
455 fd_set infd
, outfd
, infdset
, outfdset
, errfd
;
463 Shishi_key
*enckey
= NULL
, *deckey
= NULL
;
471 struct hostent
*hostdata
;
483 /* Lookup local username */
485 if (!(pw
= getpwuid (getuid ())))
487 fprintf (stderr
, "%s: Could not lookup username: %s\n", argv0
,
492 /* Process options */
495 while ((opt
= getopt (argc
, argv
, "+l:p:46vsx")) != -1)
497 while ((opt
= getopt (argc
, argv
, "+l:p:46v")) != -1)
530 fprintf (stderr
, "%s: Unknown option!\n", argv0
);
538 fprintf (stderr
, "%s: No host specified!\n", argv0
);
553 host
= argv
[optind
++];
555 if ((p
= strchr (host
, '@')))
562 /* Resolve hostname and try to make a connection */
564 memset (&hint
, '\0', sizeof (hint
));
566 hint
.ai_socktype
= SOCK_STREAM
;
568 err
= getaddrinfo (host
, port
, &hint
, &ai
);
572 fprintf (stderr
, "%s: Error looking up host: %s\n", argv0
,
577 hint
.ai_flags
= AI_PASSIVE
;
579 for (aip
= ai
; aip
; aip
= aip
->ai_next
)
582 (aip
->ai_addr
, aip
->ai_addrlen
, hostaddr
, sizeof (hostaddr
), portnr
,
583 sizeof (portnr
), NI_NUMERICHOST
| NI_NUMERICSERV
))
585 fprintf (stderr
, "%s: Error resolving address: %s\n", argv0
,
590 fprintf (stderr
, "Trying %s port %s...", hostaddr
, portnr
);
593 socket (aip
->ai_family
, aip
->ai_socktype
, aip
->ai_protocol
)) == -1)
596 fprintf (stderr
, " Could not open socket: %s\n",
601 hint
.ai_family
= aip
->ai_family
;
603 /* Bind to a privileged port */
605 for (i
= 1023; i
>= 512; i
--)
607 snprintf (lport
, sizeof (lport
), "%d", i
);
608 err
= getaddrinfo (NULL
, lport
, &hint
, &lai
);
611 fprintf (stderr
, " Error looking up localhost: %s\n",
616 err
= bind (sock
, lai
->ai_addr
, lai
->ai_addrlen
);
629 fprintf (stderr
, " Could not bind to privileged port: %s\n",
634 if (connect (sock
, aip
->ai_addr
, aip
->ai_addrlen
) == -1)
637 fprintf (stderr
, " Connection failed: %s\n", strerror (errno
));
641 fprintf (stderr
, " Connected.\n");
647 fprintf (stderr
, "%s: Could not make a connection.\n", argv0
);
651 /* Create a socket for the incoming connection for stderr output */
654 socket (aip
->ai_family
, aip
->ai_socktype
, aip
->ai_protocol
)) == -1)
656 fprintf (stderr
, "%s: Could not open socket: %s\n", argv0
,
661 hint
.ai_family
= aip
->ai_family
;
665 for (i
--; i
>= 512; i
--)
667 snprintf (lport
, sizeof (lport
), "%d", i
);
668 err
= getaddrinfo (NULL
, lport
, &hint
, &lai
);
671 fprintf (stderr
, "%s: Error looking up localhost: %s\n", argv0
,
676 err
= bind (lsock
, lai
->ai_addr
, lai
->ai_addrlen
);
688 fprintf (stderr
, "%s: Could not bind to privileged port: %s\n", argv0
,
693 if (listen (lsock
, 1))
695 fprintf (stderr
, "%s: Could not listen: %s\n", argv0
, strerror (errno
));
699 /* Drop privileges */
701 if (setuid (getuid ()))
703 fprintf (stderr
, "%s: Unable to drop privileges: %s\n", argv0
,
708 /* Send required information to the server */
711 len
[0] = sizeof (buf
[0]);
716 if (!shishi_check_version (SHISHI_VERSION
))
718 printf ("shishi_check_version() failed:\n"
719 "Header file incompatible with shared library.\n");
723 rc
= shishi_init (&h
);
726 printf ("error initializing shishi: %s\n", shishi_strerror (rc
));
730 hostdata
= gethostbyname (host
);
731 hostlen
= strlen (hostdata
->h_name
) + strlen (SERVICE
) + 2;
732 sname
= malloc (hostlen
);
733 snprintf (sname
, hostlen
, "%s/%s", SERVICE
, hostdata
->h_name
);
737 cmd
= malloc (cmdlen
);
741 safecpy (&tcmd
, &cmdlen
, "-x ", 0);
743 for (; optind
< argc
; optind
++)
745 safecpy (&tcmd
, &cmdlen
, argv
[optind
], 0);
746 if (optind
< argc
- 1)
747 safecpy (&tcmd
, &cmdlen
, " ", 0);
750 safecpy (&tcmd
, &cmdlen
, "", 1);
755 user
= (char *) shishi_principal_default (h
);
757 safewrite (sock
, lport
, strlen (lport
) + 1);
759 /* Wait for incoming connection from server */
761 if ((esock
= accept (lsock
, &raddr
, &raddrlen
)) == -1)
763 fprintf (stderr
, "%s: Could not accept stderr connection: %s\n",
764 argv0
, strerror (errno
));
770 if (auth (h
, 0, user
, sname
, sock
, cmd
, port
, &enckey
, deckey
) !=
779 safecpy (&bufp
[0], &len
[0], lport
, 1);
780 safecpy (&bufp
[0], &len
[0], luser
, 1);
784 safecpy (&bufp
[0], &len
[0], lport
, 1);
785 safecpy (&bufp
[0], &len
[0], luser
, 1);
788 safecpy (&bufp
[0], &len
[0], user
, 1);
792 safecpy (&bufp
[0], &len
[0], "-x ", 0);
795 for (; optind
< argc
; optind
++)
797 safecpy (&bufp
[0], &len
[0], argv
[optind
], 0);
798 if (optind
< argc
- 1)
799 safecpy (&bufp
[0], &len
[0], " ", 0);
805 safecpy (&bufp
[0], &len
[0], "", 1);
806 safecpy (&bufp
[0], &len
[0], user
, 1);
811 safecpy (&bufp
[0], &len
[0], "", 1);
815 fprintf (stderr
, "%s: Arguments too long!\n", argv0
);
819 if (safewrite (sock
, buf
[0], bufp
[0] - buf
[0]) == -1)
821 fprintf (stderr
, "%s: Unable to send required information: %s\n", argv0
,
830 safewrite (sock
, &auth2
, sizeof (int));
834 /* Wait for acknowledgement from server */
838 if (read (sock
, buf
[0], 1) != 1 || *buf
[0])
840 fprintf (stderr
, "%s: Didn't receive NULL byte from server: %s\n",
841 argv0
, strerror (errno
));
849 ivlen
= ivlen2
= ivlen3
= shishi_key_length (enckey
);
851 memset (iv
, 1, ivlen
);
852 iv2
= malloc (ivlen2
);
853 memset (iv2
, 3, ivlen2
);
854 iv3
= malloc (ivlen3
);
855 memset (iv3
, 0, ivlen3
);
861 /* Wait for incoming connection from server */
863 if ((esock
= accept (lsock
, &raddr
, &raddrlen
)) == -1)
865 fprintf (stderr
, "%s: Could not accept stderr connection: %s\n",
866 argv0
, strerror (errno
));
875 /* Wait for incoming connection from server */
877 if ((esock
= accept (lsock
, &raddr
, &raddrlen
)) == -1)
879 fprintf (stderr
, "%s: Could not accept stderr connection: %s\n", argv0
,
888 /* Process input/output */
890 flags
= fcntl (sock
, F_GETFL
);
891 fcntl (sock
, F_SETFL
, flags
| O_NONBLOCK
);
892 flags
= fcntl (esock
, F_GETFL
);
893 fcntl (esock
, F_SETFL
, flags
| O_NONBLOCK
);
901 FD_SET (0, &infdset
);
902 FD_SET (sock
, &infdset
);
903 FD_SET (esock
, &infdset
);
905 maxfd
= (sock
> esock
? sock
: esock
) + 1;
914 if (select (maxfd
, &infd
, &outfd
, &errfd
, NULL
) <= 0)
923 if (FD_ISSET (esock
, &infd
))
928 rc
= readenc (h
, esock
, buf
[2], &len
[2], iv2
, &ivlen2
, enckey
);
934 len
[2] = read (esock
, buf
[2], BUFLEN
);
939 if (FD_ISSET (sock
, &infdset
) || FD_ISSET (1, &outfdset
))
940 FD_CLR (esock
, &infdset
);
947 FD_SET (2, &outfdset
);
948 FD_CLR (esock
, &infdset
);
952 if (FD_ISSET (2, &outfd
))
954 wlen
= write (2, bufp
[2], len
[2]);
959 if (FD_ISSET (sock
, &infdset
) || FD_ISSET (1, &outfdset
))
960 FD_CLR (esock
, &infdset
);
971 FD_CLR (2, &outfdset
);
972 FD_SET (esock
, &infdset
);
978 if (FD_ISSET (sock
, &infd
))
983 rc
= readenc (h
, sock
, buf
[1], &len
[1], iv
, &ivlen
, enckey
);
989 len
[1] = read (sock
, buf
[1], BUFLEN
);
994 if (FD_ISSET (esock
, &infdset
) || FD_ISSET (2, &outfdset
))
995 FD_CLR (sock
, &infdset
);
1002 FD_SET (1, &outfdset
);
1003 FD_CLR (sock
, &infdset
);
1007 if (FD_ISSET (1, &outfd
))
1009 wlen
= write (1, bufp
[1], len
[1]);
1014 if (FD_ISSET (esock
, &infdset
) || FD_ISSET (2, &outfdset
))
1015 FD_CLR (sock
, &infdset
);
1026 FD_CLR (1, &outfdset
);
1027 FD_SET (sock
, &infdset
);
1033 if (FD_ISSET (0, &infd
))
1035 len
[0] = read (0, buf
[0], BUFLEN
);
1040 FD_CLR (0, &infdset
);
1041 shutdown (sock
, SHUT_WR
);
1046 FD_SET (sock
, &outfdset
);
1047 FD_CLR (0, &infdset
);
1051 if (FD_ISSET (sock
, &outfd
))
1057 writeenc (h
, sock
, bufp
[0], len
[0], &wlen
, iv3
, &ivlen3
,
1059 if (rc
!= SHISHI_OK
)
1064 wlen
= write (sock
, bufp
[0], len
[0]);
1076 FD_CLR (sock
, &outfdset
);
1077 FD_SET (0, &infdset
);
1088 fprintf (stderr
, "%s: %s\n", argv0
, strerror (errno
));