diff options
author | Damien Miller <djm@mindrot.org> | 1999-10-27 13:42:43 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 1999-10-27 13:42:43 +1000 |
commit | d4a8b7e34dd619a4debf9a206c81db26d1402ea6 (patch) | |
tree | a47d770a2f790f40d18b0982d4e55fa7cfb1fa3b |
Initial revision
-rw-r--r-- | COPYING.Ylonen | 70 | ||||
-rw-r--r-- | ChangeLog | 578 | ||||
-rw-r--r-- | ChangeLog.linux | 20 | ||||
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | Makefile.GNU | 50 | ||||
-rw-r--r-- | Makefile.inc | 11 | ||||
-rw-r--r-- | OVERVIEW | 164 | ||||
-rw-r--r-- | README | 563 | ||||
-rw-r--r-- | README.openssh | 44 | ||||
-rw-r--r-- | RFC.nroff | 1780 | ||||
-rw-r--r-- | auth-krb4.c | 209 | ||||
-rw-r--r-- | auth-passwd.c | 209 | ||||
-rw-r--r-- | auth-rh-rsa.c | 83 | ||||
-rw-r--r-- | auth-rhosts.c | 298 | ||||
-rw-r--r-- | auth-rsa.c | 478 | ||||
-rw-r--r-- | auth-skey.c | 149 | ||||
-rw-r--r-- | authfd.c | 565 | ||||
-rw-r--r-- | authfd.h | 102 | ||||
-rw-r--r-- | authfile.c | 350 | ||||
-rw-r--r-- | bufaux.c | 141 | ||||
-rw-r--r-- | bufaux.h | 51 | ||||
-rw-r--r-- | buffer.c | 150 | ||||
-rw-r--r-- | buffer.h | 66 | ||||
-rw-r--r-- | canohost.c | 234 | ||||
-rw-r--r-- | channels.c | 1500 | ||||
-rw-r--r-- | channels.h | 41 | ||||
-rw-r--r-- | cipher.c | 304 | ||||
-rw-r--r-- | cipher.h | 84 | ||||
-rw-r--r-- | clientloop.c | 924 | ||||
-rw-r--r-- | compat.c | 10 | ||||
-rw-r--r-- | compat.h | 7 | ||||
-rw-r--r-- | compress.c | 160 | ||||
-rw-r--r-- | compress.h | 46 | ||||
-rw-r--r-- | crc32.c | 120 | ||||
-rw-r--r-- | crc32.h | 25 | ||||
-rw-r--r-- | deattack.c | 180 | ||||
-rw-r--r-- | deattack.h | 27 | ||||
-rw-r--r-- | getput.h | 64 | ||||
-rw-r--r-- | helper.c | 108 | ||||
-rw-r--r-- | helper.h | 43 | ||||
-rw-r--r-- | hostfile.c | 279 | ||||
-rw-r--r-- | includes.h | 78 | ||||
-rw-r--r-- | log-client.c | 138 | ||||
-rw-r--r-- | log-server.c | 233 | ||||
-rw-r--r-- | login.c | 118 | ||||
-rw-r--r-- | match.c | 78 | ||||
-rw-r--r-- | mktemp.c | 181 | ||||
-rw-r--r-- | mktemp.h | 7 | ||||
-rw-r--r-- | mpaux.c | 46 | ||||
-rw-r--r-- | mpaux.h | 32 | ||||
-rw-r--r-- | nchan.c | 187 | ||||
-rw-r--r-- | nchan.h | 57 | ||||
-rw-r--r-- | nchan.ms | 71 | ||||
-rw-r--r-- | openssh.spec | 105 | ||||
-rw-r--r-- | packet.c | 762 | ||||
-rw-r--r-- | packet.h | 166 | ||||
-rw-r--r-- | pty.c | 264 | ||||
-rw-r--r-- | pty.h | 40 | ||||
-rw-r--r-- | radix.c | 258 | ||||
-rw-r--r-- | rc4.c | 105 | ||||
-rw-r--r-- | rc4.h | 110 | ||||
-rw-r--r-- | readconf.c | 684 | ||||
-rw-r--r-- | readconf.h | 116 | ||||
-rw-r--r-- | readpass.c | 114 | ||||
-rw-r--r-- | rsa.c | 164 | ||||
-rw-r--r-- | rsa.h | 36 | ||||
-rw-r--r-- | scp.1 | 110 | ||||
-rw-r--r-- | scp.c | 1220 | ||||
-rw-r--r-- | servconf.c | 567 | ||||
-rw-r--r-- | servconf.h | 86 | ||||
-rw-r--r-- | serverloop.c | 644 | ||||
-rw-r--r-- | ssh-add.1 | 116 | ||||
-rw-r--r-- | ssh-add.c | 254 | ||||
-rw-r--r-- | ssh-agent.1 | 124 | ||||
-rw-r--r-- | ssh-agent.c | 572 | ||||
-rw-r--r-- | ssh-keygen.1 | 155 | ||||
-rw-r--r-- | ssh-keygen.c | 552 | ||||
-rw-r--r-- | ssh.1 | 966 | ||||
-rw-r--r-- | ssh.c | 809 | ||||
-rw-r--r-- | ssh.h | 589 | ||||
-rw-r--r-- | ssh.pam | 7 | ||||
-rw-r--r-- | ssh_config | 30 | ||||
-rw-r--r-- | sshconnect.c | 1495 | ||||
-rw-r--r-- | sshd.8 | 781 | ||||
-rw-r--r-- | sshd.c | 2445 | ||||
-rwxr-xr-x | sshd.init | 49 | ||||
-rw-r--r-- | sshd_config | 44 | ||||
-rw-r--r-- | strlcpy.c | 68 | ||||
-rw-r--r-- | strlcpy.h | 4 | ||||
-rw-r--r-- | tildexpand.c | 70 | ||||
-rw-r--r-- | ttymodes.c | 359 | ||||
-rw-r--r-- | ttymodes.h | 138 | ||||
-rw-r--r-- | uidswap.c | 95 | ||||
-rw-r--r-- | uidswap.h | 30 | ||||
-rw-r--r-- | version.h | 1 | ||||
-rw-r--r-- | xmalloc.c | 56 | ||||
-rw-r--r-- | xmalloc.h | 34 |
97 files changed, 26920 insertions, 0 deletions
diff --git a/COPYING.Ylonen b/COPYING.Ylonen new file mode 100644 index 000000000..5e681edd2 --- /dev/null +++ b/COPYING.Ylonen | |||
@@ -0,0 +1,70 @@ | |||
1 | This file is part of the ssh software, Copyright (c) 1995 Tatu Ylonen, Finland | ||
2 | |||
3 | |||
4 | COPYING POLICY AND OTHER LEGAL ISSUES | ||
5 | |||
6 | As far as I am concerned, the code I have written for this software | ||
7 | can be used freely for any purpose. Any derived versions of this | ||
8 | software must be clearly marked as such, and if the derived work is | ||
9 | incompatible with the protocol description in the RFC file, it must be | ||
10 | called by a name other than "ssh" or "Secure Shell". | ||
11 | |||
12 | However, I am not implying to give any licenses to any patents or | ||
13 | copyrights held by third parties, and the software includes parts that | ||
14 | are not under my direct control. As far as I know, all included | ||
15 | source code is used in accordance with the relevant license agreements | ||
16 | and can be used freely for any purpose (the GNU license being the most | ||
17 | restrictive); see below for details. | ||
18 | |||
19 | [ RSA is no longer included. ] | ||
20 | [ IDEA is no longer included. ] | ||
21 | [ DES is now external. ] | ||
22 | [ GMP is now external. No more GNU licence. ] | ||
23 | [ Zlib is now external. ] | ||
24 | [ The make-ssh-known-hosts script is no longer included. ] | ||
25 | [ TSS has been removed. ] | ||
26 | [ MD5 is now external. ] | ||
27 | [ RC4 support has been removed. ] | ||
28 | [ Blowfish is now external. ] | ||
29 | |||
30 | The 32-bit CRC implementation in crc32.c is due to Gary S. Brown. | ||
31 | Comments in the file indicate it may be used for any purpose without | ||
32 | restrictions. | ||
33 | |||
34 | The 32-bit CRC compensation attack detector in deattack.c was | ||
35 | contributed by CORE SDI S.A. under a BSD-style license. See | ||
36 | http://www.core-sdi.com/english/ssh/ for details. | ||
37 | |||
38 | Note that any information and cryptographic algorithms used in this | ||
39 | software are publicly available on the Internet and at any major | ||
40 | bookstore, scientific library, and patent office worldwide. More | ||
41 | information can be found e.g. at "http://www.cs.hut.fi/crypto". | ||
42 | |||
43 | The legal status of this program is some combination of all these | ||
44 | permissions and restrictions. Use only at your own responsibility. | ||
45 | You will be responsible for any legal consequences yourself; I am not | ||
46 | making any claims whether possessing or using this is legal or not in | ||
47 | your country, and I am not taking any responsibility on your behalf. | ||
48 | |||
49 | |||
50 | NO WARRANTY | ||
51 | |||
52 | BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||
53 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | ||
54 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | ||
55 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | ||
56 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
57 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS | ||
58 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE | ||
59 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | ||
60 | REPAIR OR CORRECTION. | ||
61 | |||
62 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||
63 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | ||
64 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | ||
65 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | ||
66 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | ||
67 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | ||
68 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | ||
69 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||
70 | POSSIBILITY OF SUCH DAMAGES. | ||
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 000000000..08d90f78d --- /dev/null +++ b/ChangeLog | |||
@@ -0,0 +1,578 @@ | |||
1 | Fri Nov 17 16:19:20 1995 Tatu Ylonen <ylo@trance.olari.clinet.fi> | ||
2 | |||
3 | * Released 1.2.12. | ||
4 | |||
5 | * channels.c: Commented out debugging messages about output draining. | ||
6 | |||
7 | * Added file OVERVIEW to give some idea about the structure of the | ||
8 | ssh software. | ||
9 | |||
10 | Thu Nov 16 16:40:17 1995 Tatu Ylonen <ylo@trance.olari.clinet.fi> | ||
11 | |||
12 | * canohost.c (get_remote_hostname): Don't ever return NULL (causes | ||
13 | segmentation violation). | ||
14 | |||
15 | * sshconnect.c: Host ip address printed incorrectly with -v. | ||
16 | |||
17 | * Implemented SSH_TTY environment variable. | ||
18 | |||
19 | Wed Nov 15 01:47:40 1995 Tatu Ylonen <ylo@trance.olari.clinet.fi> | ||
20 | |||
21 | * Implemented server and client option KeepAlive to specify | ||
22 | whether to set SO_KEEPALIVE. Both default to "yes"; to disable | ||
23 | keepalives, set the value to "no" in both the server and the | ||
24 | client configuration files. Updated manual pages. | ||
25 | |||
26 | * sshd.c: Fixed Solaris utmp problem: wrong pid stored in utmp | ||
27 | (patch from Petri Virkkula <argon@bat.cs.hut.fi>). | ||
28 | |||
29 | * login.c (record_logout): Fixed removing user from utmp on BSD | ||
30 | (with HAVE_LIBUTIL_LOGIN). | ||
31 | |||
32 | * Added cleanup functions to be called from fatal(). Arranged for | ||
33 | utmp to be cleaned if sshd terminates by calling fatal (e.g., | ||
34 | after dropping connection). Eliminated separate client-side | ||
35 | fatal() functions and moved fatal() to log-client.c. Made all | ||
36 | cleanups, including channel_stop_listening() and packet_close() | ||
37 | be called using this mechanism. | ||
38 | |||
39 | Thu Nov 9 09:58:05 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
40 | |||
41 | * sshd.c: Permit immediate login with empty password only if | ||
42 | password authentication is allowed. | ||
43 | |||
44 | Wed Nov 8 00:43:55 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
45 | |||
46 | * Eliminated unix-domain X11 forwarding. Inet-domain forwarding is | ||
47 | now the only supported form. Renamed server option | ||
48 | X11InetForwarding to X11Forwarding, and eliminated | ||
49 | X11UnixForwarding. Updated documentation. Updated RFC (marked | ||
50 | the SSH_CMSG_X11_REQUEST_FORWARDING message (code 26) as | ||
51 | obsolete, and removed all references to it). Increased protocol | ||
52 | version number to 1.3. | ||
53 | |||
54 | * scp.c (main): Added -B (BatchMode). Updated manual page. | ||
55 | |||
56 | * Cleaned up and updated all manual pages. | ||
57 | |||
58 | * clientloop.c: Added new escape sequences ~# (lists forwarded | ||
59 | connections), ~& (background ssh when waiting for forwarded | ||
60 | connections to terminate), ~? (list available escapes). | ||
61 | Polished the output of the connection listing. Updated | ||
62 | documentation. | ||
63 | |||
64 | * uidswap.c: If _POSIX_SAVED_IDS is defined, don't change the real | ||
65 | uid. Assume that _POSIX_SAVED_IDS also applies to seteuid. | ||
66 | This may solve problems with tcp_wrappers (libwrap) showing | ||
67 | connections as coming from root. | ||
68 | |||
69 | Tue Nov 7 20:28:57 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
70 | |||
71 | * Added RandomSeed server configuration option. The argument | ||
72 | specifies the location of the random seed file. Updated | ||
73 | documentation. | ||
74 | |||
75 | * Locate perl5 in configure. Generate make-ssh-known-hosts (with | ||
76 | the correct path for perl5) in Makefile.in, and install it with | ||
77 | the other programs. Updated manual page. | ||
78 | |||
79 | * sshd.c (main): Added a call to umask to set the umask to a | ||
80 | reasonable value. | ||
81 | |||
82 | * compress.c (buffer_compress): Fixed to follow the zlib | ||
83 | documentation (which is slightly confusing). | ||
84 | |||
85 | * INSTALL: Added information about Linux libc.so.4 problem. | ||
86 | |||
87 | Mon Nov 6 15:42:36 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
88 | |||
89 | * (Actually autoconf fix) Installed patch to AC_ARG_PROGRAM. | ||
90 | |||
91 | * sshd.c, sshd.8.in: Renamed $HOME/.environment -> | ||
92 | $HOME/.ssh/environment. | ||
93 | |||
94 | * configure.in: Disable shadow password checking on convex. | ||
95 | Convex has /etc/shadow, but sets pw_passwd automatically if | ||
96 | running as root. | ||
97 | |||
98 | * Eliminated HAVE_ETC_MASTER_PASSWD (NetBSD, FreeBSD); the | ||
99 | pw_passwd field is automatically filled if running as root. | ||
100 | Put explicit code in configure.in to prevent shadow password | ||
101 | checking on FreeBSD and NetBSD. | ||
102 | |||
103 | * serverloop.c (signchld_handler): Don't print error if wait | ||
104 | returns -1. | ||
105 | |||
106 | * Makefile.in (install): Fixed modes of data files. | ||
107 | |||
108 | * Makefile.in (install): Make links for slogin.1. | ||
109 | |||
110 | * make-ssh-known-hosts: Merged a patch from melo@ci.uminho.pt to | ||
111 | fix the ping command. | ||
112 | |||
113 | Fri Nov 3 16:25:28 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
114 | |||
115 | * ssh.1.in: Added more information about X11 forwarding. | ||
116 | |||
117 | Thu Nov 2 18:42:13 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
118 | |||
119 | * Changes to use O_NONBLOCK_BROKEN consistently. | ||
120 | |||
121 | * pty.c (pty_make_controlling_tty): Use setpgid instead of | ||
122 | setsid() on Ultrix. | ||
123 | |||
124 | * includes.h: Removed redundant #undefs for Ultrix and Sony News; | ||
125 | these are already handled in configure.in. | ||
126 | |||
127 | Tue Oct 31 13:31:28 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
128 | |||
129 | * configure.in: Define SSH_WTMP to /var/adm/wtmp is wtmp not found. | ||
130 | |||
131 | * configure.in: Disable vhangup on Ultrix. I am told this fixes | ||
132 | the server problems. | ||
133 | |||
134 | Sat Oct 28 14:22:05 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
135 | |||
136 | * sshconnect.c: Fixed a bug in connecting to a multi-homed host. | ||
137 | Restructured the connecting code to never try to use the same | ||
138 | socket a second time after a failed connection. | ||
139 | |||
140 | * Makefile.in: Added explicit -m option to install, and umask 022 | ||
141 | when creating directories and the host key. | ||
142 | |||
143 | Fri Oct 27 01:05:10 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
144 | |||
145 | * Makefile.in: Added cleaning of $(ZLIBDIR) to clean and distclean. | ||
146 | |||
147 | * login.c (get_last_login_time): Fixed a typo (define -> defined). | ||
148 | |||
149 | Thu Oct 26 01:28:07 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
150 | |||
151 | * configure.in: Moved testing for ANSI C compiler after the host | ||
152 | specific code (problems on HPUX). | ||
153 | |||
154 | * Minor fixes to /etc/default/login stuff from Bryan O'Sullivan. | ||
155 | |||
156 | * Fixed .SH NAME sections in manual pages. | ||
157 | |||
158 | * compress.c: Trying to fix a mysterious bug in the compression | ||
159 | glue. | ||
160 | |||
161 | * ssh-1.2.11. | ||
162 | |||
163 | * scp.c: disable agent forwarding when running ssh from scp. | ||
164 | |||
165 | * Added compression of plaintext packets using the gzip library | ||
166 | (zlib). Client configuration options Compression and | ||
167 | CompressionLevel (1-9 as in gzip). New ssh and scp option -C | ||
168 | (to enable compression). Updated RFC. | ||
169 | |||
170 | Wed Oct 25 05:11:55 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
171 | |||
172 | * Implemented ProxyCommand stuff based on patches from Bryan | ||
173 | O'Sullivan <bos@serpentine.com>. | ||
174 | |||
175 | * Merged BSD login/logout/lastlog patches from Mark Treacy | ||
176 | <mark@labtam.oz.au>. | ||
177 | |||
178 | * sshd.c: Added chdir("/"). | ||
179 | |||
180 | Tue Oct 24 00:29:01 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
181 | |||
182 | * Merged RSA environment= patches from Felix Leitner | ||
183 | <leitner@prz.tu-berlin.de> with some changes. | ||
184 | |||
185 | * sshd.c: Made the packet code use two separate descriptors for | ||
186 | the connection (one for input, the other for output). This will | ||
187 | make future extensions easier (e.g., non-socket transports, etc.). | ||
188 | sshd -i now uses both stdin and stdout separately. | ||
189 | |||
190 | Mon Oct 23 21:29:28 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
191 | |||
192 | * sshd.c: Merged execle -> execve patches from Mark Martinec | ||
193 | <Mark.Martinec@nsc.ijs.si>. This may help with execle bugs on | ||
194 | Convex (environment not getting passed properly). This might | ||
195 | also solve similar problems on Sonys; please test! | ||
196 | |||
197 | * Removed all compatibility code for protocol version 1.0. | ||
198 | THIS MEANS THAT WE ARE NO LONGER COMPATIBLE WITH SSH VERSIONS | ||
199 | PRIOR TO 1.1.0. | ||
200 | |||
201 | * randoms.c (random_acquire_light_environmental_noise): If | ||
202 | /dev/random is available, read up to 32 bytes (256 bits) from | ||
203 | there in non-blocking mode, and mix the new random bytes into | ||
204 | the pool. | ||
205 | |||
206 | * Added client configuration option StrictHostKeyChecking | ||
207 | (disabled by default). If this is enabled, the client will not | ||
208 | automatically add new host keys to $HOME/.ssh/known_hosts; | ||
209 | instead the connection will be refused if the host key is not | ||
210 | known. Similarly, if the host key has changed, the connection | ||
211 | will be refused instead if just issuing a warning. This | ||
212 | provides additional security against man-in-the-middle/trojan | ||
213 | horse attacks (especially in scripts where there is no-one to | ||
214 | see the warnings), but may be quite inconvenient in everyday | ||
215 | interactive use unless /etc/ssh_known_hosts is very complete, | ||
216 | because new host keys must now be added manually. | ||
217 | |||
218 | * sshconnect.c (ssh_connect): Use the user's uid when creating the | ||
219 | socket and connecting it. I am hoping that this might help with | ||
220 | tcp_wrappers showing the remote user as root. | ||
221 | |||
222 | * ssh.c: Try inet-domain X11 forwarding regardless of whether we | ||
223 | can get local authorization information. If we don't, we just | ||
224 | come up with fake information; the forwarding code will anyway | ||
225 | generate its own fake information and validate that the client | ||
226 | knows that information. It will then substitute our fake | ||
227 | information for that, but that info should get ignored by the | ||
228 | server if it doesn't support it. | ||
229 | |||
230 | * Added option BatchMode to disable password/passphrase querying | ||
231 | in scripts. | ||
232 | |||
233 | * auth-rh-rsa.c: Changed to use uid-swapping when reading | ||
234 | .ssh/known_hosts. | ||
235 | |||
236 | * sshd.8.in (command): Improved documentation of file permissions | ||
237 | on the manual pages. | ||
238 | |||
239 | Thu Oct 19 21:05:51 1995 Tatu Ylonen <ylo@soikko.cs.hut.fi> | ||
240 | |||
241 | * ssh-add.c (add_file): Fixed a bug causing ssh to sometimes refer | ||
242 | to freed memory (comment -> saved_comment). | ||
243 | |||
244 | * log-server.c: Added a prefix to debug/warning/error/fatal | ||
245 | messages describing message types. Syslog does not include that | ||
246 | information automatically. | ||
247 | |||
248 | Sun Oct 8 01:56:01 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
249 | |||
250 | * Merged /etc/default/login and MAIL environment variable changes | ||
251 | from Bryan O'Sullivan <bos@serpentine.com>. | ||
252 | - mail spool file location | ||
253 | - process /etc/default/login | ||
254 | - add HAVE_ETC_DEFAULT_LOGIN | ||
255 | - new function child_get_env and read_etc_default_login (sshd.c) | ||
256 | |||
257 | * ssh-add.c (add_file): Fixed asking for passphrase. | ||
258 | |||
259 | * Makefile.in: Fixed installing configure-generated man pages when | ||
260 | compiling in a separate object directory. | ||
261 | |||
262 | * sshd.c (main): Moved RSA key generation until after allocating | ||
263 | the port number. (Actually, the code got duplicated because we | ||
264 | never listen when run from inetd.) | ||
265 | |||
266 | * ssh.c: Fixed a problem that caused scp to hang when called with | ||
267 | stdin closed. | ||
268 | |||
269 | Sat Oct 7 03:08:06 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
270 | |||
271 | * Added server config option StrictModes. It specifies whether to | ||
272 | check ownership and modes of home directory and .rhosts files. | ||
273 | |||
274 | * ssh.c: If ssh is renamed/linked to a host name, connect to that | ||
275 | host. | ||
276 | |||
277 | * serverloop.c, clientloop.c: Ignore EAGAIN reported on read from | ||
278 | connection. Solaris has a kernel bug which causes select() to | ||
279 | sometimes wake up even though there is no data available. | ||
280 | |||
281 | * Display all open connections when printing the "Waiting for | ||
282 | forwarded connections to terminate" message. | ||
283 | |||
284 | * sshd.c, readconf.c: Added X11InetForwarding and | ||
285 | X11UnixForwarding server config options. | ||
286 | |||
287 | Thu Oct 5 17:41:16 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
288 | |||
289 | * Some more SCO fixes. | ||
290 | |||
291 | Tue Oct 3 01:04:34 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
292 | |||
293 | * Fixes and cleanups in README, INSTALL, COPYING. | ||
294 | |||
295 | Mon Oct 2 03:36:08 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
296 | |||
297 | * ssh-add.c (add_file): Fixed a bug in ssh-add (xfree: NULL ...). | ||
298 | |||
299 | * Removed .BR from ".SH NAME" in man pages. | ||
300 | |||
301 | Sun Oct 1 04:16:07 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
302 | |||
303 | * ssh-1.2.10. | ||
304 | |||
305 | * configure.in: When checking that the compiler works, check that | ||
306 | it understands ANSI C prototypes. | ||
307 | |||
308 | * Made uidswap error message a debug() to avoid confusing errors | ||
309 | on AIX (AIX geteuid is brain-damaged and fails even for root). | ||
310 | |||
311 | * Fixed an error in sshd.8 (FacistLogging -> FascistLogging). | ||
312 | |||
313 | * Fixed distribution in Makefile.in (missing manual page .in files). | ||
314 | |||
315 | Sat Sep 30 17:38:46 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
316 | |||
317 | * auth-rhosts.c: Fixed serious security problem in | ||
318 | /etc/hosts.equiv authentication. | ||
319 | |||
320 | Fri Sep 29 00:41:02 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
321 | |||
322 | * Include machine/endian.h on Paragon. | ||
323 | |||
324 | * ssh-add.c (add_file): Made ssh-add keep asking for the | ||
325 | passphrase until the user just types return or cancels. | ||
326 | Make the dialog display the comment of the key. | ||
327 | |||
328 | * Read use shosts.equiv in addition to /etc/hosts.equiv. | ||
329 | |||
330 | * sshd.8 is now sshd.8.in and is processed by configure to | ||
331 | substitute the proper paths for various files. Ditto for ssh.1. | ||
332 | Ditto for make-ssh-known-hosts.1. | ||
333 | |||
334 | * configure.in: Moved /etc/sshd_pid to PIDDIR/sshd.pid. PIDDIR | ||
335 | will be /var/run if it exists, and ETCDIR otherwise. | ||
336 | |||
337 | Thu Sep 28 21:52:42 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
338 | |||
339 | * On Ultrix, check if sys/syslog.h needs to be included in | ||
340 | addition to syslog.h. | ||
341 | |||
342 | * make-ssh-known-hosts.pl: Merged Kivinen's fixes for HPUX. | ||
343 | |||
344 | * configure.in: Put -lwrap, -lsocks, etc. at the head of LIBS. | ||
345 | |||
346 | * Fixed case-insensitivity in auth-rhosts.c. | ||
347 | |||
348 | * Added missing socketpair.c to EXTRA_SRCS (needed on SCO), plus | ||
349 | other SCO fixes. | ||
350 | |||
351 | * Makefile.in: Fixed missing install_prefixes. | ||
352 | |||
353 | Wed Sep 27 03:57:00 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
354 | |||
355 | * ssh-1.2.9. | ||
356 | |||
357 | * Added SOCKS support. | ||
358 | |||
359 | * Fixed default setting of IgnoreRhosts option. | ||
360 | |||
361 | * Pass the magic cookie to xauth in stdin instead of command line; | ||
362 | the command line is visible in ps. | ||
363 | |||
364 | * Added processing $HOME/.ssh/rc and /etc/sshrc. | ||
365 | |||
366 | * Added a section to sshd.8 on what happens at login time. | ||
367 | |||
368 | Tue Sep 26 01:27:40 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
369 | |||
370 | * Don't define speed_t on SunOS 4.1.1; it conflicts with system | ||
371 | headers. | ||
372 | |||
373 | * Added support for .hushlogin. | ||
374 | |||
375 | * Added --with-etcdir. | ||
376 | |||
377 | * Read $HOME/.environment after /etc/environment. | ||
378 | |||
379 | Mon Sep 25 03:26:06 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
380 | |||
381 | * Merged patches for SCO Unix (from Michael Henits). | ||
382 | |||
383 | Sun Sep 24 22:28:02 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
384 | |||
385 | * Added ssh option ConnectionAttempts. | ||
386 | |||
387 | Sat Sep 23 12:30:15 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
388 | |||
389 | * sshd.c: Don't print last login time and /etc/motd if a command | ||
390 | has been specified (with ssh -t host command). | ||
391 | |||
392 | * Added support for passing the screen number in X11 forwarding. | ||
393 | It is implemented as a compatible protocol extension, signalled | ||
394 | by SSH_PROTOFLAG_SCREEN_NUMBER by the child. | ||
395 | |||
396 | * clientloop.c: Fixed bugs in the order in which things were | ||
397 | processed. This may solve problems with some data not getting | ||
398 | sent to the server as soon as possible (probably solves the TCP | ||
399 | forwarding delayed close problem). Also, it looked like window | ||
400 | changes might not get transmitted as early as possible in some | ||
401 | cases. | ||
402 | |||
403 | * clientloop.c: Changed to detect window size change that | ||
404 | happened while ssh was suspended. | ||
405 | |||
406 | * ssh.c: Moved the do_session function (client main loop) to | ||
407 | clientloop.c. Divided it into smaller functions. General cleanup. | ||
408 | |||
409 | * ssh-1.2.8 | ||
410 | |||
411 | Fri Sep 22 22:07:46 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
412 | |||
413 | * sshconnect.c (ssh_login): Made ssh_login take the options | ||
414 | structure as argument, instead of the individual arguments. | ||
415 | |||
416 | * auth-rhosts.c (check_rhosts_file): Added support for netgroups. | ||
417 | |||
418 | * auth-rhosts.c (check_rhosts_file): Added support for negated | ||
419 | entries. | ||
420 | |||
421 | Thu Sep 21 00:07:56 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
422 | |||
423 | * auth-rhosts.c: Restructured rhosts authentication code. | ||
424 | Hosts.equiv now has same format as .rhosts: user names are allowed. | ||
425 | |||
426 | * Added support for the Intel Paragon. | ||
427 | |||
428 | * sshd.c: Don't use X11 forwarding with spoofing if no xauth | ||
429 | program. Changed configure.in to not define XAUTH_PATH if | ||
430 | there is no xauth program. | ||
431 | |||
432 | * ssh-1.2.7 | ||
433 | |||
434 | * sshd.c: Rewrote the code to build the environment. Now also reads | ||
435 | /etc/environment. | ||
436 | |||
437 | * sshd.c: Fixed problems in libwrap code. --with-libwrap now | ||
438 | takes optional library name/path. | ||
439 | |||
440 | * ssh-1.2.6 | ||
441 | |||
442 | * Define USE_PIPES by default. | ||
443 | |||
444 | * Added support for Univel Unixware and MachTen. | ||
445 | |||
446 | * Added IgnoreRhosts server option. | ||
447 | |||
448 | * Added USE_STRLEN_FOR_AF_UNIX; it is needed at least on MachTen. | ||
449 | |||
450 | Wed Sep 20 02:41:02 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
451 | |||
452 | * sshd.c (do_child): don't call packet_close when /etc/nologin, | ||
453 | because packet_close does shutdown, and the message does not get | ||
454 | sent. | ||
455 | |||
456 | * pty.c (pty_allocate): Push ttcompat streams module. | ||
457 | |||
458 | * randoms.c (random_acquire_light_environmental_noise): Don't use | ||
459 | the second argument to gettimeofday as it is not supported on | ||
460 | all systems. | ||
461 | |||
462 | * login.c (record_login): Added NULL second argument to gettimeofday. | ||
463 | |||
464 | Tue Sep 19 13:25:48 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
465 | |||
466 | * fixed pclose wait() in sshd key regeneration (now only collects | ||
467 | easily available noise). | ||
468 | |||
469 | * configure.in: test for bsdi before bsd*. | ||
470 | |||
471 | * ssh.c: Don't print "Connection closed" if -q. | ||
472 | |||
473 | Wed Sep 13 04:19:52 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
474 | |||
475 | * Released ssh-1.2.5. | ||
476 | |||
477 | * Hopefully fixed "Waiting for forwarded connections to terminate" | ||
478 | message. | ||
479 | |||
480 | * randoms.c, md5.c: Large modifications to make these work on Cray | ||
481 | (which has no 32 bit integer type). | ||
482 | |||
483 | * Fixed a problem with forwarded connection closes not being | ||
484 | reported immediately. | ||
485 | |||
486 | * ssh.c: fixed rhosts authentication (broken by uid-swapping). | ||
487 | |||
488 | * scp.c: Don't use -l if server user not specified (it made | ||
489 | setting User in the configuration file not work). | ||
490 | |||
491 | * configure.in: don't use -pipe on BSDI. | ||
492 | |||
493 | * randoms.c: Major modifications to make it work without 32 bit | ||
494 | integers (e.g. Cray). | ||
495 | |||
496 | * md5.c: Major modifications to make it work without 32 bit | ||
497 | integers (e.g. Cray). | ||
498 | |||
499 | * Eliminated HPSUX_BROKEN_PTYS. The code is now enabled by | ||
500 | default on all systems. | ||
501 | |||
502 | Mon Sep 11 00:53:12 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
503 | |||
504 | * sshd.c: don't include sshd pathname in log messages. | ||
505 | |||
506 | * Added libwrap stuff (includes support for identd). | ||
507 | |||
508 | * Added OSF/1 C2 extended security stuff. | ||
509 | |||
510 | * Fixed interactions between getuid() and uid-swap stuff. | ||
511 | |||
512 | Sun Sep 10 00:29:27 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
513 | |||
514 | * serverloop.c: Don't send stdout data to client until after a few | ||
515 | milliseconds if there is very little data. This is because some | ||
516 | systems give data from pty one character at a time, which would | ||
517 | multiply data size by about 16. | ||
518 | |||
519 | * serverloop.c: Moved server do_session to a separate file and | ||
520 | renamed it server_loop. Split it into several functions and | ||
521 | partially rewrote it. Fixed "cat /etc/termcap | ssh foo cat" hangup. | ||
522 | |||
523 | * Screwed up something while checking stuff in under cvs. No harm, | ||
524 | but bogus log entries... | ||
525 | |||
526 | Sat Sep 9 02:24:51 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
527 | |||
528 | * minfd.c (_get_permanent_fd): Use SHELL environment variable. | ||
529 | |||
530 | * channels.c (x11_create_display_inet): Created | ||
531 | HPSUX_NONSTANDARD_X11_KLUDGE; it causes DISPLAY to contain the | ||
532 | IP address of the host instead of the name, because HPSUX uses | ||
533 | some magic shared memory communication for local connections. | ||
534 | |||
535 | * Changed SIGHUP processing in server; it should now work multiple | ||
536 | times. | ||
537 | |||
538 | * Added length limits in many debug/log/error/fatal calls just in | ||
539 | case. | ||
540 | |||
541 | * login.c (get_last_login_time): Fixed location of lastlog. | ||
542 | |||
543 | * Rewrote all uid-swapping code. New files uidswap.h, uidswap.c. | ||
544 | |||
545 | * Fixed several security problems involving chmod and chgrp (race | ||
546 | conditions). Added warnings about dubious modes for /tmp/.X11-unix. | ||
547 | |||
548 | Fri Sep 8 20:03:36 1995 Tatu Ylonen <ylo@shadows.cs.hut.fi> | ||
549 | |||
550 | * Changed readconf.c to never display anything from the config | ||
551 | file. This should now be prevented otherwise, but let's play safe. | ||
552 | |||
553 | * log-server.c: Use %.500s in syslog() just to be sure (they | ||
554 | should already be shorter than 1024 though). | ||
555 | |||
556 | * sshd.c: Moved setuid in child a little earlier (just to be | ||
557 | conservative, there was no security problem that I could detect). | ||
558 | |||
559 | * README, INSTALL: Added info about mailing list and WWW page. | ||
560 | |||
561 | * sshd.c: Added code to use SIGCHLD and wait zombies immediately. | ||
562 | |||
563 | * Merged patch to set ut_addr in utmp. | ||
564 | |||
565 | * Created ChangeLog and added it to Makefile.in. | ||
566 | |||
567 | * Use read_passphrase instead of getpass(). | ||
568 | |||
569 | * Added SSH_FALLBACK_CIPHER. Fixed a bug in default cipher | ||
570 | selection (IDEA used to be selected even if not supported by the | ||
571 | server). | ||
572 | |||
573 | * Use no encryption for key files if empty passphrase. | ||
574 | |||
575 | * Added section about --without-idea in INSTALL. | ||
576 | |||
577 | * Version 1.2.0 was released a couple of days ago. | ||
578 | |||
diff --git a/ChangeLog.linux b/ChangeLog.linux new file mode 100644 index 000000000..a28e577ac --- /dev/null +++ b/ChangeLog.linux | |||
@@ -0,0 +1,20 @@ | |||
1 | 19991027 | ||
2 | - Adapted PAM patch. | ||
3 | - Released 1.0pre2 | ||
4 | |||
5 | - Excised my buggy replacements for strlcpy and mkdtemp | ||
6 | - Imported correct OpenBSD strlcpy and mkdtemp routines. | ||
7 | - Reduced arc4random_stir entropy read to 32 bytes (256 bits) | ||
8 | - Picked up correct version number from OpenBSD | ||
9 | - Added sshd.pam PAM configuration file | ||
10 | - Added sshd.init Redhat init script | ||
11 | - Added openssh.spec RPM spec file | ||
12 | - Released 1.2pre3 | ||
13 | |||
14 | 19991026 | ||
15 | - Fixed include paths of OpenSSL functions | ||
16 | - Use OpenSSL MD5 routines | ||
17 | - Imported RC4 code from nanocrypt | ||
18 | - Wrote replacements for OpenBSD arc4random* functions | ||
19 | - Wrote replacements for strlcpy and mkdtemp | ||
20 | - Released 1.0pre1 | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..668900c3b --- /dev/null +++ b/Makefile | |||
@@ -0,0 +1,13 @@ | |||
1 | # $OpenBSD: Makefile,v 1.5 1999/10/25 20:27:26 markus Exp $ | ||
2 | |||
3 | .include <bsd.own.mk> | ||
4 | |||
5 | SUBDIR= lib ssh sshd ssh-add ssh-keygen ssh-agent scp | ||
6 | |||
7 | distribution: | ||
8 | install -C -o root -g wheel -m 0644 ${.CURDIR}/ssh_config \ | ||
9 | ${DESTDIR}/etc/ssh_config | ||
10 | install -C -o root -g wheel -m 0644 ${.CURDIR}/sshd_config \ | ||
11 | ${DESTDIR}/etc/sshd_config | ||
12 | |||
13 | .include <bsd.subdir.mk> | ||
diff --git a/Makefile.GNU b/Makefile.GNU new file mode 100644 index 000000000..f36bdb3df --- /dev/null +++ b/Makefile.GNU | |||
@@ -0,0 +1,50 @@ | |||
1 | OPT_FLAGS=-g | ||
2 | CFLAGS=$(OPT_FLAGS) -Wall -DETCDIR=\"/etc/ssh\" -DHAVE_PAM | ||
3 | TARGETS=bin/libssh.a bin/ssh bin/sshd bin/ssh-add bin/ssh-keygen bin/ssh-agent bin/scp | ||
4 | LFLAGS=-L./bin | ||
5 | LIBS=-lssh -lcrypto -lz -lutil -lpam -ldl | ||
6 | AR=ar | ||
7 | RANLIB=ranlib | ||
8 | |||
9 | OBJS= authfd.o authfile.o auth-passwd.o auth-rhosts.o auth-rh-rsa.o \ | ||
10 | auth-rsa.o bufaux.o buffer.o canohost.o channels.o cipher.o \ | ||
11 | clientloop.o compress.o crc32.o deattack.o hostfile.o \ | ||
12 | log-client.o login.o log-server.o match.o mpaux.o packet.o pty.o \ | ||
13 | readconf.o readpass.o rsa.o servconf.o serverloop.o \ | ||
14 | sshconnect.o tildexpand.o ttymodes.o uidswap.o xmalloc.o \ | ||
15 | helper.o mktemp.o strlcpy.o rc4.o | ||
16 | |||
17 | all: $(OBJS) $(TARGETS) | ||
18 | |||
19 | bin/libssh.a: authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o hostfile.o match.o mpaux.o nchan.o packet.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o helper.o rc4.o mktemp.o strlcpy.o | ||
20 | [ -d bin ] || mkdir bin | ||
21 | $(AR) rv $@ $^ | ||
22 | $(RANLIB) $@ | ||
23 | |||
24 | bin/ssh: ssh.o sshconnect.o log-client.o readconf.o clientloop.o | ||
25 | [ -d bin ] || mkdir bin | ||
26 | $(CC) -o $@ $^ $(LFLAGS) $(LIBS) | ||
27 | |||
28 | bin/sshd: sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o | ||
29 | [ -d bin ] || mkdir bin | ||
30 | $(CC) -o $@ $^ $(LFLAGS) $(LIBS) | ||
31 | |||
32 | bin/scp: scp.o | ||
33 | [ -d bin ] || mkdir bin | ||
34 | $(CC) -o $@ $^ $(LFLAGS) $(LIBS) | ||
35 | |||
36 | bin/ssh-add: ssh-add.o log-client.o | ||
37 | [ -d bin ] || mkdir bin | ||
38 | $(CC) -o $@ $^ $(LFLAGS) $(LIBS) | ||
39 | |||
40 | bin/ssh-agent: ssh-agent.o log-client.o | ||
41 | [ -d bin ] || mkdir bin | ||
42 | $(CC) -o $@ $^ $(LFLAGS) $(LIBS) | ||
43 | |||
44 | bin/ssh-keygen: ssh-keygen.o log-client.o | ||
45 | [ -d bin ] || mkdir bin | ||
46 | $(CC) -o $@ $^ $(LFLAGS) $(LIBS) | ||
47 | |||
48 | clean: | ||
49 | rm -f *.o core bin/* | ||
50 | |||
diff --git a/Makefile.inc b/Makefile.inc new file mode 100644 index 000000000..fddf3da2f --- /dev/null +++ b/Makefile.inc | |||
@@ -0,0 +1,11 @@ | |||
1 | CFLAGS+= -I${.CURDIR}/.. | ||
2 | |||
3 | .include <bsd.obj.mk> | ||
4 | |||
5 | .if exists(${.CURDIR}/../lib/${__objdir}) | ||
6 | LDADD+= -L${.CURDIR}/../lib/${__objdir} -lssh | ||
7 | DPADD+= ${.CURDIR}/../lib/${__objdir}/libssh.a | ||
8 | .else | ||
9 | LDADD+= -L${.CURDIR}/../lib -lssh | ||
10 | DPADD+= ${.CURDIR}/../lib/libssh.a | ||
11 | .endif | ||
diff --git a/OVERVIEW b/OVERVIEW new file mode 100644 index 000000000..a8b67e4e2 --- /dev/null +++ b/OVERVIEW | |||
@@ -0,0 +1,164 @@ | |||
1 | This document is inteded for those who wish to read the ssh source | ||
2 | code. This tries to give an overview of the structure of the code. | ||
3 | |||
4 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi> | ||
5 | Updated 17 Nov 1995. | ||
6 | Updated 19 Oct 1999 for OpenSSH-1.2 | ||
7 | |||
8 | The software consists of ssh (client), sshd (server), scp, sdist, and | ||
9 | the auxiliary programs ssh-keygen, ssh-agent, ssh-add, and | ||
10 | make-ssh-known-hosts. The main program for each of these is in a .c | ||
11 | file with the same name. | ||
12 | |||
13 | There are some subsystems/abstractions that are used by a number of | ||
14 | these programs. | ||
15 | |||
16 | Buffer manipulation routines | ||
17 | |||
18 | - These provide an arbitrary size buffer, where data can be appended. | ||
19 | Data can be consumed from either end. The code is used heavily | ||
20 | throughout ssh. The basic buffer manipulation functions are in | ||
21 | buffer.c (header buffer.h), and additional code to manipulate specific | ||
22 | data types is in bufaux.c. | ||
23 | |||
24 | Compression Library | ||
25 | |||
26 | - Ssh uses the GNU GZIP compression library (ZLIB). | ||
27 | |||
28 | Encryption/Decryption | ||
29 | |||
30 | - Ssh contains several encryption algorithms. These are all | ||
31 | accessed through the cipher.h interface. The interface code is | ||
32 | in cipher.c, and the implementations are in libc. | ||
33 | |||
34 | Multiple Precision Integer Library | ||
35 | |||
36 | - Uses the SSLeay BIGNUM sublibrary. | ||
37 | - Some auxiliary functions for mp-int manipulation are in mpaux.c. | ||
38 | |||
39 | Random Numbers | ||
40 | |||
41 | - Uses arc4random() and such. | ||
42 | |||
43 | RSA key generation, encryption, decryption | ||
44 | |||
45 | - Ssh uses the RSA routines in libssl. | ||
46 | |||
47 | RSA key files | ||
48 | |||
49 | - RSA keys are stored in files with a special format. The code to | ||
50 | read/write these files is in authfile.c. The files are normally | ||
51 | encrypted with a passphrase. The functions to read passphrases | ||
52 | are in readpass.c (the same code is used to read passwords). | ||
53 | |||
54 | Binary packet protocol | ||
55 | |||
56 | - The ssh binary packet protocol is implemented in packet.c. The | ||
57 | code in packet.c does not concern itself with packet types or their | ||
58 | execution; it contains code to build packets, to receive them and | ||
59 | extract data from them, and the code to compress and/or encrypt | ||
60 | packets. CRC code comes from crc32.c. | ||
61 | |||
62 | - The code in packet.c calls the buffer manipulation routines | ||
63 | (buffer.c, bufaux.c), compression routines (compress.c, zlib), | ||
64 | and the encryption routines. | ||
65 | |||
66 | X11, TCP/IP, and Agent forwarding | ||
67 | |||
68 | - Code for various types of channel forwarding is in channels.c. | ||
69 | The file defines a generic framework for arbitrary communication | ||
70 | channels inside the secure channel, and uses this framework to | ||
71 | implement X11 forwarding, TCP/IP forwarding, and authentication | ||
72 | agent forwarding. | ||
73 | The new, Protocol 1.5, channel close implementation is in nchan.c | ||
74 | |||
75 | Authentication agent | ||
76 | |||
77 | - Code to communicate with the authentication agent is in authfd.c. | ||
78 | |||
79 | Authentication methods | ||
80 | |||
81 | - Code for various authentication methods resides in auth-*.c | ||
82 | (auth-passwd.c, auth-rh-rsa.c, auth-rhosts.c, auth-rsa.c). This | ||
83 | code is linked into the server. The routines also manipulate | ||
84 | known hosts files using code in hostfile.c. Code in canohost.c | ||
85 | is used to retrieve the canonical host name of the remote host. | ||
86 | Code in match.c is used to match host names. | ||
87 | |||
88 | - In the client end, authentication code is in sshconnect.c. It | ||
89 | reads Passwords/passphrases using code in readpass.c. It reads | ||
90 | RSA key files with authfile.c. It communicates the | ||
91 | authentication agent using authfd.c. | ||
92 | |||
93 | The ssh client | ||
94 | |||
95 | - The client main program is in ssh.c. It first parses arguments | ||
96 | and reads configuration (readconf.c), then calls ssh_connect (in | ||
97 | sshconnect.c) to open a connection to the server (possibly via a | ||
98 | proxy), and performs authentication (ssh_login in sshconnect.c). | ||
99 | It then makes any pty, forwarding, etc. requests. It may call | ||
100 | code in ttymodes.c to encode current tty modes. Finally it | ||
101 | calls client_loop in clientloop.c. This does the real work for | ||
102 | the session. | ||
103 | |||
104 | - The client is suid root. It tries to temporarily give up this | ||
105 | rights while reading the configuration data. The root | ||
106 | privileges are only used to make the connection (from a | ||
107 | privileged socket). Any extra privileges are dropped before | ||
108 | calling ssh_login. | ||
109 | |||
110 | Pseudo-tty manipulation and tty modes | ||
111 | |||
112 | - Code to allocate and use a pseudo tty is in pty.c. Code to | ||
113 | encode and set terminal modes is in ttymodes.c. | ||
114 | |||
115 | Logging in (updating utmp, lastlog, etc.) | ||
116 | |||
117 | - The code to do things that are done when a user logs in are in | ||
118 | login.c. This includes things such as updating the utmp, wtmp, | ||
119 | and lastlog files. Some of the code is in sshd.c. | ||
120 | |||
121 | Writing to the system log and terminal | ||
122 | |||
123 | - The programs use the functions fatal(), log(), debug(), error() | ||
124 | in many places to write messages to system log or user's | ||
125 | terminal. The implementation that logs to system log is in | ||
126 | log-server.c; it is used in the server program. The other | ||
127 | programs use an implementation that sends output to stderr; it | ||
128 | is in log-client.c. The definitions are in ssh.h. | ||
129 | |||
130 | The sshd server (daemon) | ||
131 | |||
132 | - The sshd daemon starts by processing arguments and reading the | ||
133 | configuration file (servconf.c). It then reads the host key, | ||
134 | starts listening for connections, and generates the server key. | ||
135 | The server key will be regenerated every hour by an alarm. | ||
136 | |||
137 | - When the server receives a connection, it forks, disables the | ||
138 | regeneration alarm, and starts communicating with the client. | ||
139 | They first perform identification string exchange, then | ||
140 | negotiate encryption, then perform authentication, preparatory | ||
141 | operations, and finally the server enters the normal session | ||
142 | mode by calling server_loop in serverloop.c. This does the real | ||
143 | work, calling functions in other modules. | ||
144 | |||
145 | - The code for the server is in sshd.c. It contains a lot of | ||
146 | stuff, including: | ||
147 | - server main program | ||
148 | - waiting for connections | ||
149 | - processing new connection | ||
150 | - authentication | ||
151 | - preparatory operations | ||
152 | - building up the execution environment for the user program | ||
153 | - starting the user program. | ||
154 | |||
155 | Auxiliary files | ||
156 | |||
157 | - There are several other files in the distribution that contain | ||
158 | various auxiliary routines: | ||
159 | ssh.h the main header file for ssh (various definitions) | ||
160 | getput.h byte-order independent storage of integers | ||
161 | includes.h includes most system headers. Lots of #ifdefs. | ||
162 | tildexpand.c expand tilde in file names | ||
163 | uidswap.c uid-swapping | ||
164 | xmalloc.c "safe" malloc routines | ||
@@ -0,0 +1,563 @@ | |||
1 | Ssh (Secure Shell) is a program to log into another computer over a | ||
2 | network, to execute commands in a remote machine, and to move files | ||
3 | from one machine to another. It provides strong authentication and | ||
4 | secure communications over insecure channels. It is inteded as a | ||
5 | replacement for rlogin, rsh, rcp, and rdist. | ||
6 | |||
7 | See the file INSTALL for installation instructions. See COPYING for | ||
8 | license terms and other legal issues. See RFC for a description of | ||
9 | the protocol. There is a WWW page for ssh; see http://www.cs.hut.fi/ssh. | ||
10 | |||
11 | This file has been updated to match ssh-1.2.12. | ||
12 | |||
13 | |||
14 | FEATURES | ||
15 | |||
16 | o Strong authentication. Closes several security holes (e.g., IP, | ||
17 | routing, and DNS spoofing). New authentication methods: .rhosts | ||
18 | together with RSA based host authentication, and pure RSA | ||
19 | authentication. | ||
20 | |||
21 | o Improved privacy. All communications are automatically and | ||
22 | transparently encrypted. RSA is used for key exchange, and a | ||
23 | conventional cipher (normally IDEA, DES, or triple-DES) for | ||
24 | encrypting the session. Encryption is started before | ||
25 | authentication, and no passwords or other information is | ||
26 | transmitted in the clear. Encryption is also used to protect | ||
27 | against spoofed packets. | ||
28 | |||
29 | o Secure X11 sessions. The program automatically sets DISPLAY on | ||
30 | the server machine, and forwards any X11 connections over the | ||
31 | secure channel. Fake Xauthority information is automatically | ||
32 | generated and forwarded to the remote machine; the local client | ||
33 | automatically examines incoming X11 connections and replaces the | ||
34 | fake authorization data with the real data (never telling the | ||
35 | remote machine the real information). | ||
36 | |||
37 | o Arbitrary TCP/IP ports can be redirected through the encrypted channel | ||
38 | in both directions (e.g., for e-cash transactions). | ||
39 | |||
40 | o No retraining needed for normal users; everything happens | ||
41 | automatically, and old .rhosts files will work with strong | ||
42 | authentication if administration installs host key files. | ||
43 | |||
44 | o Never trusts the network. Minimal trust on the remote side of | ||
45 | the connection. Minimal trust on domain name servers. Pure RSA | ||
46 | authentication never trusts anything but the private key. | ||
47 | |||
48 | o Client RSA-authenticates the server machine in the beginning of | ||
49 | every connection to prevent trojan horses (by routing or DNS | ||
50 | spoofing) and man-in-the-middle attacks, and the server | ||
51 | RSA-authenticates the client machine before accepting .rhosts or | ||
52 | /etc/hosts.equiv authentication (to prevent DNS, routing, or | ||
53 | IP-spoofing). | ||
54 | |||
55 | o Host authentication key distribution can be centrally by the | ||
56 | administration, automatically when the first connection is made | ||
57 | to a machine (the key obtained on the first connection will be | ||
58 | recorded and used for authentication in the future), or manually | ||
59 | by each user for his/her own use. The central and per-user host | ||
60 | key repositories are both used and complement each other. Host | ||
61 | keys can be generated centrally or automatically when the software | ||
62 | is installed. Host authentication keys are typically 1024 bits. | ||
63 | |||
64 | o Any user can create any number of user authentication RSA keys for | ||
65 | his/her own use. Each user has a file which lists the RSA public | ||
66 | keys for which proof of possession of the corresponding private | ||
67 | key is accepted as authentication. User authentication keys are | ||
68 | typically 1024 bits. | ||
69 | |||
70 | o The server program has its own server RSA key which is | ||
71 | automatically regenerated every hour. This key is never saved in | ||
72 | any file. Exchanged session keys are encrypted using both the | ||
73 | server key and the server host key. The purpose of the separate | ||
74 | server key is to make it impossible to decipher a captured session by | ||
75 | breaking into the server machine at a later time; one hour from | ||
76 | the connection even the server machine cannot decipher the session | ||
77 | key. The key regeneration interval is configurable. The server | ||
78 | key is normally 768 bits. | ||
79 | |||
80 | o An authentication agent, running in the user's laptop or local | ||
81 | workstation, can be used to hold the user's RSA authentication | ||
82 | keys. Ssh automatically forwards the connection to the | ||
83 | authentication agent over any connections, and there is no need to | ||
84 | store the RSA authentication keys on any machine in the network | ||
85 | (except the user's own local machine). The authentication | ||
86 | protocols never reveal the keys; they can only be used to verify | ||
87 | that the user's agent has a certain key. Eventually the agent | ||
88 | could rely on a smart card to perform all authentication | ||
89 | computations. | ||
90 | |||
91 | o The software can be installed and used (with restricted | ||
92 | functionality) even without root privileges. | ||
93 | |||
94 | o The client is customizable in system-wide and per-user | ||
95 | configuration files. Most aspects of the client's operation can | ||
96 | be configured. Different options can be specified on a per-host basis. | ||
97 | |||
98 | o Automatically executes conventional rsh (after displaying a | ||
99 | warning) if the server machine is not running sshd. | ||
100 | |||
101 | o Optional compression of all data with gzip (including forwarded X11 | ||
102 | and TCP/IP port data), which may result in significant speedups on | ||
103 | slow connections. | ||
104 | |||
105 | o Complete replacement for rlogin, rsh, and rcp. | ||
106 | |||
107 | |||
108 | WHY TO USE SECURE SHELL | ||
109 | |||
110 | Currently, almost all communications in computer networks are done | ||
111 | without encryption. As a consequence, anyone who has access to any | ||
112 | machine connected to the network can listen in on any communication. | ||
113 | This is being done by hackers, curious administrators, employers, | ||
114 | criminals, industrial spies, and governments. Some networks leak off | ||
115 | enough electromagnetic radiation that data may be captured even from a | ||
116 | distance. | ||
117 | |||
118 | When you log in, your password goes in the network in plain | ||
119 | text. Thus, any listener can then use your account to do any evil he | ||
120 | likes. Many incidents have been encountered worldwide where crackers | ||
121 | have started programs on workstations without the owners knowledge | ||
122 | just to listen to the network and collect passwords. Programs for | ||
123 | doing this are available on the Internet, or can be built by a | ||
124 | competent programmer in a few hours. | ||
125 | |||
126 | Any information that you type or is printed on your screen can be | ||
127 | monitored, recorded, and analyzed. For example, an intruder who has | ||
128 | penetrated a host connected to a major network can start a program | ||
129 | that listens to all data flowing in the network, and whenever it | ||
130 | encounters a 16-digit string, it checks if it is a valid credit card | ||
131 | number (using the check digit), and saves the number plus any | ||
132 | surrounding text (to catch expiration date and holder) in a file. | ||
133 | When the intruder has collected a few thousand credit card numbers, he | ||
134 | makes smallish mail-order purchases from a few thousand stores around | ||
135 | the world, and disappears when the goods arrive but before anyone | ||
136 | suspects anything. | ||
137 | |||
138 | Businesses have trade secrets, patent applications in preparation, | ||
139 | pricing information, subcontractor information, client data, personnel | ||
140 | data, financial information, etc. Currently, anyone with access to | ||
141 | the network (any machine on the network) can listen to anything that | ||
142 | goes in the network, without any regard to normal access restrictions. | ||
143 | |||
144 | Many companies are not aware that information can so easily be | ||
145 | recovered from the network. They trust that their data is safe | ||
146 | since nobody is supposed to know that there is sensitive information | ||
147 | in the network, or because so much other data is transferred in the | ||
148 | network. This is not a safe policy. | ||
149 | |||
150 | Individual persons also have confidential information, such as | ||
151 | diaries, love letters, health care documents, information about their | ||
152 | personal interests and habits, professional data, job applications, | ||
153 | tax reports, political documents, unpublished manuscripts, etc. | ||
154 | |||
155 | One should also be aware that economical intelligence and industrial | ||
156 | espionage has recently become a major priority of the intelligence | ||
157 | agencies of major governments. President Clinton recently assigned | ||
158 | economical espionage as the primary task of the CIA, and the French | ||
159 | have repeatedly been publicly boasting about their achievements on | ||
160 | this field. | ||
161 | |||
162 | |||
163 | There is also another frightening aspect about the poor security of | ||
164 | communications. Computer storage and analysis capability has | ||
165 | increased so much that it is feasible for governments, major | ||
166 | companies, and criminal organizations to automatically analyze, | ||
167 | identify, classify, and file information about millions of people over | ||
168 | the years. Because most of the work can be automated, the cost of | ||
169 | collecting this information is getting very low. | ||
170 | |||
171 | Government agencies may be able to monitor major communication | ||
172 | systems, telephones, fax, computer networks, etc., and passively | ||
173 | collect huge amounts of information about all people with any | ||
174 | significant position in the society. Most of this information is not | ||
175 | sensitive, and many people would say there is no harm in someone | ||
176 | getting that information. However, the information starts to get | ||
177 | sensitive when someone has enough of it. You may not mind someone | ||
178 | knowing what you bought from the shop one random day, but you might | ||
179 | not like someone knowing every small thing you have bought in the last | ||
180 | ten years. | ||
181 | |||
182 | If the government some day starts to move into a more totalitarian | ||
183 | direction (one should remember that Nazi Germany was created by | ||
184 | democratic elections), there is considerable danger of an ultimate | ||
185 | totalitarian state. With enough information (the automatically | ||
186 | collected records of an individual can be manually analyzed when the | ||
187 | person becomes interesting), one can form a very detailed picture of | ||
188 | the individual's interests, opinions, beliefs, habits, friends, | ||
189 | lovers, weaknesses, etc. This information can be used to 1) locate | ||
190 | any persons who might oppose the new system 2) use deception to | ||
191 | disturb any organizations which might rise against the government 3) | ||
192 | eliminate difficult individuals without anyone understanding what | ||
193 | happened. Additionally, if the government can monitor communications | ||
194 | too effectively, it becomes too easy to locate and eliminate any | ||
195 | persons distributing information contrary to the official truth. | ||
196 | |||
197 | Fighting crime and terrorism are often used as grounds for domestic | ||
198 | surveillance and restricting encryption. These are good goals, but | ||
199 | there is considerable danger that the surveillance data starts to get | ||
200 | used for questionable purposes. I find that it is better to tolerate | ||
201 | a small amount of crime in the society than to let the society become | ||
202 | fully controlled. I am in favor of a fairly strong state, but the | ||
203 | state must never get so strong that people become unable to spread | ||
204 | contra-offical information and unable to overturn the government if it | ||
205 | is bad. The danger is that when you notice that the government is | ||
206 | too powerful, it is too late. Also, the real power may not be where | ||
207 | the official government is. | ||
208 | |||
209 | For these reasons (privacy, protecting trade secrets, and making it | ||
210 | more difficult to create a totalitarian state), I think that strong | ||
211 | cryptography should be integrated to the tools we use every day. | ||
212 | Using it causes no harm (except for those who wish to monitor | ||
213 | everything), but not using it can cause huge problems. If the society | ||
214 | changes in undesirable ways, then it will be to late to start | ||
215 | encrypting. | ||
216 | |||
217 | Encryption has had a "military" or "classified" flavor to it. There | ||
218 | are no longer any grounds for this. The military can and will use its | ||
219 | own encryption; that is no excuse to prevent the civilians from | ||
220 | protecting their privacy and secrets. Information on strong | ||
221 | encryption is available in every major bookstore, scientific library, | ||
222 | and patent office around the world, and strong encryption software is | ||
223 | available in every country on the Internet. | ||
224 | |||
225 | Some people would like to make it illegal to use encryption, or to | ||
226 | force people to use encryption that governments can break. This | ||
227 | approach offers no protection if the government turns bad. Also, the | ||
228 | "bad guys" will be using true strong encryption anyway. Good | ||
229 | encryption techniques are too widely known to make them disappear. | ||
230 | Thus, any "key escrow encryption" or other restrictions will only help | ||
231 | monitor ordinary people and petty criminals. It does not help against | ||
232 | powerful criminals, terrorists, or espionage, because they will know | ||
233 | how to use strong encryption anyway. (One source for internationally | ||
234 | available encryption software is http://www.cs.hut.fi/crypto.) | ||
235 | |||
236 | |||
237 | OVERVIEW OF SECURE SHELL | ||
238 | |||
239 | The software consists of a number of programs. | ||
240 | |||
241 | sshd Server program run on the server machine. This | ||
242 | listens for connections from client machines, and | ||
243 | whenever it receives a connection, it performs | ||
244 | authentication and starts serving the client. | ||
245 | |||
246 | ssh This is the client program used to log into another | ||
247 | machine or to execute commands on the other machine. | ||
248 | "slogin" is another name for this program. | ||
249 | |||
250 | scp Securely copies files from one machine to another. | ||
251 | |||
252 | ssh-keygen Used to create RSA keys (host keys and user | ||
253 | authentication keys). | ||
254 | |||
255 | ssh-agent Authentication agent. This can be used to hold RSA | ||
256 | keys for authentication. | ||
257 | |||
258 | ssh-add Used to register new keys with the agent. | ||
259 | |||
260 | make-ssh-known-hosts | ||
261 | Used to create the /etc/ssh_known_hosts file. | ||
262 | |||
263 | |||
264 | Ssh is the program users normally use. It is started as | ||
265 | |||
266 | ssh host | ||
267 | |||
268 | or | ||
269 | |||
270 | ssh host command | ||
271 | |||
272 | The first form opens a new shell on the remote machine (after | ||
273 | authentication). The latter form executes the command on the remote | ||
274 | machine. | ||
275 | |||
276 | When started, the ssh connects sshd on the server machine, verifies | ||
277 | that the server machine really is the machine it wanted to connect, | ||
278 | exchanges encryption keys (in a manner which prevents an outside | ||
279 | listener from getting the keys), performs authentication using .rhosts | ||
280 | and /etc/hosts.equiv, RSA authentication, or conventional password | ||
281 | based authentication. The server then (normally) allocates a | ||
282 | pseudo-terminal and starts an interactive shell or user program. | ||
283 | |||
284 | The TERM environment variable (describing the type of the user's | ||
285 | terminal) is passed from the client side to the remote side. Also, | ||
286 | terminal modes will be copied from the client side to the remote side | ||
287 | to preserve user preferences (e.g., the erase character). | ||
288 | |||
289 | If the DISPLAY variable is set on the client side, the server will | ||
290 | create a dummy X server and set DISPLAY accordingly. Any connections | ||
291 | to the dummy X server will be forwarded through the secure channel, | ||
292 | and will be made to the real X server from the client side. An | ||
293 | arbitrary number of X programs can be started during the session, and | ||
294 | starting them does not require anything special from the user. (Note | ||
295 | that the user must not manually set DISPLAY, because then it would | ||
296 | connect directly to the real display instead of going through the | ||
297 | encrypted channel). This behavior can be disabled in the | ||
298 | configuration file or by giving the -x option to the client. | ||
299 | |||
300 | Arbitrary IP ports can be forwarded over the secure channel. The | ||
301 | program then creates a port on one side, and whenever a connection is | ||
302 | opened to this port, it will be passed over the secure channel, and a | ||
303 | connection will be made from the other side to a specified host:port | ||
304 | pair. Arbitrary IP forwarding must always be explicitly requested, | ||
305 | and cannot be used to forward privileged ports (unless the user is | ||
306 | root). It is possible to specify automatic forwards in a per-user | ||
307 | configuration file, for example to make electronic cash systems work | ||
308 | securely. | ||
309 | |||
310 | If there is an authentication agent on the client side, connection to | ||
311 | it will be automatically forwarded to the server side. | ||
312 | |||
313 | For more infomation, see the manual pages ssh(1), sshd(8), scp(1), | ||
314 | ssh-keygen(1), ssh-agent(1), ssh-add(1), and make-ssh-known-hosts(1) | ||
315 | included in this distribution. | ||
316 | |||
317 | |||
318 | X11 CONNECTION FORWARDING | ||
319 | |||
320 | X11 forwarding serves two purposes: it is a convenience to the user | ||
321 | because there is no need to set the DISPLAY variable, and it provides | ||
322 | encrypted X11 connections. I cannot think of any other easy way to | ||
323 | make X11 connections encrypted; modifying the X server, clients or | ||
324 | libraries would require special work for each machine, vendor and | ||
325 | application. Widely used IP-level encryption does not seem likely for | ||
326 | several years. Thus what we have left is faking an X server on the | ||
327 | same machine where the clients are run, and forwarding the connections | ||
328 | to a real X server over the secure channel. | ||
329 | |||
330 | X11 forwarding works as follows. The client extracts Xauthority | ||
331 | information for the server. It then creates random authorization | ||
332 | data, and sends the random data to the server. The server allocates | ||
333 | an X11 display number, and stores the (fake) Xauthority data for this | ||
334 | display. Whenever an X11 connection is opened, the server forwards | ||
335 | the connection over the secure channel to the client, and the client | ||
336 | parses the first packet of the X11 protocol, substitutes real | ||
337 | authentication data for the fake data (if the fake data matched), and | ||
338 | forwards the connection to the real X server. | ||
339 | |||
340 | If the display does not have Xauthority data, the server will create a | ||
341 | unix domain socket in /tmp/.X11-unix, and use the unix domain socket | ||
342 | as the display. No authentication information is forwarded in this | ||
343 | case. X11 connections are again forwarded over the secure channel. | ||
344 | To the X server the connections appear to come from the client | ||
345 | machine, and the server must have connections allowed from the local | ||
346 | machine. Using authentication data is always recommended because not | ||
347 | using it makes the display insecure. If XDM is used, it automatically | ||
348 | generates the authentication data. | ||
349 | |||
350 | One should be careful not to use "xin" or "xstart" or other similar | ||
351 | scripts that explicitly set DISPLAY to start X sessions in a remote | ||
352 | machine, because the connection will then not go over the secure | ||
353 | channel. The recommended way to start a shell in a remote machine is | ||
354 | |||
355 | xterm -e ssh host & | ||
356 | |||
357 | and the recommended way to execute an X11 application in a remote | ||
358 | machine is | ||
359 | |||
360 | ssh -n host emacs & | ||
361 | |||
362 | If you need to type a password/passphrase for the remote machine, | ||
363 | |||
364 | ssh -f host emacs | ||
365 | |||
366 | may be useful. | ||
367 | |||
368 | |||
369 | |||
370 | RSA AUTHENTICATION | ||
371 | |||
372 | RSA authentication is based on public key cryptograpy. The idea is | ||
373 | that there are two encryption keys, one for encryption and another for | ||
374 | decryption. It is not possible (on human timescale) to derive the | ||
375 | decryption key from the encryption key. The encryption key is called | ||
376 | the public key, because it can be given to anyone and it is not | ||
377 | secret. The decryption key, on the other hand, is secret, and is | ||
378 | called the private key. | ||
379 | |||
380 | RSA authentication is based on the impossibility of deriving the | ||
381 | private key from the public key. The public key is stored on the | ||
382 | server machine in the user's $HOME/.ssh/authorized_keys file. The | ||
383 | private key is only kept on the user's local machine, laptop, or other | ||
384 | secure storage. Then the user tries to log in, the client tells the | ||
385 | server the public key that the user wishes to use for authentication. | ||
386 | The server then checks if this public key is admissible. If so, it | ||
387 | generates a 256 bit random number, encrypts it with the public key, | ||
388 | and sends the value to the client. The client then decrypts the | ||
389 | number with its private key, computes a 128 bit MD5 checksum from the | ||
390 | resulting data, and sends the checksum back to the server. (Only a | ||
391 | checksum is sent to prevent chosen-plaintext attacks against RSA.) | ||
392 | The server checks computes a checksum from the correct data, | ||
393 | and compares the checksums. Authentication is accepted if the | ||
394 | checksums match. (Theoretically this indicates that the client | ||
395 | only probably knows the correct key, but for all practical purposes | ||
396 | there is no doubt.) | ||
397 | |||
398 | The RSA private key can be protected with a passphrase. The | ||
399 | passphrase can be any string; it is hashed with MD5 to produce an | ||
400 | encryption key for IDEA, which is used to encrypt the private part of | ||
401 | the key file. With passphrase, authorization requires access to the key | ||
402 | file and the passphrase. Without passphrase, authorization only | ||
403 | depends on possession of the key file. | ||
404 | |||
405 | RSA authentication is the most secure form of authentication supported | ||
406 | by this software. It does not rely on the network, routers, domain | ||
407 | name servers, or the client machine. The only thing that matters is | ||
408 | access to the private key. | ||
409 | |||
410 | All this, of course, depends on the security of the RSA algorithm | ||
411 | itself. RSA has been widely known since about 1978, and no effective | ||
412 | methods for breaking it are known if it is used properly. Care has | ||
413 | been taken to avoid the well-known pitfalls. Breaking RSA is widely | ||
414 | believed to be equivalent to factoring, which is a very hard | ||
415 | mathematical problem that has received considerable public research. | ||
416 | So far, no effective methods are known for numbers bigger than about | ||
417 | 512 bits. However, as computer speeds and factoring methods are | ||
418 | increasing, 512 bits can no longer be considered secure. The | ||
419 | factoring work is exponential, and 768 or 1024 bits are widely | ||
420 | considered to be secure in the near future. | ||
421 | |||
422 | |||
423 | RHOSTS AUTHENTICATION | ||
424 | |||
425 | Conventional .rhosts and hosts.equiv based authentication mechanisms | ||
426 | are fundamentally insecure due to IP, DNS (domain name server) and | ||
427 | routing spoofing attacks. Additionally this authentication method | ||
428 | relies on the integrity of the client machine. These weaknesses is | ||
429 | tolerable, and been known and exploited for a long time. | ||
430 | |||
431 | Ssh provides an improved version of these types of authentication, | ||
432 | because they are very convenient for the user (and allow easy | ||
433 | transition from rsh and rlogin). It permits these types of | ||
434 | authentication, but additionally requires that the client host be | ||
435 | authenticated using RSA. | ||
436 | |||
437 | The server has a list of host keys stored in /etc/ssh_known_host, and | ||
438 | additionally each user has host keys in $HOME/.ssh/known_hosts. Ssh | ||
439 | uses the name servers to obtain the canonical name of the client host, | ||
440 | looks for its public key in its known host files, and requires the | ||
441 | client to prove that it knows the private host key. This prevents IP | ||
442 | and routing spoofing attacks (as long as the client machine private | ||
443 | host key has not been compromized), but is still vulnerable to DNS | ||
444 | attacks (to a limited extent), and relies on the integrity of the | ||
445 | client machine as to who is requesting to log in. This prevents | ||
446 | outsiders from attacking, but does not protect against very powerful | ||
447 | attackers. If maximal security is desired, only RSA authentication | ||
448 | should be used. | ||
449 | |||
450 | It is possible to enable conventional .rhosts and /etc/hosts.equiv | ||
451 | authentication (without host authentication) at compile time by giving | ||
452 | the option --with-rhosts to configure. However, this is not | ||
453 | recommended, and is not done by default. | ||
454 | |||
455 | These weaknesses are present in rsh and rlogin. No improvement in | ||
456 | security will be obtained unless rlogin and rsh are completely | ||
457 | disabled (commented out in /etc/inetd.conf). This is highly | ||
458 | recommended. | ||
459 | |||
460 | |||
461 | WEAKEST LINKS IN SECURITY | ||
462 | |||
463 | One should understand that while this software may provide | ||
464 | cryptographically secure communications, it may be easy to | ||
465 | monitor the communications at their endpoints. | ||
466 | |||
467 | Basically, anyone with root access on the local machine on which you | ||
468 | are running the software may be able to do anything. Anyone with root | ||
469 | access on the server machine may be able to monitor your | ||
470 | communications, and a very talented root user might even be able to | ||
471 | send his/her own requests to your authentication agent. | ||
472 | |||
473 | One should also be aware that computers send out electromagnetic | ||
474 | radition that can sometimes be picked up hundreds of meters away. | ||
475 | Your keyboard is particularly easy to listen to. The image on your | ||
476 | monitor might also be seen on another monitor in a van parked behind | ||
477 | your house. | ||
478 | |||
479 | Beware that unwanted visitors might come to your home or office and | ||
480 | use your machine while you are away. They might also make | ||
481 | modifications or install bugs in your hardware or software. | ||
482 | |||
483 | Beware that the most effective way for someone to decrypt your data | ||
484 | may be with a rubber hose. | ||
485 | |||
486 | |||
487 | LEGAL ISSUES | ||
488 | |||
489 | As far as I am concerned, anyone is permitted to use this software | ||
490 | freely. However, see the file COPYING for detailed copying, | ||
491 | licensing, and distribution information. | ||
492 | |||
493 | In some countries, particularly France, Russia, Iraq, and Pakistan, | ||
494 | it may be illegal to use any encryption at all without a special | ||
495 | permit, and the rumor has it that you cannot get a permit for any | ||
496 | strong encryption. | ||
497 | |||
498 | This software may be freely imported into the United States; however, | ||
499 | the United States Government may consider re-exporting it a criminal | ||
500 | offence. | ||
501 | |||
502 | Note that any information and cryptographic algorithms used in this | ||
503 | software are publicly available on the Internet and at any major | ||
504 | bookstore, scientific library, or patent office worldwide. | ||
505 | |||
506 | THERE IS NO WARRANTY FOR THIS PROGRAM. Please consult the file | ||
507 | COPYING for more information. | ||
508 | |||
509 | |||
510 | MAILING LISTS AND OTHER INFORMATION | ||
511 | |||
512 | There is a mailing list for ossh. It is ossh@sics.se. If you would | ||
513 | like to join, send a message to majordomo@sics.se with "subscribe | ||
514 | ssh" in body. | ||
515 | |||
516 | The WWW home page for ssh is http://www.cs.hut.fi/ssh. It contains an | ||
517 | archive of the mailing list, and detailed information about new | ||
518 | releases, mailing lists, and other relevant issues. | ||
519 | |||
520 | Bug reports should be sent to ossh-bugs@sics.se. | ||
521 | |||
522 | |||
523 | ABOUT THE AUTHOR | ||
524 | |||
525 | This software was written by Tatu Ylonen <ylo@cs.hut.fi>. I work as a | ||
526 | researcher at Helsinki University of Technology, Finland. For more | ||
527 | information, see http://www.cs.hut.fi/~ylo/. My PGP public key is | ||
528 | available via finger from ylo@cs.hut.fi and from the key servers. I | ||
529 | prefer PGP encrypted mail. | ||
530 | |||
531 | The author can be contacted via ordinary mail at | ||
532 | Tatu Ylonen | ||
533 | Helsinki University of Technology | ||
534 | Otakaari 1 | ||
535 | FIN-02150 ESPOO | ||
536 | Finland | ||
537 | |||
538 | Fax. +358-0-4513293 | ||
539 | |||
540 | |||
541 | ACKNOWLEDGEMENTS | ||
542 | |||
543 | I thank Tero Kivinen, Timo Rinne, Janne Snabb, and Heikki Suonsivu for | ||
544 | their help and comments in the design, implementation and porting of | ||
545 | this software. I also thank numerous contributors, including but not | ||
546 | limited to Walker Aumann, Jurgen Botz, Hans-Werner Braun, Stephane | ||
547 | Bortzmeyer, Adrian Colley, Michael Cooper, David Dombek, Jerome | ||
548 | Etienne, Bill Fithen, Mark Fullmer, Bert Gijsbers, Andreas Gustafsson, | ||
549 | Michael Henits, Steve Johnson, Thomas Koenig, Felix Leitner, Gunnar | ||
550 | Lindberg, Andrew Macpherson, Marc Martinec, Paul Mauvais, Donald | ||
551 | McKillican, Leon Mlakar, Robert Muchsel, Mark Treacy, Bryan | ||
552 | O'Sullivan, Mikael Suokas, Ollivier Robert, Jakob Schlyter, Tomasz | ||
553 | Surmacz, Alvar Vinacua, Petri Virkkula, Michael Warfield, and | ||
554 | Cristophe Wolfhugel. | ||
555 | |||
556 | Thanks also go to Philip Zimmermann, whose PGP software and the | ||
557 | associated legal battle provided inspiration, motivation, and many | ||
558 | useful techniques, and to Bruce Schneier whose book Applied | ||
559 | Cryptography has done a great service in widely distributing knowledge | ||
560 | about cryptographic methods. | ||
561 | |||
562 | |||
563 | Copyright (c) 1995 Tatu Ylonen, Espoo, Finland. | ||
diff --git a/README.openssh b/README.openssh new file mode 100644 index 000000000..02cb3c603 --- /dev/null +++ b/README.openssh | |||
@@ -0,0 +1,44 @@ | |||
1 | This is a Linux port of OpenBSD's excellent OpenSSH. | ||
2 | |||
3 | OpenSSH is based on the last free version of Tatu Ylonen's SSH with all | ||
4 | patent-encumbered algorithms removed, all known security bugs fixed, new | ||
5 | features reintroduced and many other clean-ups. | ||
6 | |||
7 | This Linux port basically consists of a few fixes to deal with the way that | ||
8 | OpenSSL is usually installed on Linux systems, a few replacements for | ||
9 | OpenBSD library functions and the introduction of partial PAM support. | ||
10 | |||
11 | The PAM support is less than optimal - it is only used when password | ||
12 | authentication is requested, so things like pam_limits will not apply if a | ||
13 | user authenticates with a RSA key. OTOH this is exactly the level of support | ||
14 | that the popular Linux SSH packages have. Perhaps a PAM hacker can rectify | ||
15 | this? | ||
16 | |||
17 | All new code is released under a XFree style license, which is very liberal. | ||
18 | This code is released with no warranties of any kind, neither I nor my | ||
19 | employer (Internet Business Solutions) will take any responsibility for | ||
20 | any loss, damage or liability arising from the use or abuse of this software. | ||
21 | |||
22 | OpenSSH depends on Zlib, OpenSSL and PAM. Use the Makefile.GNU to build it. | ||
23 | |||
24 | Damien Miller <djm@ibs.com.au> | ||
25 | Internet Business Solutions | ||
26 | |||
27 | |||
28 | Credits - | ||
29 | |||
30 | The OpenBSD team | ||
31 | 'jonchen' - the original author of PAM support of SSH | ||
32 | |||
33 | Miscellania - | ||
34 | |||
35 | This version of SSH is based upon code retrieved from the OpenBSD CVS | ||
36 | repository on 1999-10-26, which in turn was based on the last free | ||
37 | version of SSH released by Tatu Ylonen. | ||
38 | |||
39 | Code in helper.[ch] is Copyright 1999 Internet Business Solutions and | ||
40 | is released under a X11-style license (see source file for details). | ||
41 | |||
42 | (A)RC4 code in rc4.[ch] is Copyright 1999 Damien Miller. It too is | ||
43 | under a X11-style license (see source file for details). | ||
44 | |||
diff --git a/RFC.nroff b/RFC.nroff new file mode 100644 index 000000000..cc197aaff --- /dev/null +++ b/RFC.nroff | |||
@@ -0,0 +1,1780 @@ | |||
1 | .\" -*- nroff -*- | ||
2 | .\" | ||
3 | .\" $Id: RFC.nroff,v 1.1 1999/10/27 03:42:43 damien Exp $ | ||
4 | .\" | ||
5 | .pl 10.0i | ||
6 | .po 0 | ||
7 | .ll 7.2i | ||
8 | .lt 7.2i | ||
9 | .nr LL 7.2i | ||
10 | .nr LT 7.2i | ||
11 | .ds LF Ylonen | ||
12 | .ds RF FORMFEED[Page %] | ||
13 | .ds CF | ||
14 | .ds LH Internet-Draft | ||
15 | .ds RH 15 November 1995 | ||
16 | .ds CH SSH (Secure Shell) Remote Login Protocol | ||
17 | .na | ||
18 | .hy 0 | ||
19 | .in 0 | ||
20 | Network Working Group T. Ylonen | ||
21 | Internet-Draft Helsinki University of Technology | ||
22 | draft-ylonen-ssh-protocol-00.txt 15 November 1995 | ||
23 | Expires: 15 May 1996 | ||
24 | |||
25 | .in 3 | ||
26 | |||
27 | .ce | ||
28 | The SSH (Secure Shell) Remote Login Protocol | ||
29 | |||
30 | .ti 0 | ||
31 | Status of This Memo | ||
32 | |||
33 | This document is an Internet-Draft. Internet-Drafts are working | ||
34 | documents of the Internet Engineering Task Force (IETF), its areas, | ||
35 | and its working groups. Note that other groups may also distribute | ||
36 | working documents as Internet-Drafts. | ||
37 | |||
38 | Internet-Drafts are draft documents valid for a maximum of six | ||
39 | months and may be updated, replaced, or obsoleted by other docu- | ||
40 | ments at any time. It is inappropriate to use Internet-Drafts as | ||
41 | reference material or to cite them other than as ``work in pro- | ||
42 | gress.'' | ||
43 | |||
44 | To learn the current status of any Internet-Draft, please check the | ||
45 | ``1id-abstracts.txt'' listing contained in the Internet- Drafts Shadow | ||
46 | Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe), | ||
47 | munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), or | ||
48 | ftp.isi.edu (US West Coast). | ||
49 | |||
50 | The distribution of this memo is unlimited. | ||
51 | |||
52 | .ti 0 | ||
53 | Introduction | ||
54 | |||
55 | SSH (Secure Shell) is a program to log into another computer over a | ||
56 | network, to execute commands in a remote machine, and to move files | ||
57 | from one machine to another. It provides strong authentication and | ||
58 | secure communications over insecure networks. Its features include | ||
59 | the following: | ||
60 | .IP o | ||
61 | Closes several security holes (e.g., IP, routing, and DNS spoofing). | ||
62 | New authentication methods: .rhosts together with RSA [RSA] based host | ||
63 | authentication, and pure RSA authentication. | ||
64 | .IP o | ||
65 | All communications are automatically and transparently encrypted. | ||
66 | Encryption is also used to protect integrity. | ||
67 | .IP o | ||
68 | X11 connection forwarding provides secure X11 sessions. | ||
69 | .IP o | ||
70 | Arbitrary TCP/IP ports can be redirected over the encrypted channel | ||
71 | in both directions. | ||
72 | .IP o | ||
73 | Client RSA-authenticates the server machine in the beginning of every | ||
74 | connection to prevent trojan horses (by routing or DNS spoofing) and | ||
75 | man-in-the-middle attacks, and the server RSA-authenticates the client | ||
76 | machine before accepting .rhosts or /etc/hosts.equiv authentication | ||
77 | (to prevent DNS, routing, or IP spoofing). | ||
78 | .IP o | ||
79 | An authentication agent, running in the user's local workstation or | ||
80 | laptop, can be used to hold the user's RSA authentication keys. | ||
81 | .RT | ||
82 | |||
83 | The goal has been to make the software as easy to use as possible for | ||
84 | ordinary users. The protocol has been designed to be as secure as | ||
85 | possible while making it possible to create implementations that | ||
86 | are easy to use and install. The sample implementation has a number | ||
87 | of convenient features that are not described in this document as they | ||
88 | are not relevant for the protocol. | ||
89 | |||
90 | |||
91 | .ti 0 | ||
92 | Overview of the Protocol | ||
93 | |||
94 | The software consists of a server program running on a server machine, | ||
95 | and a client program running on a client machine (plus a few auxiliary | ||
96 | programs). The machines are connected by an insecure IP [RFC0791] | ||
97 | network (that can be monitored, tampered with, and spoofed by hostile | ||
98 | parties). | ||
99 | |||
100 | A connection is always initiated by the client side. The server | ||
101 | listens on a specific port waiting for connections. Many clients may | ||
102 | connect to the same server machine. | ||
103 | |||
104 | The client and the server are connected via a TCP/IP [RFC0793] socket | ||
105 | that is used for bidirectional communication. Other types of | ||
106 | transport can be used but are currently not defined. | ||
107 | |||
108 | When the client connects the server, the server accepts the connection | ||
109 | and responds by sending back its version identification string. The | ||
110 | client parses the server's identification, and sends its own | ||
111 | identification. The purpose of the identification strings is to | ||
112 | validate that the connection was to the correct port, declare the | ||
113 | protocol version number used, and to declare the software version used | ||
114 | on each side (for debugging purposes). The identification strings are | ||
115 | human-readable. If either side fails to understand or support the | ||
116 | other side's version, it closes the connection. | ||
117 | |||
118 | After the protocol identification phase, both sides switch to a packet | ||
119 | based binary protocol. The server starts by sending its host key | ||
120 | (every host has an RSA key used to authenticate the host), server key | ||
121 | (an RSA key regenerated every hour), and other information to the | ||
122 | client. The client then generates a 256 bit session key, encrypts it | ||
123 | using both RSA keys (see below for details), and sends the encrypted | ||
124 | session key and selected cipher type to the server. Both sides then | ||
125 | turn on encryption using the selected algorithm and key. The server | ||
126 | sends an encrypted confirmation message to the client. | ||
127 | |||
128 | The client then authenticates itself using any of a number of | ||
129 | authentication methods. The currently supported authentication | ||
130 | methods are .rhosts or /etc/hosts.equiv authentication (disabled by | ||
131 | default), the same with RSA-based host authentication, RSA | ||
132 | authentication, and password authentication. | ||
133 | |||
134 | After successful authentication, the client makes a number of requests | ||
135 | to prepare for the session. Typical requests include allocating a | ||
136 | pseudo tty, starting X11 [X11] or TCP/IP port forwarding, starting | ||
137 | authentication agent forwarding, and executing the shell or a command. | ||
138 | |||
139 | When a shell or command is executed, the connection enters interactive | ||
140 | session mode. In this mode, data is passed in both directions, | ||
141 | new forwarded connections may be opened, etc. The interactive session | ||
142 | normally terminates when the server sends the exit status of the | ||
143 | program to the client. | ||
144 | |||
145 | |||
146 | The protocol makes several reservations for future extensibility. | ||
147 | First of all, the initial protocol identification messages include the | ||
148 | protocol version number. Second, the first packet by both sides | ||
149 | includes a protocol flags field, which can be used to agree on | ||
150 | extensions in a compatible manner. Third, the authentication and | ||
151 | session preparation phases work so that the client sends requests to | ||
152 | the server, and the server responds with success or failure. If the | ||
153 | client sends a request that the server does not support, the server | ||
154 | simply returns failure for it. This permits compatible addition of | ||
155 | new authentication methods and preparation operations. The | ||
156 | interactive session phase, on the other hand, works asynchronously and | ||
157 | does not permit the use of any extensions (because there is no easy | ||
158 | and reliable way to signal rejection to the other side and problems | ||
159 | would be hard to debug). Any compatible extensions to this phase must | ||
160 | be agreed upon during any of the earlier phases. | ||
161 | |||
162 | .ti 0 | ||
163 | The Binary Packet Protocol | ||
164 | |||
165 | After the protocol identification strings, both sides only send | ||
166 | specially formatted packets. The packet layout is as follows: | ||
167 | .IP o | ||
168 | Packet length: 32 bit unsigned integer, coded as four 8-bit bytes, msb | ||
169 | first. Gives the length of the packet, not including the length field | ||
170 | and padding. The maximum length of a packet (not including the length | ||
171 | field and padding) is 262144 bytes. | ||
172 | .IP o | ||
173 | Padding: 1-8 bytes of random data (or zeroes if not encrypting). The | ||
174 | amount of padding is (8 - (length % 8)) bytes (where % stands for the | ||
175 | modulo operator). The rationale for always having some random padding | ||
176 | at the beginning of each packet is to make known plaintext attacks | ||
177 | more difficult. | ||
178 | .IP o | ||
179 | Packet type: 8-bit unsigned byte. The value 255 is reserved for | ||
180 | future extension. | ||
181 | .IP o | ||
182 | Data: binary data bytes, depending on the packet type. The number of | ||
183 | data bytes is the "length" field minus 5. | ||
184 | .IP o | ||
185 | Check bytes: 32-bit crc, four 8-bit bytes, msb first. The crc is the | ||
186 | Cyclic Redundancy Check, with the polynomial 0xedb88320, of the | ||
187 | Padding, Packet type, and Data fields. The crc is computed before | ||
188 | any encryption. | ||
189 | .RT | ||
190 | |||
191 | The packet, except for the length field, may be encrypted using any of | ||
192 | a number of algorithms. The length of the encrypted part (Padding + | ||
193 | Type + Data + Check) is always a multiple of 8 bytes. Typically the | ||
194 | cipher is used in a chained mode, with all packets chained together as | ||
195 | if it was a single data stream (the length field is never included in | ||
196 | the encryption process). Details of encryption are described below. | ||
197 | |||
198 | When the session starts, encryption is turned off. Encryption is | ||
199 | enabled after the client has sent the session key. The encryption | ||
200 | algorithm to use is selected by the client. | ||
201 | |||
202 | |||
203 | .ti 0 | ||
204 | Packet Compression | ||
205 | |||
206 | If compression is supported (it is an optional feature, see | ||
207 | SSH_CMSG_REQUEST_COMPRESSION below), the packet type and data fields | ||
208 | of the packet are compressed using the gzip deflate algorithm [GZIP]. | ||
209 | If compression is in effect, the packet length field indicates the | ||
210 | length of the compressed data, plus 4 for the crc. The amount of | ||
211 | padding is computed from the compressed data, so that the amount of | ||
212 | data to be encrypted becomes a multiple of 8 bytes. | ||
213 | |||
214 | When compressing, the packets (type + data portions) in each direction | ||
215 | are compressed as if they formed a continuous data stream, with only the | ||
216 | current compression block flushed between packets. This corresponds | ||
217 | to the GNU ZLIB library Z_PARTIAL_FLUSH option. The compression | ||
218 | dictionary is not flushed between packets. The two directions are | ||
219 | compressed independently of each other. | ||
220 | |||
221 | |||
222 | .ti 0 | ||
223 | Packet Encryption | ||
224 | |||
225 | The protocol supports several encryption methods. During session | ||
226 | initialization, the server sends a bitmask of all encryption methods | ||
227 | that it supports, and the client selects one of these methods. The | ||
228 | client also generates a 256-bit random session key (32 8-bit bytes) and | ||
229 | sends it to the server. | ||
230 | |||
231 | The encryption methods supported by the current implementation, and | ||
232 | their codes are: | ||
233 | .TS | ||
234 | center; | ||
235 | l r l. | ||
236 | SSH_CIPHER_NONE 0 No encryption | ||
237 | SSH_CIPHER_IDEA 1 IDEA in CFB mode | ||
238 | SSH_CIPHER_DES 2 DES in CBC mode | ||
239 | SSH_CIPHER_3DES 3 Triple-DES in CBC mode | ||
240 | SSH_CIPHER_TSS 4 An experimental stream cipher | ||
241 | SSH_CIPHER_RC4 5 RC4 | ||
242 | .TE | ||
243 | |||
244 | All implementations are required to support SSH_CIPHER_DES and | ||
245 | SSH_CIPHER_3DES. Supporting SSH_CIPHER_IDEA, SSH_CIPHER_RC4, and | ||
246 | SSH_CIPHER_NONE is recommended. Support for SSH_CIPHER_TSS is | ||
247 | optional (and it is not described in this document). Other ciphers | ||
248 | may be added at a later time; support for them is optional. | ||
249 | |||
250 | For encryption, the encrypted portion of the packet is considered a | ||
251 | linear byte stream. The length of the stream is always a multiple of | ||
252 | 8. The encrypted portions of consecutive packets (in the same | ||
253 | direction) are encrypted as if they were a continuous buffer (that is, | ||
254 | any initialization vectors are passed from the previous packet to the | ||
255 | next packet). Data in each direction is encrypted independently. | ||
256 | .IP SSH_CIPHER_DES | ||
257 | The key is taken from the first 8 bytes of the session key. The least | ||
258 | significant bit of each byte is ignored. This results in 56 bits of | ||
259 | key data. DES [DES] is used in CBC mode. The iv (initialization vector) is | ||
260 | initialized to all zeroes. | ||
261 | .IP SSH_CIPHER_3DES | ||
262 | The variant of triple-DES used here works as follows: there are three | ||
263 | independent DES-CBC ciphers, with independent initialization vectors. | ||
264 | The data (the whole encrypted data stream) is first encrypted with the | ||
265 | first cipher, then decrypted with the second cipher, and finally | ||
266 | encrypted with the third cipher. All these operations are performed | ||
267 | in CBC mode. | ||
268 | |||
269 | The key for the first cipher is taken from the first 8 bytes of the | ||
270 | session key; the key for the next cipher from the next 8 bytes, and | ||
271 | the key for the third cipher from the following 8 bytes. All three | ||
272 | initialization vectors are initialized to zero. | ||
273 | |||
274 | (Note: the variant of 3DES used here differs from some other | ||
275 | descriptions.) | ||
276 | .IP SSH_CIPHER_IDEA | ||
277 | The key is taken from the first 16 bytes of the session key. IDEA | ||
278 | [IDEA] is used in CFB mode. The initialization vector is initialized | ||
279 | to all zeroes. | ||
280 | .IP SSH_CIPHER_TSS | ||
281 | All 32 bytes of the session key are used as the key. | ||
282 | |||
283 | There is no reference available for the TSS algorithm; it is currently | ||
284 | only documented in the sample implementation source code. The | ||
285 | security of this cipher is unknown (but it is quite fast). The cipher | ||
286 | is basically a stream cipher that uses MD5 as a random number | ||
287 | generator and takes feedback from the data. | ||
288 | .IP SSH_CIPHER_RC4 | ||
289 | The first 16 bytes of the session key are used as the key for the | ||
290 | server to client direction. The remaining 16 bytes are used as the | ||
291 | key for the client to server direction. This gives independent | ||
292 | 128-bit keys for each direction. | ||
293 | |||
294 | This algorithm is the alleged RC4 cipher posted to the Usenet in 1995. | ||
295 | It is widely believed to be equivalent with the original RSADSI RC4 | ||
296 | cipher. This is a very fast algorithm. | ||
297 | .RT | ||
298 | |||
299 | |||
300 | .ti 0 | ||
301 | Data Type Encodings | ||
302 | |||
303 | The Data field of each packet contains data encoded as described in | ||
304 | this section. There may be several data items; each item is coded as | ||
305 | described here, and their representations are concatenated together | ||
306 | (without any alignment or padding). | ||
307 | |||
308 | Each data type is stored as follows: | ||
309 | .IP "8-bit byte" | ||
310 | The byte is stored directly as a single byte. | ||
311 | .IP "32-bit unsigned integer" | ||
312 | Stored in 4 bytes, msb first. | ||
313 | .IP "Arbitrary length binary string" | ||
314 | First 4 bytes are the length of the string, msb first (not including | ||
315 | the length itself). The following "length" bytes are the string | ||
316 | value. There are no terminating null characters. | ||
317 | .IP "Multiple-precision integer" | ||
318 | First 2 bytes are the number of bits in the integer, msb first (for | ||
319 | example, the value 0x00012345 would have 17 bits). The value zero has | ||
320 | zero bits. It is permissible that the number of bits be larger than the | ||
321 | real number of bits. | ||
322 | |||
323 | The number of bits is followed by (bits + 7) / 8 bytes of binary data, | ||
324 | msb first, giving the value of the integer. | ||
325 | .RT | ||
326 | |||
327 | |||
328 | .ti 0 | ||
329 | TCP/IP Port Number and Other Options | ||
330 | |||
331 | The server listens for connections on TCP/IP port 22. | ||
332 | |||
333 | The client may connect the server from any port. However, if the | ||
334 | client wishes to use any form of .rhosts or /etc/hosts.equiv | ||
335 | authentication, it must connect from a privileged port (less than | ||
336 | 1024). | ||
337 | |||
338 | For the IP Type of Service field [RFC0791], it is recommended that | ||
339 | interactive sessions (those having a user terminal or forwarding X11 | ||
340 | connections) use the IPTOS_LOWDELAY, and non-interactive connections | ||
341 | use IPTOS_THROUGHPUT. | ||
342 | |||
343 | It is recommended that keepalives are used, because otherwise programs | ||
344 | on the server may never notice if the other end of the connection is | ||
345 | rebooted. | ||
346 | |||
347 | |||
348 | .ti 0 | ||
349 | Protocol Version Identification | ||
350 | |||
351 | After the socket is opened, the server sends an identification string, | ||
352 | which is of the form | ||
353 | "SSH-<protocolmajor>.<protocolminor>-<version>\\n", where | ||
354 | <protocolmajor> and <protocolminor> are integers and specify the | ||
355 | protocol version number (not software distribution version). | ||
356 | <version> is server side software version string (max 40 characters); | ||
357 | it is not interpreted by the remote side but may be useful for | ||
358 | debugging. | ||
359 | |||
360 | The client parses the server's string, and sends a corresponding | ||
361 | string with its own information in response. If the server has lower | ||
362 | version number, and the client contains special code to emulate it, | ||
363 | the client responds with the lower number; otherwise it responds with | ||
364 | its own number. The server then compares the version number the | ||
365 | client sent with its own, and determines whether they can work | ||
366 | together. The server either disconnects, or sends the first packet | ||
367 | using the binary packet protocol and both sides start working | ||
368 | according to the lower of the protocol versions. | ||
369 | |||
370 | By convention, changes which keep the protocol compatible with | ||
371 | previous versions keep the same major protocol version; changes that | ||
372 | are not compatible increment the major version (which will hopefully | ||
373 | never happen). The version described in this document is 1.3. | ||
374 | |||
375 | The client will | ||
376 | |||
377 | .ti 0 | ||
378 | Key Exchange and Server Host Authentication | ||
379 | |||
380 | The first message sent by the server using the packet protocol is | ||
381 | SSH_SMSG_PUBLIC_KEY. It declares the server's host key, server public | ||
382 | key, supported ciphers, supported authentication methods, and flags | ||
383 | for protocol extensions. It also contains a 64-bit random number | ||
384 | (cookie) that must be returned in the client's reply (to make IP | ||
385 | spoofing more difficult). No encryption is used for this message. | ||
386 | |||
387 | Both sides compute a session id as follows. The modulus of the server | ||
388 | key is interpreted as a byte string (without explicit length field, | ||
389 | with minimum length able to hold the whole value), most significant | ||
390 | byte first. This string is concatenated with the server host key | ||
391 | interpreted the same way. Additionally, the cookie is concatenated | ||
392 | with this. Both sides compute MD5 of the resulting string. The | ||
393 | resulting 16 bytes (128 bits) are stored by both parties and are | ||
394 | called the session id. | ||
395 | |||
396 | The client responds with a SSH_CMSG_SESSION_KEY message, which | ||
397 | contains the selected cipher type, a copy of the 64-bit cookie sent by | ||
398 | the server, client's protocol flags, and a session key encrypted | ||
399 | with both the server's host key and server key. No encryption is used | ||
400 | for this message. | ||
401 | |||
402 | The session key is 32 8-bit bytes (a total of 256 random bits | ||
403 | generated by the client). The client first xors the 16 bytes of the | ||
404 | session id with the first 16 bytes of the session key. The resulting | ||
405 | string is then encrypted using the smaller key (one with smaller | ||
406 | modulus), and the result is then encrypted using the other key. The | ||
407 | number of bits in the public modulus of the two keys must differ by at | ||
408 | least 128 bits. | ||
409 | |||
410 | At each encryption step, a multiple-precision integer is constructed | ||
411 | from the data to be encrypted as follows (the integer is here | ||
412 | interpreted as a sequence of bytes, msb first; the number of bytes is | ||
413 | the number of bytes needed to represent the modulus). | ||
414 | |||
415 | The most significant byte (which is only partial as the value must be | ||
416 | less than the public modulus, which is never a power of two) is zero. | ||
417 | |||
418 | The next byte contains the value 2 (which stands for public-key | ||
419 | encrypted data in the PKCS standard [PKCS#1]). Then, there are | ||
420 | non-zero random bytes to fill any unused space, a zero byte, and the | ||
421 | data to be encrypted in the least significant bytes, the last byte of | ||
422 | the data in the least significant byte. | ||
423 | |||
424 | This algorithm is used twice. First, it is used to encrypt the 32 | ||
425 | random bytes generated by the client to be used as the session key | ||
426 | (xored by the session id). This value is converted to an integer as | ||
427 | described above, and encrypted with RSA using the key with the smaller | ||
428 | modulus. The resulting integer is converted to a byte stream, msb | ||
429 | first. This byte stream is padded and encrypted identically using the | ||
430 | key with the larger modulus. | ||
431 | |||
432 | After the client has sent the session key, it starts to use the | ||
433 | selected algorithm and key for decrypting any received packets, and | ||
434 | for encrypting any sent packets. Separate ciphers are used for | ||
435 | different directions (that is, both directions have separate | ||
436 | initialization vectors or other state for the ciphers). | ||
437 | |||
438 | When the server has received the session key message, and has turned | ||
439 | on encryption, it sends a SSH_SMSG_SUCCESS message to the client. | ||
440 | |||
441 | The recommended size of the host key is 1024 bits, and 768 bits for | ||
442 | the server key. The minimum size is 512 bits for the smaller key. | ||
443 | |||
444 | |||
445 | .ti 0 | ||
446 | Declaring the User Name | ||
447 | |||
448 | The client then sends a SSH_CMSG_USER message to the server. This | ||
449 | message specifies the user name to log in as. | ||
450 | |||
451 | The server validates that such a user exists, checks whether | ||
452 | authentication is needed, and responds with either SSH_SMSG_SUCCESS or | ||
453 | SSH_SMSG_FAILURE. SSH_SMSG_SUCCESS indicates that no authentication | ||
454 | is needed for this user (no password), and authentication phase has | ||
455 | now been completed. SSH_SMSG_FAILURE indicates that authentication is | ||
456 | needed (or the user does not exist). | ||
457 | |||
458 | If the user does not exist, it is recommended that this returns | ||
459 | failure, but the server keeps reading messages from the client, and | ||
460 | responds to any messages (except SSH_MSG_DISCONNECT, SSH_MSG_IGNORE, | ||
461 | and SSH_MSG_DEBUG) with SSH_SMSG_FAILURE. This way the client cannot | ||
462 | be certain whether the user exists. | ||
463 | |||
464 | |||
465 | .ti 0 | ||
466 | Authentication Phase | ||
467 | |||
468 | Provided the server didn't immediately accept the login, an | ||
469 | authentication exchange begins. The client sends messages to the | ||
470 | server requesting different types of authentication in arbitrary order as | ||
471 | many times as desired (however, the server may close the connection | ||
472 | after a timeout). The server always responds with SSH_SMSG_SUCCESS if | ||
473 | it has accepted the authentication, and with SSH_SMSG_FAILURE if it has | ||
474 | denied authentication with the requested method or it does not | ||
475 | recognize the message. Some authentication methods cause an exchange | ||
476 | of further messages before the final result is sent. The | ||
477 | authentication phase ends when the server responds with success. | ||
478 | |||
479 | The recommended value for the authentication timeout (timeout before | ||
480 | disconnecting if no successful authentication has been made) is 5 | ||
481 | minutes. | ||
482 | |||
483 | The following authentication methods are currently supported: | ||
484 | .TS | ||
485 | center; | ||
486 | l r l. | ||
487 | SSH_AUTH_RHOSTS 1 .rhosts or /etc/hosts.equiv | ||
488 | SSH_AUTH_RSA 2 pure RSA authentication | ||
489 | SSH_AUTH_PASSWORD 3 password authentication | ||
490 | SSH_AUTH_RHOSTS_RSA 4 .rhosts with RSA host authentication | ||
491 | .TE | ||
492 | .IP SSH_AUTH_RHOSTS | ||
493 | |||
494 | This is the authentication method used by rlogin and rsh [RFC1282]. | ||
495 | |||
496 | The client sends SSH_CMSG_AUTH_RHOSTS with the client-side user name | ||
497 | as an argument. | ||
498 | |||
499 | The server checks whether to permit authentication. On UNIX systems, | ||
500 | this is usually done by checking /etc/hosts.equiv, and .rhosts in the | ||
501 | user's home directory. The connection must come from a privileged | ||
502 | port. | ||
503 | |||
504 | It is recommended that the server checks that there are no IP options | ||
505 | (such as source routing) specified for the socket before accepting | ||
506 | this type of authentication. The client host name should be | ||
507 | reverse-mapped and then forward mapped to ensure that it has the | ||
508 | proper IP-address. | ||
509 | |||
510 | This authentication method trusts the remote host (root on the remote | ||
511 | host can pretend to be any other user on that host), the name | ||
512 | services, and partially the network: anyone who can see packets coming | ||
513 | out from the server machine can do IP-spoofing and pretend to be any | ||
514 | machine; however, the protocol prevents blind IP-spoofing (which used | ||
515 | to be possible with rlogin). | ||
516 | |||
517 | Many sites probably want to disable this authentication method because | ||
518 | of the fundamental insecurity of conventional .rhosts or | ||
519 | /etc/hosts.equiv authentication when faced with spoofing. It is | ||
520 | recommended that this method not be supported by the server by | ||
521 | default. | ||
522 | .IP SSH_AUTH_RHOSTS_RSA | ||
523 | |||
524 | In addition to conventional .rhosts and hosts.equiv authentication, | ||
525 | this method additionally requires that the client host be | ||
526 | authenticated using RSA. | ||
527 | |||
528 | The client sends SSH_CMSG_AUTH_RHOSTS_RSA specifying the client-side | ||
529 | user name, and the public host key of the client host. | ||
530 | |||
531 | The server first checks if normal .rhosts or /etc/hosts.equiv | ||
532 | authentication would be accepted, and if not, responds with | ||
533 | SSH_SMSG_FAILURE. Otherwise, it checks whether it knows the host key | ||
534 | for the client machine (using the same name for the host that was used | ||
535 | for checking the .rhosts and /etc/hosts.equiv files). If it does not | ||
536 | know the RSA key for the client, access is denied and SSH_SMSG_FAILURE | ||
537 | is sent. | ||
538 | |||
539 | If the server knows the host key of the client machine, it verifies | ||
540 | that the given host key matches that known for the client. If not, | ||
541 | access is denied and SSH_SMSG_FAILURE is sent. | ||
542 | |||
543 | The server then sends a SSH_SMSG_AUTH_RSA_CHALLENGE message containing | ||
544 | an encrypted challenge for the client. The challenge is 32 8-bit | ||
545 | random bytes (256 bits). When encrypted, the highest (partial) byte | ||
546 | is left as zero, the next byte contains the value 2, the following are | ||
547 | non-zero random bytes, followed by a zero byte, and the challenge put | ||
548 | in the remaining bytes. This is then encrypted using RSA with the | ||
549 | client host's public key. (The padding and encryption algorithm is | ||
550 | the same as that used for the session key.) | ||
551 | |||
552 | The client decrypts the challenge using its private host key, | ||
553 | concatenates this with the session id, and computes an MD5 checksum | ||
554 | of the resulting 48 bytes. The MD5 output is returned as 16 bytes in | ||
555 | a SSH_CMSG_AUTH_RSA_RESPONSE message. (MD5 is used to deter chosen | ||
556 | plaintext attacks against RSA; the session id binds it to a specific | ||
557 | session). | ||
558 | |||
559 | The server verifies that the MD5 of the decrypted challenge returned by | ||
560 | the client matches that of the original value, and sends SSH_SMSG_SUCCESS if | ||
561 | so. Otherwise it sends SSH_SMSG_FAILURE and refuses the | ||
562 | authentication attempt. | ||
563 | |||
564 | This authentication method trusts the client side machine in that root | ||
565 | on that machine can pretend to be any user on that machine. | ||
566 | Additionally, it trusts the client host key. The name and/or IP | ||
567 | address of the client host is only used to select the public host key. | ||
568 | The same host name is used when scanning .rhosts or /etc/hosts.equiv | ||
569 | and when selecting the host key. It would in principle be possible to | ||
570 | eliminate the host name entirely and substitute it directly by the | ||
571 | host key. IP and/or DNS [RFC1034] spoofing can only be used | ||
572 | to pretend to be a host for which the attacker has the private host | ||
573 | key. | ||
574 | .IP SSH_AUTH_RSA | ||
575 | |||
576 | The idea behind RSA authentication is that the server recognizes the | ||
577 | public key offered by the client, generates a random challenge, and | ||
578 | encrypts the challenge with the public key. The client must then | ||
579 | prove that it has the corresponding private key by decrypting the | ||
580 | challenge. | ||
581 | |||
582 | The client sends SSH_CMSG_AUTH_RSA with public key modulus (n) as an | ||
583 | argument. | ||
584 | |||
585 | The server may respond immediately with SSH_SMSG_FAILURE if it does | ||
586 | not permit authentication with this key. Otherwise it generates a | ||
587 | challenge, encrypts it using the user's public key (stored on the | ||
588 | server and identified using the modulus), and sends | ||
589 | SSH_SMSG_AUTH_RSA_CHALLENGE with the challenge (mp-int) as an | ||
590 | argument. | ||
591 | |||
592 | The challenge is 32 8-bit random bytes (256 bits). When encrypted, | ||
593 | the highest (partial) byte is left as zero, the next byte contains the | ||
594 | value 2, the following are non-zero random bytes, followed by a zero | ||
595 | byte, and the challenge put in the remaining bytes. This is then | ||
596 | encrypted with the public key. (The padding and encryption algorithm | ||
597 | is the same as that used for the session key.) | ||
598 | |||
599 | The client decrypts the challenge using its private key, concatenates | ||
600 | it with the session id, and computes an MD5 checksum of the resulting | ||
601 | 48 bytes. The MD5 output is returned as 16 bytes in a | ||
602 | SSH_CMSG_AUTH_RSA_RESPONSE message. (Note that the MD5 is necessary | ||
603 | to avoid chosen plaintext attacks against RSA; the session id binds it | ||
604 | to a specific session.) | ||
605 | |||
606 | The server verifies that the MD5 of the decrypted challenge returned | ||
607 | by the client matches that of the original value, and sends | ||
608 | SSH_SMSG_SUCCESS if so. Otherwise it sends SSH_SMSG_FAILURE and | ||
609 | refuses the authentication attempt. | ||
610 | |||
611 | This authentication method does not trust the remote host, the | ||
612 | network, name services, or anything else. Authentication is based | ||
613 | solely on the possession of the private identification keys. Anyone | ||
614 | in possession of the private keys can log in, but nobody else. | ||
615 | |||
616 | The server may have additional requirements for a successful | ||
617 | authentiation. For example, to limit damage due to a compromised RSA | ||
618 | key, a server might restrict access to a limited set of hosts. | ||
619 | .IP SSH_AUTH_PASSWORD | ||
620 | |||
621 | The client sends a SSH_CMSG_AUTH_PASSWORD message with the plain text | ||
622 | password. (Note that even though the password is plain text inside | ||
623 | the message, it is normally encrypted by the packet mechanism.) | ||
624 | |||
625 | The server verifies the password, and sends SSH_SMSG_SUCCESS if | ||
626 | authentication was accepted and SSH_SMSG_FAILURE otherwise. | ||
627 | |||
628 | Note that the password is read from the user by the client; the user | ||
629 | never interacts with a login program. | ||
630 | |||
631 | This authentication method does not trust the remote host, the | ||
632 | network, name services or anything else. Authentication is based | ||
633 | solely on the possession of the password. Anyone in possession of the | ||
634 | password can log in, but nobody else. | ||
635 | .RT | ||
636 | |||
637 | .ti 0 | ||
638 | Preparatory Operations | ||
639 | |||
640 | After successful authentication, the server waits for a request from | ||
641 | the client, processes the request, and responds with SSH_SMSG_SUCCESS | ||
642 | whenever a request has been successfully processed. If it receives a | ||
643 | message that it does not recognize or it fails to honor a request, it | ||
644 | returns SSH_SMSG_FAILURE. It is expected that new message types might | ||
645 | be added to this phase in future. | ||
646 | |||
647 | The following messages are currently defined for this phase. | ||
648 | .IP SSH_CMSG_REQUEST_COMPRESSION | ||
649 | Requests that compression be enabled for this session. A | ||
650 | gzip-compatible compression level (1-9) is passed as an argument. | ||
651 | .IP SSH_CMSG_REQUEST_PTY | ||
652 | Requests that a pseudo terminal device be allocated for this session. | ||
653 | The user terminal type and terminal modes are supplied as arguments. | ||
654 | .IP SSH_CMSG_X11_REQUEST_FORWARDING | ||
655 | Requests forwarding of X11 connections from the remote machine to the | ||
656 | local machine over the secure channel. Causes an internet-domain | ||
657 | socket to be allocated and the DISPLAY variable to be set on the server. | ||
658 | X11 authentication data is automatically passed to the server, and the | ||
659 | client may implement spoofing of authentication data for added | ||
660 | security. The authentication data is passed as arguments. | ||
661 | .IP SSH_CMSG_PORT_FORWARD_REQUEST | ||
662 | Requests forwarding of a TCP/IP port on the server host over the | ||
663 | secure channel. What happens is that whenever a connection is made to | ||
664 | the port on the server, a connection will be made from the client end | ||
665 | to the specified host/port. Any user can forward unprivileged ports; | ||
666 | only the root can forward privileged ports (as determined by | ||
667 | authentication done earlier). | ||
668 | .IP SSH_CMSG_AGENT_REQUEST_FORWARDING | ||
669 | Requests forwarding of the connection to the authentication agent. | ||
670 | .IP SSH_CMSG_EXEC_SHELL | ||
671 | Starts a shell (command interpreter) for the user, and moves into | ||
672 | interactive session mode. | ||
673 | .IP SSH_CMSG_EXEC_CMD | ||
674 | Executes the given command (actually "<shell> -c <command>" or | ||
675 | equivalent) for the user, and moves into interactive session mode. | ||
676 | .RT | ||
677 | |||
678 | |||
679 | .ti 0 | ||
680 | Interactive Session and Exchange of Data | ||
681 | |||
682 | During the interactive session, any data written by the shell or | ||
683 | command running on the server machine is forwarded to stdin or | ||
684 | stderr on the client machine, and any input available from stdin on | ||
685 | the client machine is forwarded to the program on the server machine. | ||
686 | |||
687 | All exchange is asynchronous; either side can send at any time, and | ||
688 | there are no acknowledgements (TCP/IP already provides reliable | ||
689 | transport, and the packet protocol protects against tampering or IP | ||
690 | spoofing). | ||
691 | |||
692 | When the client receives EOF from its standard input, it will send | ||
693 | SSH_CMSG_EOF; however, this in no way terminates the exchange. The | ||
694 | exchange terminates and interactive mode is left when the server sends | ||
695 | SSH_SMSG_EXITSTATUS to indicate that the client program has | ||
696 | terminated. Alternatively, either side may disconnect at any time by | ||
697 | sending SSH_MSG_DISCONNECT or closing the connection. | ||
698 | |||
699 | The server may send any of the following messages: | ||
700 | .IP SSH_SMSG_STDOUT_DATA | ||
701 | Data written to stdout by the program running on the server. The data | ||
702 | is passed as a string argument. The client writes this data to | ||
703 | stdout. | ||
704 | .IP SSH_SMSG_STDERR_DATA | ||
705 | Data written to stderr by the program running on the server. The data | ||
706 | is passed as a string argument. The client writes this data to | ||
707 | stderr. (Note that if the program is running on a tty, it is not | ||
708 | possible to separate stdout and stderr data, and all data will be sent | ||
709 | as stdout data.) | ||
710 | .IP SSH_SMSG_EXITSTATUS | ||
711 | Indicates that the shell or command has exited. Exit status is passed | ||
712 | as an integer argument. This message causes termination of the | ||
713 | interactive session. | ||
714 | .IP SSH_SMSG_AGENT_OPEN | ||
715 | Indicates that someone on the server side is requesting a connection | ||
716 | to the authentication agent. The server-side channel number is passed | ||
717 | as an argument. The client must respond with either | ||
718 | SSH_CHANNEL_OPEN_CONFIRMATION or SSH_CHANNEL_OPEN_FAILURE. | ||
719 | .IP SSH_SMSG_X11_OPEN | ||
720 | Indicates that a connection has been made to the X11 socket on the | ||
721 | server side and should be forwarded to the real X server. An integer | ||
722 | argument indicates the channel number allocated for this connection on | ||
723 | the server side. The client should send back either | ||
724 | SSH_MSG_CHANNEL_OPEN_CONFIRMATION or SSH_MSG_CHANNEL_OPEN_FAILURE with | ||
725 | the same server side channel number. | ||
726 | .IP SSH_MSG_PORT_OPEN | ||
727 | Indicates that a connection has been made to a port on the server side | ||
728 | for which forwarding has been requested. Arguments are server side | ||
729 | channel number, host name to connect to, and port to connect to. The | ||
730 | client should send back either | ||
731 | SSH_MSG_CHANNEL_OPEN_CONFIRMATION or SSH_MSG_CHANNEL_OPEN_FAILURE with | ||
732 | the same server side channel number. | ||
733 | .IP SSH_MSG_CHANNEL_OPEN_CONFIRMATION | ||
734 | This is sent by the server to indicate that it has opened a connection | ||
735 | as requested in a previous message. The first argument indicates the | ||
736 | client side channel number, and the second argument is the channel number | ||
737 | that the server has allocated for this connection. | ||
738 | .IP SSH_MSG_CHANNEL_OPEN_FAILURE | ||
739 | This is sent by the server to indicate that it failed to open a | ||
740 | connection as requested in a previous message. The client-side | ||
741 | channel number is passed as an argument. The client will close the | ||
742 | descriptor associated with the channel and free the channel. | ||
743 | .IP SSH_MSG_CHANNEL_DATA | ||
744 | This packet contains data for a channel from the server. The first | ||
745 | argument is the client-side channel number, and the second argument (a | ||
746 | string) is the data. | ||
747 | .IP SSH_MSG_CHANNEL_CLOSE | ||
748 | This is sent by the server to indicate that whoever was in the other | ||
749 | end of the channel has closed it. The argument is the client side channel | ||
750 | number. The client will let all buffered data in the channel to | ||
751 | drain, and when ready, will close the socket, free the channel, and | ||
752 | send the server a SSH_MSG_CHANNEL_CLOSE_CONFIRMATION message for the | ||
753 | channel. | ||
754 | .IP SSH_MSG_CHANNEL_CLOSE_CONFIRMATION | ||
755 | This is send by the server to indicate that a channel previously | ||
756 | closed by the client has now been closed on the server side as well. | ||
757 | The argument indicates the client channel number. The client frees | ||
758 | the channel. | ||
759 | .RT | ||
760 | |||
761 | The client may send any of the following messages: | ||
762 | .IP SSH_CMSG_STDIN_DATA | ||
763 | This is data to be sent as input to the program running on the server. | ||
764 | The data is passed as a string. | ||
765 | .IP SSH_CMSG_EOF | ||
766 | Indicates that the client has encountered EOF while reading standard | ||
767 | input. The server will allow any buffered input data to drain, and | ||
768 | will then close the input to the program. | ||
769 | .IP SSH_CMSG_WINDOW_SIZE | ||
770 | Indicates that window size on the client has been changed. The server | ||
771 | updates the window size of the tty and causes SIGWINCH to be sent to | ||
772 | the program. The new window size is passed as four integer arguments: | ||
773 | row, col, xpixel, ypixel. | ||
774 | .IP SSH_MSG_PORT_OPEN | ||
775 | Indicates that a connection has been made to a port on the client side | ||
776 | for which forwarding has been requested. Arguments are client side | ||
777 | channel number, host name to connect to, and port to connect to. The | ||
778 | server should send back either SSH_MSG_CHANNEL_OPEN_CONFIRMATION or | ||
779 | SSH_MSG_CHANNEL_OPEN_FAILURE with the same client side channel number. | ||
780 | .IP SSH_MSG_CHANNEL_OPEN_CONFIRMATION | ||
781 | This is sent by the client to indicate that it has opened a connection | ||
782 | as requested in a previous message. The first argument indicates the | ||
783 | server side channel number, and the second argument is the channel | ||
784 | number that the client has allocated for this connection. | ||
785 | .IP SSH_MSG_CHANNEL_OPEN_FAILURE | ||
786 | This is sent by the client to indicate that it failed to open a | ||
787 | connection as requested in a previous message. The server side | ||
788 | channel number is passed as an argument. The server will close the | ||
789 | descriptor associated with the channel and free the channel. | ||
790 | .IP SSH_MSG_CHANNEL_DATA | ||
791 | This packet contains data for a channel from the client. The first | ||
792 | argument is the server side channel number, and the second argument (a | ||
793 | string) is the data. | ||
794 | .IP SSH_MSG_CHANNEL_CLOSE | ||
795 | This is sent by the client to indicate that whoever was in the other | ||
796 | end of the channel has closed it. The argument is the server channel | ||
797 | number. The server will allow buffered data to drain, and when ready, | ||
798 | will close the socket, free the channel, and send the client a | ||
799 | SSH_MSG_CHANNEL_CLOSE_CONFIRMATION message for the channel. | ||
800 | .IP SSH_MSG_CHANNEL_CLOSE_CONFIRMATION | ||
801 | This is send by the client to indicate that a channel previously | ||
802 | closed by the server has now been closed on the client side as well. | ||
803 | The argument indicates the server channel number. The server frees | ||
804 | the channel. | ||
805 | .RT | ||
806 | |||
807 | Any unsupported messages during interactive mode cause the connection | ||
808 | to be terminated with SSH_MSG_DISCONNECT and an error message. | ||
809 | Compatible protocol upgrades should agree about any extensions during | ||
810 | the preparation phase or earlier. | ||
811 | |||
812 | |||
813 | .ti 0 | ||
814 | Termination of the Connection | ||
815 | |||
816 | Normal termination of the connection is always initiated by the server | ||
817 | by sending SSH_SMSG_EXITSTATUS after the program has exited. The | ||
818 | client responds to this message by sending SSH_CMSG_EXIT_CONFIRMATION | ||
819 | and closes the socket; the server then closes the socket. There are | ||
820 | two purposes for the confirmation: some systems may lose previously | ||
821 | sent data when the socket is closed, and closing the client side first | ||
822 | causes any TCP/IP TIME_WAIT [RFC0793] waits to occur on the client side, not | ||
823 | consuming server resources. | ||
824 | |||
825 | If the program terminates due to a signal, the server will send | ||
826 | SSH_MSG_DISCONNECT with an appropriate message. If the connection is | ||
827 | closed, all file descriptors to the program will be closed and the | ||
828 | server will exit. If the program runs on a tty, the kernel sends it | ||
829 | the SIGHUP signal when the pty master side is closed. | ||
830 | |||
831 | .ti 0 | ||
832 | Protocol Flags | ||
833 | |||
834 | Both the server and the client pass 32 bits of protocol flags to the | ||
835 | other side. The flags are intended for compatible protocol extension; | ||
836 | the server first announces which added capabilities it supports, and | ||
837 | the client then sends the capabilities that it supports. | ||
838 | |||
839 | The following flags are currently defined (the values are bit masks): | ||
840 | .IP "1 SSH_PROTOFLAG_SCREEN_NUMBER" | ||
841 | This flag can only be sent by the client. It indicates that the X11 | ||
842 | forwarding requests it sends will include the screen number. | ||
843 | .IP "2 SSH_PROTOFLAG_HOST_IN_FWD_OPEN" | ||
844 | If both sides specify this flag, SSH_SMSG_X11_OPEN and | ||
845 | SSH_MSG_PORT_OPEN messages will contain an additional field containing | ||
846 | a description of the host at the other end of the connection. | ||
847 | .RT | ||
848 | |||
849 | .ti 0 | ||
850 | Detailed Description of Packet Types and Formats | ||
851 | |||
852 | The supported packet types and the corresponding message numbers are | ||
853 | given in the following table. Messages with _MSG_ in their name may | ||
854 | be sent by either side. Messages with _CMSG_ are only sent by the | ||
855 | client, and messages with _SMSG_ only by the server. | ||
856 | |||
857 | A packet may contain additional data after the arguments specified | ||
858 | below. Any such data should be ignored by the receiver. However, it | ||
859 | is recommended that no such data be stored without good reason. (This | ||
860 | helps build compatible extensions.) | ||
861 | .IP "0 SSH_MSG_NONE" | ||
862 | This code is reserved. This message type is never sent. | ||
863 | .IP "1 SSH_MSG_DISCONNECT" | ||
864 | .TS | ||
865 | ; | ||
866 | l l. | ||
867 | string Cause of disconnection | ||
868 | .TE | ||
869 | This message may be sent by either party at any time. It causes the | ||
870 | immediate disconnection of the connection. The message is intended to | ||
871 | be displayed to a human, and describes the reason for disconnection. | ||
872 | .IP "2 SSH_SMSG_PUBLIC_KEY" | ||
873 | .TS | ||
874 | ; | ||
875 | l l. | ||
876 | 8 bytes anti_spoofing_cookie | ||
877 | 32-bit int server_key_bits | ||
878 | mp-int server_key_public_exponent | ||
879 | mp-int server_key_public_modulus | ||
880 | 32-bit int host_key_bits | ||
881 | mp-int host_key_public_exponent | ||
882 | mp-int host_key_public_modulus | ||
883 | 32-bit int protocol_flags | ||
884 | 32-bit int supported_ciphers_mask | ||
885 | 32-bit int supported_authentications_mask | ||
886 | .TE | ||
887 | Sent as the first message by the server. This message gives the | ||
888 | server's host key, server key, protocol flags (intended for compatible | ||
889 | protocol extension), supported_ciphers_mask (which is the | ||
890 | bitwise or of (1 << cipher_number), where << is the left shift | ||
891 | operator, for all supported ciphers), and | ||
892 | supported_authentications_mask (which is the bitwise or of (1 << | ||
893 | authentication_type) for all supported authentication types). The | ||
894 | anti_spoofing_cookie is 64 random bytes, and must be sent back | ||
895 | verbatim by the client in its reply. It is used to make IP-spoofing | ||
896 | more difficult (encryption and host keys are the real defense against | ||
897 | spoofing). | ||
898 | .IP "3 SSH_CMSG_SESSION_KEY" | ||
899 | .TS | ||
900 | ; | ||
901 | l l. | ||
902 | 1 byte cipher_type (must be one of the supported values) | ||
903 | 8 bytes anti_spoofing_cookie (must match data sent by the server) | ||
904 | mp-int double-encrypted session key | ||
905 | 32-bit int protocol_flags | ||
906 | .TE | ||
907 | Sent by the client as the first message in the session. Selects the | ||
908 | cipher to use, and sends the encrypted session key to the server. The | ||
909 | anti_spoofing_cookie must be the same bytes that were sent by the | ||
910 | server. Protocol_flags is intended for negotiating compatible | ||
911 | protocol extensions. | ||
912 | .IP "4 SSH_CMSG_USER" | ||
913 | .TS | ||
914 | ; | ||
915 | l l. | ||
916 | string user login name on server | ||
917 | .TE | ||
918 | Sent by the client to begin authentication. Specifies the user name | ||
919 | on the server to log in as. The server responds with SSH_SMSG_SUCCESS | ||
920 | if no authentication is needed for this user, or SSH_SMSG_FAILURE if | ||
921 | authentication is needed (or the user does not exist). [Note to the | ||
922 | implementator: the user name is of arbitrary size. The implementation | ||
923 | must be careful not to overflow internal buffers.] | ||
924 | .IP "5 SSH_CMSG_AUTH_RHOSTS" | ||
925 | .TS | ||
926 | ; | ||
927 | l l. | ||
928 | string client-side user name | ||
929 | .TE | ||
930 | Requests authentication using /etc/hosts.equiv and .rhosts (or | ||
931 | equivalent mechanisms). This authentication method is normally | ||
932 | disabled in the server because it is not secure (but this is the | ||
933 | method used by rsh and rlogin). The server responds with | ||
934 | SSH_SMSG_SUCCESS if authentication was successful, and | ||
935 | SSH_SMSG_FAILURE if access was not granted. The server should check | ||
936 | that the client side port number is less than 1024 (a privileged | ||
937 | port), and immediately reject authentication if it is not. Supporting | ||
938 | this authentication method is optional. This method should normally | ||
939 | not be enabled in the server because it is not safe. (However, not | ||
940 | enabling this only helps if rlogind and rshd are disabled.) | ||
941 | .IP "6 SSH_CMSG_AUTH_RSA" | ||
942 | .TS | ||
943 | ; | ||
944 | l l. | ||
945 | mp-int identity_public_modulus | ||
946 | .TE | ||
947 | Requests authentication using pure RSA authentication. The server | ||
948 | checks if the given key is permitted to log in, and if so, responds | ||
949 | with SSH_SMSG_AUTH_RSA_CHALLENGE. Otherwise, it responds with | ||
950 | SSH_SMSG_FAILURE. The client often tries several different keys in | ||
951 | sequence until one supported by the server is found. Authentication | ||
952 | is accepted if the client gives the correct response to the challenge. | ||
953 | The server is free to add other criteria for authentication, such as a | ||
954 | requirement that the connection must come from a certain host. Such | ||
955 | additions are not visible at the protocol level. Supporting this | ||
956 | authentication method is optional but recommended. | ||
957 | .IP "7 SSH_SMSG_AUTH_RSA_CHALLENGE" | ||
958 | .TS | ||
959 | ; | ||
960 | l l. | ||
961 | mp-int encrypted challenge | ||
962 | .TE | ||
963 | Presents an RSA authentication challenge to the client. The challenge | ||
964 | is a 256-bit random value encrypted as described elsewhere in this | ||
965 | document. The client must decrypt the challenge using the RSA private | ||
966 | key, compute MD5 of the challenge plus session id, and send back the | ||
967 | resulting 16 bytes using SSH_CMSG_AUTH_RSA_RESPONSE. | ||
968 | .IP "8 SSH_CMSG_AUTH_RSA_RESPONSE" | ||
969 | .TS | ||
970 | ; | ||
971 | l l. | ||
972 | 16 bytes MD5 of decrypted challenge | ||
973 | .TE | ||
974 | This message is sent by the client in response to an RSA challenge. | ||
975 | The MD5 checksum is returned instead of the decrypted challenge to | ||
976 | deter known-plaintext attacks against the RSA key. The server | ||
977 | responds to this message with either SSH_SMSG_SUCCESS or | ||
978 | SSH_SMSG_FAILURE. | ||
979 | .IP "9 SSH_CMSG_AUTH_PASSWORD" | ||
980 | .TS | ||
981 | ; | ||
982 | l l. | ||
983 | string plain text password | ||
984 | .TE | ||
985 | Requests password authentication using the given password. Note that | ||
986 | even though the password is plain text inside the packet, the whole | ||
987 | packet is normally encrypted by the packet layer. It would not be | ||
988 | possible for the client to perform password encryption/hashing, | ||
989 | because it cannot know which kind of encryption/hashing, if any, the | ||
990 | server uses. The server responds to this message with | ||
991 | SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE. | ||
992 | .IP "10 SSH_CMSG_REQUEST_PTY" | ||
993 | .TS | ||
994 | ; | ||
995 | l l. | ||
996 | string TERM environment variable value (e.g. vt100) | ||
997 | 32-bit int terminal height, rows (e.g., 24) | ||
998 | 32-bit int terminal width, columns (e.g., 80) | ||
999 | 32-bit int terminal width, pixels (0 if no graphics) (e.g., 480) | ||
1000 | 32-bit int terminal height, pixels (0 if no graphics) (e.g., 640) | ||
1001 | n bytes tty modes encoded in binary | ||
1002 | .TE | ||
1003 | Requests a pseudo-terminal to be allocated for this command. This | ||
1004 | message can be used regardless of whether the session will later | ||
1005 | execute the shell or a command. If a pty has been requested with this | ||
1006 | message, the shell or command will run on a pty. Otherwise it will | ||
1007 | communicate with the server using pipes, sockets or some other similar | ||
1008 | mechanism. | ||
1009 | |||
1010 | The terminal type gives the type of the user's terminal. In the UNIX | ||
1011 | environment it is passed to the shell or command in the TERM | ||
1012 | environment variable. | ||
1013 | |||
1014 | The width and height values give the initial size of the user's | ||
1015 | terminal or window. All values can be zero if not supported by the | ||
1016 | operating system. The server will pass these values to the kernel if | ||
1017 | supported. | ||
1018 | |||
1019 | Terminal modes are encoded into a byte stream in a portable format. | ||
1020 | The exact format is described later in this document. | ||
1021 | |||
1022 | The server responds to the request with either SSH_SMSG_SUCCESS or | ||
1023 | SSH_SMSG_FAILURE. If the server does not have the concept of pseudo | ||
1024 | terminals, it should return success if it is possible to execute a | ||
1025 | shell or a command so that it looks to the client as if it was running | ||
1026 | on a pseudo terminal. | ||
1027 | .IP "11 SSH_CMSG_WINDOW_SIZE" | ||
1028 | .TS | ||
1029 | ; | ||
1030 | l l. | ||
1031 | 32-bit int terminal height, rows | ||
1032 | 32-bit int terminal width, columns | ||
1033 | 32-bit int terminal width, pixels | ||
1034 | 32-bit int terminal height, pixels | ||
1035 | .TE | ||
1036 | This message can only be sent by the client during the interactive | ||
1037 | session. This indicates that the size of the user's window has | ||
1038 | changed, and provides the new size. The server will update the | ||
1039 | kernel's notion of the window size, and a SIGWINCH signal or | ||
1040 | equivalent will be sent to the shell or command (if supported by the | ||
1041 | operating system). | ||
1042 | .IP "12 SSH_CMSG_EXEC_SHELL" | ||
1043 | |||
1044 | (no arguments) | ||
1045 | |||
1046 | Starts a shell (command interpreter), and enters interactive session | ||
1047 | mode. | ||
1048 | .IP "13 SSH_CMSG_EXEC_CMD" | ||
1049 | .TS | ||
1050 | ; | ||
1051 | l l. | ||
1052 | string command to execute | ||
1053 | .TE | ||
1054 | Starts executing the given command, and enters interactive session | ||
1055 | mode. On UNIX, the command is run as "<shell> -c <command>", where | ||
1056 | <shell> is the user's login shell. | ||
1057 | .IP "14 SSH_SMSG_SUCCESS" | ||
1058 | |||
1059 | (no arguments) | ||
1060 | |||
1061 | This message is sent by the server in response to the session key, a | ||
1062 | successful authentication request, and a successfully completed | ||
1063 | preparatory operation. | ||
1064 | .IP "15 SSH_SMSG_FAILURE" | ||
1065 | |||
1066 | (no arguments) | ||
1067 | |||
1068 | This message is sent by the server in response to a failed | ||
1069 | authentication operation to indicate that the user has not yet been | ||
1070 | successfully authenticated, and in response to a failed preparatory | ||
1071 | operation. This is also sent in response to an authentication or | ||
1072 | preparatory operation request that is not recognized or supported. | ||
1073 | .IP "16 SSH_CMSG_STDIN_DATA" | ||
1074 | .TS | ||
1075 | ; | ||
1076 | l l. | ||
1077 | string data | ||
1078 | .TE | ||
1079 | Delivers data from the client to be supplied as input to the shell or | ||
1080 | program running on the server side. This message can only be used in | ||
1081 | the interactive session mode. No acknowledgement is sent for this | ||
1082 | message. | ||
1083 | .IP "17 SSH_SMSG_STDOUT_DATA" | ||
1084 | .TS | ||
1085 | ; | ||
1086 | l l. | ||
1087 | string data | ||
1088 | .TE | ||
1089 | Delivers data from the server that was read from the standard output of | ||
1090 | the shell or program running on the server side. This message can | ||
1091 | only be used in the interactive session mode. No acknowledgement is | ||
1092 | sent for this message. | ||
1093 | .IP "18 SSH_SMSG_STDERR_DATA" | ||
1094 | .TS | ||
1095 | ; | ||
1096 | l l. | ||
1097 | string data | ||
1098 | .TE | ||
1099 | Delivers data from the server that was read from the standard error of | ||
1100 | the shell or program running on the server side. This message can | ||
1101 | only be used in the interactive session mode. No acknowledgement is | ||
1102 | sent for this message. | ||
1103 | .IP "19 SSH_CMSG_EOF" | ||
1104 | |||
1105 | (no arguments) | ||
1106 | |||
1107 | This message is sent by the client to indicate that EOF has been | ||
1108 | reached on the input. Upon receiving this message, and after all | ||
1109 | buffered input data has been sent to the shell or program, the server | ||
1110 | will close the input file descriptor to the program. This message can | ||
1111 | only be used in the interactive session mode. No acknowledgement is | ||
1112 | sent for this message. | ||
1113 | .IP "20 SSH_SMSG_EXITSTATUS" | ||
1114 | .TS | ||
1115 | ; | ||
1116 | l l. | ||
1117 | 32-bit int exit status of the command | ||
1118 | .TE | ||
1119 | Returns the exit status of the shell or program after it has exited. | ||
1120 | The client should respond with SSH_CMSG_EXIT_CONFIRMATION when it has | ||
1121 | received this message. This will be the last message sent by the | ||
1122 | server. If the program being executed dies with a signal instead of | ||
1123 | exiting normally, the server should terminate the session with | ||
1124 | SSH_MSG_DISCONNECT (which can be used to pass a human-readable string | ||
1125 | indicating that the program died due to a signal) instead of using | ||
1126 | this message. | ||
1127 | .IP "21 SSH_MSG_CHANNEL_OPEN_CONFIRMATION" | ||
1128 | .TS | ||
1129 | ; | ||
1130 | l l. | ||
1131 | 32-bit int remote_channel | ||
1132 | 32-bit int local_channel | ||
1133 | .TE | ||
1134 | This is sent in response to any channel open request if the channel | ||
1135 | has been successfully opened. Remote_channel is the channel number | ||
1136 | received in the initial open request; local_channel is the channel | ||
1137 | number the side sending this message has allocated for the channel. | ||
1138 | Data can be transmitted on the channel after this message. | ||
1139 | .IP "22 SSH_MSG_CHANNEL_OPEN_FAILURE" | ||
1140 | .TS | ||
1141 | ; | ||
1142 | l l. | ||
1143 | 32-bit int remote_channel | ||
1144 | .TE | ||
1145 | This message indicates that an earlier channel open request by the | ||
1146 | other side has failed or has been denied. Remote_channel is the | ||
1147 | channel number given in the original request. | ||
1148 | .IP "23 SSH_MSG_CHANNEL_DATA" | ||
1149 | .TS | ||
1150 | ; | ||
1151 | l l. | ||
1152 | 32-bit int remote_channel | ||
1153 | string data | ||
1154 | .TE | ||
1155 | Data is transmitted in a channel in these messages. A channel is | ||
1156 | bidirectional, and both sides can send these messages. There is no | ||
1157 | acknowledgement for these messages. It is possible that either side | ||
1158 | receives these messages after it has sent SSH_MSG_CHANNEL_CLOSE for | ||
1159 | the channel. These messages cannot be received after the party has | ||
1160 | sent or received SSH_MSG_CHANNEL_CLOSE_CONFIRMATION. | ||
1161 | .IP "24 SSH_MSG_CHANNEL_CLOSE" | ||
1162 | .TS | ||
1163 | ; | ||
1164 | l l. | ||
1165 | 32-bit int remote_channel | ||
1166 | .TE | ||
1167 | When a channel is closed at one end of the connection, that side sends | ||
1168 | this message. Upon receiving this message, the channel should be | ||
1169 | closed. When this message is received, if the channel is already | ||
1170 | closed (the receiving side has sent this message for the same channel | ||
1171 | earlier), the channel is freed and no further action is taken; | ||
1172 | otherwise the channel is freed and SSH_MSG_CHANNEL_CLOSE_CONFIRMATION | ||
1173 | is sent in response. (It is possible that the channel is closed | ||
1174 | simultaneously at both ends.) | ||
1175 | .IP "25 SSH_MSG_CHANNEL_CLOSE_CONFIRMATION" | ||
1176 | .TS | ||
1177 | ; | ||
1178 | l l. | ||
1179 | 32-bit int remote_channel | ||
1180 | .TE | ||
1181 | This message is sent in response to SSH_MSG_CHANNEL_CLOSE unless the | ||
1182 | channel was already closed. When this message is sent or received, | ||
1183 | the channel is freed. | ||
1184 | .IP "26 (OBSOLETED; was unix-domain X11 forwarding) | ||
1185 | .IP "27 SSH_SMSG_X11_OPEN" | ||
1186 | .TS | ||
1187 | ; | ||
1188 | l l. | ||
1189 | 32-bit int local_channel | ||
1190 | string originator_string (see below) | ||
1191 | .TE | ||
1192 | This message can be sent by the server during the interactive session | ||
1193 | mode to indicate that a client has connected the fake X server. | ||
1194 | Local_channel is the channel number that the server has allocated for | ||
1195 | the connection. The client should try to open a connection to the | ||
1196 | real X server, and respond with SSH_MSG_CHANNEL_OPEN_CONFIRMATION or | ||
1197 | SSH_MSG_CHANNEL_OPEN_FAILURE. | ||
1198 | |||
1199 | The field originator_string is present if both sides | ||
1200 | specified SSH_PROTOFLAG_HOST_IN_FWD_OPEN in the protocol flags. It | ||
1201 | contains a description of the host originating the connection. | ||
1202 | .IP "28 SSH_CMSG_PORT_FORWARD_REQUEST" | ||
1203 | .TS | ||
1204 | ; | ||
1205 | l l. | ||
1206 | 32-bit int server_port | ||
1207 | string host_to_connect | ||
1208 | 32-bit int port_to_connect | ||
1209 | .TE | ||
1210 | Sent by the client in the preparatory phase, this message requests | ||
1211 | that server_port on the server machine be forwarded over the secure | ||
1212 | channel to the client machine, and from there to the specified host | ||
1213 | and port. The server should start listening on the port, and send | ||
1214 | SSH_MSG_PORT_OPEN whenever a connection is made to it. Supporting | ||
1215 | this message is optional, and the server is free to reject any forward | ||
1216 | request. For example, it is highly recommended that unless the user | ||
1217 | has been authenticated as root, forwarding any privileged port numbers | ||
1218 | (below 1024) is denied. | ||
1219 | .IP "29 SSH_MSG_PORT_OPEN" | ||
1220 | .TS | ||
1221 | ; | ||
1222 | l l. | ||
1223 | 32-bit int local_channel | ||
1224 | string host_name | ||
1225 | 32-bit int port | ||
1226 | string originator_string (see below) | ||
1227 | .TE | ||
1228 | Sent by either party in interactive session mode, this message | ||
1229 | indicates that a connection has been opened to a forwarded TCP/IP | ||
1230 | port. Local_channel is the channel number that the sending party has | ||
1231 | allocated for the connection. Host_name is the host the connection | ||
1232 | should be be forwarded to, and the port is the port on that host to | ||
1233 | connect. The receiving party should open the connection, and respond | ||
1234 | with SSH_MSG_CHANNEL_OPEN_CONFIRMATION or | ||
1235 | SSH_MSG_CHANNEL_OPEN_FAILURE. It is recommended that the receiving | ||
1236 | side check the host_name and port for validity to avoid compromising | ||
1237 | local security by compromised remote side software. Particularly, it | ||
1238 | is recommended that the client permit connections only to those ports | ||
1239 | for which it has requested forwarding with SSH_CMSG_PORT_FORWARD_REQUEST. | ||
1240 | |||
1241 | The field originator_string is present if both sides | ||
1242 | specified SSH_PROTOFLAG_HOST_IN_FWD_OPEN in the protocol flags. It | ||
1243 | contains a description of the host originating the connection. | ||
1244 | .IP "30 SSH_CMSG_AGENT_REQUEST_FORWARDING" | ||
1245 | |||
1246 | (no arguments) | ||
1247 | |||
1248 | Requests that the connection to the authentication agent be forwarded | ||
1249 | over the secure channel. The method used by clients to contact the | ||
1250 | authentication agent within each machine is implementation and machine | ||
1251 | dependent. If the server accepts this request, it should arrange that | ||
1252 | any clients run from this session will actually contact the server | ||
1253 | program when they try to contact the authentication agent. The server | ||
1254 | should then send a SSH_SMSG_AGENT_OPEN to open a channel to the agent, | ||
1255 | and the client should forward the connection to the real | ||
1256 | authentication agent. Supporting this message is optional. | ||
1257 | .IP "31 SSH_SMSG_AGENT_OPEN" | ||
1258 | .TS | ||
1259 | ; | ||
1260 | l l. | ||
1261 | 32-bit int local_channel | ||
1262 | .TE | ||
1263 | Sent by the server in interactive session mode, this message requests | ||
1264 | opening a channel to the authentication agent. The client should open | ||
1265 | a channel, and respond with either SSH_MSG_CHANNEL_OPEN_CONFIRMATION | ||
1266 | or SSH_MSG_CHANNEL_OPEN_FAILURE. | ||
1267 | .IP "32 SSH_MSG_IGNORE" | ||
1268 | .TS | ||
1269 | ; | ||
1270 | l l. | ||
1271 | string data | ||
1272 | .TE | ||
1273 | Either party may send this message at any time. This message, and the | ||
1274 | argument string, is silently ignored. This message might be used in | ||
1275 | some implementations to make traffic analysis more difficult. This | ||
1276 | message is not currently sent by the implementation, but all | ||
1277 | implementations are required to recognize and ignore it. | ||
1278 | .IP "33 SSH_CMSG_EXIT_CONFIRMATION" | ||
1279 | |||
1280 | (no arguments) | ||
1281 | |||
1282 | Sent by the client in response to SSH_SMSG_EXITSTATUS. This is the | ||
1283 | last message sent by the client. | ||
1284 | .IP "34 SSH_CMSG_X11_REQUEST_FORWARDING" | ||
1285 | .TS | ||
1286 | ; | ||
1287 | l l. | ||
1288 | string x11_authentication_protocol | ||
1289 | string x11_authentication_data | ||
1290 | 32-bit int screen number (if SSH_PROTOFLAG_SCREEN_NUMBER) | ||
1291 | .TE | ||
1292 | Sent by the client during the preparatory phase, this message requests | ||
1293 | that the server create a fake X11 display and set the DISPLAY | ||
1294 | environment variable accordingly. An internet-domain display is | ||
1295 | preferable. The given authentication protocol and the associated data | ||
1296 | should be recorded by the server so that it is used as authentication | ||
1297 | on connections (e.g., in .Xauthority). The authentication protocol | ||
1298 | must be one of the supported X11 authentication protocols, e.g., | ||
1299 | "MIT-MAGIC-COOKIE-1". Authentication data must be a lowercase hex | ||
1300 | string of even length. Its interpretation is protocol dependent. | ||
1301 | The data is in a format that can be used with e.g. the xauth program. | ||
1302 | Supporting this message is optional. | ||
1303 | |||
1304 | The client is permitted (and recommended) to generate fake | ||
1305 | authentication information and send fake information to the server. | ||
1306 | This way, a corrupt server will not have access to the user's terminal | ||
1307 | after the connection has terminated. The correct authorization codes | ||
1308 | will also not be left hanging around in files on the server (many | ||
1309 | users keep the same X session for months, thus protecting the | ||
1310 | authorization data becomes important). | ||
1311 | |||
1312 | X11 authentication spoofing works by initially sending fake (random) | ||
1313 | authentication data to the server, and interpreting the first packet | ||
1314 | sent by the X11 client after the connection has been opened. The | ||
1315 | first packet contains the client's authentication. If the packet | ||
1316 | contains the correct fake data, it is replaced by the client by the | ||
1317 | correct authentication data, and then sent to the X server. | ||
1318 | .IP "35 SSH_CMSG_AUTH_RHOSTS_RSA" | ||
1319 | .TS | ||
1320 | ; | ||
1321 | l l. | ||
1322 | string clint-side user name | ||
1323 | 32-bit int client_host_key_bits | ||
1324 | mp-int client_host_key_public_exponent | ||
1325 | mp-int client_host_key_public_modulus | ||
1326 | .TE | ||
1327 | Requests authentication using /etc/hosts.equiv and .rhosts (or | ||
1328 | equivalent) together with RSA host authentication. The server should | ||
1329 | check that the client side port number is less than 1024 (a privileged | ||
1330 | port), and immediately reject authentication if it is not. The server | ||
1331 | responds with SSH_SMSG_FAILURE or SSH_SMSG_AUTH_RSA_CHALLENGE. The | ||
1332 | client must respond to the challenge with the proper | ||
1333 | SSH_CMSG_AUTH_RSA_RESPONSE. The server then responds with success if | ||
1334 | access was granted, or failure if the client gave a wrong response. | ||
1335 | Supporting this authentication method is optional but recommended in | ||
1336 | most environments. | ||
1337 | .IP "36 SSH_MSG_DEBUG" | ||
1338 | .TS | ||
1339 | ; | ||
1340 | l l. | ||
1341 | string debugging message sent to the other side | ||
1342 | .TE | ||
1343 | This message may be sent by either party at any time. It is used to | ||
1344 | send debugging messages that may be informative to the user in | ||
1345 | solving various problems. For example, if authentication fails | ||
1346 | because of some configuration error (e.g., incorrect permissions for | ||
1347 | some file), it can be very helpful for the user to make the cause of | ||
1348 | failure available. On the other hand, one should not make too much | ||
1349 | information available for security reasons. It is recommended that | ||
1350 | the client provides an option to display the debugging information | ||
1351 | sent by the sender (the user probably does not want to see it by default). | ||
1352 | The server can log debugging data sent by the client (if any). Either | ||
1353 | party is free to ignore any received debugging data. Every | ||
1354 | implementation must be able to receive this message, but no | ||
1355 | implementation is required to send these. | ||
1356 | .IP "37 SSH_CMSG_REQUEST_COMPRESSION" | ||
1357 | .TS | ||
1358 | ; | ||
1359 | l l. | ||
1360 | 32-bit int gzip compression level (1-9) | ||
1361 | .TE | ||
1362 | This message can be sent by the client in the preparatory operations | ||
1363 | phase. The server responds with SSH_SMSG_FAILURE if it does not | ||
1364 | support compression or does not want to compress; it responds with | ||
1365 | SSH_SMSG_SUCCESS if it accepted the compression request. In the | ||
1366 | latter case the response to this packet will still be uncompressed, | ||
1367 | but all further packets in either direction will be compressed by gzip. | ||
1368 | .RT | ||
1369 | |||
1370 | |||
1371 | .ti 0 | ||
1372 | Encoding of Terminal Modes | ||
1373 | |||
1374 | Terminal modes (as passed in SSH_CMSG_REQUEST_PTY) are encoded into a | ||
1375 | byte stream. It is intended that the coding be portable across | ||
1376 | different environments. | ||
1377 | |||
1378 | The tty mode description is a stream of bytes. The stream consists of | ||
1379 | opcode-argument pairs. It is terminated by opcode TTY_OP_END (0). | ||
1380 | Opcodes 1-127 have one-byte arguments. Opcodes 128-159 have 32-bit | ||
1381 | integer arguments (stored msb first). Opcodes 160-255 are not yet | ||
1382 | defined, and cause parsing to stop (they should only be used after any | ||
1383 | other data). | ||
1384 | |||
1385 | The client puts in the stream any modes it knows about, and the server | ||
1386 | ignores any modes it does not know about. This allows some degree of | ||
1387 | machine-independence, at least between systems that use a POSIX-like | ||
1388 | [POSIX] tty interface. The protocol can support other systems as | ||
1389 | well, but the client may need to fill reasonable values for a number | ||
1390 | of parameters so the server pty gets set to a reasonable mode (the | ||
1391 | server leaves all unspecified mode bits in their default values, and | ||
1392 | only some combinations make sense). | ||
1393 | |||
1394 | The following opcodes have been defined. The naming of opcodes mostly | ||
1395 | follows the POSIX terminal mode flags. | ||
1396 | .IP "0 TTY_OP_END" | ||
1397 | Indicates end of options. | ||
1398 | .IP "1 VINTR" | ||
1399 | Interrupt character; 255 if none. Similarly for the other characters. | ||
1400 | Not all of these characters are supported on all systems. | ||
1401 | .IP "2 VQUIT" | ||
1402 | The quit character (sends SIGQUIT signal on UNIX systems). | ||
1403 | .IP "3 VERASE" | ||
1404 | Erase the character to left of the cursor. | ||
1405 | .IP "4 VKILL" | ||
1406 | Kill the current input line. | ||
1407 | .IP "5 VEOF " | ||
1408 | End-of-file character (sends EOF from the terminal). | ||
1409 | .IP "6 VEOL " | ||
1410 | End-of-line character in addition to carriage return and/or linefeed. | ||
1411 | .IP "7 VEOL2" | ||
1412 | Additional end-of-line character. | ||
1413 | .IP "8 VSTART" | ||
1414 | Continues paused output (normally ^Q). | ||
1415 | .IP "9 VSTOP" | ||
1416 | Pauses output (^S). | ||
1417 | .IP "10 VSUSP" | ||
1418 | Suspends the current program. | ||
1419 | .IP "11 VDSUSP" | ||
1420 | Another suspend character. | ||
1421 | .IP "12 VREPRINT" | ||
1422 | Reprints the current input line. | ||
1423 | .IP "13 VWERASE" | ||
1424 | Erases a word left of cursor. | ||
1425 | .IP "14 VLNEXT" | ||
1426 | More special input characters; these are probably not supported on | ||
1427 | most systems. | ||
1428 | .IP "15 VFLUSH" | ||
1429 | .IP "16 VSWTCH" | ||
1430 | .IP "17 VSTATUS" | ||
1431 | .IP "18 VDISCARD" | ||
1432 | |||
1433 | .IP "30 IGNPAR" | ||
1434 | The ignore parity flag. The next byte should be 0 if this flag is not | ||
1435 | set, and 1 if it is set. | ||
1436 | .IP "31 PARMRK" | ||
1437 | More flags. The exact definitions can be found in the POSIX standard. | ||
1438 | .IP "32 INPCK" | ||
1439 | .IP "33 ISTRIP" | ||
1440 | .IP "34 INLCR" | ||
1441 | .IP "35 IGNCR" | ||
1442 | .IP "36 ICRNL" | ||
1443 | .IP "37 IUCLC" | ||
1444 | .IP "38 IXON" | ||
1445 | .IP "39 IXANY" | ||
1446 | .IP "40 IXOFF" | ||
1447 | .IP "41 IMAXBEL" | ||
1448 | |||
1449 | .IP "50 ISIG" | ||
1450 | .IP "51 ICANON" | ||
1451 | .IP "52 XCASE" | ||
1452 | .IP "53 ECHO" | ||
1453 | .IP "54 ECHOE" | ||
1454 | .IP "55 ECHOK" | ||
1455 | .IP "56 ECHONL" | ||
1456 | .IP "57 NOFLSH" | ||
1457 | .IP "58 TOSTOP" | ||
1458 | .IP "59 IEXTEN" | ||
1459 | .IP "60 ECHOCTL" | ||
1460 | .IP "61 ECHOKE" | ||
1461 | .IP "62 PENDIN" | ||
1462 | |||
1463 | .IP "70 OPOST" | ||
1464 | .IP "71 OLCUC" | ||
1465 | .IP "72 ONLCR" | ||
1466 | .IP "73 OCRNL" | ||
1467 | .IP "74 ONOCR" | ||
1468 | .IP "75 ONLRET" | ||
1469 | |||
1470 | .IP "90 CS7" | ||
1471 | .IP "91 CS8" | ||
1472 | .IP "92 PARENB" | ||
1473 | .IP "93 PARODD" | ||
1474 | |||
1475 | .IP "192 TTY_OP_ISPEED" | ||
1476 | Specifies the input baud rate in bits per second. | ||
1477 | .IP "193 TTY_OP_OSPEED" | ||
1478 | Specifies the output baud rate in bits per second. | ||
1479 | .RT | ||
1480 | |||
1481 | |||
1482 | .ti 0 | ||
1483 | The Authentication Agent Protocol | ||
1484 | |||
1485 | The authentication agent is a program that can be used to hold RSA | ||
1486 | authentication keys for the user (in future, it might hold data for | ||
1487 | other authentication types as well). An authorized program can send | ||
1488 | requests to the agent to generate a proper response to an RSA | ||
1489 | challenge. How the connection is made to the agent (or its | ||
1490 | representative) inside a host and how access control is done inside a | ||
1491 | host is implementation-dependent; however, how it is forwarded and how | ||
1492 | one interacts with it is specified in this protocol. The connection | ||
1493 | to the agent is normally automatically forwarded over the secure | ||
1494 | channel. | ||
1495 | |||
1496 | A program that wishes to use the agent first opens a connection to its | ||
1497 | local representative (typically, the agent itself or an SSH server). | ||
1498 | It then writes a request to the connection, and waits for response. | ||
1499 | It is recommended that at least five minutes of timeout are provided | ||
1500 | waiting for the agent to respond to an authentication challenge (this | ||
1501 | gives sufficient time for the user to cut-and-paste the challenge to a | ||
1502 | separate machine, perform the computation there, and cut-and-paste the | ||
1503 | result back if so desired). | ||
1504 | |||
1505 | Messages sent to and by the agent are in the following format: | ||
1506 | .TS | ||
1507 | ; | ||
1508 | l l. | ||
1509 | 4 bytes Length, msb first. Does not include length itself. | ||
1510 | 1 byte Packet type. The value 255 is reserved for future extensions. | ||
1511 | data Any data, depending on packet type. Encoding as in the ssh packet | ||
1512 | protocol. | ||
1513 | .TE | ||
1514 | |||
1515 | The following message types are currently defined: | ||
1516 | .IP "1 SSH_AGENTC_REQUEST_RSA_IDENTITIES" | ||
1517 | |||
1518 | (no arguments) | ||
1519 | |||
1520 | Requests the agent to send a list of all RSA keys for which it can | ||
1521 | answer a challenge. | ||
1522 | .IP "2 SSH_AGENT_RSA_IDENTITIES_ANSWER" | ||
1523 | .TS | ||
1524 | ; | ||
1525 | l l. | ||
1526 | 32-bit int howmany | ||
1527 | howmany times: | ||
1528 | 32-bit int bits | ||
1529 | mp-int public exponent | ||
1530 | mp-int public modulus | ||
1531 | string comment | ||
1532 | .TE | ||
1533 | The agent sends this message in response to the to | ||
1534 | SSH_AGENTC_REQUEST_RSA_IDENTITIES. The answer lists all RSA keys for | ||
1535 | which the agent can answer a challenge. The comment field is intended | ||
1536 | to help identify each key; it may be printed by an application to | ||
1537 | indicate which key is being used. If the agent is not holding any | ||
1538 | keys, howmany will be zero. | ||
1539 | .IP "3 SSH_AGENTC_RSA_CHALLENGE | ||
1540 | .TS | ||
1541 | ; | ||
1542 | l l. | ||
1543 | 32-bit int bits | ||
1544 | mp-int public exponent | ||
1545 | mp-int public modulus | ||
1546 | mp-int challenge | ||
1547 | 16 bytes session_id | ||
1548 | 32-bit int response_type | ||
1549 | .TE | ||
1550 | Requests RSA decryption of random challenge to authenticate the other | ||
1551 | side. The challenge will be decrypted with the RSA private key | ||
1552 | corresponding to the given public key. | ||
1553 | |||
1554 | The decrypted challenge must contain a zero in the highest (partial) | ||
1555 | byte, 2 in the next byte, followed by non-zero random bytes, a zero | ||
1556 | byte, and then the real challenge value in the lowermost bytes. The | ||
1557 | real challenge must be 32 8-bit bytes (256 bits). | ||
1558 | |||
1559 | Response_type indicates the format of the response to be returned. | ||
1560 | Currently the only supported value is 1, which means to compute MD5 of | ||
1561 | the real challenge plus session id, and return the resulting 16 bytes | ||
1562 | in a SSH_AGENT_RSA_RESPONSE message. | ||
1563 | .IP "4 SSH_AGENT_RSA_RESPONSE" | ||
1564 | .TS | ||
1565 | ; | ||
1566 | l l. | ||
1567 | 16 bytes MD5 of decrypted challenge | ||
1568 | .TE | ||
1569 | Answers an RSA authentication challenge. The response is 16 bytes: | ||
1570 | the MD5 checksum of the 32-byte challenge. | ||
1571 | .IP "5 SSH_AGENT_FAILURE" | ||
1572 | |||
1573 | (no arguments) | ||
1574 | |||
1575 | This message is sent whenever the agent fails to answer a request | ||
1576 | properly. For example, if the agent cannot answer a challenge (e.g., | ||
1577 | no longer has the proper key), it can respond with this. The agent | ||
1578 | also responds with this message if it receives a message it does not | ||
1579 | recognize. | ||
1580 | .IP "6 SSH_AGENT_SUCCESS" | ||
1581 | |||
1582 | (no arguments) | ||
1583 | |||
1584 | This message is sent by the agent as a response to certain requests | ||
1585 | that do not otherwise cause a message be sent. Currently, this is | ||
1586 | only sent in response to SSH_AGENTC_ADD_RSA_IDENTITY and | ||
1587 | SSH_AGENTC_REMOVE_RSA_IDENTITY. | ||
1588 | .IP "7 SSH_AGENTC_ADD_RSA_IDENTITY" | ||
1589 | .TS | ||
1590 | ; | ||
1591 | l l. | ||
1592 | 32-bit int bits | ||
1593 | mp-int public modulus | ||
1594 | mp-int public exponent | ||
1595 | mp-int private exponent | ||
1596 | mp-int multiplicative inverse of p mod q | ||
1597 | mp-int p | ||
1598 | mp-int q | ||
1599 | string comment | ||
1600 | .TE | ||
1601 | Registers an RSA key with the agent. After this request, the agent can | ||
1602 | use this RSA key to answer requests. The agent responds with | ||
1603 | SSH_AGENT_SUCCESS or SSH_AGENT_FAILURE. | ||
1604 | .IP "8 SSH_AGENT_REMOVE_RSA_IDENTITY" | ||
1605 | .TS | ||
1606 | ; | ||
1607 | l l. | ||
1608 | 32-bit int bits | ||
1609 | mp-int public exponent | ||
1610 | mp-int public modulus | ||
1611 | .TE | ||
1612 | Removes an RSA key from the agent. The agent will no longer accept | ||
1613 | challenges for this key and will not list it as a supported identity. | ||
1614 | The agent responds with SSH_AGENT_SUCCESS or SSH_AGENT_FAILURE. | ||
1615 | .RT | ||
1616 | |||
1617 | If the agent receives a message that it does not understand, it | ||
1618 | responds with SSH_AGENT_FAILURE. This permits compatible future | ||
1619 | extensions. | ||
1620 | |||
1621 | It is possible that several clients have a connection open to the | ||
1622 | authentication agent simultaneously. Each client will use a separate | ||
1623 | connection (thus, any SSH connection can have multiple agent | ||
1624 | connections active simultaneously). | ||
1625 | |||
1626 | |||
1627 | .ti 0 | ||
1628 | References | ||
1629 | |||
1630 | .IP "[DES] " | ||
1631 | FIPS PUB 46-1: Data Encryption Standard. National Bureau of | ||
1632 | Standards, January 1988. FIPS PUB 81: DES Modes of Operation. | ||
1633 | National Bureau of Standards, December 1980. Bruce Schneier: Applied | ||
1634 | Cryptography. John Wiley & Sons, 1994. J. Seberry and J. Pieprzyk: | ||
1635 | Cryptography: An Introduction to Computer Security. Prentice-Hall, | ||
1636 | 1989. | ||
1637 | .IP "[GZIP] " | ||
1638 | The GNU GZIP program; available for anonymous ftp at prep.ai.mit.edu. | ||
1639 | Please let me know if you know a paper describing the algorithm. | ||
1640 | .IP "[IDEA] " | ||
1641 | Xuejia Lai: On the Design and Security of Block Ciphers, ETH Series in | ||
1642 | Information Processing, vol. 1, Hartung-Gorre Verlag, Konstanz, | ||
1643 | Switzerland, 1992. Bruce Schneier: Applied Cryptography, John Wiley & | ||
1644 | Sons, 1994. See also the following patents: PCT/CH91/00117, EP 0 482 | ||
1645 | 154 B1, US Pat. 5,214,703. | ||
1646 | .IP [PKCS#1] | ||
1647 | PKCS #1: RSA Encryption Standard. Version 1.5, RSA Laboratories, | ||
1648 | November 1993. Available for anonymous ftp at ftp.rsa.com. | ||
1649 | .IP [POSIX] | ||
1650 | Portable Operating System Interface (POSIX) - Part 1: Application | ||
1651 | Program Interface (API) [C language], ISO/IEC 9945-1, IEEE Std 1003.1, | ||
1652 | 1990. | ||
1653 | .IP [RFC0791] | ||
1654 | J. Postel: Internet Protocol, RFC 791, USC/ISI, September 1981. | ||
1655 | .IP [RFC0793] | ||
1656 | J. Postel: Transmission Control Protocol, RFC 793, USC/ISI, September | ||
1657 | 1981. | ||
1658 | .IP [RFC1034] | ||
1659 | P. Mockapetris: Domain Names - Concepts and Facilities, RFC 1034, | ||
1660 | USC/ISI, November 1987. | ||
1661 | .IP [RFC1282] | ||
1662 | B. Kantor: BSD Rlogin, RFC 1258, UCSD, December 1991. | ||
1663 | .IP "[RSA] " | ||
1664 | Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1994. See | ||
1665 | also R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic | ||
1666 | Communications System and Method. US Patent 4,405,829, 1983. | ||
1667 | .IP "[X11] " | ||
1668 | R. Scheifler: X Window System Protocol, X Consortium Standard, Version | ||
1669 | 11, Release 6. Massachusetts Institute of Technology, Laboratory of | ||
1670 | Computer Science, 1994. | ||
1671 | .RT | ||
1672 | |||
1673 | |||
1674 | .ti 0 | ||
1675 | Security Considerations | ||
1676 | |||
1677 | This protocol deals with the very issue of user authentication and | ||
1678 | security. | ||
1679 | |||
1680 | First of all, as an implementation issue, the server program will have | ||
1681 | to run as root (or equivalent) on the server machine. This is because | ||
1682 | the server program will need be able to change to an arbitrary user | ||
1683 | id. The server must also be able to create a privileged TCP/IP port. | ||
1684 | |||
1685 | The client program will need to run as root if any variant of .rhosts | ||
1686 | authentication is to be used. This is because the client program will | ||
1687 | need to create a privileged port. The client host key is also usually | ||
1688 | stored in a file which is readable by root only. The client needs the | ||
1689 | host key in .rhosts authentication only. Root privileges can be | ||
1690 | dropped as soon as the privileged port has been created and the host | ||
1691 | key has been read. | ||
1692 | |||
1693 | The SSH protocol offers major security advantages over existing telnet | ||
1694 | and rlogin protocols. | ||
1695 | .IP o | ||
1696 | IP spoofing is restricted to closing a connection (by encryption, host | ||
1697 | keys, and the special random cookie). If encryption is not used, IP | ||
1698 | spoofing is possible for those who can hear packets going out from the | ||
1699 | server. | ||
1700 | .IP o | ||
1701 | DNS spoofing is made ineffective (by host keys). | ||
1702 | .IP o | ||
1703 | Routing spoofing is made ineffective (by host keys). | ||
1704 | .IP o | ||
1705 | All data is encrypted with strong algorithms to make eavesdropping as | ||
1706 | difficult as possible. This includes encrypting any authentication | ||
1707 | information such as passwords. The information for decrypting session | ||
1708 | keys is destroyed every hour. | ||
1709 | .IP o | ||
1710 | Strong authentication methods: .rhosts combined with RSA host | ||
1711 | authentication, and pure RSA authentication. | ||
1712 | .IP o | ||
1713 | X11 connections and arbitrary TCP/IP ports can be forwarded securely. | ||
1714 | .IP o | ||
1715 | Man-in-the-middle attacks are deterred by using the server host key to | ||
1716 | encrypt the session key. | ||
1717 | .IP o | ||
1718 | Trojan horses to catch a password by routing manipulation are deterred | ||
1719 | by checking that the host key of the server machine matches that | ||
1720 | stored on the client host. | ||
1721 | .RT | ||
1722 | |||
1723 | The security of SSH against man-in-the-middle attacks and the security | ||
1724 | of the new form of .rhosts authentication, as well as server host | ||
1725 | validation, depends on the integrity of the host key and the files | ||
1726 | containing known host keys. | ||
1727 | |||
1728 | The host key is normally stored in a root-readable file. If the host | ||
1729 | key is compromised, it permits attackers to use IP, DNS and routing | ||
1730 | spoofing as with current rlogin and rsh. It should never be any worse | ||
1731 | than the current situation. | ||
1732 | |||
1733 | The files containing known host keys are not sensitive. However, if an | ||
1734 | attacker gets to modify the known host key files, it has the same | ||
1735 | consequences as a compromised host key, because the attacker can then | ||
1736 | change the recorded host key. | ||
1737 | |||
1738 | The security improvements obtained by this protocol for X11 are of | ||
1739 | particular significance. Previously, there has been no way to protect | ||
1740 | data communicated between an X server and a client running on a remote | ||
1741 | machine. By creating a fake display on the server, and forwarding all | ||
1742 | X11 requests over the secure channel, SSH can be used to run any X11 | ||
1743 | applications securely without any cooperation with the vendors of the | ||
1744 | X server or the application. | ||
1745 | |||
1746 | Finally, the security of this program relies on the strength of the | ||
1747 | underlying cryptographic algorithms. The RSA algorithm is used for | ||
1748 | authentication key exchange. It is widely believed to be secure. Of | ||
1749 | the algorithms used to encrypt the session, DES has a rather small key | ||
1750 | these days, probably permitting governments and organized criminals to | ||
1751 | break it in very short time with specialized hardware. 3DES is | ||
1752 | probably safe (but slower). IDEA is widely believed to be secure. | ||
1753 | People have varying degrees of confidence in the other algorithms. | ||
1754 | This program is not secure if used with no encryption at all. | ||
1755 | |||
1756 | |||
1757 | .ti 0 | ||
1758 | Additional Information | ||
1759 | |||
1760 | Additional information (especially on the implementation and mailing | ||
1761 | lists) is available via WWW at http://www.cs.hut.fi/ssh. | ||
1762 | |||
1763 | Comments should be sent to Tatu Ylonen <ylo@cs.hut.fi> or the SSH | ||
1764 | Mailing List <ssh@clinet.fi>. | ||
1765 | |||
1766 | .ti 0 | ||
1767 | Author's Address | ||
1768 | |||
1769 | .TS | ||
1770 | ; | ||
1771 | l. | ||
1772 | Tatu Ylonen | ||
1773 | Helsinki University of Technology | ||
1774 | Otakaari 1 | ||
1775 | FIN-02150 Espoo, Finland | ||
1776 | |||
1777 | Phone: +358-0-451-3374 | ||
1778 | Fax: +358-0-451-3293 | ||
1779 | EMail: ylo@cs.hut.fi | ||
1780 | .TE | ||
diff --git a/auth-krb4.c b/auth-krb4.c new file mode 100644 index 000000000..720f3a4c4 --- /dev/null +++ b/auth-krb4.c | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | |||
3 | auth-kerberos.c | ||
4 | |||
5 | Dug Song <dugsong@UMICH.EDU> | ||
6 | |||
7 | Kerberos v4 authentication and ticket-passing routines. | ||
8 | |||
9 | $Id: auth-krb4.c,v 1.1 1999/10/27 03:42:43 damien Exp $ | ||
10 | */ | ||
11 | |||
12 | #include "includes.h" | ||
13 | #include "packet.h" | ||
14 | #include "xmalloc.h" | ||
15 | #include "ssh.h" | ||
16 | |||
17 | #ifdef KRB4 | ||
18 | int ssh_tf_init(uid_t uid) | ||
19 | { | ||
20 | extern char *ticket; | ||
21 | char *tkt_root = TKT_ROOT; | ||
22 | struct stat st; | ||
23 | int fd; | ||
24 | |||
25 | /* Set unique ticket string manually since we're still root. */ | ||
26 | ticket = xmalloc(MAXPATHLEN); | ||
27 | #ifdef AFS | ||
28 | if (lstat("/ticket", &st) != -1) | ||
29 | tkt_root = "/ticket/"; | ||
30 | #endif /* AFS */ | ||
31 | snprintf(ticket, MAXPATHLEN, "%s%d_%d", tkt_root, uid, getpid()); | ||
32 | (void) krb_set_tkt_string(ticket); | ||
33 | |||
34 | /* Make sure we own this ticket file, and we created it. */ | ||
35 | if (lstat(ticket, &st) == -1 && errno == ENOENT) { | ||
36 | /* good, no ticket file exists. create it. */ | ||
37 | if ((fd = open(ticket, O_RDWR|O_CREAT|O_EXCL, 0600)) != -1) { | ||
38 | close(fd); | ||
39 | return 1; | ||
40 | } | ||
41 | } | ||
42 | else { | ||
43 | /* file exists. make sure server_user owns it (e.g. just passed ticket), | ||
44 | and that it isn't a symlink, and that it is mode 600. */ | ||
45 | if (st.st_mode == (S_IFREG|S_IRUSR|S_IWUSR) && st.st_uid == uid) | ||
46 | return 1; | ||
47 | } | ||
48 | /* Failure. */ | ||
49 | log("WARNING: bad ticket file %s", ticket); | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | int auth_krb4(const char *server_user, KTEXT auth, char **client) | ||
54 | { | ||
55 | AUTH_DAT adat = { 0 }; | ||
56 | KTEXT_ST reply; | ||
57 | char instance[INST_SZ]; | ||
58 | int r, s; | ||
59 | u_int cksum; | ||
60 | Key_schedule schedule; | ||
61 | struct sockaddr_in local, foreign; | ||
62 | |||
63 | s = packet_get_connection_in(); | ||
64 | |||
65 | r = sizeof(local); | ||
66 | memset(&local, 0, sizeof(local)); | ||
67 | if (getsockname(s, (struct sockaddr *) &local, &r) < 0) | ||
68 | debug("getsockname failed: %.100s", strerror(errno)); | ||
69 | r = sizeof(foreign); | ||
70 | memset(&foreign, 0, sizeof(foreign)); | ||
71 | if (getpeername(s, (struct sockaddr *)&foreign, &r) < 0) | ||
72 | debug("getpeername failed: %.100s", strerror(errno)); | ||
73 | |||
74 | instance[0] = '*'; instance[1] = 0; | ||
75 | |||
76 | /* Get the encrypted request, challenge, and session key. */ | ||
77 | if ((r = krb_rd_req(auth, KRB4_SERVICE_NAME, instance, 0, &adat, ""))) { | ||
78 | packet_send_debug("Kerberos V4 krb_rd_req: %.100s", krb_err_txt[r]); | ||
79 | return 0; | ||
80 | } | ||
81 | des_key_sched((des_cblock *)adat.session, schedule); | ||
82 | |||
83 | *client = xmalloc(MAX_K_NAME_SZ); | ||
84 | (void) snprintf(*client, MAX_K_NAME_SZ, "%s%s%s@%s", adat.pname, | ||
85 | *adat.pinst ? "." : "", adat.pinst, adat.prealm); | ||
86 | |||
87 | /* Check ~/.klogin authorization now. */ | ||
88 | if (kuserok(&adat, (char *)server_user) != KSUCCESS) { | ||
89 | packet_send_debug("Kerberos V4 .klogin authorization failed!"); | ||
90 | log("Kerberos V4 .klogin authorization failed for %s to account %s", | ||
91 | *client, server_user); | ||
92 | return 0; | ||
93 | } | ||
94 | /* Increment the checksum, and return it encrypted with the session key. */ | ||
95 | cksum = adat.checksum + 1; | ||
96 | cksum = htonl(cksum); | ||
97 | |||
98 | /* If we can't successfully encrypt the checksum, we send back an empty | ||
99 | message, admitting our failure. */ | ||
100 | if ((r = krb_mk_priv((u_char *)&cksum, reply.dat, sizeof(cksum)+1, | ||
101 | schedule, &adat.session, &local, &foreign)) < 0) { | ||
102 | packet_send_debug("Kerberos V4 mk_priv: (%d) %s", r, krb_err_txt[r]); | ||
103 | reply.dat[0] = 0; | ||
104 | reply.length = 0; | ||
105 | } | ||
106 | else | ||
107 | reply.length = r; | ||
108 | |||
109 | /* Clear session key. */ | ||
110 | memset(&adat.session, 0, sizeof(&adat.session)); | ||
111 | |||
112 | packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE); | ||
113 | packet_put_string((char *) reply.dat, reply.length); | ||
114 | packet_send(); | ||
115 | packet_write_wait(); | ||
116 | return 1; | ||
117 | } | ||
118 | #endif /* KRB4 */ | ||
119 | |||
120 | #ifdef AFS | ||
121 | int auth_kerberos_tgt(struct passwd *pw, const char *string) | ||
122 | { | ||
123 | CREDENTIALS creds; | ||
124 | extern char *ticket; | ||
125 | int r; | ||
126 | |||
127 | if (!radix_to_creds(string, &creds)) { | ||
128 | log("Protocol error decoding Kerberos V4 tgt"); | ||
129 | packet_send_debug("Protocol error decoding Kerberos V4 tgt"); | ||
130 | goto auth_kerberos_tgt_failure; | ||
131 | } | ||
132 | if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ | ||
133 | strlcpy(creds.service, "krbtgt", sizeof creds.service); | ||
134 | |||
135 | if (strcmp(creds.service, "krbtgt")) { | ||
136 | log("Kerberos V4 tgt (%s%s%s@%s) rejected for uid %d", | ||
137 | creds.pname, creds.pinst[0] ? "." : "", creds.pinst, creds.realm, | ||
138 | pw->pw_uid); | ||
139 | packet_send_debug("Kerberos V4 tgt (%s%s%s@%s) rejected for uid %d", | ||
140 | creds.pname, creds.pinst[0] ? "." : "", creds.pinst, | ||
141 | creds.realm, pw->pw_uid); | ||
142 | goto auth_kerberos_tgt_failure; | ||
143 | } | ||
144 | if (!ssh_tf_init(pw->pw_uid) || | ||
145 | (r = in_tkt(creds.pname, creds.pinst)) || | ||
146 | (r = save_credentials(creds.service, creds.instance, creds.realm, | ||
147 | creds.session, creds.lifetime, creds.kvno, | ||
148 | &creds.ticket_st, creds.issue_date))) { | ||
149 | xfree(ticket); | ||
150 | ticket = NULL; | ||
151 | packet_send_debug("Kerberos V4 tgt refused: couldn't save credentials"); | ||
152 | goto auth_kerberos_tgt_failure; | ||
153 | } | ||
154 | /* Successful authentication, passed all checks. */ | ||
155 | chown(ticket, pw->pw_uid, pw->pw_gid); | ||
156 | packet_send_debug("Kerberos V4 tgt accepted (%s.%s@%s, %s%s%s@%s)", | ||
157 | creds.service, creds.instance, creds.realm, | ||
158 | creds.pname, creds.pinst[0] ? "." : "", | ||
159 | creds.pinst, creds.realm); | ||
160 | |||
161 | packet_start(SSH_SMSG_SUCCESS); | ||
162 | packet_send(); | ||
163 | packet_write_wait(); | ||
164 | return 1; | ||
165 | |||
166 | auth_kerberos_tgt_failure: | ||
167 | memset(&creds, 0, sizeof(creds)); | ||
168 | packet_start(SSH_SMSG_FAILURE); | ||
169 | packet_send(); | ||
170 | packet_write_wait(); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | int auth_afs_token(char *server_user, uid_t uid, const char *string) | ||
175 | { | ||
176 | CREDENTIALS creds; | ||
177 | |||
178 | if (!radix_to_creds(string, &creds)) { | ||
179 | log("Protocol error decoding AFS token"); | ||
180 | packet_send_debug("Protocol error decoding AFS token"); | ||
181 | packet_start(SSH_SMSG_FAILURE); | ||
182 | packet_send(); | ||
183 | packet_write_wait(); | ||
184 | return 0; | ||
185 | } | ||
186 | if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ | ||
187 | strlcpy(creds.service, "afs", sizeof creds.service); | ||
188 | |||
189 | if (strncmp(creds.pname, "AFS ID ", 7) == 0) | ||
190 | uid = atoi(creds.pname + 7); | ||
191 | |||
192 | if (kafs_settoken(creds.realm, uid, &creds)) { | ||
193 | log("AFS token (%s@%s) rejected for uid %d", creds.pname, | ||
194 | creds.realm, uid); | ||
195 | packet_send_debug("AFS token (%s@%s) rejected for uid %d", creds.pname, | ||
196 | creds.realm, uid); | ||
197 | packet_start(SSH_SMSG_FAILURE); | ||
198 | packet_send(); | ||
199 | packet_write_wait(); | ||
200 | return 0; | ||
201 | } | ||
202 | packet_send_debug("AFS token accepted (%s@%s, %s@%s)", creds.service, | ||
203 | creds.realm, creds.pname, creds.realm); | ||
204 | packet_start(SSH_SMSG_SUCCESS); | ||
205 | packet_send(); | ||
206 | packet_write_wait(); | ||
207 | return 1; | ||
208 | } | ||
209 | #endif /* AFS */ | ||
diff --git a/auth-passwd.c b/auth-passwd.c new file mode 100644 index 000000000..7d6846789 --- /dev/null +++ b/auth-passwd.c | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | |||
3 | auth-passwd.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Mar 18 05:11:38 1995 ylo | ||
11 | |||
12 | Password authentication. This file contains the functions to check whether | ||
13 | the password is valid for the user. | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: auth-passwd.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
19 | |||
20 | #include "packet.h" | ||
21 | #include "ssh.h" | ||
22 | #include "servconf.h" | ||
23 | #include "xmalloc.h" | ||
24 | |||
25 | #ifdef KRB4 | ||
26 | extern char *ticket; | ||
27 | #endif /* KRB4 */ | ||
28 | |||
29 | #ifdef HAVE_PAM | ||
30 | #include <security/pam_appl.h> | ||
31 | extern pam_handle_t *pamh; | ||
32 | extern int retval; | ||
33 | extern char* pampasswd; | ||
34 | extern int origretval; | ||
35 | #endif /* HAVE_PAM */ | ||
36 | |||
37 | /* Tries to authenticate the user using password. Returns true if | ||
38 | authentication succeeds. */ | ||
39 | |||
40 | int auth_password(struct passwd *pw, const char *password) | ||
41 | { | ||
42 | extern ServerOptions options; | ||
43 | char *encrypted_password; | ||
44 | |||
45 | if (pw->pw_uid == 0 && options.permit_root_login == 2) | ||
46 | { | ||
47 | /*packet_send_debug("Server does not permit root login with password.");*/ | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | if (*password == '\0' && options.permit_empty_passwd == 0) | ||
52 | { | ||
53 | /*packet_send_debug("Server does not permit empty password login.");*/ | ||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | /* deny if no user. */ | ||
58 | if (pw == NULL) | ||
59 | return 0; | ||
60 | |||
61 | #ifdef HAVE_PAM | ||
62 | retval = origretval; | ||
63 | |||
64 | pampasswd = xstrdup(password); | ||
65 | |||
66 | if (retval == PAM_SUCCESS) | ||
67 | retval = pam_authenticate ((pam_handle_t *)pamh, 0); | ||
68 | |||
69 | if (retval == PAM_SUCCESS) | ||
70 | retval = pam_acct_mgmt ((pam_handle_t *)pamh, 0); | ||
71 | |||
72 | xfree(pampasswd); | ||
73 | |||
74 | if (retval == PAM_SUCCESS) | ||
75 | retval = pam_open_session ((pam_handle_t *)pamh, 0); | ||
76 | |||
77 | return (retval == PAM_SUCCESS); | ||
78 | |||
79 | #else /* HAVE_PAM */ | ||
80 | |||
81 | #ifdef SKEY | ||
82 | if (options.skey_authentication == 1) { | ||
83 | if (strncasecmp(password, "s/key", 5) == 0) { | ||
84 | char *skeyinfo = skey_keyinfo(pw->pw_name); | ||
85 | if(skeyinfo == NULL){ | ||
86 | debug("generating fake skeyinfo for %.100s.", pw->pw_name); | ||
87 | skeyinfo = skey_fake_keyinfo(pw->pw_name); | ||
88 | } | ||
89 | if(skeyinfo != NULL) | ||
90 | packet_send_debug(skeyinfo); | ||
91 | /* Try again. */ | ||
92 | return 0; | ||
93 | } | ||
94 | else if (skey_haskey(pw->pw_name) == 0 && | ||
95 | skey_passcheck(pw->pw_name, (char *)password) != -1) { | ||
96 | /* Authentication succeeded. */ | ||
97 | return 1; | ||
98 | } | ||
99 | /* Fall back to ordinary passwd authentication. */ | ||
100 | } | ||
101 | #endif | ||
102 | |||
103 | #if defined(KRB4) | ||
104 | /* Support for Kerberos v4 authentication - Dug Song <dugsong@UMICH.EDU> */ | ||
105 | if (options.kerberos_authentication) | ||
106 | { | ||
107 | AUTH_DAT adata; | ||
108 | KTEXT_ST tkt; | ||
109 | struct hostent *hp; | ||
110 | unsigned long faddr; | ||
111 | char localhost[MAXHOSTNAMELEN]; /* local host name */ | ||
112 | char phost[INST_SZ]; /* host instance */ | ||
113 | char realm[REALM_SZ]; /* local Kerberos realm */ | ||
114 | int r; | ||
115 | |||
116 | /* Try Kerberos password authentication only for non-root | ||
117 | users and only if Kerberos is installed. */ | ||
118 | if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) { | ||
119 | |||
120 | /* Set up our ticket file. */ | ||
121 | if (!ssh_tf_init(pw->pw_uid)) { | ||
122 | log("Couldn't initialize Kerberos ticket file for %s!", | ||
123 | pw->pw_name); | ||
124 | goto kerberos_auth_failure; | ||
125 | } | ||
126 | /* Try to get TGT using our password. */ | ||
127 | r = krb_get_pw_in_tkt((char *)pw->pw_name, "", realm, "krbtgt", realm, | ||
128 | DEFAULT_TKT_LIFE, (char *)password); | ||
129 | if (r != INTK_OK) { | ||
130 | packet_send_debug("Kerberos V4 password authentication for %s " | ||
131 | "failed: %s", pw->pw_name, krb_err_txt[r]); | ||
132 | goto kerberos_auth_failure; | ||
133 | } | ||
134 | /* Successful authentication. */ | ||
135 | chown(ticket, pw->pw_uid, pw->pw_gid); | ||
136 | |||
137 | (void) gethostname(localhost, sizeof(localhost)); | ||
138 | (void) strlcpy(phost, (char *)krb_get_phost(localhost), INST_SZ); | ||
139 | |||
140 | /* Now that we have a TGT, try to get a local "rcmd" ticket to | ||
141 | ensure that we are not talking to a bogus Kerberos server. */ | ||
142 | r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33); | ||
143 | |||
144 | if (r == KSUCCESS) { | ||
145 | if (!(hp = gethostbyname(localhost))) { | ||
146 | log("Couldn't get local host address!"); | ||
147 | goto kerberos_auth_failure; | ||
148 | } | ||
149 | memmove((void *)&faddr, (void *)hp->h_addr, sizeof(faddr)); | ||
150 | |||
151 | /* Verify our "rcmd" ticket. */ | ||
152 | r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, faddr, &adata, ""); | ||
153 | if (r == RD_AP_UNDEC) { | ||
154 | /* Probably didn't have a srvtab on localhost. Allow login. */ | ||
155 | log("Kerberos V4 TGT for %s unverifiable, no srvtab installed? " | ||
156 | "krb_rd_req: %s", pw->pw_name, krb_err_txt[r]); | ||
157 | } | ||
158 | else if (r != KSUCCESS) { | ||
159 | log("Kerberos V4 %s ticket unverifiable: %s", | ||
160 | KRB4_SERVICE_NAME, krb_err_txt[r]); | ||
161 | goto kerberos_auth_failure; | ||
162 | } | ||
163 | } | ||
164 | else if (r == KDC_PR_UNKNOWN) { | ||
165 | /* Allow login if no rcmd service exists, but log the error. */ | ||
166 | log("Kerberos V4 TGT for %s unverifiable: %s; %s.%s " | ||
167 | "not registered, or srvtab is wrong?", pw->pw_name, | ||
168 | krb_err_txt[r], KRB4_SERVICE_NAME, phost); | ||
169 | } | ||
170 | else { | ||
171 | /* TGT is bad, forget it. Possibly spoofed! */ | ||
172 | packet_send_debug("WARNING: Kerberos V4 TGT possibly spoofed for" | ||
173 | "%s: %s", pw->pw_name, krb_err_txt[r]); | ||
174 | goto kerberos_auth_failure; | ||
175 | } | ||
176 | |||
177 | /* Authentication succeeded. */ | ||
178 | return 1; | ||
179 | |||
180 | kerberos_auth_failure: | ||
181 | (void) dest_tkt(); | ||
182 | xfree(ticket); | ||
183 | ticket = NULL; | ||
184 | if (!options.kerberos_or_local_passwd ) return 0; | ||
185 | } | ||
186 | else { | ||
187 | /* Logging in as root or no local Kerberos realm. */ | ||
188 | packet_send_debug("Unable to authenticate to Kerberos."); | ||
189 | } | ||
190 | /* Fall back to ordinary passwd authentication. */ | ||
191 | } | ||
192 | #endif /* KRB4 */ | ||
193 | |||
194 | /* Check for users with no password. */ | ||
195 | if (strcmp(password, "") == 0 && strcmp(pw->pw_passwd, "") == 0) | ||
196 | { | ||
197 | packet_send_debug("Login permitted without a password because the account has no password."); | ||
198 | return 1; /* The user has no password and an empty password was tried. */ | ||
199 | } | ||
200 | |||
201 | /* Encrypt the candidate password using the proper salt. */ | ||
202 | encrypted_password = crypt(password, | ||
203 | (pw->pw_passwd[0] && pw->pw_passwd[1]) ? | ||
204 | pw->pw_passwd : "xx"); | ||
205 | |||
206 | /* Authentication is accepted if the encrypted passwords are identical. */ | ||
207 | return (strcmp(encrypted_password, pw->pw_passwd) == 0); | ||
208 | #endif /* HAVE_PAM */ | ||
209 | } | ||
diff --git a/auth-rh-rsa.c b/auth-rh-rsa.c new file mode 100644 index 000000000..c433578bf --- /dev/null +++ b/auth-rh-rsa.c | |||
@@ -0,0 +1,83 @@ | |||
1 | /* | ||
2 | |||
3 | auth-rh-rsa.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sun May 7 03:08:06 1995 ylo | ||
11 | |||
12 | Rhosts or /etc/hosts.equiv authentication combined with RSA host | ||
13 | authentication. | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: auth-rh-rsa.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
19 | |||
20 | #include "packet.h" | ||
21 | #include "ssh.h" | ||
22 | #include "xmalloc.h" | ||
23 | #include "uidswap.h" | ||
24 | |||
25 | /* Tries to authenticate the user using the .rhosts file and the host using | ||
26 | its host key. Returns true if authentication succeeds. | ||
27 | .rhosts and .shosts will be ignored if ignore_rhosts is non-zero. */ | ||
28 | |||
29 | int auth_rhosts_rsa(struct passwd *pw, const char *client_user, | ||
30 | unsigned int client_host_key_bits, | ||
31 | BIGNUM *client_host_key_e, BIGNUM *client_host_key_n, | ||
32 | int ignore_rhosts, int strict_modes) | ||
33 | { | ||
34 | const char *canonical_hostname; | ||
35 | HostStatus host_status; | ||
36 | BIGNUM *ke, *kn; | ||
37 | |||
38 | debug("Trying rhosts with RSA host authentication for %.100s", client_user); | ||
39 | |||
40 | /* Check if we would accept it using rhosts authentication. */ | ||
41 | if (!auth_rhosts(pw, client_user, ignore_rhosts, strict_modes)) | ||
42 | return 0; | ||
43 | |||
44 | canonical_hostname = get_canonical_hostname(); | ||
45 | |||
46 | debug("Rhosts RSA authentication: canonical host %.900s", | ||
47 | canonical_hostname); | ||
48 | |||
49 | /* Check if we know the host and its host key. */ | ||
50 | /* Check system-wide host file. */ | ||
51 | ke = BN_new(); | ||
52 | kn = BN_new(); | ||
53 | host_status = check_host_in_hostfile(SSH_SYSTEM_HOSTFILE, canonical_hostname, | ||
54 | client_host_key_bits, client_host_key_e, | ||
55 | client_host_key_n, ke, kn); | ||
56 | BN_free(ke); | ||
57 | BN_free(kn); | ||
58 | if (host_status != HOST_OK) { | ||
59 | /* The host key was not found. */ | ||
60 | debug("Rhosts with RSA host authentication denied: unknown or invalid host key"); | ||
61 | packet_send_debug("Your host key cannot be verified: unknown or invalid host key."); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | /* A matching host key was found and is known. */ | ||
66 | |||
67 | /* Perform the challenge-response dialog with the client for the host key. */ | ||
68 | if (!auth_rsa_challenge_dialog(client_host_key_bits, | ||
69 | client_host_key_e, client_host_key_n)) | ||
70 | { | ||
71 | log("Client on %.800s failed to respond correctly to host authentication.", | ||
72 | canonical_hostname); | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | /* We have authenticated the user using .rhosts or /etc/hosts.equiv, and | ||
77 | the host using RSA. We accept the authentication. */ | ||
78 | |||
79 | log("Rhosts with RSA host authentication accepted for %.100s, %.100s on %.700s.", | ||
80 | pw->pw_name, client_user, canonical_hostname); | ||
81 | packet_send_debug("Rhosts with RSA host authentication accepted."); | ||
82 | return 1; | ||
83 | } | ||
diff --git a/auth-rhosts.c b/auth-rhosts.c new file mode 100644 index 000000000..ebf2fcbc2 --- /dev/null +++ b/auth-rhosts.c | |||
@@ -0,0 +1,298 @@ | |||
1 | /* | ||
2 | |||
3 | auth-rhosts.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Fri Mar 17 05:12:18 1995 ylo | ||
11 | |||
12 | Rhosts authentication. This file contains code to check whether to admit | ||
13 | the login based on rhosts authentication. This file also processes | ||
14 | /etc/hosts.equiv. | ||
15 | |||
16 | */ | ||
17 | |||
18 | #include "includes.h" | ||
19 | RCSID("$Id: auth-rhosts.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
20 | |||
21 | #include "packet.h" | ||
22 | #include "ssh.h" | ||
23 | #include "xmalloc.h" | ||
24 | #include "uidswap.h" | ||
25 | |||
26 | /* This function processes an rhosts-style file (.rhosts, .shosts, or | ||
27 | /etc/hosts.equiv). This returns true if authentication can be granted | ||
28 | based on the file, and returns zero otherwise. */ | ||
29 | |||
30 | int check_rhosts_file(const char *filename, const char *hostname, | ||
31 | const char *ipaddr, const char *client_user, | ||
32 | const char *server_user) | ||
33 | { | ||
34 | FILE *f; | ||
35 | char buf[1024]; /* Must not be larger than host, user, dummy below. */ | ||
36 | |||
37 | /* Open the .rhosts file. */ | ||
38 | f = fopen(filename, "r"); | ||
39 | if (!f) | ||
40 | return 0; /* Cannot read the .rhosts - deny access. */ | ||
41 | |||
42 | /* Go through the file, checking every entry. */ | ||
43 | while (fgets(buf, sizeof(buf), f)) | ||
44 | { | ||
45 | /* All three must be at least as big as buf to avoid overflows. */ | ||
46 | char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp; | ||
47 | int negated; | ||
48 | |||
49 | for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) | ||
50 | ; | ||
51 | if (*cp == '#' || *cp == '\n' || !*cp) | ||
52 | continue; | ||
53 | |||
54 | /* NO_PLUS is supported at least on OSF/1. We skip it (we don't ever | ||
55 | support the plus syntax). */ | ||
56 | if (strncmp(cp, "NO_PLUS", 7) == 0) | ||
57 | continue; | ||
58 | |||
59 | /* This should be safe because each buffer is as big as the whole | ||
60 | string, and thus cannot be overwritten. */ | ||
61 | switch (sscanf(buf, "%s %s %s", hostbuf, userbuf, dummy)) | ||
62 | { | ||
63 | case 0: | ||
64 | packet_send_debug("Found empty line in %.100s.", filename); | ||
65 | continue; /* Empty line? */ | ||
66 | case 1: | ||
67 | /* Host name only. */ | ||
68 | strlcpy(userbuf, server_user, sizeof(userbuf)); | ||
69 | break; | ||
70 | case 2: | ||
71 | /* Got both host and user name. */ | ||
72 | break; | ||
73 | case 3: | ||
74 | packet_send_debug("Found garbage in %.100s.", filename); | ||
75 | continue; /* Extra garbage */ | ||
76 | default: | ||
77 | continue; /* Weird... */ | ||
78 | } | ||
79 | |||
80 | host = hostbuf; | ||
81 | user = userbuf; | ||
82 | negated = 0; | ||
83 | |||
84 | /* Process negated host names, or positive netgroups. */ | ||
85 | if (host[0] == '-') | ||
86 | { | ||
87 | negated = 1; | ||
88 | host++; | ||
89 | } | ||
90 | else | ||
91 | if (host[0] == '+') | ||
92 | host++; | ||
93 | |||
94 | if (user[0] == '-') | ||
95 | { | ||
96 | negated = 1; | ||
97 | user++; | ||
98 | } | ||
99 | else | ||
100 | if (user[0] == '+') | ||
101 | user++; | ||
102 | |||
103 | /* Check for empty host/user names (particularly '+'). */ | ||
104 | if (!host[0] || !user[0]) | ||
105 | { | ||
106 | /* We come here if either was '+' or '-'. */ | ||
107 | packet_send_debug("Ignoring wild host/user names in %.100s.", | ||
108 | filename); | ||
109 | continue; | ||
110 | } | ||
111 | |||
112 | /* Verify that host name matches. */ | ||
113 | if (host[0] == '@') | ||
114 | { | ||
115 | if (!innetgr(host + 1, hostname, NULL, NULL) && | ||
116 | !innetgr(host + 1, ipaddr, NULL, NULL)) | ||
117 | continue; | ||
118 | } | ||
119 | else | ||
120 | if (strcasecmp(host, hostname) && strcmp(host, ipaddr) != 0) | ||
121 | continue; /* Different hostname. */ | ||
122 | |||
123 | /* Verify that user name matches. */ | ||
124 | if (user[0] == '@') | ||
125 | { | ||
126 | if (!innetgr(user + 1, NULL, client_user, NULL)) | ||
127 | continue; | ||
128 | } | ||
129 | else | ||
130 | if (strcmp(user, client_user) != 0) | ||
131 | continue; /* Different username. */ | ||
132 | |||
133 | /* Found the user and host. */ | ||
134 | fclose(f); | ||
135 | |||
136 | /* If the entry was negated, deny access. */ | ||
137 | if (negated) | ||
138 | { | ||
139 | packet_send_debug("Matched negative entry in %.100s.", | ||
140 | filename); | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | /* Accept authentication. */ | ||
145 | return 1; | ||
146 | } | ||
147 | |||
148 | /* Authentication using this file denied. */ | ||
149 | fclose(f); | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | /* Tries to authenticate the user using the .shosts or .rhosts file. | ||
154 | Returns true if authentication succeeds. If ignore_rhosts is | ||
155 | true, only /etc/hosts.equiv will be considered (.rhosts and .shosts | ||
156 | are ignored). */ | ||
157 | |||
158 | int auth_rhosts(struct passwd *pw, const char *client_user, | ||
159 | int ignore_rhosts, int strict_modes) | ||
160 | { | ||
161 | char buf[1024]; | ||
162 | const char *hostname, *ipaddr; | ||
163 | int port; | ||
164 | struct stat st; | ||
165 | static const char *rhosts_files[] = { ".shosts", ".rhosts", NULL }; | ||
166 | unsigned int rhosts_file_index; | ||
167 | |||
168 | /* Quick check: if the user has no .shosts or .rhosts files, return failure | ||
169 | immediately without doing costly lookups from name servers. */ | ||
170 | /* Switch to the user's uid. */ | ||
171 | temporarily_use_uid(pw->pw_uid); | ||
172 | for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; | ||
173 | rhosts_file_index++) | ||
174 | { | ||
175 | /* Check users .rhosts or .shosts. */ | ||
176 | snprintf(buf, sizeof buf, "%.500s/%.100s", | ||
177 | pw->pw_dir, rhosts_files[rhosts_file_index]); | ||
178 | if (stat(buf, &st) >= 0) | ||
179 | break; | ||
180 | } | ||
181 | /* Switch back to privileged uid. */ | ||
182 | restore_uid(); | ||
183 | |||
184 | if (!rhosts_files[rhosts_file_index] && stat("/etc/hosts.equiv", &st) < 0 && | ||
185 | stat(SSH_HOSTS_EQUIV, &st) < 0) | ||
186 | return 0; /* The user has no .shosts or .rhosts file and there are no | ||
187 | system-wide files. */ | ||
188 | |||
189 | /* Get the name, address, and port of the remote host. */ | ||
190 | hostname = get_canonical_hostname(); | ||
191 | ipaddr = get_remote_ipaddr(); | ||
192 | port = get_remote_port(); | ||
193 | |||
194 | /* Check that the connection comes from a privileged port. | ||
195 | Rhosts authentication only makes sense for priviledged programs. | ||
196 | Of course, if the intruder has root access on his local machine, | ||
197 | he can connect from any port. So do not use .rhosts | ||
198 | authentication from machines that you do not trust. */ | ||
199 | if (port >= IPPORT_RESERVED || | ||
200 | port < IPPORT_RESERVED / 2) | ||
201 | { | ||
202 | log("Connection from %.100s from nonpriviledged port %d", | ||
203 | hostname, port); | ||
204 | packet_send_debug("Your ssh client is not running as root."); | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */ | ||
209 | if (pw->pw_uid != 0) | ||
210 | { | ||
211 | if (check_rhosts_file("/etc/hosts.equiv", hostname, ipaddr, client_user, | ||
212 | pw->pw_name)) | ||
213 | { | ||
214 | packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.", | ||
215 | hostname, ipaddr); | ||
216 | return 1; | ||
217 | } | ||
218 | if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user, | ||
219 | pw->pw_name)) | ||
220 | { | ||
221 | packet_send_debug("Accepted for %.100s [%.100s] by %.100s.", | ||
222 | hostname, ipaddr, SSH_HOSTS_EQUIV); | ||
223 | return 1; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | /* Check that the home directory is owned by root or the user, and is not | ||
228 | group or world writable. */ | ||
229 | if (stat(pw->pw_dir, &st) < 0) | ||
230 | { | ||
231 | log("Rhosts authentication refused for %.100: no home directory %.200s", | ||
232 | pw->pw_name, pw->pw_dir); | ||
233 | packet_send_debug("Rhosts authentication refused for %.100: no home directory %.200s", | ||
234 | pw->pw_name, pw->pw_dir); | ||
235 | return 0; | ||
236 | } | ||
237 | if (strict_modes && | ||
238 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
239 | (st.st_mode & 022) != 0)) | ||
240 | { | ||
241 | log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", | ||
242 | pw->pw_name); | ||
243 | packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", | ||
244 | pw->pw_name); | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | /* Check all .rhosts files (currently .shosts and .rhosts). */ | ||
249 | /* Temporarily use the user's uid. */ | ||
250 | temporarily_use_uid(pw->pw_uid); | ||
251 | for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; | ||
252 | rhosts_file_index++) | ||
253 | { | ||
254 | /* Check users .rhosts or .shosts. */ | ||
255 | snprintf(buf, sizeof buf, "%.500s/%.100s", | ||
256 | pw->pw_dir, rhosts_files[rhosts_file_index]); | ||
257 | if (stat(buf, &st) < 0) | ||
258 | continue; /* No such file. */ | ||
259 | |||
260 | /* Make sure that the file is either owned by the user or by root, | ||
261 | and make sure it is not writable by anyone but the owner. This is | ||
262 | to help avoid novices accidentally allowing access to their account | ||
263 | by anyone. */ | ||
264 | if (strict_modes && | ||
265 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
266 | (st.st_mode & 022) != 0)) | ||
267 | { | ||
268 | log("Rhosts authentication refused for %.100s: bad modes for %.200s", | ||
269 | pw->pw_name, buf); | ||
270 | packet_send_debug("Bad file modes for %.200s", buf); | ||
271 | continue; | ||
272 | } | ||
273 | |||
274 | /* Check if we have been configured to ignore .rhosts and .shosts | ||
275 | files. */ | ||
276 | if (ignore_rhosts) | ||
277 | { | ||
278 | packet_send_debug("Server has been configured to ignore %.100s.", | ||
279 | rhosts_files[rhosts_file_index]); | ||
280 | continue; | ||
281 | } | ||
282 | |||
283 | /* Check if authentication is permitted by the file. */ | ||
284 | if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name)) | ||
285 | { | ||
286 | packet_send_debug("Accepted by %.100s.", | ||
287 | rhosts_files[rhosts_file_index]); | ||
288 | /* Restore the privileged uid. */ | ||
289 | restore_uid(); | ||
290 | return 1; | ||
291 | } | ||
292 | } | ||
293 | |||
294 | /* Rhosts authentication denied. */ | ||
295 | /* Restore the privileged uid. */ | ||
296 | restore_uid(); | ||
297 | return 0; | ||
298 | } | ||
diff --git a/auth-rsa.c b/auth-rsa.c new file mode 100644 index 000000000..8de86d2de --- /dev/null +++ b/auth-rsa.c | |||
@@ -0,0 +1,478 @@ | |||
1 | /* | ||
2 | |||
3 | auth-rsa.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Mar 27 01:46:52 1995 ylo | ||
11 | |||
12 | RSA-based authentication. This code determines whether to admit a login | ||
13 | based on RSA authentication. This file also contains functions to check | ||
14 | validity of the host key. | ||
15 | |||
16 | */ | ||
17 | |||
18 | #include "includes.h" | ||
19 | RCSID("$Id: auth-rsa.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
20 | |||
21 | #include "rsa.h" | ||
22 | #include "packet.h" | ||
23 | #include "xmalloc.h" | ||
24 | #include "ssh.h" | ||
25 | #include "mpaux.h" | ||
26 | #include "uidswap.h" | ||
27 | |||
28 | #include <openssl/rsa.h> | ||
29 | #include <openssl/md5.h> | ||
30 | |||
31 | /* Flags that may be set in authorized_keys options. */ | ||
32 | extern int no_port_forwarding_flag; | ||
33 | extern int no_agent_forwarding_flag; | ||
34 | extern int no_x11_forwarding_flag; | ||
35 | extern int no_pty_flag; | ||
36 | extern char *forced_command; | ||
37 | extern struct envstring *custom_environment; | ||
38 | |||
39 | /* Session identifier that is used to bind key exchange and authentication | ||
40 | responses to a particular session. */ | ||
41 | extern unsigned char session_id[16]; | ||
42 | |||
43 | /* The .ssh/authorized_keys file contains public keys, one per line, in the | ||
44 | following format: | ||
45 | options bits e n comment | ||
46 | where bits, e and n are decimal numbers, | ||
47 | and comment is any string of characters up to newline. The maximum | ||
48 | length of a line is 8000 characters. See the documentation for a | ||
49 | description of the options. | ||
50 | */ | ||
51 | |||
52 | /* Performs the RSA authentication challenge-response dialog with the client, | ||
53 | and returns true (non-zero) if the client gave the correct answer to | ||
54 | our challenge; returns zero if the client gives a wrong answer. */ | ||
55 | |||
56 | int | ||
57 | auth_rsa_challenge_dialog(unsigned int bits, BIGNUM *e, BIGNUM *n) | ||
58 | { | ||
59 | BIGNUM *challenge, *encrypted_challenge, *aux; | ||
60 | RSA *pk; | ||
61 | BN_CTX *ctx = BN_CTX_new(); | ||
62 | unsigned char buf[32], mdbuf[16], response[16]; | ||
63 | MD5_CTX md; | ||
64 | unsigned int i; | ||
65 | int plen, len; | ||
66 | |||
67 | encrypted_challenge = BN_new(); | ||
68 | challenge = BN_new(); | ||
69 | aux = BN_new(); | ||
70 | |||
71 | /* Generate a random challenge. */ | ||
72 | BN_rand(challenge, 256, 0, 0); | ||
73 | BN_mod(challenge, challenge, n, ctx); | ||
74 | |||
75 | /* Create the public key data structure. */ | ||
76 | pk = RSA_new(); | ||
77 | pk->e = BN_new(); | ||
78 | BN_copy(pk->e, e); | ||
79 | pk->n = BN_new(); | ||
80 | BN_copy(pk->n, n); | ||
81 | |||
82 | /* Encrypt the challenge with the public key. */ | ||
83 | rsa_public_encrypt(encrypted_challenge, challenge, pk); | ||
84 | RSA_free(pk); | ||
85 | |||
86 | /* Send the encrypted challenge to the client. */ | ||
87 | packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); | ||
88 | packet_put_bignum(encrypted_challenge); | ||
89 | packet_send(); | ||
90 | packet_write_wait(); | ||
91 | |||
92 | /* The response is MD5 of decrypted challenge plus session id. */ | ||
93 | len = BN_num_bytes(challenge); | ||
94 | assert(len <= 32 && len); | ||
95 | memset(buf, 0, 32); | ||
96 | BN_bn2bin(challenge, buf + 32 - len); | ||
97 | MD5_Init(&md); | ||
98 | MD5_Update(&md, buf, 32); | ||
99 | MD5_Update(&md, session_id, 16); | ||
100 | MD5_Final(mdbuf, &md); | ||
101 | |||
102 | /* We will no longer need these. */ | ||
103 | BN_clear_free(encrypted_challenge); | ||
104 | BN_clear_free(challenge); | ||
105 | BN_clear_free(aux); | ||
106 | BN_CTX_free(ctx); | ||
107 | |||
108 | /* Wait for a response. */ | ||
109 | packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE); | ||
110 | packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE); | ||
111 | for (i = 0; i < 16; i++) | ||
112 | response[i] = packet_get_char(); | ||
113 | |||
114 | /* Verify that the response is the original challenge. */ | ||
115 | if (memcmp(response, mdbuf, 16) != 0) | ||
116 | { | ||
117 | /* Wrong answer. */ | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | /* Correct answer. */ | ||
122 | return 1; | ||
123 | } | ||
124 | |||
125 | /* Performs the RSA authentication dialog with the client. This returns | ||
126 | 0 if the client could not be authenticated, and 1 if authentication was | ||
127 | successful. This may exit if there is a serious protocol violation. */ | ||
128 | |||
129 | int | ||
130 | auth_rsa(struct passwd *pw, BIGNUM *client_n, int strict_modes) | ||
131 | { | ||
132 | char line[8192]; | ||
133 | int authenticated; | ||
134 | unsigned int bits; | ||
135 | FILE *f; | ||
136 | unsigned long linenum = 0; | ||
137 | struct stat st; | ||
138 | BIGNUM *e, *n; | ||
139 | |||
140 | /* Temporarily use the user's uid. */ | ||
141 | temporarily_use_uid(pw->pw_uid); | ||
142 | |||
143 | /* The authorized keys. */ | ||
144 | snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, | ||
145 | SSH_USER_PERMITTED_KEYS); | ||
146 | |||
147 | /* Fail quietly if file does not exist */ | ||
148 | if (stat(line, &st) < 0) | ||
149 | { | ||
150 | /* Restore the privileged uid. */ | ||
151 | restore_uid(); | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | /* Open the file containing the authorized keys. */ | ||
156 | f = fopen(line, "r"); | ||
157 | if (!f) | ||
158 | { | ||
159 | /* Restore the privileged uid. */ | ||
160 | restore_uid(); | ||
161 | packet_send_debug("Could not open %.900s for reading.", line); | ||
162 | packet_send_debug("If your home is on an NFS volume, it may need to be world-readable."); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | if (strict_modes) { | ||
167 | int fail=0; | ||
168 | char buf[1024]; | ||
169 | /* Check open file in order to avoid open/stat races */ | ||
170 | if (fstat(fileno(f), &st) < 0 || | ||
171 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
172 | (st.st_mode & 022) != 0) { | ||
173 | snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " | ||
174 | "bad ownership or modes for '%s'.", pw->pw_name, line); | ||
175 | fail=1; | ||
176 | }else{ | ||
177 | /* Check path to SSH_USER_PERMITTED_KEYS */ | ||
178 | int i; | ||
179 | static const char *check[] = { | ||
180 | "", SSH_USER_DIR, NULL | ||
181 | }; | ||
182 | for (i=0; check[i]; i++) { | ||
183 | snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); | ||
184 | if (stat(line, &st) < 0 || | ||
185 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
186 | (st.st_mode & 022) != 0) { | ||
187 | snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " | ||
188 | "bad ownership or modes for '%s'.", pw->pw_name, line); | ||
189 | fail=1; | ||
190 | break; | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | if (fail) { | ||
195 | log(buf); | ||
196 | packet_send_debug(buf); | ||
197 | restore_uid(); | ||
198 | return 0; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | /* Flag indicating whether authentication has succeeded. */ | ||
203 | authenticated = 0; | ||
204 | |||
205 | /* Initialize mp-int variables. */ | ||
206 | e = BN_new(); | ||
207 | n = BN_new(); | ||
208 | |||
209 | /* Go though the accepted keys, looking for the current key. If found, | ||
210 | perform a challenge-response dialog to verify that the user really has | ||
211 | the corresponding private key. */ | ||
212 | while (fgets(line, sizeof(line), f)) | ||
213 | { | ||
214 | char *cp; | ||
215 | char *options; | ||
216 | |||
217 | linenum++; | ||
218 | |||
219 | /* Skip leading whitespace. */ | ||
220 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | ||
221 | ; | ||
222 | |||
223 | /* Skip empty and comment lines. */ | ||
224 | if (!*cp || *cp == '\n' || *cp == '#') | ||
225 | continue; | ||
226 | |||
227 | /* Check if there are options for this key, and if so, save their | ||
228 | starting address and skip the option part for now. If there are no | ||
229 | options, set the starting address to NULL. */ | ||
230 | if (*cp < '0' || *cp > '9') | ||
231 | { | ||
232 | int quoted = 0; | ||
233 | options = cp; | ||
234 | for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) | ||
235 | { | ||
236 | if (*cp == '\\' && cp[1] == '"') | ||
237 | cp++; /* Skip both */ | ||
238 | else | ||
239 | if (*cp == '"') | ||
240 | quoted = !quoted; | ||
241 | } | ||
242 | } | ||
243 | else | ||
244 | options = NULL; | ||
245 | |||
246 | /* Parse the key from the line. */ | ||
247 | if (!auth_rsa_read_key(&cp, &bits, e, n)) | ||
248 | { | ||
249 | debug("%.100s, line %lu: bad key syntax", | ||
250 | SSH_USER_PERMITTED_KEYS, linenum); | ||
251 | packet_send_debug("%.100s, line %lu: bad key syntax", | ||
252 | SSH_USER_PERMITTED_KEYS, linenum); | ||
253 | continue; | ||
254 | } | ||
255 | /* cp now points to the comment part. */ | ||
256 | |||
257 | /* Check if the we have found the desired key (identified by its | ||
258 | modulus). */ | ||
259 | if (BN_cmp(n, client_n) != 0) | ||
260 | continue; /* Wrong key. */ | ||
261 | |||
262 | /* We have found the desired key. */ | ||
263 | |||
264 | /* Perform the challenge-response dialog for this key. */ | ||
265 | if (!auth_rsa_challenge_dialog(bits, e, n)) | ||
266 | { | ||
267 | /* Wrong response. */ | ||
268 | log("Wrong response to RSA authentication challenge."); | ||
269 | packet_send_debug("Wrong response to RSA authentication challenge."); | ||
270 | continue; | ||
271 | } | ||
272 | |||
273 | /* Correct response. The client has been successfully authenticated. | ||
274 | Note that we have not yet processed the options; this will be reset | ||
275 | if the options cause the authentication to be rejected. */ | ||
276 | authenticated = 1; | ||
277 | |||
278 | /* RSA part of authentication was accepted. Now process the options. */ | ||
279 | if (options) | ||
280 | { | ||
281 | while (*options && *options != ' ' && *options != '\t') | ||
282 | { | ||
283 | cp = "no-port-forwarding"; | ||
284 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
285 | { | ||
286 | packet_send_debug("Port forwarding disabled."); | ||
287 | no_port_forwarding_flag = 1; | ||
288 | options += strlen(cp); | ||
289 | goto next_option; | ||
290 | } | ||
291 | cp = "no-agent-forwarding"; | ||
292 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
293 | { | ||
294 | packet_send_debug("Agent forwarding disabled."); | ||
295 | no_agent_forwarding_flag = 1; | ||
296 | options += strlen(cp); | ||
297 | goto next_option; | ||
298 | } | ||
299 | cp = "no-X11-forwarding"; | ||
300 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
301 | { | ||
302 | packet_send_debug("X11 forwarding disabled."); | ||
303 | no_x11_forwarding_flag = 1; | ||
304 | options += strlen(cp); | ||
305 | goto next_option; | ||
306 | } | ||
307 | cp = "no-pty"; | ||
308 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
309 | { | ||
310 | packet_send_debug("Pty allocation disabled."); | ||
311 | no_pty_flag = 1; | ||
312 | options += strlen(cp); | ||
313 | goto next_option; | ||
314 | } | ||
315 | cp = "command=\""; | ||
316 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
317 | { | ||
318 | int i; | ||
319 | options += strlen(cp); | ||
320 | forced_command = xmalloc(strlen(options) + 1); | ||
321 | i = 0; | ||
322 | while (*options) | ||
323 | { | ||
324 | if (*options == '"') | ||
325 | break; | ||
326 | if (*options == '\\' && options[1] == '"') | ||
327 | { | ||
328 | options += 2; | ||
329 | forced_command[i++] = '"'; | ||
330 | continue; | ||
331 | } | ||
332 | forced_command[i++] = *options++; | ||
333 | } | ||
334 | if (!*options) | ||
335 | { | ||
336 | debug("%.100s, line %lu: missing end quote", | ||
337 | SSH_USER_PERMITTED_KEYS, linenum); | ||
338 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
339 | SSH_USER_PERMITTED_KEYS, linenum); | ||
340 | continue; | ||
341 | } | ||
342 | forced_command[i] = 0; | ||
343 | packet_send_debug("Forced command: %.900s", forced_command); | ||
344 | options++; | ||
345 | goto next_option; | ||
346 | } | ||
347 | cp = "environment=\""; | ||
348 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
349 | { | ||
350 | int i; | ||
351 | char *s; | ||
352 | struct envstring *new_envstring; | ||
353 | options += strlen(cp); | ||
354 | s = xmalloc(strlen(options) + 1); | ||
355 | i = 0; | ||
356 | while (*options) | ||
357 | { | ||
358 | if (*options == '"') | ||
359 | break; | ||
360 | if (*options == '\\' && options[1] == '"') | ||
361 | { | ||
362 | options += 2; | ||
363 | s[i++] = '"'; | ||
364 | continue; | ||
365 | } | ||
366 | s[i++] = *options++; | ||
367 | } | ||
368 | if (!*options) | ||
369 | { | ||
370 | debug("%.100s, line %lu: missing end quote", | ||
371 | SSH_USER_PERMITTED_KEYS, linenum); | ||
372 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
373 | SSH_USER_PERMITTED_KEYS, linenum); | ||
374 | continue; | ||
375 | } | ||
376 | s[i] = 0; | ||
377 | packet_send_debug("Adding to environment: %.900s", s); | ||
378 | debug("Adding to environment: %.900s", s); | ||
379 | options++; | ||
380 | new_envstring = xmalloc(sizeof(struct envstring)); | ||
381 | new_envstring->s = s; | ||
382 | new_envstring->next = custom_environment; | ||
383 | custom_environment = new_envstring; | ||
384 | goto next_option; | ||
385 | } | ||
386 | cp = "from=\""; | ||
387 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
388 | { | ||
389 | char *patterns = xmalloc(strlen(options) + 1); | ||
390 | int i; | ||
391 | options += strlen(cp); | ||
392 | i = 0; | ||
393 | while (*options) | ||
394 | { | ||
395 | if (*options == '"') | ||
396 | break; | ||
397 | if (*options == '\\' && options[1] == '"') | ||
398 | { | ||
399 | options += 2; | ||
400 | patterns[i++] = '"'; | ||
401 | continue; | ||
402 | } | ||
403 | patterns[i++] = *options++; | ||
404 | } | ||
405 | if (!*options) | ||
406 | { | ||
407 | debug("%.100s, line %lu: missing end quote", | ||
408 | SSH_USER_PERMITTED_KEYS, linenum); | ||
409 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
410 | SSH_USER_PERMITTED_KEYS, linenum); | ||
411 | continue; | ||
412 | } | ||
413 | patterns[i] = 0; | ||
414 | options++; | ||
415 | if (!match_hostname(get_canonical_hostname(), patterns, | ||
416 | strlen(patterns)) && | ||
417 | !match_hostname(get_remote_ipaddr(), patterns, | ||
418 | strlen(patterns))) | ||
419 | { | ||
420 | log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).", | ||
421 | pw->pw_name, get_canonical_hostname(), | ||
422 | get_remote_ipaddr()); | ||
423 | packet_send_debug("Your host '%.200s' is not permitted to use this key for login.", | ||
424 | get_canonical_hostname()); | ||
425 | xfree(patterns); | ||
426 | authenticated = 0; | ||
427 | break; | ||
428 | } | ||
429 | xfree(patterns); | ||
430 | /* Host name matches. */ | ||
431 | goto next_option; | ||
432 | } | ||
433 | bad_option: | ||
434 | /* Unknown option. */ | ||
435 | log("Bad options in %.100s file, line %lu: %.50s", | ||
436 | SSH_USER_PERMITTED_KEYS, linenum, options); | ||
437 | packet_send_debug("Bad options in %.100s file, line %lu: %.50s", | ||
438 | SSH_USER_PERMITTED_KEYS, linenum, options); | ||
439 | authenticated = 0; | ||
440 | break; | ||
441 | |||
442 | next_option: | ||
443 | /* Skip the comma, and move to the next option (or break out | ||
444 | if there are no more). */ | ||
445 | if (!*options) | ||
446 | fatal("Bugs in auth-rsa.c option processing."); | ||
447 | if (*options == ' ' || *options == '\t') | ||
448 | break; /* End of options. */ | ||
449 | if (*options != ',') | ||
450 | goto bad_option; | ||
451 | options++; | ||
452 | /* Process the next option. */ | ||
453 | continue; | ||
454 | } | ||
455 | } | ||
456 | |||
457 | /* Break out of the loop if authentication was successful; otherwise | ||
458 | continue searching. */ | ||
459 | if (authenticated) | ||
460 | break; | ||
461 | } | ||
462 | |||
463 | /* Restore the privileged uid. */ | ||
464 | restore_uid(); | ||
465 | |||
466 | /* Close the file. */ | ||
467 | fclose(f); | ||
468 | |||
469 | /* Clear any mp-int variables. */ | ||
470 | BN_clear_free(n); | ||
471 | BN_clear_free(e); | ||
472 | |||
473 | if (authenticated) | ||
474 | packet_send_debug("RSA authentication accepted."); | ||
475 | |||
476 | /* Return authentication result. */ | ||
477 | return authenticated; | ||
478 | } | ||
diff --git a/auth-skey.c b/auth-skey.c new file mode 100644 index 000000000..9ec170494 --- /dev/null +++ b/auth-skey.c | |||
@@ -0,0 +1,149 @@ | |||
1 | #include "includes.h" | ||
2 | RCSID("$Id: auth-skey.c,v 1.2 1999/10/16 20:57:52 deraadt Exp $"); | ||
3 | |||
4 | #include "ssh.h" | ||
5 | #include <sha1.h> | ||
6 | |||
7 | /* from %OpenBSD: skeylogin.c,v 1.32 1999/08/16 14:46:56 millert Exp % */ | ||
8 | |||
9 | |||
10 | #define ROUND(x) (((x)[0] << 24) + (((x)[1]) << 16) + (((x)[2]) << 8) + \ | ||
11 | ((x)[3])) | ||
12 | |||
13 | /* | ||
14 | * hash_collapse() | ||
15 | */ | ||
16 | static u_int32_t | ||
17 | hash_collapse(s) | ||
18 | u_char *s; | ||
19 | { | ||
20 | int len, target; | ||
21 | u_int32_t i; | ||
22 | |||
23 | if ((strlen(s) % sizeof(u_int32_t)) == 0) | ||
24 | target = strlen(s); /* Multiple of 4 */ | ||
25 | else | ||
26 | target = strlen(s) - (strlen(s) % sizeof(u_int32_t)); | ||
27 | |||
28 | for (i = 0, len = 0; len < target; len += 4) | ||
29 | i ^= ROUND(s + len); | ||
30 | |||
31 | return i; | ||
32 | } | ||
33 | char * | ||
34 | skey_fake_keyinfo(char *username) | ||
35 | { | ||
36 | int i; | ||
37 | u_int ptr; | ||
38 | u_char hseed[SKEY_MAX_SEED_LEN], flg = 1, *up; | ||
39 | char pbuf[SKEY_MAX_PW_LEN+1]; | ||
40 | static char skeyprompt[SKEY_MAX_CHALLENGE+1]; | ||
41 | char *secret = NULL; | ||
42 | size_t secretlen = 0; | ||
43 | SHA1_CTX ctx; | ||
44 | char *p, *u; | ||
45 | |||
46 | /* | ||
47 | * Base first 4 chars of seed on hostname. | ||
48 | * Add some filler for short hostnames if necessary. | ||
49 | */ | ||
50 | if (gethostname(pbuf, sizeof(pbuf)) == -1) | ||
51 | *(p = pbuf) = '.'; | ||
52 | else | ||
53 | for (p = pbuf; *p && isalnum(*p); p++) | ||
54 | if (isalpha(*p) && isupper(*p)) | ||
55 | *p = tolower(*p); | ||
56 | if (*p && pbuf - p < 4) | ||
57 | (void)strncpy(p, "asjd", 4 - (pbuf - p)); | ||
58 | pbuf[4] = '\0'; | ||
59 | |||
60 | /* Hash the username if possible */ | ||
61 | if ((up = SHA1Data(username, strlen(username), NULL)) != NULL) { | ||
62 | struct stat sb; | ||
63 | time_t t; | ||
64 | int fd; | ||
65 | |||
66 | /* Collapse the hash */ | ||
67 | ptr = hash_collapse(up); | ||
68 | memset(up, 0, strlen(up)); | ||
69 | |||
70 | /* See if the random file's there, else use ctime */ | ||
71 | if ((fd = open(_SKEY_RAND_FILE_PATH_, O_RDONLY)) != -1 | ||
72 | && fstat(fd, &sb) == 0 && | ||
73 | sb.st_size > (off_t)SKEY_MAX_SEED_LEN && | ||
74 | lseek(fd, ptr % (sb.st_size - SKEY_MAX_SEED_LEN), | ||
75 | SEEK_SET) != -1 && read(fd, hseed, | ||
76 | SKEY_MAX_SEED_LEN) == SKEY_MAX_SEED_LEN) { | ||
77 | close(fd); | ||
78 | secret = hseed; | ||
79 | secretlen = SKEY_MAX_SEED_LEN; | ||
80 | flg = 0; | ||
81 | } else if (!stat(_PATH_MEM, &sb) || !stat("/", &sb)) { | ||
82 | t = sb.st_ctime; | ||
83 | secret = ctime(&t); | ||
84 | secretlen = strlen(secret); | ||
85 | flg = 0; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | /* Put that in your pipe and smoke it */ | ||
90 | if (flg == 0) { | ||
91 | /* Hash secret value with username */ | ||
92 | SHA1Init(&ctx); | ||
93 | SHA1Update(&ctx, secret, secretlen); | ||
94 | SHA1Update(&ctx, username, strlen(username)); | ||
95 | SHA1End(&ctx, up); | ||
96 | |||
97 | /* Zero out */ | ||
98 | memset(secret, 0, secretlen); | ||
99 | |||
100 | /* Now hash the hash */ | ||
101 | SHA1Init(&ctx); | ||
102 | SHA1Update(&ctx, up, strlen(up)); | ||
103 | SHA1End(&ctx, up); | ||
104 | |||
105 | ptr = hash_collapse(up + 4); | ||
106 | |||
107 | for (i = 4; i < 9; i++) { | ||
108 | pbuf[i] = (ptr % 10) + '0'; | ||
109 | ptr /= 10; | ||
110 | } | ||
111 | pbuf[i] = '\0'; | ||
112 | |||
113 | /* Sequence number */ | ||
114 | ptr = ((up[2] + up[3]) % 99) + 1; | ||
115 | |||
116 | memset(up, 0, 20); /* SHA1 specific */ | ||
117 | free(up); | ||
118 | |||
119 | (void)snprintf(skeyprompt, sizeof skeyprompt, | ||
120 | "otp-%.*s %d %.*s", | ||
121 | SKEY_MAX_HASHNAME_LEN, | ||
122 | skey_get_algorithm(), | ||
123 | ptr, SKEY_MAX_SEED_LEN, | ||
124 | pbuf); | ||
125 | } else { | ||
126 | /* Base last 8 chars of seed on username */ | ||
127 | u = username; | ||
128 | i = 8; | ||
129 | p = &pbuf[4]; | ||
130 | do { | ||
131 | if (*u == 0) { | ||
132 | /* Pad remainder with zeros */ | ||
133 | while (--i >= 0) | ||
134 | *p++ = '0'; | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | *p++ = (*u++ % 10) + '0'; | ||
139 | } while (--i != 0); | ||
140 | pbuf[12] = '\0'; | ||
141 | |||
142 | (void)snprintf(skeyprompt, sizeof skeyprompt, | ||
143 | "otp-%.*s %d %.*s", | ||
144 | SKEY_MAX_HASHNAME_LEN, | ||
145 | skey_get_algorithm(), | ||
146 | 99, SKEY_MAX_SEED_LEN, pbuf); | ||
147 | } | ||
148 | return skeyprompt; | ||
149 | } | ||
diff --git a/authfd.c b/authfd.c new file mode 100644 index 000000000..07893caf3 --- /dev/null +++ b/authfd.c | |||
@@ -0,0 +1,565 @@ | |||
1 | /* | ||
2 | |||
3 | authfd.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Mar 29 01:30:28 1995 ylo | ||
11 | |||
12 | Functions for connecting the local authentication agent. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: authfd.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
18 | |||
19 | #include "ssh.h" | ||
20 | #include "rsa.h" | ||
21 | #include "authfd.h" | ||
22 | #include "buffer.h" | ||
23 | #include "bufaux.h" | ||
24 | #include "xmalloc.h" | ||
25 | #include "getput.h" | ||
26 | |||
27 | #include <openssl/rsa.h> | ||
28 | |||
29 | /* Returns the number of the authentication fd, or -1 if there is none. */ | ||
30 | |||
31 | int | ||
32 | ssh_get_authentication_socket() | ||
33 | { | ||
34 | const char *authsocket; | ||
35 | int sock; | ||
36 | struct sockaddr_un sunaddr; | ||
37 | |||
38 | authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); | ||
39 | if (!authsocket) | ||
40 | return -1; | ||
41 | |||
42 | sunaddr.sun_family = AF_UNIX; | ||
43 | strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); | ||
44 | |||
45 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | ||
46 | if (sock < 0) | ||
47 | return -1; | ||
48 | |||
49 | if (connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) | ||
50 | { | ||
51 | close(sock); | ||
52 | return -1; | ||
53 | } | ||
54 | |||
55 | return sock; | ||
56 | } | ||
57 | |||
58 | /* Closes the agent socket if it should be closed (depends on how it was | ||
59 | obtained). The argument must have been returned by | ||
60 | ssh_get_authentication_socket(). */ | ||
61 | |||
62 | void ssh_close_authentication_socket(int sock) | ||
63 | { | ||
64 | if (getenv(SSH_AUTHSOCKET_ENV_NAME)) | ||
65 | close(sock); | ||
66 | } | ||
67 | |||
68 | /* Opens and connects a private socket for communication with the | ||
69 | authentication agent. Returns the file descriptor (which must be | ||
70 | shut down and closed by the caller when no longer needed). | ||
71 | Returns NULL if an error occurred and the connection could not be | ||
72 | opened. */ | ||
73 | |||
74 | AuthenticationConnection *ssh_get_authentication_connection() | ||
75 | { | ||
76 | AuthenticationConnection *auth; | ||
77 | int sock; | ||
78 | |||
79 | sock = ssh_get_authentication_socket(); | ||
80 | |||
81 | /* Fail if we couldn't obtain a connection. This happens if we exited | ||
82 | due to a timeout. */ | ||
83 | if (sock < 0) | ||
84 | return NULL; | ||
85 | |||
86 | /* Applocate the connection structure and initialize it. */ | ||
87 | auth = xmalloc(sizeof(*auth)); | ||
88 | auth->fd = sock; | ||
89 | buffer_init(&auth->packet); | ||
90 | buffer_init(&auth->identities); | ||
91 | auth->howmany = 0; | ||
92 | |||
93 | return auth; | ||
94 | } | ||
95 | |||
96 | /* Closes the connection to the authentication agent and frees any associated | ||
97 | memory. */ | ||
98 | |||
99 | void ssh_close_authentication_connection(AuthenticationConnection *ac) | ||
100 | { | ||
101 | buffer_free(&ac->packet); | ||
102 | buffer_free(&ac->identities); | ||
103 | close(ac->fd); | ||
104 | /* Free the connection data structure. */ | ||
105 | xfree(ac); | ||
106 | } | ||
107 | |||
108 | /* Returns the first authentication identity held by the agent. | ||
109 | Returns true if an identity is available, 0 otherwise. | ||
110 | The caller must initialize the integers before the call, and free the | ||
111 | comment after a successful call (before calling ssh_get_next_identity). */ | ||
112 | |||
113 | int | ||
114 | ssh_get_first_identity(AuthenticationConnection *auth, | ||
115 | int *bitsp, BIGNUM *e, BIGNUM *n, char **comment) | ||
116 | { | ||
117 | unsigned char msg[8192]; | ||
118 | int len, l; | ||
119 | |||
120 | /* Send a message to the agent requesting for a list of the identities | ||
121 | it can represent. */ | ||
122 | msg[0] = 0; | ||
123 | msg[1] = 0; | ||
124 | msg[2] = 0; | ||
125 | msg[3] = 1; | ||
126 | msg[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES; | ||
127 | if (write(auth->fd, msg, 5) != 5) | ||
128 | { | ||
129 | error("write auth->fd: %.100s", strerror(errno)); | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | /* Read the length of the response. XXX implement timeouts here. */ | ||
134 | len = 4; | ||
135 | while (len > 0) | ||
136 | { | ||
137 | l = read(auth->fd, msg + 4 - len, len); | ||
138 | if (l <= 0) | ||
139 | { | ||
140 | error("read auth->fd: %.100s", strerror(errno)); | ||
141 | return 0; | ||
142 | } | ||
143 | len -= l; | ||
144 | } | ||
145 | |||
146 | /* Extract the length, and check it for sanity. (We cannot trust | ||
147 | authentication agents). */ | ||
148 | len = GET_32BIT(msg); | ||
149 | if (len < 1 || len > 256*1024) | ||
150 | fatal("Authentication reply message too long: %d\n", len); | ||
151 | |||
152 | /* Read the packet itself. */ | ||
153 | buffer_clear(&auth->identities); | ||
154 | while (len > 0) | ||
155 | { | ||
156 | l = len; | ||
157 | if (l > sizeof(msg)) | ||
158 | l = sizeof(msg); | ||
159 | l = read(auth->fd, msg, l); | ||
160 | if (l <= 0) | ||
161 | fatal("Incomplete authentication reply."); | ||
162 | buffer_append(&auth->identities, (char *)msg, l); | ||
163 | len -= l; | ||
164 | } | ||
165 | |||
166 | /* Get message type, and verify that we got a proper answer. */ | ||
167 | buffer_get(&auth->identities, (char *)msg, 1); | ||
168 | if (msg[0] != SSH_AGENT_RSA_IDENTITIES_ANSWER) | ||
169 | fatal("Bad authentication reply message type: %d", msg[0]); | ||
170 | |||
171 | /* Get the number of entries in the response and check it for sanity. */ | ||
172 | auth->howmany = buffer_get_int(&auth->identities); | ||
173 | if (auth->howmany > 1024) | ||
174 | fatal("Too many identities in authentication reply: %d\n", auth->howmany); | ||
175 | |||
176 | /* Return the first entry (if any). */ | ||
177 | return ssh_get_next_identity(auth, bitsp, e, n, comment); | ||
178 | } | ||
179 | |||
180 | /* Returns the next authentication identity for the agent. Other functions | ||
181 | can be called between this and ssh_get_first_identity or two calls of this | ||
182 | function. This returns 0 if there are no more identities. The caller | ||
183 | must free comment after a successful return. */ | ||
184 | |||
185 | int | ||
186 | ssh_get_next_identity(AuthenticationConnection *auth, | ||
187 | int *bitsp, BIGNUM *e, BIGNUM *n, char **comment) | ||
188 | { | ||
189 | /* Return failure if no more entries. */ | ||
190 | if (auth->howmany <= 0) | ||
191 | return 0; | ||
192 | |||
193 | /* Get the next entry from the packet. These will abort with a fatal | ||
194 | error if the packet is too short or contains corrupt data. */ | ||
195 | *bitsp = buffer_get_int(&auth->identities); | ||
196 | buffer_get_bignum(&auth->identities, e); | ||
197 | buffer_get_bignum(&auth->identities, n); | ||
198 | *comment = buffer_get_string(&auth->identities, NULL); | ||
199 | |||
200 | /* Decrement the number of remaining entries. */ | ||
201 | auth->howmany--; | ||
202 | |||
203 | return 1; | ||
204 | } | ||
205 | |||
206 | /* Generates a random challenge, sends it to the agent, and waits for response | ||
207 | from the agent. Returns true (non-zero) if the agent gave the correct | ||
208 | answer, zero otherwise. Response type selects the style of response | ||
209 | desired, with 0 corresponding to protocol version 1.0 (no longer supported) | ||
210 | and 1 corresponding to protocol version 1.1. */ | ||
211 | |||
212 | int | ||
213 | ssh_decrypt_challenge(AuthenticationConnection *auth, | ||
214 | int bits, BIGNUM *e, BIGNUM *n, BIGNUM *challenge, | ||
215 | unsigned char session_id[16], | ||
216 | unsigned int response_type, | ||
217 | unsigned char response[16]) | ||
218 | { | ||
219 | Buffer buffer; | ||
220 | unsigned char buf[8192]; | ||
221 | int len, l, i; | ||
222 | |||
223 | /* Response type 0 is no longer supported. */ | ||
224 | if (response_type == 0) | ||
225 | fatal("Compatibility with ssh protocol version 1.0 no longer supported."); | ||
226 | |||
227 | /* Format a message to the agent. */ | ||
228 | buf[0] = SSH_AGENTC_RSA_CHALLENGE; | ||
229 | buffer_init(&buffer); | ||
230 | buffer_append(&buffer, (char *)buf, 1); | ||
231 | buffer_put_int(&buffer, bits); | ||
232 | buffer_put_bignum(&buffer, e); | ||
233 | buffer_put_bignum(&buffer, n); | ||
234 | buffer_put_bignum(&buffer, challenge); | ||
235 | buffer_append(&buffer, (char *)session_id, 16); | ||
236 | buffer_put_int(&buffer, response_type); | ||
237 | |||
238 | /* Get the length of the message, and format it in the buffer. */ | ||
239 | len = buffer_len(&buffer); | ||
240 | PUT_32BIT(buf, len); | ||
241 | |||
242 | /* Send the length and then the packet to the agent. */ | ||
243 | if (write(auth->fd, buf, 4) != 4 || | ||
244 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != | ||
245 | buffer_len(&buffer)) | ||
246 | { | ||
247 | error("Error writing to authentication socket."); | ||
248 | error_cleanup: | ||
249 | buffer_free(&buffer); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | /* Wait for response from the agent. First read the length of the | ||
254 | response packet. */ | ||
255 | len = 4; | ||
256 | while (len > 0) | ||
257 | { | ||
258 | l = read(auth->fd, buf + 4 - len, len); | ||
259 | if (l <= 0) | ||
260 | { | ||
261 | error("Error reading response length from authentication socket."); | ||
262 | goto error_cleanup; | ||
263 | } | ||
264 | len -= l; | ||
265 | } | ||
266 | |||
267 | /* Extract the length, and check it for sanity. */ | ||
268 | len = GET_32BIT(buf); | ||
269 | if (len > 256*1024) | ||
270 | fatal("Authentication response too long: %d", len); | ||
271 | |||
272 | /* Read the rest of the response in tothe buffer. */ | ||
273 | buffer_clear(&buffer); | ||
274 | while (len > 0) | ||
275 | { | ||
276 | l = len; | ||
277 | if (l > sizeof(buf)) | ||
278 | l = sizeof(buf); | ||
279 | l = read(auth->fd, buf, l); | ||
280 | if (l <= 0) | ||
281 | { | ||
282 | error("Error reading response from authentication socket."); | ||
283 | goto error_cleanup; | ||
284 | } | ||
285 | buffer_append(&buffer, (char *)buf, l); | ||
286 | len -= l; | ||
287 | } | ||
288 | |||
289 | /* Get the type of the packet. */ | ||
290 | buffer_get(&buffer, (char *)buf, 1); | ||
291 | |||
292 | /* Check for agent failure message. */ | ||
293 | if (buf[0] == SSH_AGENT_FAILURE) | ||
294 | { | ||
295 | log("Agent admitted failure to authenticate using the key."); | ||
296 | goto error_cleanup; | ||
297 | } | ||
298 | |||
299 | /* Now it must be an authentication response packet. */ | ||
300 | if (buf[0] != SSH_AGENT_RSA_RESPONSE) | ||
301 | fatal("Bad authentication response: %d", buf[0]); | ||
302 | |||
303 | /* Get the response from the packet. This will abort with a fatal error | ||
304 | if the packet is corrupt. */ | ||
305 | for (i = 0; i < 16; i++) | ||
306 | response[i] = buffer_get_char(&buffer); | ||
307 | |||
308 | /* The buffer containing the packet is no longer needed. */ | ||
309 | buffer_free(&buffer); | ||
310 | |||
311 | /* Correct answer. */ | ||
312 | return 1; | ||
313 | } | ||
314 | |||
315 | /* Adds an identity to the authentication server. This call is not meant to | ||
316 | be used by normal applications. */ | ||
317 | |||
318 | int ssh_add_identity(AuthenticationConnection *auth, | ||
319 | RSA *key, const char *comment) | ||
320 | { | ||
321 | Buffer buffer; | ||
322 | unsigned char buf[8192]; | ||
323 | int len, l, type; | ||
324 | |||
325 | /* Format a message to the agent. */ | ||
326 | buffer_init(&buffer); | ||
327 | buffer_put_char(&buffer, SSH_AGENTC_ADD_RSA_IDENTITY); | ||
328 | buffer_put_int(&buffer, BN_num_bits(key->n)); | ||
329 | buffer_put_bignum(&buffer, key->n); | ||
330 | buffer_put_bignum(&buffer, key->e); | ||
331 | buffer_put_bignum(&buffer, key->d); | ||
332 | /* To keep within the protocol: p < q for ssh. in SSL p > q */ | ||
333 | buffer_put_bignum(&buffer, key->iqmp); /* ssh key->u */ | ||
334 | buffer_put_bignum(&buffer, key->q); /* ssh key->p, SSL key->q */ | ||
335 | buffer_put_bignum(&buffer, key->p); /* ssh key->q, SSL key->p */ | ||
336 | buffer_put_string(&buffer, comment, strlen(comment)); | ||
337 | |||
338 | /* Get the length of the message, and format it in the buffer. */ | ||
339 | len = buffer_len(&buffer); | ||
340 | PUT_32BIT(buf, len); | ||
341 | |||
342 | /* Send the length and then the packet to the agent. */ | ||
343 | if (write(auth->fd, buf, 4) != 4 || | ||
344 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != | ||
345 | buffer_len(&buffer)) | ||
346 | { | ||
347 | error("Error writing to authentication socket."); | ||
348 | error_cleanup: | ||
349 | buffer_free(&buffer); | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | /* Wait for response from the agent. First read the length of the | ||
354 | response packet. */ | ||
355 | len = 4; | ||
356 | while (len > 0) | ||
357 | { | ||
358 | l = read(auth->fd, buf + 4 - len, len); | ||
359 | if (l <= 0) | ||
360 | { | ||
361 | error("Error reading response length from authentication socket."); | ||
362 | goto error_cleanup; | ||
363 | } | ||
364 | len -= l; | ||
365 | } | ||
366 | |||
367 | /* Extract the length, and check it for sanity. */ | ||
368 | len = GET_32BIT(buf); | ||
369 | if (len > 256*1024) | ||
370 | fatal("Add identity response too long: %d", len); | ||
371 | |||
372 | /* Read the rest of the response in tothe buffer. */ | ||
373 | buffer_clear(&buffer); | ||
374 | while (len > 0) | ||
375 | { | ||
376 | l = len; | ||
377 | if (l > sizeof(buf)) | ||
378 | l = sizeof(buf); | ||
379 | l = read(auth->fd, buf, l); | ||
380 | if (l <= 0) | ||
381 | { | ||
382 | error("Error reading response from authentication socket."); | ||
383 | goto error_cleanup; | ||
384 | } | ||
385 | buffer_append(&buffer, (char *)buf, l); | ||
386 | len -= l; | ||
387 | } | ||
388 | |||
389 | /* Get the type of the packet. */ | ||
390 | type = buffer_get_char(&buffer); | ||
391 | switch (type) | ||
392 | { | ||
393 | case SSH_AGENT_FAILURE: | ||
394 | buffer_free(&buffer); | ||
395 | return 0; | ||
396 | case SSH_AGENT_SUCCESS: | ||
397 | buffer_free(&buffer); | ||
398 | return 1; | ||
399 | default: | ||
400 | fatal("Bad response to add identity from authentication agent: %d", | ||
401 | type); | ||
402 | } | ||
403 | /*NOTREACHED*/ | ||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | /* Removes an identity from the authentication server. This call is not meant | ||
408 | to be used by normal applications. */ | ||
409 | |||
410 | int ssh_remove_identity(AuthenticationConnection *auth, RSA *key) | ||
411 | { | ||
412 | Buffer buffer; | ||
413 | unsigned char buf[8192]; | ||
414 | int len, l, type; | ||
415 | |||
416 | /* Format a message to the agent. */ | ||
417 | buffer_init(&buffer); | ||
418 | buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY); | ||
419 | buffer_put_int(&buffer, BN_num_bits(key->n)); | ||
420 | buffer_put_bignum(&buffer, key->e); | ||
421 | buffer_put_bignum(&buffer, key->n); | ||
422 | |||
423 | /* Get the length of the message, and format it in the buffer. */ | ||
424 | len = buffer_len(&buffer); | ||
425 | PUT_32BIT(buf, len); | ||
426 | |||
427 | /* Send the length and then the packet to the agent. */ | ||
428 | if (write(auth->fd, buf, 4) != 4 || | ||
429 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != | ||
430 | buffer_len(&buffer)) | ||
431 | { | ||
432 | error("Error writing to authentication socket."); | ||
433 | error_cleanup: | ||
434 | buffer_free(&buffer); | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | /* Wait for response from the agent. First read the length of the | ||
439 | response packet. */ | ||
440 | len = 4; | ||
441 | while (len > 0) | ||
442 | { | ||
443 | l = read(auth->fd, buf + 4 - len, len); | ||
444 | if (l <= 0) | ||
445 | { | ||
446 | error("Error reading response length from authentication socket."); | ||
447 | goto error_cleanup; | ||
448 | } | ||
449 | len -= l; | ||
450 | } | ||
451 | |||
452 | /* Extract the length, and check it for sanity. */ | ||
453 | len = GET_32BIT(buf); | ||
454 | if (len > 256*1024) | ||
455 | fatal("Remove identity response too long: %d", len); | ||
456 | |||
457 | /* Read the rest of the response in tothe buffer. */ | ||
458 | buffer_clear(&buffer); | ||
459 | while (len > 0) | ||
460 | { | ||
461 | l = len; | ||
462 | if (l > sizeof(buf)) | ||
463 | l = sizeof(buf); | ||
464 | l = read(auth->fd, buf, l); | ||
465 | if (l <= 0) | ||
466 | { | ||
467 | error("Error reading response from authentication socket."); | ||
468 | goto error_cleanup; | ||
469 | } | ||
470 | buffer_append(&buffer, (char *)buf, l); | ||
471 | len -= l; | ||
472 | } | ||
473 | |||
474 | /* Get the type of the packet. */ | ||
475 | type = buffer_get_char(&buffer); | ||
476 | switch (type) | ||
477 | { | ||
478 | case SSH_AGENT_FAILURE: | ||
479 | buffer_free(&buffer); | ||
480 | return 0; | ||
481 | case SSH_AGENT_SUCCESS: | ||
482 | buffer_free(&buffer); | ||
483 | return 1; | ||
484 | default: | ||
485 | fatal("Bad response to remove identity from authentication agent: %d", | ||
486 | type); | ||
487 | } | ||
488 | /*NOTREACHED*/ | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | /* Removes all identities from the agent. This call is not meant | ||
493 | to be used by normal applications. */ | ||
494 | |||
495 | int ssh_remove_all_identities(AuthenticationConnection *auth) | ||
496 | { | ||
497 | Buffer buffer; | ||
498 | unsigned char buf[8192]; | ||
499 | int len, l, type; | ||
500 | |||
501 | /* Get the length of the message, and format it in the buffer. */ | ||
502 | PUT_32BIT(buf, 1); | ||
503 | buf[4] = SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES; | ||
504 | |||
505 | /* Send the length and then the packet to the agent. */ | ||
506 | if (write(auth->fd, buf, 5) != 5) | ||
507 | { | ||
508 | error("Error writing to authentication socket."); | ||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | /* Wait for response from the agent. First read the length of the | ||
513 | response packet. */ | ||
514 | len = 4; | ||
515 | while (len > 0) | ||
516 | { | ||
517 | l = read(auth->fd, buf + 4 - len, len); | ||
518 | if (l <= 0) | ||
519 | { | ||
520 | error("Error reading response length from authentication socket."); | ||
521 | return 0; | ||
522 | } | ||
523 | len -= l; | ||
524 | } | ||
525 | |||
526 | /* Extract the length, and check it for sanity. */ | ||
527 | len = GET_32BIT(buf); | ||
528 | if (len > 256*1024) | ||
529 | fatal("Remove identity response too long: %d", len); | ||
530 | |||
531 | /* Read the rest of the response into the buffer. */ | ||
532 | buffer_init(&buffer); | ||
533 | while (len > 0) | ||
534 | { | ||
535 | l = len; | ||
536 | if (l > sizeof(buf)) | ||
537 | l = sizeof(buf); | ||
538 | l = read(auth->fd, buf, l); | ||
539 | if (l <= 0) | ||
540 | { | ||
541 | error("Error reading response from authentication socket."); | ||
542 | buffer_free(&buffer); | ||
543 | return 0; | ||
544 | } | ||
545 | buffer_append(&buffer, (char *)buf, l); | ||
546 | len -= l; | ||
547 | } | ||
548 | |||
549 | /* Get the type of the packet. */ | ||
550 | type = buffer_get_char(&buffer); | ||
551 | switch (type) | ||
552 | { | ||
553 | case SSH_AGENT_FAILURE: | ||
554 | buffer_free(&buffer); | ||
555 | return 0; | ||
556 | case SSH_AGENT_SUCCESS: | ||
557 | buffer_free(&buffer); | ||
558 | return 1; | ||
559 | default: | ||
560 | fatal("Bad response to remove identity from authentication agent: %d", | ||
561 | type); | ||
562 | } | ||
563 | /*NOTREACHED*/ | ||
564 | return 0; | ||
565 | } | ||
diff --git a/authfd.h b/authfd.h new file mode 100644 index 000000000..1def920e3 --- /dev/null +++ b/authfd.h | |||
@@ -0,0 +1,102 @@ | |||
1 | /* | ||
2 | |||
3 | authfd.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Mar 29 01:17:41 1995 ylo | ||
11 | |||
12 | Functions to interface with the SSH_AUTHENTICATION_FD socket. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: authfd.h,v 1.1 1999/10/27 03:42:43 damien Exp $"); */ | ||
17 | |||
18 | #ifndef AUTHFD_H | ||
19 | #define AUTHFD_H | ||
20 | |||
21 | #include "buffer.h" | ||
22 | |||
23 | /* Messages for the authentication agent connection. */ | ||
24 | #define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 | ||
25 | #define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 | ||
26 | #define SSH_AGENTC_RSA_CHALLENGE 3 | ||
27 | #define SSH_AGENT_RSA_RESPONSE 4 | ||
28 | #define SSH_AGENT_FAILURE 5 | ||
29 | #define SSH_AGENT_SUCCESS 6 | ||
30 | #define SSH_AGENTC_ADD_RSA_IDENTITY 7 | ||
31 | #define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 | ||
32 | #define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 | ||
33 | |||
34 | typedef struct | ||
35 | { | ||
36 | int fd; | ||
37 | Buffer packet; | ||
38 | Buffer identities; | ||
39 | int howmany; | ||
40 | } AuthenticationConnection; | ||
41 | |||
42 | /* Returns the number of the authentication fd, or -1 if there is none. */ | ||
43 | int ssh_get_authentication_socket(); | ||
44 | |||
45 | /* This should be called for any descriptor returned by | ||
46 | ssh_get_authentication_socket(). Depending on the way the descriptor was | ||
47 | obtained, this may close the descriptor. */ | ||
48 | void ssh_close_authentication_socket(int authfd); | ||
49 | |||
50 | /* Opens and connects a private socket for communication with the | ||
51 | authentication agent. Returns NULL if an error occurred and the | ||
52 | connection could not be opened. The connection should be closed by | ||
53 | the caller by calling ssh_close_authentication_connection(). */ | ||
54 | AuthenticationConnection *ssh_get_authentication_connection(); | ||
55 | |||
56 | /* Closes the connection to the authentication agent and frees any associated | ||
57 | memory. */ | ||
58 | void ssh_close_authentication_connection(AuthenticationConnection *ac); | ||
59 | |||
60 | /* Returns the first authentication identity held by the agent. | ||
61 | Returns true if an identity is available, 0 otherwise. | ||
62 | The caller must initialize the integers before the call, and free the | ||
63 | comment after a successful call (before calling ssh_get_next_identity). */ | ||
64 | int ssh_get_first_identity(AuthenticationConnection *connection, | ||
65 | int *bitsp, BIGNUM *e, BIGNUM *n, char **comment); | ||
66 | |||
67 | /* Returns the next authentication identity for the agent. Other functions | ||
68 | can be called between this and ssh_get_first_identity or two calls of this | ||
69 | function. This returns 0 if there are no more identities. The caller | ||
70 | must free comment after a successful return. */ | ||
71 | int ssh_get_next_identity(AuthenticationConnection *connection, | ||
72 | int *bitsp, BIGNUM *e, BIGNUM *n, char **comment); | ||
73 | |||
74 | /* Requests the agent to decrypt the given challenge. Returns true if | ||
75 | the agent claims it was able to decrypt it. */ | ||
76 | int ssh_decrypt_challenge(AuthenticationConnection *auth, | ||
77 | int bits, BIGNUM *e, BIGNUM *n, BIGNUM *challenge, | ||
78 | unsigned char session_id[16], | ||
79 | unsigned int response_type, | ||
80 | unsigned char response[16]); | ||
81 | |||
82 | /* Adds an identity to the authentication server. This call is not meant to | ||
83 | be used by normal applications. This returns true if the identity | ||
84 | was successfully added. */ | ||
85 | int ssh_add_identity(AuthenticationConnection *connection, | ||
86 | RSA *key, const char *comment); | ||
87 | |||
88 | /* Removes the identity from the authentication server. This call is | ||
89 | not meant to be used by normal applications. This returns true if the | ||
90 | identity was successfully added. */ | ||
91 | int ssh_remove_identity(AuthenticationConnection *connection, | ||
92 | RSA *key); | ||
93 | |||
94 | /* Removes all identities from the authentication agent. This call is not | ||
95 | meant to be used by normal applications. This returns true if the | ||
96 | operation was successful. */ | ||
97 | int ssh_remove_all_identities(AuthenticationConnection *connection); | ||
98 | |||
99 | /* Closes the connection to the authentication agent. */ | ||
100 | void ssh_close_authentication(AuthenticationConnection *connection); | ||
101 | |||
102 | #endif /* AUTHFD_H */ | ||
diff --git a/authfile.c b/authfile.c new file mode 100644 index 000000000..49390e083 --- /dev/null +++ b/authfile.c | |||
@@ -0,0 +1,350 @@ | |||
1 | /* | ||
2 | |||
3 | authfile.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Mar 27 03:52:05 1995 ylo | ||
11 | |||
12 | This file contains functions for reading and writing identity files, and | ||
13 | for reading the passphrase from the user. | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: authfile.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
19 | |||
20 | #include <openssl/bn.h> | ||
21 | #include "xmalloc.h" | ||
22 | #include "buffer.h" | ||
23 | #include "bufaux.h" | ||
24 | #include "cipher.h" | ||
25 | #include "ssh.h" | ||
26 | |||
27 | /* Version identification string for identity files. */ | ||
28 | #define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n" | ||
29 | |||
30 | /* Saves the authentication (private) key in a file, encrypting it with | ||
31 | passphrase. The identification of the file (lowest 64 bits of n) | ||
32 | will precede the key to provide identification of the key without | ||
33 | needing a passphrase. */ | ||
34 | |||
35 | int | ||
36 | save_private_key(const char *filename, const char *passphrase, | ||
37 | RSA *key, const char *comment) | ||
38 | { | ||
39 | Buffer buffer, encrypted; | ||
40 | char buf[100], *cp; | ||
41 | int f, i; | ||
42 | CipherContext cipher; | ||
43 | int cipher_type; | ||
44 | u_int32_t rand; | ||
45 | |||
46 | /* If the passphrase is empty, use SSH_CIPHER_NONE to ease converting to | ||
47 | another cipher; otherwise use SSH_AUTHFILE_CIPHER. */ | ||
48 | if (strcmp(passphrase, "") == 0) | ||
49 | cipher_type = SSH_CIPHER_NONE; | ||
50 | else | ||
51 | cipher_type = SSH_AUTHFILE_CIPHER; | ||
52 | |||
53 | /* This buffer is used to built the secret part of the private key. */ | ||
54 | buffer_init(&buffer); | ||
55 | |||
56 | /* Put checkbytes for checking passphrase validity. */ | ||
57 | rand = arc4random(); | ||
58 | buf[0] = rand & 0xff; | ||
59 | buf[1] = (rand >> 8) & 0xff; | ||
60 | buf[2] = buf[0]; | ||
61 | buf[3] = buf[1]; | ||
62 | buffer_append(&buffer, buf, 4); | ||
63 | |||
64 | /* Store the private key (n and e will not be stored because they will | ||
65 | be stored in plain text, and storing them also in encrypted format | ||
66 | would just give known plaintext). */ | ||
67 | buffer_put_bignum(&buffer, key->d); | ||
68 | buffer_put_bignum(&buffer, key->iqmp); | ||
69 | buffer_put_bignum(&buffer, key->q); /* reverse from SSL p */ | ||
70 | buffer_put_bignum(&buffer, key->p); /* reverse from SSL q */ | ||
71 | |||
72 | /* Pad the part to be encrypted until its size is a multiple of 8. */ | ||
73 | while (buffer_len(&buffer) % 8 != 0) | ||
74 | buffer_put_char(&buffer, 0); | ||
75 | |||
76 | /* This buffer will be used to contain the data in the file. */ | ||
77 | buffer_init(&encrypted); | ||
78 | |||
79 | /* First store keyfile id string. */ | ||
80 | cp = AUTHFILE_ID_STRING; | ||
81 | for (i = 0; cp[i]; i++) | ||
82 | buffer_put_char(&encrypted, cp[i]); | ||
83 | buffer_put_char(&encrypted, 0); | ||
84 | |||
85 | /* Store cipher type. */ | ||
86 | buffer_put_char(&encrypted, cipher_type); | ||
87 | buffer_put_int(&encrypted, 0); /* For future extension */ | ||
88 | |||
89 | /* Store public key. This will be in plain text. */ | ||
90 | buffer_put_int(&encrypted, BN_num_bits(key->n)); | ||
91 | buffer_put_bignum(&encrypted, key->n); | ||
92 | buffer_put_bignum(&encrypted, key->e); | ||
93 | buffer_put_string(&encrypted, comment, strlen(comment)); | ||
94 | |||
95 | /* Allocate space for the private part of the key in the buffer. */ | ||
96 | buffer_append_space(&encrypted, &cp, buffer_len(&buffer)); | ||
97 | |||
98 | cipher_set_key_string(&cipher, cipher_type, passphrase, 1); | ||
99 | cipher_encrypt(&cipher, (unsigned char *)cp, | ||
100 | (unsigned char *)buffer_ptr(&buffer), | ||
101 | buffer_len(&buffer)); | ||
102 | memset(&cipher, 0, sizeof(cipher)); | ||
103 | |||
104 | /* Destroy temporary data. */ | ||
105 | memset(buf, 0, sizeof(buf)); | ||
106 | buffer_free(&buffer); | ||
107 | |||
108 | /* Write to a file. */ | ||
109 | f = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600); | ||
110 | if (f < 0) | ||
111 | return 0; | ||
112 | |||
113 | if (write(f, buffer_ptr(&encrypted), buffer_len(&encrypted)) != | ||
114 | buffer_len(&encrypted)) | ||
115 | { | ||
116 | debug("Write to key file %.200s failed: %.100s", filename, | ||
117 | strerror(errno)); | ||
118 | buffer_free(&encrypted); | ||
119 | close(f); | ||
120 | remove(filename); | ||
121 | return 0; | ||
122 | } | ||
123 | close(f); | ||
124 | buffer_free(&encrypted); | ||
125 | return 1; | ||
126 | } | ||
127 | |||
128 | /* Loads the public part of the key file. Returns 0 if an error | ||
129 | was encountered (the file does not exist or is not readable), and | ||
130 | non-zero otherwise. */ | ||
131 | |||
132 | int | ||
133 | load_public_key(const char *filename, RSA *pub, | ||
134 | char **comment_return) | ||
135 | { | ||
136 | int f, i; | ||
137 | off_t len; | ||
138 | Buffer buffer; | ||
139 | char *cp; | ||
140 | |||
141 | /* Read data from the file into the buffer. */ | ||
142 | f = open(filename, O_RDONLY); | ||
143 | if (f < 0) | ||
144 | return 0; | ||
145 | |||
146 | len = lseek(f, (off_t)0, SEEK_END); | ||
147 | lseek(f, (off_t)0, SEEK_SET); | ||
148 | |||
149 | buffer_init(&buffer); | ||
150 | buffer_append_space(&buffer, &cp, len); | ||
151 | |||
152 | if (read(f, cp, (size_t)len) != (size_t)len) | ||
153 | { | ||
154 | debug("Read from key file %.200s failed: %.100s", filename, | ||
155 | strerror(errno)); | ||
156 | buffer_free(&buffer); | ||
157 | close(f); | ||
158 | return 0; | ||
159 | } | ||
160 | close(f); | ||
161 | |||
162 | /* Check that it is at least big enought to contain the ID string. */ | ||
163 | if (len < strlen(AUTHFILE_ID_STRING) + 1) | ||
164 | { | ||
165 | debug("Bad key file %.200s.", filename); | ||
166 | buffer_free(&buffer); | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | /* Make sure it begins with the id string. Consume the id string from | ||
171 | the buffer. */ | ||
172 | for (i = 0; i < (unsigned int)strlen(AUTHFILE_ID_STRING) + 1; i++) | ||
173 | if (buffer_get_char(&buffer) != (unsigned char)AUTHFILE_ID_STRING[i]) | ||
174 | { | ||
175 | debug("Bad key file %.200s.", filename); | ||
176 | buffer_free(&buffer); | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | /* Skip cipher type and reserved data. */ | ||
181 | (void)buffer_get_char(&buffer); /* cipher type */ | ||
182 | (void)buffer_get_int(&buffer); /* reserved */ | ||
183 | |||
184 | /* Read the public key from the buffer. */ | ||
185 | buffer_get_int(&buffer); | ||
186 | pub->n = BN_new(); | ||
187 | buffer_get_bignum(&buffer, pub->n); | ||
188 | pub->e = BN_new(); | ||
189 | buffer_get_bignum(&buffer, pub->e); | ||
190 | if (comment_return) | ||
191 | *comment_return = buffer_get_string(&buffer, NULL); | ||
192 | /* The encrypted private part is not parsed by this function. */ | ||
193 | |||
194 | buffer_free(&buffer); | ||
195 | |||
196 | return 1; | ||
197 | } | ||
198 | |||
199 | /* Loads the private key from the file. Returns 0 if an error is encountered | ||
200 | (file does not exist or is not readable, or passphrase is bad). | ||
201 | This initializes the private key. */ | ||
202 | |||
203 | int | ||
204 | load_private_key(const char *filename, const char *passphrase, | ||
205 | RSA *prv, char **comment_return) | ||
206 | { | ||
207 | int f, i, check1, check2, cipher_type; | ||
208 | off_t len; | ||
209 | Buffer buffer, decrypted; | ||
210 | char *cp; | ||
211 | CipherContext cipher; | ||
212 | BN_CTX *ctx; | ||
213 | BIGNUM *aux; | ||
214 | struct stat st; | ||
215 | |||
216 | /* Read the file into the buffer. */ | ||
217 | f = open(filename, O_RDONLY); | ||
218 | if (f < 0) | ||
219 | return 0; | ||
220 | |||
221 | /* We assume we are called under uid of the owner of the file */ | ||
222 | if (fstat(f, &st) < 0 || | ||
223 | (st.st_uid != 0 && st.st_uid != getuid()) || | ||
224 | (st.st_mode & 077) != 0) { | ||
225 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
226 | error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); | ||
227 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
228 | error("Bad ownership or mode(0%3.3o) for '%s'.", | ||
229 | st.st_mode & 0777, filename); | ||
230 | error("It is recommended that your private key files are NOT accessible by others."); | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | len = lseek(f, (off_t)0, SEEK_END); | ||
235 | lseek(f, (off_t)0, SEEK_SET); | ||
236 | |||
237 | buffer_init(&buffer); | ||
238 | buffer_append_space(&buffer, &cp, len); | ||
239 | |||
240 | if (read(f, cp, (size_t)len) != (size_t)len) | ||
241 | { | ||
242 | debug("Read from key file %.200s failed: %.100s", filename, | ||
243 | strerror(errno)); | ||
244 | buffer_free(&buffer); | ||
245 | close(f); | ||
246 | return 0; | ||
247 | } | ||
248 | close(f); | ||
249 | |||
250 | /* Check that it is at least big enought to contain the ID string. */ | ||
251 | if (len < strlen(AUTHFILE_ID_STRING) + 1) | ||
252 | { | ||
253 | debug("Bad key file %.200s.", filename); | ||
254 | buffer_free(&buffer); | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | /* Make sure it begins with the id string. Consume the id string from | ||
259 | the buffer. */ | ||
260 | for (i = 0; i < (unsigned int)strlen(AUTHFILE_ID_STRING) + 1; i++) | ||
261 | if (buffer_get_char(&buffer) != (unsigned char)AUTHFILE_ID_STRING[i]) | ||
262 | { | ||
263 | debug("Bad key file %.200s.", filename); | ||
264 | buffer_free(&buffer); | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | /* Read cipher type. */ | ||
269 | cipher_type = buffer_get_char(&buffer); | ||
270 | (void)buffer_get_int(&buffer); /* Reserved data. */ | ||
271 | |||
272 | /* Read the public key from the buffer. */ | ||
273 | buffer_get_int(&buffer); | ||
274 | prv->n = BN_new(); | ||
275 | buffer_get_bignum(&buffer, prv->n); | ||
276 | prv->e = BN_new(); | ||
277 | buffer_get_bignum(&buffer, prv->e); | ||
278 | if (comment_return) | ||
279 | *comment_return = buffer_get_string(&buffer, NULL); | ||
280 | else | ||
281 | xfree(buffer_get_string(&buffer, NULL)); | ||
282 | |||
283 | /* Check that it is a supported cipher. */ | ||
284 | if (((cipher_mask() | SSH_CIPHER_NONE | SSH_AUTHFILE_CIPHER) & | ||
285 | (1 << cipher_type)) == 0) | ||
286 | { | ||
287 | debug("Unsupported cipher %.100s used in key file %.200s.", | ||
288 | cipher_name(cipher_type), filename); | ||
289 | buffer_free(&buffer); | ||
290 | goto fail; | ||
291 | } | ||
292 | |||
293 | /* Initialize space for decrypted data. */ | ||
294 | buffer_init(&decrypted); | ||
295 | buffer_append_space(&decrypted, &cp, buffer_len(&buffer)); | ||
296 | |||
297 | /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ | ||
298 | cipher_set_key_string(&cipher, cipher_type, passphrase, 0); | ||
299 | cipher_decrypt(&cipher, (unsigned char *)cp, | ||
300 | (unsigned char *)buffer_ptr(&buffer), | ||
301 | buffer_len(&buffer)); | ||
302 | |||
303 | buffer_free(&buffer); | ||
304 | |||
305 | check1 = buffer_get_char(&decrypted); | ||
306 | check2 = buffer_get_char(&decrypted); | ||
307 | if (check1 != buffer_get_char(&decrypted) || | ||
308 | check2 != buffer_get_char(&decrypted)) | ||
309 | { | ||
310 | if (strcmp(passphrase, "") != 0) | ||
311 | debug("Bad passphrase supplied for key file %.200s.", filename); | ||
312 | /* Bad passphrase. */ | ||
313 | buffer_free(&decrypted); | ||
314 | fail: | ||
315 | BN_clear_free(prv->n); | ||
316 | BN_clear_free(prv->e); | ||
317 | if (comment_return) | ||
318 | xfree(*comment_return); | ||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | /* Read the rest of the private key. */ | ||
323 | prv->d = BN_new(); | ||
324 | buffer_get_bignum(&decrypted, prv->d); | ||
325 | prv->iqmp = BN_new(); | ||
326 | buffer_get_bignum(&decrypted, prv->iqmp); /* u */ | ||
327 | /* in SSL and SSH p and q are exchanged */ | ||
328 | prv->q = BN_new(); | ||
329 | buffer_get_bignum(&decrypted, prv->q); /* p */ | ||
330 | prv->p = BN_new(); | ||
331 | buffer_get_bignum(&decrypted, prv->p); /* q */ | ||
332 | |||
333 | ctx = BN_CTX_new(); | ||
334 | aux = BN_new(); | ||
335 | |||
336 | BN_sub(aux, prv->q, BN_value_one()); | ||
337 | prv->dmq1 = BN_new(); | ||
338 | BN_mod(prv->dmq1, prv->d, aux, ctx); | ||
339 | |||
340 | BN_sub(aux, prv->p, BN_value_one()); | ||
341 | prv->dmp1 = BN_new(); | ||
342 | BN_mod(prv->dmp1, prv->d, aux, ctx); | ||
343 | |||
344 | BN_clear_free(aux); | ||
345 | BN_CTX_free(ctx); | ||
346 | |||
347 | buffer_free(&decrypted); | ||
348 | |||
349 | return 1; | ||
350 | } | ||
diff --git a/bufaux.c b/bufaux.c new file mode 100644 index 000000000..1ae39d67a --- /dev/null +++ b/bufaux.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | |||
3 | bufaux.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Mar 29 02:24:47 1995 ylo | ||
11 | |||
12 | Auxiliary functions for storing and retrieving various data types to/from | ||
13 | Buffers. | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: bufaux.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
19 | |||
20 | #include "ssh.h" | ||
21 | #include <openssl/bn.h> | ||
22 | #include "bufaux.h" | ||
23 | #include "xmalloc.h" | ||
24 | #include "getput.h" | ||
25 | |||
26 | /* Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed | ||
27 | by (bits+7)/8 bytes of binary data, msb first. */ | ||
28 | |||
29 | void | ||
30 | buffer_put_bignum(Buffer *buffer, BIGNUM *value) | ||
31 | { | ||
32 | int bits = BN_num_bits(value); | ||
33 | int bin_size = (bits + 7) / 8; | ||
34 | char *buf = xmalloc(bin_size); | ||
35 | int oi; | ||
36 | char msg[2]; | ||
37 | |||
38 | /* Get the value of in binary */ | ||
39 | oi = BN_bn2bin(value, buf); | ||
40 | assert(oi == bin_size); | ||
41 | |||
42 | /* Store the number of bits in the buffer in two bytes, msb first. */ | ||
43 | PUT_16BIT(msg, bits); | ||
44 | buffer_append(buffer, msg, 2); | ||
45 | /* Store the binary data. */ | ||
46 | buffer_append(buffer, buf, oi); | ||
47 | /* Clear the temporary data. */ | ||
48 | memset(buf, 0, bin_size); | ||
49 | xfree(buf); | ||
50 | } | ||
51 | |||
52 | /* Retrieves an BIGNUM from the buffer. */ | ||
53 | |||
54 | int | ||
55 | buffer_get_bignum(Buffer *buffer, BIGNUM *value) | ||
56 | { | ||
57 | int bits, bytes; | ||
58 | unsigned char buf[2], *bin; | ||
59 | |||
60 | /* Get the number for bits. */ | ||
61 | buffer_get(buffer, (char *)buf, 2); | ||
62 | bits = GET_16BIT(buf); | ||
63 | /* Compute the number of binary bytes that follow. */ | ||
64 | bytes = (bits + 7) / 8; | ||
65 | bin = xmalloc(bytes); | ||
66 | buffer_get(buffer, bin, bytes); | ||
67 | BN_bin2bn(bin, bytes, value); | ||
68 | xfree(bin); | ||
69 | |||
70 | return 2 + bytes; | ||
71 | } | ||
72 | |||
73 | /* Returns an integer from the buffer (4 bytes, msb first). */ | ||
74 | |||
75 | unsigned int buffer_get_int(Buffer *buffer) | ||
76 | { | ||
77 | unsigned char buf[4]; | ||
78 | buffer_get(buffer, (char *)buf, 4); | ||
79 | return GET_32BIT(buf); | ||
80 | } | ||
81 | |||
82 | /* Stores an integer in the buffer in 4 bytes, msb first. */ | ||
83 | |||
84 | void buffer_put_int(Buffer *buffer, unsigned int value) | ||
85 | { | ||
86 | char buf[4]; | ||
87 | PUT_32BIT(buf, value); | ||
88 | buffer_append(buffer, buf, 4); | ||
89 | } | ||
90 | |||
91 | /* Returns an arbitrary binary string from the buffer. The string cannot | ||
92 | be longer than 256k. The returned value points to memory allocated | ||
93 | with xmalloc; it is the responsibility of the calling function to free | ||
94 | the data. If length_ptr is non-NULL, the length of the returned data | ||
95 | will be stored there. A null character will be automatically appended | ||
96 | to the returned string, and is not counted in length. */ | ||
97 | |||
98 | char *buffer_get_string(Buffer *buffer, unsigned int *length_ptr) | ||
99 | { | ||
100 | unsigned int len; | ||
101 | char *value; | ||
102 | /* Get the length. */ | ||
103 | len = buffer_get_int(buffer); | ||
104 | if (len > 256*1024) | ||
105 | fatal("Received packet with bad string length %d", len); | ||
106 | /* Allocate space for the string. Add one byte for a null character. */ | ||
107 | value = xmalloc(len + 1); | ||
108 | /* Get the string. */ | ||
109 | buffer_get(buffer, value, len); | ||
110 | /* Append a null character to make processing easier. */ | ||
111 | value[len] = 0; | ||
112 | /* Optionally return the length of the string. */ | ||
113 | if (length_ptr) | ||
114 | *length_ptr = len; | ||
115 | return value; | ||
116 | } | ||
117 | |||
118 | /* Stores and arbitrary binary string in the buffer. */ | ||
119 | |||
120 | void buffer_put_string(Buffer *buffer, const void *buf, unsigned int len) | ||
121 | { | ||
122 | buffer_put_int(buffer, len); | ||
123 | buffer_append(buffer, buf, len); | ||
124 | } | ||
125 | |||
126 | /* Returns a character from the buffer (0 - 255). */ | ||
127 | |||
128 | int buffer_get_char(Buffer *buffer) | ||
129 | { | ||
130 | char ch; | ||
131 | buffer_get(buffer, &ch, 1); | ||
132 | return (unsigned char)ch; | ||
133 | } | ||
134 | |||
135 | /* Stores a character in the buffer. */ | ||
136 | |||
137 | void buffer_put_char(Buffer *buffer, int value) | ||
138 | { | ||
139 | char ch = value; | ||
140 | buffer_append(buffer, &ch, 1); | ||
141 | } | ||
diff --git a/bufaux.h b/bufaux.h new file mode 100644 index 000000000..bfc668485 --- /dev/null +++ b/bufaux.h | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | |||
3 | bufaux.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Mar 29 02:18:23 1995 ylo | ||
11 | |||
12 | */ | ||
13 | |||
14 | /* RCSID("$Id: bufaux.h,v 1.1 1999/10/27 03:42:43 damien Exp $"); */ | ||
15 | |||
16 | #ifndef BUFAUX_H | ||
17 | #define BUFAUX_H | ||
18 | |||
19 | #include "buffer.h" | ||
20 | |||
21 | /* Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed | ||
22 | by (bits+7)/8 bytes of binary data, msb first. */ | ||
23 | void buffer_put_bignum(Buffer *buffer, BIGNUM *value); | ||
24 | |||
25 | /* Retrieves an BIGNUM from the buffer. */ | ||
26 | int buffer_get_bignum(Buffer *buffer, BIGNUM *value); | ||
27 | |||
28 | /* Returns an integer from the buffer (4 bytes, msb first). */ | ||
29 | unsigned int buffer_get_int(Buffer *buffer); | ||
30 | |||
31 | /* Stores an integer in the buffer in 4 bytes, msb first. */ | ||
32 | void buffer_put_int(Buffer *buffer, unsigned int value); | ||
33 | |||
34 | /* Returns a character from the buffer (0 - 255). */ | ||
35 | int buffer_get_char(Buffer *buffer); | ||
36 | |||
37 | /* Stores a character in the buffer. */ | ||
38 | void buffer_put_char(Buffer *buffer, int value); | ||
39 | |||
40 | /* Returns an arbitrary binary string from the buffer. The string cannot | ||
41 | be longer than 256k. The returned value points to memory allocated | ||
42 | with xmalloc; it is the responsibility of the calling function to free | ||
43 | the data. If length_ptr is non-NULL, the length of the returned data | ||
44 | will be stored there. A null character will be automatically appended | ||
45 | to the returned string, and is not counted in length. */ | ||
46 | char *buffer_get_string(Buffer *buffer, unsigned int *length_ptr); | ||
47 | |||
48 | /* Stores and arbitrary binary string in the buffer. */ | ||
49 | void buffer_put_string(Buffer *buffer, const void *buf, unsigned int len); | ||
50 | |||
51 | #endif /* BUFAUX_H */ | ||
diff --git a/buffer.c b/buffer.c new file mode 100644 index 000000000..e183d1017 --- /dev/null +++ b/buffer.c | |||
@@ -0,0 +1,150 @@ | |||
1 | /* | ||
2 | |||
3 | buffer.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Mar 18 04:15:33 1995 ylo | ||
11 | |||
12 | Functions for manipulating fifo buffers (that can grow if needed). | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: buffer.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
18 | |||
19 | #include "xmalloc.h" | ||
20 | #include "buffer.h" | ||
21 | #include "ssh.h" | ||
22 | |||
23 | /* Initializes the buffer structure. */ | ||
24 | |||
25 | void buffer_init(Buffer *buffer) | ||
26 | { | ||
27 | buffer->alloc = 4096; | ||
28 | buffer->buf = xmalloc(buffer->alloc); | ||
29 | buffer->offset = 0; | ||
30 | buffer->end = 0; | ||
31 | } | ||
32 | |||
33 | /* Frees any memory used for the buffer. */ | ||
34 | |||
35 | void buffer_free(Buffer *buffer) | ||
36 | { | ||
37 | memset(buffer->buf, 0, buffer->alloc); | ||
38 | xfree(buffer->buf); | ||
39 | } | ||
40 | |||
41 | /* Clears any data from the buffer, making it empty. This does not actually | ||
42 | zero the memory. */ | ||
43 | |||
44 | void buffer_clear(Buffer *buffer) | ||
45 | { | ||
46 | buffer->offset = 0; | ||
47 | buffer->end = 0; | ||
48 | } | ||
49 | |||
50 | /* Appends data to the buffer, expanding it if necessary. */ | ||
51 | |||
52 | void buffer_append(Buffer *buffer, const char *data, unsigned int len) | ||
53 | { | ||
54 | char *cp; | ||
55 | buffer_append_space(buffer, &cp, len); | ||
56 | memcpy(cp, data, len); | ||
57 | } | ||
58 | |||
59 | /* Appends space to the buffer, expanding the buffer if necessary. | ||
60 | This does not actually copy the data into the buffer, but instead | ||
61 | returns a pointer to the allocated region. */ | ||
62 | |||
63 | void buffer_append_space(Buffer *buffer, char **datap, unsigned int len) | ||
64 | { | ||
65 | /* If the buffer is empty, start using it from the beginning. */ | ||
66 | if (buffer->offset == buffer->end) | ||
67 | { | ||
68 | buffer->offset = 0; | ||
69 | buffer->end = 0; | ||
70 | } | ||
71 | |||
72 | restart: | ||
73 | /* If there is enough space to store all data, store it now. */ | ||
74 | if (buffer->end + len < buffer->alloc) | ||
75 | { | ||
76 | *datap = buffer->buf + buffer->end; | ||
77 | buffer->end += len; | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | /* If the buffer is quite empty, but all data is at the end, move the | ||
82 | data to the beginning and retry. */ | ||
83 | if (buffer->offset > buffer->alloc / 2) | ||
84 | { | ||
85 | memmove(buffer->buf, buffer->buf + buffer->offset, | ||
86 | buffer->end - buffer->offset); | ||
87 | buffer->end -= buffer->offset; | ||
88 | buffer->offset = 0; | ||
89 | goto restart; | ||
90 | } | ||
91 | |||
92 | /* Increase the size of the buffer and retry. */ | ||
93 | buffer->alloc += len + 32768; | ||
94 | buffer->buf = xrealloc(buffer->buf, buffer->alloc); | ||
95 | goto restart; | ||
96 | } | ||
97 | |||
98 | /* Returns the number of bytes of data in the buffer. */ | ||
99 | |||
100 | unsigned int buffer_len(Buffer *buffer) | ||
101 | { | ||
102 | return buffer->end - buffer->offset; | ||
103 | } | ||
104 | |||
105 | /* Gets data from the beginning of the buffer. */ | ||
106 | |||
107 | void buffer_get(Buffer *buffer, char *buf, unsigned int len) | ||
108 | { | ||
109 | if (len > buffer->end - buffer->offset) | ||
110 | fatal("buffer_get trying to get more bytes than in buffer"); | ||
111 | memcpy(buf, buffer->buf + buffer->offset, len); | ||
112 | buffer->offset += len; | ||
113 | } | ||
114 | |||
115 | /* Consumes the given number of bytes from the beginning of the buffer. */ | ||
116 | |||
117 | void buffer_consume(Buffer *buffer, unsigned int bytes) | ||
118 | { | ||
119 | if (bytes > buffer->end - buffer->offset) | ||
120 | fatal("buffer_get trying to get more bytes than in buffer"); | ||
121 | buffer->offset += bytes; | ||
122 | } | ||
123 | |||
124 | /* Consumes the given number of bytes from the end of the buffer. */ | ||
125 | |||
126 | void buffer_consume_end(Buffer *buffer, unsigned int bytes) | ||
127 | { | ||
128 | if (bytes > buffer->end - buffer->offset) | ||
129 | fatal("buffer_get trying to get more bytes than in buffer"); | ||
130 | buffer->end -= bytes; | ||
131 | } | ||
132 | |||
133 | /* Returns a pointer to the first used byte in the buffer. */ | ||
134 | |||
135 | char *buffer_ptr(Buffer *buffer) | ||
136 | { | ||
137 | return buffer->buf + buffer->offset; | ||
138 | } | ||
139 | |||
140 | /* Dumps the contents of the buffer to stderr. */ | ||
141 | |||
142 | void buffer_dump(Buffer *buffer) | ||
143 | { | ||
144 | int i; | ||
145 | unsigned char *ucp = (unsigned char *)buffer->buf; | ||
146 | |||
147 | for (i = buffer->offset; i < buffer->end; i++) | ||
148 | fprintf(stderr, " %02x", ucp[i]); | ||
149 | fprintf(stderr, "\n"); | ||
150 | } | ||
diff --git a/buffer.h b/buffer.h new file mode 100644 index 000000000..d0369dc35 --- /dev/null +++ b/buffer.h | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | |||
3 | buffer.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Mar 18 04:12:25 1995 ylo | ||
11 | |||
12 | Code for manipulating FIFO buffers. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: buffer.h,v 1.1 1999/10/27 03:42:43 damien Exp $"); */ | ||
17 | |||
18 | #ifndef BUFFER_H | ||
19 | #define BUFFER_H | ||
20 | |||
21 | typedef struct | ||
22 | { | ||
23 | char *buf; /* Buffer for data. */ | ||
24 | unsigned int alloc; /* Number of bytes allocated for data. */ | ||
25 | unsigned int offset; /* Offset of first byte containing data. */ | ||
26 | unsigned int end; /* Offset of last byte containing data. */ | ||
27 | } Buffer; | ||
28 | |||
29 | /* Initializes the buffer structure. */ | ||
30 | void buffer_init(Buffer *buffer); | ||
31 | |||
32 | /* Frees any memory used for the buffer. */ | ||
33 | void buffer_free(Buffer *buffer); | ||
34 | |||
35 | /* Clears any data from the buffer, making it empty. This does not actually | ||
36 | zero the memory. */ | ||
37 | void buffer_clear(Buffer *buffer); | ||
38 | |||
39 | /* Appends data to the buffer, expanding it if necessary. */ | ||
40 | void buffer_append(Buffer *buffer, const char *data, unsigned int len); | ||
41 | |||
42 | /* Appends space to the buffer, expanding the buffer if necessary. | ||
43 | This does not actually copy the data into the buffer, but instead | ||
44 | returns a pointer to the allocated region. */ | ||
45 | void buffer_append_space(Buffer *buffer, char **datap, unsigned int len); | ||
46 | |||
47 | /* Returns the number of bytes of data in the buffer. */ | ||
48 | unsigned int buffer_len(Buffer *buffer); | ||
49 | |||
50 | /* Gets data from the beginning of the buffer. */ | ||
51 | void buffer_get(Buffer *buffer, char *buf, unsigned int len); | ||
52 | |||
53 | /* Consumes the given number of bytes from the beginning of the buffer. */ | ||
54 | void buffer_consume(Buffer *buffer, unsigned int bytes); | ||
55 | |||
56 | /* Consumes the given number of bytes from the end of the buffer. */ | ||
57 | void buffer_consume_end(Buffer *buffer, unsigned int bytes); | ||
58 | |||
59 | /* Returns a pointer to the first used byte in the buffer. */ | ||
60 | char *buffer_ptr(Buffer *buffer); | ||
61 | |||
62 | /* Dumps the contents of the buffer to stderr in hex. This intended for | ||
63 | debugging purposes only. */ | ||
64 | void buffer_dump(Buffer *buffer); | ||
65 | |||
66 | #endif /* BUFFER_H */ | ||
diff --git a/canohost.c b/canohost.c new file mode 100644 index 000000000..85d97292b --- /dev/null +++ b/canohost.c | |||
@@ -0,0 +1,234 @@ | |||
1 | /* | ||
2 | |||
3 | canohost.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sun Jul 2 17:52:22 1995 ylo | ||
11 | |||
12 | Functions for returning the canonical host name of the remote site. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: canohost.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
18 | |||
19 | #include "packet.h" | ||
20 | #include "xmalloc.h" | ||
21 | #include "ssh.h" | ||
22 | |||
23 | /* Return the canonical name of the host at the other end of the socket. | ||
24 | The caller should free the returned string with xfree. */ | ||
25 | |||
26 | char *get_remote_hostname(int socket) | ||
27 | { | ||
28 | struct sockaddr_in from; | ||
29 | int fromlen, i; | ||
30 | struct hostent *hp; | ||
31 | char name[MAXHOSTNAMELEN]; | ||
32 | |||
33 | /* Get IP address of client. */ | ||
34 | fromlen = sizeof(from); | ||
35 | memset(&from, 0, sizeof(from)); | ||
36 | if (getpeername(socket, (struct sockaddr *)&from, &fromlen) < 0) | ||
37 | { | ||
38 | error("getpeername failed: %.100s", strerror(errno)); | ||
39 | strlcpy(name, "UNKNOWN", sizeof name); | ||
40 | goto check_ip_options; | ||
41 | } | ||
42 | |||
43 | /* Map the IP address to a host name. */ | ||
44 | hp = gethostbyaddr((char *)&from.sin_addr, sizeof(struct in_addr), | ||
45 | from.sin_family); | ||
46 | if (hp) | ||
47 | { | ||
48 | /* Got host name, find canonic host name. */ | ||
49 | if (strchr(hp->h_name, '.') != 0) | ||
50 | strlcpy(name, hp->h_name, sizeof(name)); | ||
51 | else if (hp->h_aliases != 0 | ||
52 | && hp->h_aliases[0] != 0 | ||
53 | && strchr(hp->h_aliases[0], '.') != 0) | ||
54 | strlcpy(name, hp->h_aliases[0], sizeof(name)); | ||
55 | else | ||
56 | strlcpy(name, hp->h_name, sizeof(name)); | ||
57 | |||
58 | /* Convert it to all lowercase (which is expected by the rest of this | ||
59 | software). */ | ||
60 | for (i = 0; name[i]; i++) | ||
61 | if (isupper(name[i])) | ||
62 | name[i] = tolower(name[i]); | ||
63 | |||
64 | /* Map it back to an IP address and check that the given address actually | ||
65 | is an address of this host. This is necessary because anyone with | ||
66 | access to a name server can define arbitrary names for an IP address. | ||
67 | Mapping from name to IP address can be trusted better (but can still | ||
68 | be fooled if the intruder has access to the name server of the | ||
69 | domain). */ | ||
70 | hp = gethostbyname(name); | ||
71 | if (!hp) | ||
72 | { | ||
73 | log("reverse mapping checking gethostbyname for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name); | ||
74 | strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); | ||
75 | goto check_ip_options; | ||
76 | } | ||
77 | /* Look for the address from the list of addresses. */ | ||
78 | for (i = 0; hp->h_addr_list[i]; i++) | ||
79 | if (memcmp(hp->h_addr_list[i], &from.sin_addr, sizeof(from.sin_addr)) | ||
80 | == 0) | ||
81 | break; | ||
82 | /* If we reached the end of the list, the address was not there. */ | ||
83 | if (!hp->h_addr_list[i]) | ||
84 | { | ||
85 | /* Address not found for the host name. */ | ||
86 | log("Address %.100s maps to %.600s, but this does not map back to the address - POSSIBLE BREAKIN ATTEMPT!", | ||
87 | inet_ntoa(from.sin_addr), name); | ||
88 | strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); | ||
89 | goto check_ip_options; | ||
90 | } | ||
91 | /* Address was found for the host name. We accept the host name. */ | ||
92 | } | ||
93 | else | ||
94 | { | ||
95 | /* Host name not found. Use ascii representation of the address. */ | ||
96 | strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); | ||
97 | log("Could not reverse map address %.100s.", name); | ||
98 | } | ||
99 | |||
100 | check_ip_options: | ||
101 | |||
102 | /* If IP options are supported, make sure there are none (log and clear | ||
103 | them if any are found). Basically we are worried about source routing; | ||
104 | it can be used to pretend you are somebody (ip-address) you are not. | ||
105 | That itself may be "almost acceptable" under certain circumstances, | ||
106 | but rhosts autentication is useless if source routing is accepted. | ||
107 | Notice also that if we just dropped source routing here, the other | ||
108 | side could use IP spoofing to do rest of the interaction and could still | ||
109 | bypass security. So we exit here if we detect any IP options. */ | ||
110 | { | ||
111 | unsigned char options[200], *ucp; | ||
112 | char text[1024], *cp; | ||
113 | int option_size, ipproto; | ||
114 | struct protoent *ip; | ||
115 | |||
116 | if ((ip = getprotobyname("ip")) != NULL) | ||
117 | ipproto = ip->p_proto; | ||
118 | else | ||
119 | ipproto = IPPROTO_IP; | ||
120 | option_size = sizeof(options); | ||
121 | if (getsockopt(0, ipproto, IP_OPTIONS, (char *)options, | ||
122 | &option_size) >= 0 && option_size != 0) | ||
123 | { | ||
124 | cp = text; | ||
125 | /* Note: "text" buffer must be at least 3x as big as options. */ | ||
126 | for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3) | ||
127 | sprintf(cp, " %2.2x", *ucp); | ||
128 | log("Connection from %.100s with IP options:%.800s", | ||
129 | inet_ntoa(from.sin_addr), text); | ||
130 | packet_disconnect("Connection from %.100s with IP options:%.800s", | ||
131 | inet_ntoa(from.sin_addr), text); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | return xstrdup(name); | ||
136 | } | ||
137 | |||
138 | static char *canonical_host_name = NULL; | ||
139 | static char *canonical_host_ip = NULL; | ||
140 | |||
141 | /* Return the canonical name of the host in the other side of the current | ||
142 | connection. The host name is cached, so it is efficient to call this | ||
143 | several times. */ | ||
144 | |||
145 | const char *get_canonical_hostname() | ||
146 | { | ||
147 | /* Check if we have previously retrieved this same name. */ | ||
148 | if (canonical_host_name != NULL) | ||
149 | return canonical_host_name; | ||
150 | |||
151 | /* Get the real hostname if socket; otherwise return UNKNOWN. */ | ||
152 | if (packet_get_connection_in() == packet_get_connection_out()) | ||
153 | canonical_host_name = get_remote_hostname(packet_get_connection_in()); | ||
154 | else | ||
155 | canonical_host_name = xstrdup("UNKNOWN"); | ||
156 | |||
157 | return canonical_host_name; | ||
158 | } | ||
159 | |||
160 | /* Returns the IP-address of the remote host as a string. The returned | ||
161 | string need not be freed. */ | ||
162 | |||
163 | const char *get_remote_ipaddr() | ||
164 | { | ||
165 | struct sockaddr_in from; | ||
166 | int fromlen, socket; | ||
167 | |||
168 | /* Check if we have previously retrieved this same name. */ | ||
169 | if (canonical_host_ip != NULL) | ||
170 | return canonical_host_ip; | ||
171 | |||
172 | /* If not a socket, return UNKNOWN. */ | ||
173 | if (packet_get_connection_in() != packet_get_connection_out()) | ||
174 | { | ||
175 | canonical_host_ip = xstrdup("UNKNOWN"); | ||
176 | return canonical_host_ip; | ||
177 | } | ||
178 | |||
179 | /* Get client socket. */ | ||
180 | socket = packet_get_connection_in(); | ||
181 | |||
182 | /* Get IP address of client. */ | ||
183 | fromlen = sizeof(from); | ||
184 | memset(&from, 0, sizeof(from)); | ||
185 | if (getpeername(socket, (struct sockaddr *)&from, &fromlen) < 0) | ||
186 | { | ||
187 | error("getpeername failed: %.100s", strerror(errno)); | ||
188 | return NULL; | ||
189 | } | ||
190 | |||
191 | /* Get the IP address in ascii. */ | ||
192 | canonical_host_ip = xstrdup(inet_ntoa(from.sin_addr)); | ||
193 | |||
194 | /* Return ip address string. */ | ||
195 | return canonical_host_ip; | ||
196 | } | ||
197 | |||
198 | /* Returns the port of the peer of the socket. */ | ||
199 | |||
200 | int get_peer_port(int sock) | ||
201 | { | ||
202 | struct sockaddr_in from; | ||
203 | int fromlen; | ||
204 | |||
205 | /* Get IP address of client. */ | ||
206 | fromlen = sizeof(from); | ||
207 | memset(&from, 0, sizeof(from)); | ||
208 | if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0) | ||
209 | { | ||
210 | error("getpeername failed: %.100s", strerror(errno)); | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | /* Return port number. */ | ||
215 | return ntohs(from.sin_port); | ||
216 | } | ||
217 | |||
218 | /* Returns the port number of the remote host. */ | ||
219 | |||
220 | int get_remote_port() | ||
221 | { | ||
222 | int socket; | ||
223 | |||
224 | /* If the connection is not a socket, return 65535. This is intentionally | ||
225 | chosen to be an unprivileged port number. */ | ||
226 | if (packet_get_connection_in() != packet_get_connection_out()) | ||
227 | return 65535; | ||
228 | |||
229 | /* Get client socket. */ | ||
230 | socket = packet_get_connection_in(); | ||
231 | |||
232 | /* Get and return the peer port number. */ | ||
233 | return get_peer_port(socket); | ||
234 | } | ||
diff --git a/channels.c b/channels.c new file mode 100644 index 000000000..38a65a07f --- /dev/null +++ b/channels.c | |||
@@ -0,0 +1,1500 @@ | |||
1 | /* | ||
2 | |||
3 | channels.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Fri Mar 24 16:35:24 1995 ylo | ||
11 | |||
12 | This file contains functions for generic socket connection forwarding. | ||
13 | There is also code for initiating connection forwarding for X11 connections, | ||
14 | arbitrary tcp/ip connections, and the authentication agent connection. | ||
15 | |||
16 | */ | ||
17 | |||
18 | #include "includes.h" | ||
19 | RCSID("$Id: channels.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
20 | |||
21 | #include "ssh.h" | ||
22 | #include "packet.h" | ||
23 | #include "xmalloc.h" | ||
24 | #include "buffer.h" | ||
25 | #include "authfd.h" | ||
26 | #include "uidswap.h" | ||
27 | #include "servconf.h" | ||
28 | |||
29 | #include "channels.h" | ||
30 | #include "nchan.h" | ||
31 | #include "compat.h" | ||
32 | |||
33 | /* Maximum number of fake X11 displays to try. */ | ||
34 | #define MAX_DISPLAYS 1000 | ||
35 | |||
36 | /* Max len of agent socket */ | ||
37 | #define MAX_SOCKET_NAME 100 | ||
38 | |||
39 | /* Pointer to an array containing all allocated channels. The array is | ||
40 | dynamically extended as needed. */ | ||
41 | static Channel *channels = NULL; | ||
42 | |||
43 | /* Size of the channel array. All slots of the array must always be | ||
44 | initialized (at least the type field); unused slots are marked with | ||
45 | type SSH_CHANNEL_FREE. */ | ||
46 | static int channels_alloc = 0; | ||
47 | |||
48 | /* Maximum file descriptor value used in any of the channels. This is updated | ||
49 | in channel_allocate. */ | ||
50 | static int channel_max_fd_value = 0; | ||
51 | |||
52 | /* Name and directory of socket for authentication agent forwarding. */ | ||
53 | static char *channel_forwarded_auth_socket_name = NULL; | ||
54 | static char *channel_forwarded_auth_socket_dir = NULL; | ||
55 | |||
56 | /* Saved X11 authentication protocol name. */ | ||
57 | char *x11_saved_proto = NULL; | ||
58 | |||
59 | /* Saved X11 authentication data. This is the real data. */ | ||
60 | char *x11_saved_data = NULL; | ||
61 | unsigned int x11_saved_data_len = 0; | ||
62 | |||
63 | /* Fake X11 authentication data. This is what the server will be sending | ||
64 | us; we should replace any occurrences of this by the real data. */ | ||
65 | char *x11_fake_data = NULL; | ||
66 | unsigned int x11_fake_data_len; | ||
67 | |||
68 | /* Data structure for storing which hosts are permitted for forward requests. | ||
69 | The local sides of any remote forwards are stored in this array to prevent | ||
70 | a corrupt remote server from accessing arbitrary TCP/IP ports on our | ||
71 | local network (which might be behind a firewall). */ | ||
72 | typedef struct | ||
73 | { | ||
74 | char *host; /* Host name. */ | ||
75 | int port; /* Port number. */ | ||
76 | } ForwardPermission; | ||
77 | |||
78 | /* List of all permitted host/port pairs to connect. */ | ||
79 | static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; | ||
80 | /* Number of permitted host/port pairs in the array. */ | ||
81 | static int num_permitted_opens = 0; | ||
82 | /* If this is true, all opens are permitted. This is the case on the | ||
83 | server on which we have to trust the client anyway, and the user could | ||
84 | do anything after logging in anyway. */ | ||
85 | static int all_opens_permitted = 0; | ||
86 | |||
87 | /* This is set to true if both sides support SSH_PROTOFLAG_HOST_IN_FWD_OPEN. */ | ||
88 | static int have_hostname_in_open = 0; | ||
89 | |||
90 | /* Sets specific protocol options. */ | ||
91 | |||
92 | void channel_set_options(int hostname_in_open) | ||
93 | { | ||
94 | have_hostname_in_open = hostname_in_open; | ||
95 | } | ||
96 | |||
97 | /* Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually | ||
98 | called by the server, because the user could connect to any port anyway, | ||
99 | and the server has no way to know but to trust the client anyway. */ | ||
100 | |||
101 | void channel_permit_all_opens() | ||
102 | { | ||
103 | all_opens_permitted = 1; | ||
104 | } | ||
105 | |||
106 | /* Allocate a new channel object and set its type and socket. | ||
107 | This will cause remote_name to be freed. */ | ||
108 | |||
109 | int channel_allocate(int type, int sock, char *remote_name) | ||
110 | { | ||
111 | int i, old_channels; | ||
112 | |||
113 | /* Update the maximum file descriptor value. */ | ||
114 | if (sock > channel_max_fd_value) | ||
115 | channel_max_fd_value = sock; | ||
116 | |||
117 | /* Do initial allocation if this is the first call. */ | ||
118 | if (channels_alloc == 0) | ||
119 | { | ||
120 | channels_alloc = 10; | ||
121 | channels = xmalloc(channels_alloc * sizeof(Channel)); | ||
122 | for (i = 0; i < channels_alloc; i++) | ||
123 | channels[i].type = SSH_CHANNEL_FREE; | ||
124 | |||
125 | /* Kludge: arrange a call to channel_stop_listening if we terminate | ||
126 | with fatal(). */ | ||
127 | fatal_add_cleanup((void (*)(void *))channel_stop_listening, NULL); | ||
128 | } | ||
129 | |||
130 | /* Try to find a free slot where to put the new channel. */ | ||
131 | for (i = 0; i < channels_alloc; i++) | ||
132 | if (channels[i].type == SSH_CHANNEL_FREE) | ||
133 | { | ||
134 | /* Found a free slot. Initialize the fields and return its number. */ | ||
135 | buffer_init(&channels[i].input); | ||
136 | buffer_init(&channels[i].output); | ||
137 | channels[i].self = i; | ||
138 | channels[i].type = type; | ||
139 | channels[i].x11 = 0; | ||
140 | channels[i].sock = sock; | ||
141 | channels[i].remote_id = -1; | ||
142 | channels[i].remote_name = remote_name; | ||
143 | chan_init_iostates(&channels[i]); | ||
144 | return i; | ||
145 | } | ||
146 | |||
147 | /* There are no free slots. Must expand the array. */ | ||
148 | old_channels = channels_alloc; | ||
149 | channels_alloc += 10; | ||
150 | channels = xrealloc(channels, channels_alloc * sizeof(Channel)); | ||
151 | for (i = old_channels; i < channels_alloc; i++) | ||
152 | channels[i].type = SSH_CHANNEL_FREE; | ||
153 | |||
154 | /* We know that the next one after the old maximum channel number is now | ||
155 | available. Initialize and return its number. */ | ||
156 | buffer_init(&channels[old_channels].input); | ||
157 | buffer_init(&channels[old_channels].output); | ||
158 | channels[old_channels].self = old_channels; | ||
159 | channels[old_channels].type = type; | ||
160 | channels[old_channels].x11 = 0; | ||
161 | channels[old_channels].sock = sock; | ||
162 | channels[old_channels].remote_id = -1; | ||
163 | channels[old_channels].remote_name = remote_name; | ||
164 | chan_init_iostates(&channels[old_channels]); | ||
165 | return old_channels; | ||
166 | } | ||
167 | |||
168 | /* Free the channel and close its socket. */ | ||
169 | |||
170 | void channel_free(int channel) | ||
171 | { | ||
172 | assert(channel >= 0 && channel < channels_alloc && | ||
173 | channels[channel].type != SSH_CHANNEL_FREE); | ||
174 | if(compat13) | ||
175 | shutdown(channels[channel].sock, SHUT_RDWR); | ||
176 | close(channels[channel].sock); | ||
177 | buffer_free(&channels[channel].input); | ||
178 | buffer_free(&channels[channel].output); | ||
179 | channels[channel].type = SSH_CHANNEL_FREE; | ||
180 | if (channels[channel].remote_name) | ||
181 | { | ||
182 | xfree(channels[channel].remote_name); | ||
183 | channels[channel].remote_name = NULL; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | /* This is called just before select() to add any bits relevant to | ||
188 | channels in the select bitmasks. */ | ||
189 | |||
190 | void channel_prepare_select(fd_set *readset, fd_set *writeset) | ||
191 | { | ||
192 | int i; | ||
193 | Channel *ch; | ||
194 | unsigned char *ucp; | ||
195 | unsigned int proto_len, data_len; | ||
196 | |||
197 | for (i = 0; i < channels_alloc; i++) | ||
198 | { | ||
199 | ch = &channels[i]; | ||
200 | redo: | ||
201 | switch (ch->type) | ||
202 | { | ||
203 | case SSH_CHANNEL_X11_LISTENER: | ||
204 | case SSH_CHANNEL_PORT_LISTENER: | ||
205 | case SSH_CHANNEL_AUTH_SOCKET: | ||
206 | FD_SET(ch->sock, readset); | ||
207 | break; | ||
208 | |||
209 | case SSH_CHANNEL_OPEN: | ||
210 | if(compat13){ | ||
211 | if (buffer_len(&ch->input) < 32768) | ||
212 | FD_SET(ch->sock, readset); | ||
213 | if (buffer_len(&ch->output) > 0) | ||
214 | FD_SET(ch->sock, writeset); | ||
215 | break; | ||
216 | } | ||
217 | /* test whether sockets are 'alive' for read/write */ | ||
218 | if (ch->istate == CHAN_INPUT_OPEN) | ||
219 | if (buffer_len(&ch->input) < 32768) | ||
220 | FD_SET(ch->sock, readset); | ||
221 | if (ch->ostate == CHAN_OUTPUT_OPEN || ch->ostate == CHAN_OUTPUT_WAIT_DRAIN){ | ||
222 | if (buffer_len(&ch->output) > 0){ | ||
223 | FD_SET(ch->sock, writeset); | ||
224 | }else if(ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { | ||
225 | chan_obuf_empty(ch); | ||
226 | } | ||
227 | } | ||
228 | break; | ||
229 | |||
230 | case SSH_CHANNEL_INPUT_DRAINING: | ||
231 | if (!compat13) | ||
232 | fatal("cannot happen: IN_DRAIN"); | ||
233 | if (buffer_len(&ch->input) == 0) | ||
234 | { | ||
235 | packet_start(SSH_MSG_CHANNEL_CLOSE); | ||
236 | packet_put_int(ch->remote_id); | ||
237 | packet_send(); | ||
238 | ch->type = SSH_CHANNEL_CLOSED; | ||
239 | debug("Closing channel %d after input drain.", i); | ||
240 | break; | ||
241 | } | ||
242 | break; | ||
243 | |||
244 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
245 | if (!compat13) | ||
246 | fatal("cannot happen: OUT_DRAIN"); | ||
247 | if (buffer_len(&ch->output) == 0) | ||
248 | { | ||
249 | /* debug("Freeing channel %d after output drain.", i); */ | ||
250 | channel_free(i); | ||
251 | break; | ||
252 | } | ||
253 | FD_SET(ch->sock, writeset); | ||
254 | break; | ||
255 | |||
256 | case SSH_CHANNEL_X11_OPEN: | ||
257 | /* This is a special state for X11 authentication spoofing. An | ||
258 | opened X11 connection (when authentication spoofing is being | ||
259 | done) remains in this state until the first packet has been | ||
260 | completely read. The authentication data in that packet is | ||
261 | then substituted by the real data if it matches the fake data, | ||
262 | and the channel is put into normal mode. */ | ||
263 | |||
264 | /* Check if the fixed size part of the packet is in buffer. */ | ||
265 | if (buffer_len(&ch->output) < 12) | ||
266 | break; | ||
267 | |||
268 | /* Parse the lengths of variable-length fields. */ | ||
269 | ucp = (unsigned char *)buffer_ptr(&ch->output); | ||
270 | if (ucp[0] == 0x42) | ||
271 | { /* Byte order MSB first. */ | ||
272 | proto_len = 256 * ucp[6] + ucp[7]; | ||
273 | data_len = 256 * ucp[8] + ucp[9]; | ||
274 | } | ||
275 | else | ||
276 | if (ucp[0] == 0x6c) | ||
277 | { /* Byte order LSB first. */ | ||
278 | proto_len = ucp[6] + 256 * ucp[7]; | ||
279 | data_len = ucp[8] + 256 * ucp[9]; | ||
280 | } | ||
281 | else | ||
282 | { | ||
283 | debug("Initial X11 packet contains bad byte order byte: 0x%x", | ||
284 | ucp[0]); | ||
285 | ch->type = SSH_CHANNEL_OPEN; | ||
286 | goto reject; | ||
287 | } | ||
288 | |||
289 | /* Check if the whole packet is in buffer. */ | ||
290 | if (buffer_len(&ch->output) < | ||
291 | 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) | ||
292 | break; | ||
293 | |||
294 | /* Check if authentication protocol matches. */ | ||
295 | if (proto_len != strlen(x11_saved_proto) || | ||
296 | memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) | ||
297 | { | ||
298 | debug("X11 connection uses different authentication protocol."); | ||
299 | ch->type = SSH_CHANNEL_OPEN; | ||
300 | goto reject; | ||
301 | } | ||
302 | |||
303 | /* Check if authentication data matches our fake data. */ | ||
304 | if (data_len != x11_fake_data_len || | ||
305 | memcmp(ucp + 12 + ((proto_len + 3) & ~3), | ||
306 | x11_fake_data, x11_fake_data_len) != 0) | ||
307 | { | ||
308 | debug("X11 auth data does not match fake data."); | ||
309 | ch->type = SSH_CHANNEL_OPEN; | ||
310 | goto reject; | ||
311 | } | ||
312 | |||
313 | /* Received authentication protocol and data match our fake data. | ||
314 | Substitute the fake data with real data. */ | ||
315 | assert(x11_fake_data_len == x11_saved_data_len); | ||
316 | memcpy(ucp + 12 + ((proto_len + 3) & ~3), | ||
317 | x11_saved_data, x11_saved_data_len); | ||
318 | |||
319 | /* Start normal processing for the channel. */ | ||
320 | ch->type = SSH_CHANNEL_OPEN; | ||
321 | /* Enable X11 Problem FIX */ | ||
322 | ch->x11 = 1; | ||
323 | goto redo; | ||
324 | |||
325 | reject: | ||
326 | /* We have received an X11 connection that has bad authentication | ||
327 | information. */ | ||
328 | log("X11 connection rejected because of wrong authentication.\r\n"); | ||
329 | buffer_clear(&ch->input); | ||
330 | buffer_clear(&ch->output); | ||
331 | if (compat13) { | ||
332 | close(ch->sock); | ||
333 | ch->sock = -1; | ||
334 | ch->type = SSH_CHANNEL_CLOSED; | ||
335 | packet_start(SSH_MSG_CHANNEL_CLOSE); | ||
336 | packet_put_int(ch->remote_id); | ||
337 | packet_send(); | ||
338 | }else{ | ||
339 | debug("X11 rejected %d 0x%x 0x%x", ch->self, ch->istate, ch->ostate); | ||
340 | chan_read_failed(ch); | ||
341 | chan_write_failed(ch); | ||
342 | debug("X11 rejected %d 0x%x 0x%x", ch->self, ch->istate, ch->ostate); | ||
343 | } | ||
344 | break; | ||
345 | |||
346 | case SSH_CHANNEL_FREE: | ||
347 | default: | ||
348 | continue; | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | |||
353 | /* After select, perform any appropriate operations for channels which | ||
354 | have events pending. */ | ||
355 | |||
356 | void channel_after_select(fd_set *readset, fd_set *writeset) | ||
357 | { | ||
358 | struct sockaddr addr; | ||
359 | int addrlen, newsock, i, newch, len; | ||
360 | Channel *ch; | ||
361 | char buf[16384], *remote_hostname; | ||
362 | |||
363 | /* Loop over all channels... */ | ||
364 | for (i = 0; i < channels_alloc; i++) | ||
365 | { | ||
366 | ch = &channels[i]; | ||
367 | switch (ch->type) | ||
368 | { | ||
369 | case SSH_CHANNEL_X11_LISTENER: | ||
370 | /* This is our fake X11 server socket. */ | ||
371 | if (FD_ISSET(ch->sock, readset)) | ||
372 | { | ||
373 | debug("X11 connection requested."); | ||
374 | addrlen = sizeof(addr); | ||
375 | newsock = accept(ch->sock, &addr, &addrlen); | ||
376 | if (newsock < 0) | ||
377 | { | ||
378 | error("accept: %.100s", strerror(errno)); | ||
379 | break; | ||
380 | } | ||
381 | remote_hostname = get_remote_hostname(newsock); | ||
382 | snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", | ||
383 | remote_hostname, get_peer_port(newsock)); | ||
384 | xfree(remote_hostname); | ||
385 | newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, | ||
386 | xstrdup(buf)); | ||
387 | packet_start(SSH_SMSG_X11_OPEN); | ||
388 | packet_put_int(newch); | ||
389 | if (have_hostname_in_open) | ||
390 | packet_put_string(buf, strlen(buf)); | ||
391 | packet_send(); | ||
392 | } | ||
393 | break; | ||
394 | |||
395 | case SSH_CHANNEL_PORT_LISTENER: | ||
396 | /* This socket is listening for connections to a forwarded TCP/IP | ||
397 | port. */ | ||
398 | if (FD_ISSET(ch->sock, readset)) | ||
399 | { | ||
400 | debug("Connection to port %d forwarding to %.100s:%d requested.", | ||
401 | ch->listening_port, ch->path, ch->host_port); | ||
402 | addrlen = sizeof(addr); | ||
403 | newsock = accept(ch->sock, &addr, &addrlen); | ||
404 | if (newsock < 0) | ||
405 | { | ||
406 | error("accept: %.100s", strerror(errno)); | ||
407 | break; | ||
408 | } | ||
409 | remote_hostname = get_remote_hostname(newsock); | ||
410 | snprintf(buf, sizeof buf, "port %d, connection from %.200s port %d", | ||
411 | ch->listening_port, remote_hostname, | ||
412 | get_peer_port(newsock)); | ||
413 | xfree(remote_hostname); | ||
414 | newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, | ||
415 | xstrdup(buf)); | ||
416 | packet_start(SSH_MSG_PORT_OPEN); | ||
417 | packet_put_int(newch); | ||
418 | packet_put_string(ch->path, strlen(ch->path)); | ||
419 | packet_put_int(ch->host_port); | ||
420 | if (have_hostname_in_open) | ||
421 | packet_put_string(buf, strlen(buf)); | ||
422 | packet_send(); | ||
423 | } | ||
424 | break; | ||
425 | |||
426 | case SSH_CHANNEL_AUTH_SOCKET: | ||
427 | /* This is the authentication agent socket listening for connections | ||
428 | from clients. */ | ||
429 | if (FD_ISSET(ch->sock, readset)) | ||
430 | { | ||
431 | int nchan; | ||
432 | len = sizeof(addr); | ||
433 | newsock = accept(ch->sock, &addr, &len); | ||
434 | if (newsock < 0) | ||
435 | { | ||
436 | error("accept from auth socket: %.100s", strerror(errno)); | ||
437 | break; | ||
438 | } | ||
439 | |||
440 | nchan = channel_allocate(SSH_CHANNEL_OPENING, newsock, | ||
441 | xstrdup("accepted auth socket")); | ||
442 | packet_start(SSH_SMSG_AGENT_OPEN); | ||
443 | packet_put_int(nchan); | ||
444 | packet_send(); | ||
445 | } | ||
446 | break; | ||
447 | |||
448 | case SSH_CHANNEL_OPEN: | ||
449 | /* This is an open two-way communication channel. It is not of | ||
450 | interest to us at this point what kind of data is being | ||
451 | transmitted. */ | ||
452 | |||
453 | /* Read available incoming data and append it to buffer; | ||
454 | shutdown socket, if read or write failes */ | ||
455 | if (FD_ISSET(ch->sock, readset)) | ||
456 | { | ||
457 | len = read(ch->sock, buf, sizeof(buf)); | ||
458 | if (len <= 0) | ||
459 | { | ||
460 | if (compat13) { | ||
461 | buffer_consume(&ch->output, buffer_len(&ch->output)); | ||
462 | ch->type = SSH_CHANNEL_INPUT_DRAINING; | ||
463 | debug("Channel %d status set to input draining.", i); | ||
464 | }else{ | ||
465 | chan_read_failed(ch); | ||
466 | } | ||
467 | break; | ||
468 | } | ||
469 | buffer_append(&ch->input, buf, len); | ||
470 | } | ||
471 | /* Send buffered output data to the socket. */ | ||
472 | if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) | ||
473 | { | ||
474 | len = write(ch->sock, buffer_ptr(&ch->output), | ||
475 | buffer_len(&ch->output)); | ||
476 | if (len <= 0) | ||
477 | { | ||
478 | if (compat13) { | ||
479 | buffer_consume(&ch->output, buffer_len(&ch->output)); | ||
480 | debug("Channel %d status set to input draining.", i); | ||
481 | ch->type = SSH_CHANNEL_INPUT_DRAINING; | ||
482 | }else{ | ||
483 | chan_write_failed(ch); | ||
484 | } | ||
485 | break; | ||
486 | } | ||
487 | buffer_consume(&ch->output, len); | ||
488 | } | ||
489 | break; | ||
490 | |||
491 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
492 | if (!compat13) | ||
493 | fatal("cannot happen: OUT_DRAIN"); | ||
494 | /* Send buffered output data to the socket. */ | ||
495 | if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) | ||
496 | { | ||
497 | len = write(ch->sock, buffer_ptr(&ch->output), | ||
498 | buffer_len(&ch->output)); | ||
499 | if (len <= 0) | ||
500 | buffer_consume(&ch->output, buffer_len(&ch->output)); | ||
501 | else | ||
502 | buffer_consume(&ch->output, len); | ||
503 | } | ||
504 | break; | ||
505 | |||
506 | case SSH_CHANNEL_X11_OPEN: | ||
507 | case SSH_CHANNEL_FREE: | ||
508 | default: | ||
509 | continue; | ||
510 | } | ||
511 | } | ||
512 | } | ||
513 | |||
514 | /* If there is data to send to the connection, send some of it now. */ | ||
515 | |||
516 | void channel_output_poll() | ||
517 | { | ||
518 | int len, i; | ||
519 | Channel *ch; | ||
520 | |||
521 | for (i = 0; i < channels_alloc; i++) | ||
522 | { | ||
523 | ch = &channels[i]; | ||
524 | /* We are only interested in channels that can have buffered incoming | ||
525 | data. */ | ||
526 | if (ch->type != SSH_CHANNEL_OPEN && | ||
527 | ch->type != SSH_CHANNEL_INPUT_DRAINING) | ||
528 | continue; | ||
529 | |||
530 | /* Get the amount of buffered data for this channel. */ | ||
531 | len = buffer_len(&ch->input); | ||
532 | if (len > 0) | ||
533 | { | ||
534 | /* Send some data for the other side over the secure connection. */ | ||
535 | if (packet_is_interactive()) | ||
536 | { | ||
537 | if (len > 1024) | ||
538 | len = 512; | ||
539 | } | ||
540 | else | ||
541 | { | ||
542 | if (len > 16384) | ||
543 | len = 16384; /* Keep the packets at reasonable size. */ | ||
544 | } | ||
545 | packet_start(SSH_MSG_CHANNEL_DATA); | ||
546 | packet_put_int(ch->remote_id); | ||
547 | packet_put_string(buffer_ptr(&ch->input), len); | ||
548 | packet_send(); | ||
549 | buffer_consume(&ch->input, len); | ||
550 | } | ||
551 | else if(ch->istate == CHAN_INPUT_WAIT_DRAIN) | ||
552 | { | ||
553 | if (compat13) | ||
554 | fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); | ||
555 | /* input-buffer is empty and read-socket shutdown: | ||
556 | tell peer, that we will not send more data: send IEOF */ | ||
557 | chan_ibuf_empty(ch); | ||
558 | } | ||
559 | } | ||
560 | } | ||
561 | |||
562 | /* This is called when a packet of type CHANNEL_DATA has just been received. | ||
563 | The message type has already been consumed, but channel number and data | ||
564 | is still there. */ | ||
565 | |||
566 | void channel_input_data(int payload_len) | ||
567 | { | ||
568 | int channel; | ||
569 | char *data; | ||
570 | unsigned int data_len; | ||
571 | |||
572 | /* Get the channel number and verify it. */ | ||
573 | channel = packet_get_int(); | ||
574 | if (channel < 0 || channel >= channels_alloc || | ||
575 | channels[channel].type == SSH_CHANNEL_FREE) | ||
576 | packet_disconnect("Received data for nonexistent channel %d.", channel); | ||
577 | |||
578 | /* Ignore any data for non-open channels (might happen on close) */ | ||
579 | if (channels[channel].type != SSH_CHANNEL_OPEN && | ||
580 | channels[channel].type != SSH_CHANNEL_X11_OPEN) | ||
581 | return; | ||
582 | |||
583 | /* Get the data. */ | ||
584 | data = packet_get_string(&data_len); | ||
585 | packet_integrity_check(payload_len, 4 + 4+data_len, SSH_MSG_CHANNEL_DATA); | ||
586 | buffer_append(&channels[channel].output, data, data_len); | ||
587 | xfree(data); | ||
588 | } | ||
589 | |||
590 | /* Returns true if no channel has too much buffered data, and false if | ||
591 | one or more channel is overfull. */ | ||
592 | |||
593 | int channel_not_very_much_buffered_data() | ||
594 | { | ||
595 | unsigned int i; | ||
596 | Channel *ch; | ||
597 | |||
598 | for (i = 0; i < channels_alloc; i++) | ||
599 | { | ||
600 | ch = &channels[i]; | ||
601 | switch (ch->type) | ||
602 | { | ||
603 | case SSH_CHANNEL_X11_LISTENER: | ||
604 | case SSH_CHANNEL_PORT_LISTENER: | ||
605 | case SSH_CHANNEL_AUTH_SOCKET: | ||
606 | continue; | ||
607 | case SSH_CHANNEL_OPEN: | ||
608 | if (buffer_len(&ch->input) > 32768) | ||
609 | return 0; | ||
610 | if (buffer_len(&ch->output) > 32768) | ||
611 | return 0; | ||
612 | continue; | ||
613 | case SSH_CHANNEL_INPUT_DRAINING: | ||
614 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
615 | case SSH_CHANNEL_X11_OPEN: | ||
616 | case SSH_CHANNEL_FREE: | ||
617 | default: | ||
618 | continue; | ||
619 | } | ||
620 | } | ||
621 | return 1; | ||
622 | } | ||
623 | |||
624 | /* This is called after receiving CHANNEL_CLOSE/IEOF. */ | ||
625 | |||
626 | void channel_input_close() | ||
627 | { | ||
628 | int channel; | ||
629 | |||
630 | /* Get the channel number and verify it. */ | ||
631 | channel = packet_get_int(); | ||
632 | if (channel < 0 || channel >= channels_alloc || | ||
633 | channels[channel].type == SSH_CHANNEL_FREE) | ||
634 | packet_disconnect("Received data for nonexistent channel %d.", channel); | ||
635 | |||
636 | if(!compat13){ | ||
637 | /* proto version 1.5 overloads CLOSE with IEOF */ | ||
638 | chan_rcvd_ieof(&channels[channel]); | ||
639 | return; | ||
640 | } | ||
641 | |||
642 | /* Send a confirmation that we have closed the channel and no more data is | ||
643 | coming for it. */ | ||
644 | packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); | ||
645 | packet_put_int(channels[channel].remote_id); | ||
646 | packet_send(); | ||
647 | |||
648 | /* If the channel is in closed state, we have sent a close request, and | ||
649 | the other side will eventually respond with a confirmation. Thus, | ||
650 | we cannot free the channel here, because then there would be no-one to | ||
651 | receive the confirmation. The channel gets freed when the confirmation | ||
652 | arrives. */ | ||
653 | if (channels[channel].type != SSH_CHANNEL_CLOSED) | ||
654 | { | ||
655 | /* Not a closed channel - mark it as draining, which will cause it to | ||
656 | be freed later. */ | ||
657 | buffer_consume(&channels[channel].input, | ||
658 | buffer_len(&channels[channel].input)); | ||
659 | channels[channel].type = SSH_CHANNEL_OUTPUT_DRAINING; | ||
660 | /* debug("Setting status to output draining; output len = %d", | ||
661 | buffer_len(&channels[channel].output)); */ | ||
662 | } | ||
663 | } | ||
664 | |||
665 | /* This is called after receiving CHANNEL_CLOSE_CONFIRMATION/OCLOSE. */ | ||
666 | |||
667 | void channel_input_close_confirmation() | ||
668 | { | ||
669 | int channel; | ||
670 | |||
671 | /* Get the channel number and verify it. */ | ||
672 | channel = packet_get_int(); | ||
673 | if (channel < 0 || channel >= channels_alloc) | ||
674 | packet_disconnect("Received close confirmation for out-of-range channel %d.", | ||
675 | channel); | ||
676 | |||
677 | if(!compat13){ | ||
678 | /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ | ||
679 | chan_rcvd_oclose(&channels[channel]); | ||
680 | return; | ||
681 | } | ||
682 | |||
683 | if (channels[channel].type != SSH_CHANNEL_CLOSED) | ||
684 | packet_disconnect("Received close confirmation for non-closed channel %d (type %d).", | ||
685 | channel, channels[channel].type); | ||
686 | |||
687 | /* Free the channel. */ | ||
688 | channel_free(channel); | ||
689 | } | ||
690 | |||
691 | /* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ | ||
692 | |||
693 | void channel_input_open_confirmation() | ||
694 | { | ||
695 | int channel, remote_channel; | ||
696 | |||
697 | /* Get the channel number and verify it. */ | ||
698 | channel = packet_get_int(); | ||
699 | if (channel < 0 || channel >= channels_alloc || | ||
700 | channels[channel].type != SSH_CHANNEL_OPENING) | ||
701 | packet_disconnect("Received open confirmation for non-opening channel %d.", | ||
702 | channel); | ||
703 | |||
704 | /* Get remote side's id for this channel. */ | ||
705 | remote_channel = packet_get_int(); | ||
706 | |||
707 | /* Record the remote channel number and mark that the channel is now open. */ | ||
708 | channels[channel].remote_id = remote_channel; | ||
709 | channels[channel].type = SSH_CHANNEL_OPEN; | ||
710 | } | ||
711 | |||
712 | /* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ | ||
713 | |||
714 | void channel_input_open_failure() | ||
715 | { | ||
716 | int channel; | ||
717 | |||
718 | /* Get the channel number and verify it. */ | ||
719 | channel = packet_get_int(); | ||
720 | if (channel < 0 || channel >= channels_alloc || | ||
721 | channels[channel].type != SSH_CHANNEL_OPENING) | ||
722 | packet_disconnect("Received open failure for non-opening channel %d.", | ||
723 | channel); | ||
724 | |||
725 | /* Free the channel. This will also close the socket. */ | ||
726 | channel_free(channel); | ||
727 | } | ||
728 | |||
729 | /* Stops listening for channels, and removes any unix domain sockets that | ||
730 | we might have. */ | ||
731 | |||
732 | void channel_stop_listening() | ||
733 | { | ||
734 | int i; | ||
735 | for (i = 0; i < channels_alloc; i++) | ||
736 | { | ||
737 | switch (channels[i].type) | ||
738 | { | ||
739 | case SSH_CHANNEL_AUTH_SOCKET: | ||
740 | close(channels[i].sock); | ||
741 | remove(channels[i].path); | ||
742 | channel_free(i); | ||
743 | break; | ||
744 | case SSH_CHANNEL_PORT_LISTENER: | ||
745 | case SSH_CHANNEL_X11_LISTENER: | ||
746 | close(channels[i].sock); | ||
747 | channel_free(i); | ||
748 | break; | ||
749 | default: | ||
750 | break; | ||
751 | } | ||
752 | } | ||
753 | } | ||
754 | |||
755 | /* Closes the sockets of all channels. This is used to close extra file | ||
756 | descriptors after a fork. */ | ||
757 | |||
758 | void channel_close_all() | ||
759 | { | ||
760 | int i; | ||
761 | for (i = 0; i < channels_alloc; i++) | ||
762 | { | ||
763 | if (channels[i].type != SSH_CHANNEL_FREE) | ||
764 | close(channels[i].sock); | ||
765 | } | ||
766 | } | ||
767 | |||
768 | /* Returns the maximum file descriptor number used by the channels. */ | ||
769 | |||
770 | int channel_max_fd() | ||
771 | { | ||
772 | return channel_max_fd_value; | ||
773 | } | ||
774 | |||
775 | /* Returns true if any channel is still open. */ | ||
776 | |||
777 | int channel_still_open() | ||
778 | { | ||
779 | unsigned int i; | ||
780 | for (i = 0; i < channels_alloc; i++) | ||
781 | switch (channels[i].type) | ||
782 | { | ||
783 | case SSH_CHANNEL_FREE: | ||
784 | case SSH_CHANNEL_X11_LISTENER: | ||
785 | case SSH_CHANNEL_PORT_LISTENER: | ||
786 | case SSH_CHANNEL_CLOSED: | ||
787 | case SSH_CHANNEL_AUTH_SOCKET: | ||
788 | continue; | ||
789 | case SSH_CHANNEL_OPENING: | ||
790 | case SSH_CHANNEL_OPEN: | ||
791 | case SSH_CHANNEL_X11_OPEN: | ||
792 | return 1; | ||
793 | case SSH_CHANNEL_INPUT_DRAINING: | ||
794 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
795 | if (!compat13) | ||
796 | fatal("cannot happen: OUT_DRAIN"); | ||
797 | return 1; | ||
798 | default: | ||
799 | fatal("channel_still_open: bad channel type %d", channels[i].type); | ||
800 | /*NOTREACHED*/ | ||
801 | } | ||
802 | return 0; | ||
803 | } | ||
804 | |||
805 | /* Returns a message describing the currently open forwarded | ||
806 | connections, suitable for sending to the client. The message | ||
807 | contains crlf pairs for newlines. */ | ||
808 | |||
809 | char *channel_open_message() | ||
810 | { | ||
811 | Buffer buffer; | ||
812 | int i; | ||
813 | char buf[512], *cp; | ||
814 | |||
815 | buffer_init(&buffer); | ||
816 | snprintf(buf, sizeof buf, "The following connections are open:\r\n"); | ||
817 | buffer_append(&buffer, buf, strlen(buf)); | ||
818 | for (i = 0; i < channels_alloc; i++){ | ||
819 | Channel *c=&channels[i]; | ||
820 | switch (c->type) | ||
821 | { | ||
822 | case SSH_CHANNEL_FREE: | ||
823 | case SSH_CHANNEL_X11_LISTENER: | ||
824 | case SSH_CHANNEL_PORT_LISTENER: | ||
825 | case SSH_CHANNEL_CLOSED: | ||
826 | case SSH_CHANNEL_AUTH_SOCKET: | ||
827 | continue; | ||
828 | case SSH_CHANNEL_OPENING: | ||
829 | case SSH_CHANNEL_OPEN: | ||
830 | case SSH_CHANNEL_X11_OPEN: | ||
831 | case SSH_CHANNEL_INPUT_DRAINING: | ||
832 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
833 | snprintf(buf, sizeof buf, " #%d/%d %.300s\r\n", | ||
834 | c->self,c->type,c->remote_name); | ||
835 | buffer_append(&buffer, buf, strlen(buf)); | ||
836 | continue; | ||
837 | default: | ||
838 | fatal("channel_still_open: bad channel type %d", c->type); | ||
839 | /*NOTREACHED*/ | ||
840 | } | ||
841 | } | ||
842 | buffer_append(&buffer, "\0", 1); | ||
843 | cp = xstrdup(buffer_ptr(&buffer)); | ||
844 | buffer_free(&buffer); | ||
845 | return cp; | ||
846 | } | ||
847 | |||
848 | /* Initiate forwarding of connections to local port "port" through the secure | ||
849 | channel to host:port from remote side. */ | ||
850 | |||
851 | void channel_request_local_forwarding(int port, const char *host, | ||
852 | int host_port) | ||
853 | { | ||
854 | int ch, sock; | ||
855 | struct sockaddr_in sin; | ||
856 | extern Options options; | ||
857 | |||
858 | if (strlen(host) > sizeof(channels[0].path) - 1) | ||
859 | packet_disconnect("Forward host name too long."); | ||
860 | |||
861 | /* Create a port to listen for the host. */ | ||
862 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
863 | if (sock < 0) | ||
864 | packet_disconnect("socket: %.100s", strerror(errno)); | ||
865 | |||
866 | /* Initialize socket address. */ | ||
867 | memset(&sin, 0, sizeof(sin)); | ||
868 | sin.sin_family = AF_INET; | ||
869 | if (options.gateway_ports == 1) | ||
870 | sin.sin_addr.s_addr = htonl(INADDR_ANY); | ||
871 | else | ||
872 | sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | ||
873 | sin.sin_port = htons(port); | ||
874 | |||
875 | /* Bind the socket to the address. */ | ||
876 | if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | ||
877 | packet_disconnect("bind: %.100s", strerror(errno)); | ||
878 | |||
879 | /* Start listening for connections on the socket. */ | ||
880 | if (listen(sock, 5) < 0) | ||
881 | packet_disconnect("listen: %.100s", strerror(errno)); | ||
882 | |||
883 | /* Allocate a channel number for the socket. */ | ||
884 | ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, | ||
885 | xstrdup("port listener")); | ||
886 | strcpy(channels[ch].path, host); /* note: host name stored here */ | ||
887 | channels[ch].host_port = host_port; /* port on host to connect to */ | ||
888 | channels[ch].listening_port = port; /* port being listened */ | ||
889 | } | ||
890 | |||
891 | /* Initiate forwarding of connections to port "port" on remote host through | ||
892 | the secure channel to host:port from local side. */ | ||
893 | |||
894 | void channel_request_remote_forwarding(int port, const char *host, | ||
895 | int remote_port) | ||
896 | { | ||
897 | int payload_len; | ||
898 | /* Record locally that connection to this host/port is permitted. */ | ||
899 | if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) | ||
900 | fatal("channel_request_remote_forwarding: too many forwards"); | ||
901 | permitted_opens[num_permitted_opens].host = xstrdup(host); | ||
902 | permitted_opens[num_permitted_opens].port = remote_port; | ||
903 | num_permitted_opens++; | ||
904 | |||
905 | /* Send the forward request to the remote side. */ | ||
906 | packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); | ||
907 | packet_put_int(port); | ||
908 | packet_put_string(host, strlen(host)); | ||
909 | packet_put_int(remote_port); | ||
910 | packet_send(); | ||
911 | packet_write_wait(); | ||
912 | |||
913 | /* Wait for response from the remote side. It will send a disconnect | ||
914 | message on failure, and we will never see it here. */ | ||
915 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | ||
916 | } | ||
917 | |||
918 | /* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates | ||
919 | listening for the port, and sends back a success reply (or disconnect | ||
920 | message if there was an error). This never returns if there was an | ||
921 | error. */ | ||
922 | |||
923 | void channel_input_port_forward_request(int is_root) | ||
924 | { | ||
925 | int port, host_port; | ||
926 | char *hostname; | ||
927 | |||
928 | /* Get arguments from the packet. */ | ||
929 | port = packet_get_int(); | ||
930 | hostname = packet_get_string(NULL); | ||
931 | host_port = packet_get_int(); | ||
932 | |||
933 | /* Port numbers are 16 bit quantities. */ | ||
934 | if ((port & 0xffff) != port) | ||
935 | packet_disconnect("Requested forwarding of nonexistent port %d.", port); | ||
936 | |||
937 | /* Check that an unprivileged user is not trying to forward a privileged | ||
938 | port. */ | ||
939 | if (port < IPPORT_RESERVED && !is_root) | ||
940 | packet_disconnect("Requested forwarding of port %d but user is not root.", | ||
941 | port); | ||
942 | |||
943 | /* Initiate forwarding. */ | ||
944 | channel_request_local_forwarding(port, hostname, host_port); | ||
945 | |||
946 | /* Free the argument string. */ | ||
947 | xfree(hostname); | ||
948 | } | ||
949 | |||
950 | /* This is called after receiving PORT_OPEN message. This attempts to connect | ||
951 | to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or | ||
952 | CHANNEL_OPEN_FAILURE. */ | ||
953 | |||
954 | void channel_input_port_open(int payload_len) | ||
955 | { | ||
956 | int remote_channel, sock, newch, host_port, i; | ||
957 | struct sockaddr_in sin; | ||
958 | char *host, *originator_string; | ||
959 | struct hostent *hp; | ||
960 | int host_len, originator_len; | ||
961 | |||
962 | /* Get remote channel number. */ | ||
963 | remote_channel = packet_get_int(); | ||
964 | |||
965 | /* Get host name to connect to. */ | ||
966 | host = packet_get_string(&host_len); | ||
967 | |||
968 | /* Get port to connect to. */ | ||
969 | host_port = packet_get_int(); | ||
970 | |||
971 | /* Get remote originator name. */ | ||
972 | if (have_hostname_in_open) | ||
973 | originator_string = packet_get_string(&originator_len); | ||
974 | else | ||
975 | originator_string = xstrdup("unknown (remote did not supply name)"); | ||
976 | |||
977 | packet_integrity_check(payload_len, | ||
978 | 4 + 4 + host_len + 4 + 4 + originator_len, | ||
979 | SSH_MSG_PORT_OPEN); | ||
980 | |||
981 | /* Check if opening that port is permitted. */ | ||
982 | if (!all_opens_permitted) | ||
983 | { | ||
984 | /* Go trough all permitted ports. */ | ||
985 | for (i = 0; i < num_permitted_opens; i++) | ||
986 | if (permitted_opens[i].port == host_port && | ||
987 | strcmp(permitted_opens[i].host, host) == 0) | ||
988 | break; | ||
989 | |||
990 | /* Check if we found the requested port among those permitted. */ | ||
991 | if (i >= num_permitted_opens) | ||
992 | { | ||
993 | /* The port is not permitted. */ | ||
994 | log("Received request to connect to %.100s:%d, but the request was denied.", | ||
995 | host, host_port); | ||
996 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | ||
997 | packet_put_int(remote_channel); | ||
998 | packet_send(); | ||
999 | } | ||
1000 | } | ||
1001 | |||
1002 | memset(&sin, 0, sizeof(sin)); | ||
1003 | sin.sin_addr.s_addr = inet_addr(host); | ||
1004 | if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) | ||
1005 | { | ||
1006 | /* It was a valid numeric host address. */ | ||
1007 | sin.sin_family = AF_INET; | ||
1008 | } | ||
1009 | else | ||
1010 | { | ||
1011 | /* Look up the host address from the name servers. */ | ||
1012 | hp = gethostbyname(host); | ||
1013 | if (!hp) | ||
1014 | { | ||
1015 | error("%.100s: unknown host.", host); | ||
1016 | goto fail; | ||
1017 | } | ||
1018 | if (!hp->h_addr_list[0]) | ||
1019 | { | ||
1020 | error("%.100s: host has no IP address.", host); | ||
1021 | goto fail; | ||
1022 | } | ||
1023 | sin.sin_family = hp->h_addrtype; | ||
1024 | memcpy(&sin.sin_addr, hp->h_addr_list[0], | ||
1025 | sizeof(sin.sin_addr)); | ||
1026 | } | ||
1027 | sin.sin_port = htons(host_port); | ||
1028 | |||
1029 | /* Create the socket. */ | ||
1030 | sock = socket(sin.sin_family, SOCK_STREAM, 0); | ||
1031 | if (sock < 0) | ||
1032 | { | ||
1033 | error("socket: %.100s", strerror(errno)); | ||
1034 | goto fail; | ||
1035 | } | ||
1036 | |||
1037 | /* Connect to the host/port. */ | ||
1038 | if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | ||
1039 | { | ||
1040 | error("connect %.100s:%d: %.100s", host, host_port, | ||
1041 | strerror(errno)); | ||
1042 | close(sock); | ||
1043 | goto fail; | ||
1044 | } | ||
1045 | |||
1046 | /* Successful connection. */ | ||
1047 | |||
1048 | /* Allocate a channel for this connection. */ | ||
1049 | newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); | ||
1050 | channels[newch].remote_id = remote_channel; | ||
1051 | |||
1052 | /* Send a confirmation to the remote host. */ | ||
1053 | packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); | ||
1054 | packet_put_int(remote_channel); | ||
1055 | packet_put_int(newch); | ||
1056 | packet_send(); | ||
1057 | |||
1058 | /* Free the argument string. */ | ||
1059 | xfree(host); | ||
1060 | |||
1061 | return; | ||
1062 | |||
1063 | fail: | ||
1064 | /* Free the argument string. */ | ||
1065 | xfree(host); | ||
1066 | |||
1067 | /* Send refusal to the remote host. */ | ||
1068 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | ||
1069 | packet_put_int(remote_channel); | ||
1070 | packet_send(); | ||
1071 | } | ||
1072 | |||
1073 | /* Creates an internet domain socket for listening for X11 connections. | ||
1074 | Returns a suitable value for the DISPLAY variable, or NULL if an error | ||
1075 | occurs. */ | ||
1076 | |||
1077 | char *x11_create_display_inet(int screen_number) | ||
1078 | { | ||
1079 | extern ServerOptions options; | ||
1080 | int display_number, port, sock; | ||
1081 | struct sockaddr_in sin; | ||
1082 | char buf[512]; | ||
1083 | char hostname[MAXHOSTNAMELEN]; | ||
1084 | |||
1085 | for (display_number = options.x11_display_offset; display_number < MAX_DISPLAYS; display_number++) | ||
1086 | { | ||
1087 | port = 6000 + display_number; | ||
1088 | memset(&sin, 0, sizeof(sin)); | ||
1089 | sin.sin_family = AF_INET; | ||
1090 | sin.sin_addr.s_addr = htonl(INADDR_ANY); | ||
1091 | sin.sin_port = htons(port); | ||
1092 | |||
1093 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
1094 | if (sock < 0) | ||
1095 | { | ||
1096 | error("socket: %.100s", strerror(errno)); | ||
1097 | return NULL; | ||
1098 | } | ||
1099 | |||
1100 | if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | ||
1101 | { | ||
1102 | debug("bind port %d: %.100s", port, strerror(errno)); | ||
1103 | shutdown(sock, SHUT_RDWR); | ||
1104 | close(sock); | ||
1105 | continue; | ||
1106 | } | ||
1107 | break; | ||
1108 | } | ||
1109 | if (display_number >= MAX_DISPLAYS) | ||
1110 | { | ||
1111 | error("Failed to allocate internet-domain X11 display socket."); | ||
1112 | return NULL; | ||
1113 | } | ||
1114 | |||
1115 | /* Start listening for connections on the socket. */ | ||
1116 | if (listen(sock, 5) < 0) | ||
1117 | { | ||
1118 | error("listen: %.100s", strerror(errno)); | ||
1119 | shutdown(sock, SHUT_RDWR); | ||
1120 | close(sock); | ||
1121 | return NULL; | ||
1122 | } | ||
1123 | |||
1124 | /* Set up a suitable value for the DISPLAY variable. */ | ||
1125 | if (gethostname(hostname, sizeof(hostname)) < 0) | ||
1126 | fatal("gethostname: %.100s", strerror(errno)); | ||
1127 | snprintf(buf, sizeof buf, "%.400s:%d.%d", hostname, | ||
1128 | display_number, screen_number); | ||
1129 | |||
1130 | /* Allocate a channel for the socket. */ | ||
1131 | (void)channel_allocate(SSH_CHANNEL_X11_LISTENER, sock, | ||
1132 | xstrdup("X11 inet listener")); | ||
1133 | |||
1134 | /* Return a suitable value for the DISPLAY environment variable. */ | ||
1135 | return xstrdup(buf); | ||
1136 | } | ||
1137 | |||
1138 | #ifndef X_UNIX_PATH | ||
1139 | #define X_UNIX_PATH "/tmp/.X11-unix/X" | ||
1140 | #endif | ||
1141 | |||
1142 | static | ||
1143 | int | ||
1144 | connect_local_xsocket(unsigned dnr) | ||
1145 | { | ||
1146 | static const char *const x_sockets[] = { | ||
1147 | X_UNIX_PATH "%u", | ||
1148 | "/var/X/.X11-unix/X" "%u", | ||
1149 | "/usr/spool/sockets/X11/" "%u", | ||
1150 | NULL | ||
1151 | }; | ||
1152 | int sock; | ||
1153 | struct sockaddr_un addr; | ||
1154 | const char *const *path; | ||
1155 | |||
1156 | for (path = x_sockets; *path; ++path) | ||
1157 | { | ||
1158 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | ||
1159 | if (sock < 0) | ||
1160 | error("socket: %.100s", strerror(errno)); | ||
1161 | memset(&addr, 0, sizeof(addr)); | ||
1162 | addr.sun_family = AF_UNIX; | ||
1163 | snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr); | ||
1164 | if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) | ||
1165 | return sock; | ||
1166 | close(sock); | ||
1167 | } | ||
1168 | error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); | ||
1169 | return -1; | ||
1170 | } | ||
1171 | |||
1172 | |||
1173 | /* This is called when SSH_SMSG_X11_OPEN is received. The packet contains | ||
1174 | the remote channel number. We should do whatever we want, and respond | ||
1175 | with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ | ||
1176 | |||
1177 | void x11_input_open(int payload_len) | ||
1178 | { | ||
1179 | int remote_channel, display_number, sock, newch; | ||
1180 | const char *display; | ||
1181 | struct sockaddr_in sin; | ||
1182 | char buf[1024], *cp, *remote_host; | ||
1183 | struct hostent *hp; | ||
1184 | int remote_len; | ||
1185 | |||
1186 | /* Get remote channel number. */ | ||
1187 | remote_channel = packet_get_int(); | ||
1188 | |||
1189 | /* Get remote originator name. */ | ||
1190 | if (have_hostname_in_open) | ||
1191 | remote_host = packet_get_string(&remote_len); | ||
1192 | else | ||
1193 | remote_host = xstrdup("unknown (remote did not supply name)"); | ||
1194 | |||
1195 | debug("Received X11 open request."); | ||
1196 | packet_integrity_check(payload_len, 4 + 4+remote_len, SSH_SMSG_X11_OPEN); | ||
1197 | |||
1198 | /* Try to open a socket for the local X server. */ | ||
1199 | display = getenv("DISPLAY"); | ||
1200 | if (!display) | ||
1201 | { | ||
1202 | error("DISPLAY not set."); | ||
1203 | goto fail; | ||
1204 | } | ||
1205 | |||
1206 | /* Now we decode the value of the DISPLAY variable and make a connection | ||
1207 | to the real X server. */ | ||
1208 | |||
1209 | /* Check if it is a unix domain socket. Unix domain displays are in one | ||
1210 | of the following formats: unix:d[.s], :d[.s], ::d[.s] */ | ||
1211 | if (strncmp(display, "unix:", 5) == 0 || | ||
1212 | display[0] == ':') | ||
1213 | { | ||
1214 | /* Connect to the unix domain socket. */ | ||
1215 | if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) | ||
1216 | { | ||
1217 | error("Could not parse display number from DISPLAY: %.100s", | ||
1218 | display); | ||
1219 | goto fail; | ||
1220 | } | ||
1221 | /* Create a socket. */ | ||
1222 | sock = connect_local_xsocket(display_number); | ||
1223 | if (sock < 0) | ||
1224 | goto fail; | ||
1225 | |||
1226 | /* OK, we now have a connection to the display. */ | ||
1227 | goto success; | ||
1228 | } | ||
1229 | |||
1230 | /* Connect to an inet socket. The DISPLAY value is supposedly | ||
1231 | hostname:d[.s], where hostname may also be numeric IP address. */ | ||
1232 | strncpy(buf, display, sizeof(buf)); | ||
1233 | buf[sizeof(buf) - 1] = 0; | ||
1234 | cp = strchr(buf, ':'); | ||
1235 | if (!cp) | ||
1236 | { | ||
1237 | error("Could not find ':' in DISPLAY: %.100s", display); | ||
1238 | goto fail; | ||
1239 | } | ||
1240 | *cp = 0; | ||
1241 | /* buf now contains the host name. But first we parse the display number. */ | ||
1242 | if (sscanf(cp + 1, "%d", &display_number) != 1) | ||
1243 | { | ||
1244 | error("Could not parse display number from DISPLAY: %.100s", | ||
1245 | display); | ||
1246 | goto fail; | ||
1247 | } | ||
1248 | |||
1249 | /* Try to parse the host name as a numeric IP address. */ | ||
1250 | memset(&sin, 0, sizeof(sin)); | ||
1251 | sin.sin_addr.s_addr = inet_addr(buf); | ||
1252 | if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) | ||
1253 | { | ||
1254 | /* It was a valid numeric host address. */ | ||
1255 | sin.sin_family = AF_INET; | ||
1256 | } | ||
1257 | else | ||
1258 | { | ||
1259 | /* Not a numeric IP address. */ | ||
1260 | /* Look up the host address from the name servers. */ | ||
1261 | hp = gethostbyname(buf); | ||
1262 | if (!hp) | ||
1263 | { | ||
1264 | error("%.100s: unknown host.", buf); | ||
1265 | goto fail; | ||
1266 | } | ||
1267 | if (!hp->h_addr_list[0]) | ||
1268 | { | ||
1269 | error("%.100s: host has no IP address.", buf); | ||
1270 | goto fail; | ||
1271 | } | ||
1272 | sin.sin_family = hp->h_addrtype; | ||
1273 | memcpy(&sin.sin_addr, hp->h_addr_list[0], | ||
1274 | sizeof(sin.sin_addr)); | ||
1275 | } | ||
1276 | /* Set port number. */ | ||
1277 | sin.sin_port = htons(6000 + display_number); | ||
1278 | |||
1279 | /* Create a socket. */ | ||
1280 | sock = socket(sin.sin_family, SOCK_STREAM, 0); | ||
1281 | if (sock < 0) | ||
1282 | { | ||
1283 | error("socket: %.100s", strerror(errno)); | ||
1284 | goto fail; | ||
1285 | } | ||
1286 | /* Connect it to the display. */ | ||
1287 | if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | ||
1288 | { | ||
1289 | error("connect %.100s:%d: %.100s", buf, 6000 + display_number, | ||
1290 | strerror(errno)); | ||
1291 | close(sock); | ||
1292 | goto fail; | ||
1293 | } | ||
1294 | |||
1295 | success: | ||
1296 | /* We have successfully obtained a connection to the real X display. */ | ||
1297 | |||
1298 | /* Allocate a channel for this connection. */ | ||
1299 | if (x11_saved_proto == NULL) | ||
1300 | newch = channel_allocate(SSH_CHANNEL_OPEN, sock, remote_host); | ||
1301 | else | ||
1302 | newch = channel_allocate(SSH_CHANNEL_X11_OPEN, sock, remote_host); | ||
1303 | channels[newch].remote_id = remote_channel; | ||
1304 | |||
1305 | /* Send a confirmation to the remote host. */ | ||
1306 | packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); | ||
1307 | packet_put_int(remote_channel); | ||
1308 | packet_put_int(newch); | ||
1309 | packet_send(); | ||
1310 | |||
1311 | return; | ||
1312 | |||
1313 | fail: | ||
1314 | /* Send refusal to the remote host. */ | ||
1315 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | ||
1316 | packet_put_int(remote_channel); | ||
1317 | packet_send(); | ||
1318 | } | ||
1319 | |||
1320 | /* Requests forwarding of X11 connections, generates fake authentication | ||
1321 | data, and enables authentication spoofing. */ | ||
1322 | |||
1323 | void x11_request_forwarding_with_spoofing(const char *proto, const char *data) | ||
1324 | { | ||
1325 | unsigned int data_len = (unsigned int)strlen(data) / 2; | ||
1326 | unsigned int i, value; | ||
1327 | char *new_data; | ||
1328 | int screen_number; | ||
1329 | const char *cp; | ||
1330 | u_int32_t rand = 0; | ||
1331 | |||
1332 | cp = getenv("DISPLAY"); | ||
1333 | if (cp) | ||
1334 | cp = strchr(cp, ':'); | ||
1335 | if (cp) | ||
1336 | cp = strchr(cp, '.'); | ||
1337 | if (cp) | ||
1338 | screen_number = atoi(cp + 1); | ||
1339 | else | ||
1340 | screen_number = 0; | ||
1341 | |||
1342 | /* Save protocol name. */ | ||
1343 | x11_saved_proto = xstrdup(proto); | ||
1344 | |||
1345 | /* Extract real authentication data and generate fake data of the same | ||
1346 | length. */ | ||
1347 | x11_saved_data = xmalloc(data_len); | ||
1348 | x11_fake_data = xmalloc(data_len); | ||
1349 | for (i = 0; i < data_len; i++) | ||
1350 | { | ||
1351 | if (sscanf(data + 2 * i, "%2x", &value) != 1) | ||
1352 | fatal("x11_request_forwarding: bad authentication data: %.100s", data); | ||
1353 | if (i % 4 == 0) | ||
1354 | rand = arc4random(); | ||
1355 | x11_saved_data[i] = value; | ||
1356 | x11_fake_data[i] = rand & 0xff; | ||
1357 | rand >>= 8; | ||
1358 | } | ||
1359 | x11_saved_data_len = data_len; | ||
1360 | x11_fake_data_len = data_len; | ||
1361 | |||
1362 | /* Convert the fake data into hex. */ | ||
1363 | new_data = xmalloc(2 * data_len + 1); | ||
1364 | for (i = 0; i < data_len; i++) | ||
1365 | sprintf(new_data + 2 * i, "%02x", (unsigned char)x11_fake_data[i]); | ||
1366 | |||
1367 | /* Send the request packet. */ | ||
1368 | packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); | ||
1369 | packet_put_string(proto, strlen(proto)); | ||
1370 | packet_put_string(new_data, strlen(new_data)); | ||
1371 | packet_put_int(screen_number); | ||
1372 | packet_send(); | ||
1373 | packet_write_wait(); | ||
1374 | xfree(new_data); | ||
1375 | } | ||
1376 | |||
1377 | /* Sends a message to the server to request authentication fd forwarding. */ | ||
1378 | |||
1379 | void auth_request_forwarding() | ||
1380 | { | ||
1381 | packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); | ||
1382 | packet_send(); | ||
1383 | packet_write_wait(); | ||
1384 | } | ||
1385 | |||
1386 | /* Returns the name of the forwarded authentication socket. Returns NULL | ||
1387 | if there is no forwarded authentication socket. The returned value | ||
1388 | points to a static buffer. */ | ||
1389 | |||
1390 | char *auth_get_socket_name() | ||
1391 | { | ||
1392 | return channel_forwarded_auth_socket_name; | ||
1393 | } | ||
1394 | |||
1395 | /* removes the agent forwarding socket */ | ||
1396 | |||
1397 | void cleanup_socket(void) { | ||
1398 | remove(channel_forwarded_auth_socket_name); | ||
1399 | rmdir(channel_forwarded_auth_socket_dir); | ||
1400 | } | ||
1401 | |||
1402 | /* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. | ||
1403 | This starts forwarding authentication requests. */ | ||
1404 | |||
1405 | void auth_input_request_forwarding(struct passwd *pw) | ||
1406 | { | ||
1407 | int sock, newch; | ||
1408 | struct sockaddr_un sunaddr; | ||
1409 | |||
1410 | if (auth_get_socket_name() != NULL) | ||
1411 | fatal("Protocol error: authentication forwarding requested twice."); | ||
1412 | |||
1413 | /* Temporarily drop privileged uid for mkdir/bind. */ | ||
1414 | temporarily_use_uid(pw->pw_uid); | ||
1415 | |||
1416 | /* Allocate a buffer for the socket name, and format the name. */ | ||
1417 | channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME); | ||
1418 | channel_forwarded_auth_socket_dir = xmalloc(MAX_SOCKET_NAME); | ||
1419 | strlcpy(channel_forwarded_auth_socket_dir, "/tmp/ssh-XXXXXXXX", MAX_SOCKET_NAME); | ||
1420 | |||
1421 | /* Create private directory for socket */ | ||
1422 | if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL) | ||
1423 | packet_disconnect("mkdtemp: %.100s", strerror(errno)); | ||
1424 | snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME, | ||
1425 | "%s/agent.%d", channel_forwarded_auth_socket_dir, (int)getpid()); | ||
1426 | |||
1427 | if (atexit(cleanup_socket) < 0) { | ||
1428 | int saved=errno; | ||
1429 | cleanup_socket(); | ||
1430 | packet_disconnect("socket: %.100s", strerror(saved)); | ||
1431 | } | ||
1432 | |||
1433 | /* Create the socket. */ | ||
1434 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | ||
1435 | if (sock < 0) | ||
1436 | packet_disconnect("socket: %.100s", strerror(errno)); | ||
1437 | |||
1438 | /* Bind it to the name. */ | ||
1439 | memset(&sunaddr, 0, sizeof(sunaddr)); | ||
1440 | sunaddr.sun_family = AF_UNIX; | ||
1441 | strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, | ||
1442 | sizeof(sunaddr.sun_path)); | ||
1443 | |||
1444 | if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) | ||
1445 | packet_disconnect("bind: %.100s", strerror(errno)); | ||
1446 | |||
1447 | /* Restore the privileged uid. */ | ||
1448 | restore_uid(); | ||
1449 | |||
1450 | /* Start listening on the socket. */ | ||
1451 | if (listen(sock, 5) < 0) | ||
1452 | packet_disconnect("listen: %.100s", strerror(errno)); | ||
1453 | |||
1454 | /* Allocate a channel for the authentication agent socket. */ | ||
1455 | newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock, | ||
1456 | xstrdup("auth socket")); | ||
1457 | strcpy(channels[newch].path, channel_forwarded_auth_socket_name); | ||
1458 | } | ||
1459 | |||
1460 | /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ | ||
1461 | |||
1462 | void auth_input_open_request() | ||
1463 | { | ||
1464 | int remch, sock, newch; | ||
1465 | char *dummyname; | ||
1466 | |||
1467 | /* Read the remote channel number from the message. */ | ||
1468 | remch = packet_get_int(); | ||
1469 | |||
1470 | /* Get a connection to the local authentication agent (this may again get | ||
1471 | forwarded). */ | ||
1472 | sock = ssh_get_authentication_socket(); | ||
1473 | |||
1474 | /* If we could not connect the agent, send an error message back to | ||
1475 | the server. This should never happen unless the agent | ||
1476 | dies, because authentication forwarding is only enabled if we have an | ||
1477 | agent. */ | ||
1478 | if (sock < 0){ | ||
1479 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | ||
1480 | packet_put_int(remch); | ||
1481 | packet_send(); | ||
1482 | return; | ||
1483 | } | ||
1484 | |||
1485 | debug("Forwarding authentication connection."); | ||
1486 | |||
1487 | /* Dummy host name. This will be freed when the channel is freed; it will | ||
1488 | still be valid in the packet_put_string below since the channel cannot | ||
1489 | yet be freed at that point. */ | ||
1490 | dummyname = xstrdup("authentication agent connection"); | ||
1491 | |||
1492 | newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname); | ||
1493 | channels[newch].remote_id = remch; | ||
1494 | |||
1495 | /* Send a confirmation to the remote host. */ | ||
1496 | packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); | ||
1497 | packet_put_int(remch); | ||
1498 | packet_put_int(newch); | ||
1499 | packet_send(); | ||
1500 | } | ||
diff --git a/channels.h b/channels.h new file mode 100644 index 000000000..9794ef50d --- /dev/null +++ b/channels.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* RCSID("$Id: channels.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
2 | |||
3 | #ifndef CHANNELS_H | ||
4 | #define CHANNELS_H | ||
5 | |||
6 | /* Definitions for channel types. */ | ||
7 | #define SSH_CHANNEL_FREE 0 /* This channel is free (unused). */ | ||
8 | #define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 conn. */ | ||
9 | #define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ | ||
10 | #define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ | ||
11 | #define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ | ||
12 | #define SSH_CHANNEL_CLOSED 5 /* waiting for close confirmation */ | ||
13 | /* SSH_CHANNEL_AUTH_FD 6 authentication fd */ | ||
14 | #define SSH_CHANNEL_AUTH_SOCKET 7 /* authentication socket */ | ||
15 | /* SSH_CHANNEL_AUTH_SOCKET_FD 8 connection to auth socket */ | ||
16 | #define SSH_CHANNEL_X11_OPEN 9 /* reading first X11 packet */ | ||
17 | #define SSH_CHANNEL_INPUT_DRAINING 10 /* sending remaining data to conn */ | ||
18 | #define SSH_CHANNEL_OUTPUT_DRAINING 11 /* sending remaining data to app */ | ||
19 | |||
20 | /* Data structure for channel data. This is iniailized in channel_allocate | ||
21 | and cleared in channel_free. */ | ||
22 | |||
23 | typedef struct Channel | ||
24 | { | ||
25 | int type; /* channel type/state */ | ||
26 | int self; /* my own channel identifier */ | ||
27 | int remote_id; /* channel identifier for remote peer */ | ||
28 | /* peer can be reached over encrypted connection, via packet-sent */ | ||
29 | int istate; | ||
30 | int ostate; | ||
31 | int x11; | ||
32 | int sock; /* data socket, linked to this channel */ | ||
33 | Buffer input; /* data read from socket, to be sent over encrypted connection */ | ||
34 | Buffer output; /* data received over encrypted connection for send on socket */ | ||
35 | char path[200]; /* path for unix domain sockets, or host name for forwards */ | ||
36 | int listening_port; /* port being listened for forwards */ | ||
37 | int host_port; /* remote port to connect for forwards */ | ||
38 | char *remote_name; /* remote hostname */ | ||
39 | } Channel; | ||
40 | |||
41 | #endif | ||
diff --git a/cipher.c b/cipher.c new file mode 100644 index 000000000..b47e7ecd8 --- /dev/null +++ b/cipher.c | |||
@@ -0,0 +1,304 @@ | |||
1 | /* | ||
2 | |||
3 | cipher.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Apr 19 17:41:39 1995 ylo | ||
11 | |||
12 | */ | ||
13 | |||
14 | #include "includes.h" | ||
15 | RCSID("$Id: cipher.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
16 | |||
17 | #include "ssh.h" | ||
18 | #include "cipher.h" | ||
19 | |||
20 | #include <openssl/md5.h> | ||
21 | |||
22 | /* | ||
23 | * What kind of tripple DES are these 2 routines? | ||
24 | * | ||
25 | * Why is there a redundant initialization vector? | ||
26 | * | ||
27 | * If only iv3 was used, then, this would till effect have been | ||
28 | * outer-cbc. However, there is also a private iv1 == iv2 which | ||
29 | * perhaps makes differential analysis easier. On the other hand, the | ||
30 | * private iv1 probably makes the CRC-32 attack ineffective. This is a | ||
31 | * result of that there is no longer any known iv1 to use when | ||
32 | * choosing the X block. | ||
33 | */ | ||
34 | void | ||
35 | SSH_3CBC_ENCRYPT(des_key_schedule ks1, | ||
36 | des_key_schedule ks2, des_cblock *iv2, | ||
37 | des_key_schedule ks3, des_cblock *iv3, | ||
38 | void *dest, void *src, | ||
39 | unsigned int len) | ||
40 | { | ||
41 | des_cblock iv1; | ||
42 | |||
43 | memcpy(&iv1, iv2, 8); | ||
44 | |||
45 | des_cbc_encrypt(src, dest, len, ks1, &iv1, DES_ENCRYPT); | ||
46 | memcpy(&iv1, dest + len - 8, 8); | ||
47 | |||
48 | des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_DECRYPT); | ||
49 | memcpy(iv2, &iv1, 8); /* Note how iv1 == iv2 on entry and exit. */ | ||
50 | |||
51 | des_cbc_encrypt(dest, dest, len, ks3, iv3, DES_ENCRYPT); | ||
52 | memcpy(iv3, dest + len - 8, 8); | ||
53 | } | ||
54 | |||
55 | void | ||
56 | SSH_3CBC_DECRYPT(des_key_schedule ks1, | ||
57 | des_key_schedule ks2, des_cblock *iv2, | ||
58 | des_key_schedule ks3, des_cblock *iv3, | ||
59 | void *dest, void *src, | ||
60 | unsigned int len) | ||
61 | { | ||
62 | des_cblock iv1; | ||
63 | |||
64 | memcpy(&iv1, iv2, 8); | ||
65 | |||
66 | des_cbc_encrypt(src, dest, len, ks3, iv3, DES_DECRYPT); | ||
67 | memcpy(iv3, src + len - 8, 8); | ||
68 | |||
69 | des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_ENCRYPT); | ||
70 | memcpy(iv2, dest + len - 8, 8); | ||
71 | |||
72 | des_cbc_encrypt(dest, dest, len, ks1, &iv1, DES_DECRYPT); | ||
73 | /* memcpy(&iv1, iv2, 8); */ /* Note how iv1 == iv2 on entry and exit. */ | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * SSH uses a variation on Blowfish, all bytes must be swapped before | ||
78 | * and after encryption/decryption. Thus the swap_bytes stuff (yuk). | ||
79 | */ | ||
80 | static | ||
81 | void | ||
82 | swap_bytes(const unsigned char *src, unsigned char *dst_, int n) | ||
83 | { | ||
84 | u_int32_t *dst = (u_int32_t *)dst_; /* dst must be properly aligned. */ | ||
85 | union { | ||
86 | u_int32_t i; | ||
87 | char c[4]; | ||
88 | } t; | ||
89 | |||
90 | /* assert((n & 7) == 0); */ | ||
91 | |||
92 | /* Process 8 bytes every lap. */ | ||
93 | for (n = n / 8; n > 0; n--) | ||
94 | { | ||
95 | t.c[3] = *src++; | ||
96 | t.c[2] = *src++; | ||
97 | t.c[1] = *src++; | ||
98 | t.c[0] = *src++; | ||
99 | *dst++ = t.i; | ||
100 | |||
101 | t.c[3] = *src++; | ||
102 | t.c[2] = *src++; | ||
103 | t.c[1] = *src++; | ||
104 | t.c[0] = *src++; | ||
105 | *dst++ = t.i; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | void (*cipher_attack_detected)(const char *fmt, ...) = fatal; | ||
110 | |||
111 | static inline | ||
112 | void | ||
113 | detect_cbc_attack(const unsigned char *src, | ||
114 | unsigned int len) | ||
115 | { | ||
116 | return; | ||
117 | |||
118 | log("CRC-32 CBC insertion attack detected"); | ||
119 | cipher_attack_detected("CRC-32 CBC insertion attack detected"); | ||
120 | } | ||
121 | |||
122 | /* Names of all encryption algorithms. These must match the numbers defined | ||
123 | int cipher.h. */ | ||
124 | static char *cipher_names[] = | ||
125 | { | ||
126 | "none", | ||
127 | "idea", | ||
128 | "des", | ||
129 | "3des", | ||
130 | "tss", | ||
131 | "rc4", | ||
132 | "blowfish" | ||
133 | }; | ||
134 | |||
135 | /* Returns a bit mask indicating which ciphers are supported by this | ||
136 | implementation. The bit mask has the corresponding bit set of each | ||
137 | supported cipher. */ | ||
138 | |||
139 | unsigned int cipher_mask() | ||
140 | { | ||
141 | unsigned int mask = 0; | ||
142 | mask |= 1 << SSH_CIPHER_3DES; /* Mandatory */ | ||
143 | mask |= 1 << SSH_CIPHER_BLOWFISH; | ||
144 | return mask; | ||
145 | } | ||
146 | |||
147 | /* Returns the name of the cipher. */ | ||
148 | |||
149 | const | ||
150 | char *cipher_name(int cipher) | ||
151 | { | ||
152 | if (cipher < 0 || cipher >= sizeof(cipher_names) / sizeof(cipher_names[0]) || | ||
153 | cipher_names[cipher] == NULL) | ||
154 | fatal("cipher_name: bad cipher number: %d", cipher); | ||
155 | return cipher_names[cipher]; | ||
156 | } | ||
157 | |||
158 | /* Parses the name of the cipher. Returns the number of the corresponding | ||
159 | cipher, or -1 on error. */ | ||
160 | |||
161 | int | ||
162 | cipher_number(const char *name) | ||
163 | { | ||
164 | int i; | ||
165 | for (i = 0; i < sizeof(cipher_names) / sizeof(cipher_names[0]); i++) | ||
166 | if (strcmp(cipher_names[i], name) == 0 && | ||
167 | (cipher_mask() & (1 << i))) | ||
168 | return i; | ||
169 | return -1; | ||
170 | } | ||
171 | |||
172 | /* Selects the cipher, and keys if by computing the MD5 checksum of the | ||
173 | passphrase and using the resulting 16 bytes as the key. */ | ||
174 | |||
175 | void cipher_set_key_string(CipherContext *context, int cipher, | ||
176 | const char *passphrase, int for_encryption) | ||
177 | { | ||
178 | MD5_CTX md; | ||
179 | unsigned char digest[16]; | ||
180 | |||
181 | MD5_Init(&md); | ||
182 | MD5_Update(&md, (const unsigned char *)passphrase, strlen(passphrase)); | ||
183 | MD5_Final(digest, &md); | ||
184 | |||
185 | cipher_set_key(context, cipher, digest, 16, for_encryption); | ||
186 | |||
187 | memset(digest, 0, sizeof(digest)); | ||
188 | memset(&md, 0, sizeof(md)); | ||
189 | } | ||
190 | |||
191 | /* Selects the cipher to use and sets the key. */ | ||
192 | |||
193 | void cipher_set_key(CipherContext *context, int cipher, | ||
194 | const unsigned char *key, int keylen, int for_encryption) | ||
195 | { | ||
196 | unsigned char padded[32]; | ||
197 | |||
198 | /* Set cipher type. */ | ||
199 | context->type = cipher; | ||
200 | |||
201 | /* Get 32 bytes of key data. Pad if necessary. (So that code below does | ||
202 | not need to worry about key size). */ | ||
203 | memset(padded, 0, sizeof(padded)); | ||
204 | memcpy(padded, key, keylen < sizeof(padded) ? keylen : sizeof(padded)); | ||
205 | |||
206 | /* Initialize the initialization vector. */ | ||
207 | switch (cipher) | ||
208 | { | ||
209 | case SSH_CIPHER_NONE: | ||
210 | /* Has to stay for authfile saving of private key with no passphrase */ | ||
211 | break; | ||
212 | |||
213 | case SSH_CIPHER_3DES: | ||
214 | /* Note: the least significant bit of each byte of key is parity, | ||
215 | and must be ignored by the implementation. 16 bytes of key are | ||
216 | used (first and last keys are the same). */ | ||
217 | if (keylen < 16) | ||
218 | error("Key length %d is insufficient for 3DES.", keylen); | ||
219 | des_set_key((void*)padded, context->u.des3.key1); | ||
220 | des_set_key((void*)(padded + 8), context->u.des3.key2); | ||
221 | if (keylen <= 16) | ||
222 | des_set_key((void*)padded, context->u.des3.key3); | ||
223 | else | ||
224 | des_set_key((void*)(padded + 16), context->u.des3.key3); | ||
225 | memset(context->u.des3.iv2, 0, sizeof(context->u.des3.iv2)); | ||
226 | memset(context->u.des3.iv3, 0, sizeof(context->u.des3.iv3)); | ||
227 | break; | ||
228 | |||
229 | case SSH_CIPHER_BLOWFISH: | ||
230 | BF_set_key(&context->u.bf.key, keylen, padded); | ||
231 | memset(context->u.bf.iv, 0, 8); | ||
232 | break; | ||
233 | |||
234 | default: | ||
235 | fatal("cipher_set_key: unknown cipher: %d", cipher); | ||
236 | } | ||
237 | memset(padded, 0, sizeof(padded)); | ||
238 | } | ||
239 | |||
240 | /* Encrypts data using the cipher. */ | ||
241 | |||
242 | void cipher_encrypt(CipherContext *context, unsigned char *dest, | ||
243 | const unsigned char *src, unsigned int len) | ||
244 | { | ||
245 | assert((len & 7) == 0); | ||
246 | |||
247 | switch (context->type) | ||
248 | { | ||
249 | case SSH_CIPHER_NONE: | ||
250 | memcpy(dest, src, len); | ||
251 | break; | ||
252 | |||
253 | case SSH_CIPHER_3DES: | ||
254 | SSH_3CBC_ENCRYPT(context->u.des3.key1, | ||
255 | context->u.des3.key2, &context->u.des3.iv2, | ||
256 | context->u.des3.key3, &context->u.des3.iv3, | ||
257 | dest, (void*)src, len); | ||
258 | break; | ||
259 | |||
260 | case SSH_CIPHER_BLOWFISH: | ||
261 | swap_bytes(src, dest, len); | ||
262 | BF_cbc_encrypt(dest, dest, len, | ||
263 | &context->u.bf.key, context->u.bf.iv, BF_ENCRYPT); | ||
264 | swap_bytes(dest, dest, len); | ||
265 | break; | ||
266 | |||
267 | default: | ||
268 | fatal("cipher_encrypt: unknown cipher: %d", context->type); | ||
269 | } | ||
270 | } | ||
271 | |||
272 | /* Decrypts data using the cipher. */ | ||
273 | |||
274 | void cipher_decrypt(CipherContext *context, unsigned char *dest, | ||
275 | const unsigned char *src, unsigned int len) | ||
276 | { | ||
277 | assert((len & 7) == 0); | ||
278 | |||
279 | switch (context->type) | ||
280 | { | ||
281 | case SSH_CIPHER_NONE: | ||
282 | memcpy(dest, src, len); | ||
283 | break; | ||
284 | |||
285 | case SSH_CIPHER_3DES: | ||
286 | /* CRC-32 attack? */ | ||
287 | SSH_3CBC_DECRYPT(context->u.des3.key1, | ||
288 | context->u.des3.key2, &context->u.des3.iv2, | ||
289 | context->u.des3.key3, &context->u.des3.iv3, | ||
290 | dest, (void*)src, len); | ||
291 | break; | ||
292 | |||
293 | case SSH_CIPHER_BLOWFISH: | ||
294 | detect_cbc_attack(src, len); | ||
295 | swap_bytes(src, dest, len); | ||
296 | BF_cbc_encrypt((void*)dest, dest, len, | ||
297 | &context->u.bf.key, context->u.bf.iv, BF_DECRYPT); | ||
298 | swap_bytes(dest, dest, len); | ||
299 | break; | ||
300 | |||
301 | default: | ||
302 | fatal("cipher_decrypt: unknown cipher: %d", context->type); | ||
303 | } | ||
304 | } | ||
diff --git a/cipher.h b/cipher.h new file mode 100644 index 000000000..4ecb8f8da --- /dev/null +++ b/cipher.h | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | |||
3 | cipher.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Apr 19 16:50:42 1995 ylo | ||
11 | |||
12 | */ | ||
13 | |||
14 | /* RCSID("$Id: cipher.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
15 | |||
16 | #ifndef CIPHER_H | ||
17 | #define CIPHER_H | ||
18 | |||
19 | #include <openssl/des.h> | ||
20 | #include <openssl/blowfish.h> | ||
21 | |||
22 | /* Cipher types. New types can be added, but old types should not be removed | ||
23 | for compatibility. The maximum allowed value is 31. */ | ||
24 | #define SSH_CIPHER_NOT_SET -1 /* None selected (invalid number). */ | ||
25 | #define SSH_CIPHER_NONE 0 /* no encryption */ | ||
26 | #define SSH_CIPHER_IDEA 1 /* IDEA CFB */ | ||
27 | #define SSH_CIPHER_DES 2 /* DES CBC */ | ||
28 | #define SSH_CIPHER_3DES 3 /* 3DES CBC */ | ||
29 | #define SSH_CIPHER_TSS 4 /* TRI's Simple Stream encryption CBC */ | ||
30 | #define SSH_CIPHER_RC4 5 /* Alleged RC4 */ | ||
31 | #define SSH_CIPHER_BLOWFISH 6 | ||
32 | |||
33 | typedef struct { | ||
34 | unsigned int type; | ||
35 | union { | ||
36 | struct { | ||
37 | des_key_schedule key1; | ||
38 | des_key_schedule key2; | ||
39 | des_cblock iv2; | ||
40 | des_key_schedule key3; | ||
41 | des_cblock iv3; | ||
42 | } des3; | ||
43 | struct { | ||
44 | struct bf_key_st key; | ||
45 | unsigned char iv[8]; | ||
46 | } bf; | ||
47 | } u; | ||
48 | } CipherContext; | ||
49 | |||
50 | /* Returns a bit mask indicating which ciphers are supported by this | ||
51 | implementation. The bit mask has the corresponding bit set of each | ||
52 | supported cipher. */ | ||
53 | unsigned int cipher_mask(); | ||
54 | |||
55 | /* Returns the name of the cipher. */ | ||
56 | const char *cipher_name(int cipher); | ||
57 | |||
58 | /* Parses the name of the cipher. Returns the number of the corresponding | ||
59 | cipher, or -1 on error. */ | ||
60 | int cipher_number(const char *name); | ||
61 | |||
62 | /* Selects the cipher to use and sets the key. If for_encryption is true, | ||
63 | the key is setup for encryption; otherwise it is setup for decryption. */ | ||
64 | void cipher_set_key(CipherContext *context, int cipher, | ||
65 | const unsigned char *key, int keylen, int for_encryption); | ||
66 | |||
67 | /* Sets key for the cipher by computing the MD5 checksum of the passphrase, | ||
68 | and using the resulting 16 bytes as the key. */ | ||
69 | void cipher_set_key_string(CipherContext *context, int cipher, | ||
70 | const char *passphrase, int for_encryption); | ||
71 | |||
72 | /* Encrypts data using the cipher. */ | ||
73 | void cipher_encrypt(CipherContext *context, unsigned char *dest, | ||
74 | const unsigned char *src, unsigned int len); | ||
75 | |||
76 | /* Decrypts data using the cipher. */ | ||
77 | void cipher_decrypt(CipherContext *context, unsigned char *dest, | ||
78 | const unsigned char *src, unsigned int len); | ||
79 | |||
80 | /* If and CRC-32 attack is detected this function is called. Defaults | ||
81 | * to fatal, changed to packet_disconnect in sshd and ssh. */ | ||
82 | extern void (*cipher_attack_detected)(const char *fmt, ...); | ||
83 | |||
84 | #endif /* CIPHER_H */ | ||
diff --git a/clientloop.c b/clientloop.c new file mode 100644 index 000000000..43373b72e --- /dev/null +++ b/clientloop.c | |||
@@ -0,0 +1,924 @@ | |||
1 | /* | ||
2 | |||
3 | clientloop.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | |||
11 | Created: Sat Sep 23 12:23:57 1995 ylo | ||
12 | |||
13 | The main loop for the interactive session (client side). | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: clientloop.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
19 | |||
20 | #include "xmalloc.h" | ||
21 | #include "ssh.h" | ||
22 | #include "packet.h" | ||
23 | #include "buffer.h" | ||
24 | #include "authfd.h" | ||
25 | |||
26 | /* Flag indicating whether quiet mode is on. */ | ||
27 | extern int quiet_flag; | ||
28 | |||
29 | /* Flag indicating that stdin should be redirected from /dev/null. */ | ||
30 | extern int stdin_null_flag; | ||
31 | |||
32 | /* Name of the host we are connecting to. This is the name given on the | ||
33 | command line, or the HostName specified for the user-supplied name | ||
34 | in a configuration file. */ | ||
35 | extern char *host; | ||
36 | |||
37 | /* Flag to indicate that we have received a window change signal which has | ||
38 | not yet been processed. This will cause a message indicating the new | ||
39 | window size to be sent to the server a little later. This is volatile | ||
40 | because this is updated in a signal handler. */ | ||
41 | static volatile int received_window_change_signal = 0; | ||
42 | |||
43 | /* Terminal modes, as saved by enter_raw_mode. */ | ||
44 | static struct termios saved_tio; | ||
45 | |||
46 | /* Flag indicating whether we are in raw mode. This is used by enter_raw_mode | ||
47 | and leave_raw_mode. */ | ||
48 | static int in_raw_mode = 0; | ||
49 | |||
50 | /* Flag indicating whether the user\'s terminal is in non-blocking mode. */ | ||
51 | static int in_non_blocking_mode = 0; | ||
52 | |||
53 | /* Common data for the client loop code. */ | ||
54 | static int escape_pending; /* Last character was the escape character */ | ||
55 | static int last_was_cr; /* Last character was a newline. */ | ||
56 | static int exit_status; /* Used to store the exit status of the command. */ | ||
57 | static int stdin_eof; /* EOF has been encountered on standard error. */ | ||
58 | static Buffer stdin_buffer; /* Buffer for stdin data. */ | ||
59 | static Buffer stdout_buffer; /* Buffer for stdout data. */ | ||
60 | static Buffer stderr_buffer; /* Buffer for stderr data. */ | ||
61 | static unsigned int buffer_high; /* Soft max buffer size. */ | ||
62 | static int max_fd; /* Maximum file descriptor number in select(). */ | ||
63 | static int connection_in; /* Connection to server (input). */ | ||
64 | static int connection_out; /* Connection to server (output). */ | ||
65 | static unsigned long stdin_bytes, stdout_bytes, stderr_bytes; | ||
66 | static int quit_pending; /* Set to non-zero to quit the client loop. */ | ||
67 | static int escape_char; /* Escape character. */ | ||
68 | |||
69 | /* Returns the user\'s terminal to normal mode if it had been put in raw | ||
70 | mode. */ | ||
71 | |||
72 | void leave_raw_mode() | ||
73 | { | ||
74 | if (!in_raw_mode) | ||
75 | return; | ||
76 | in_raw_mode = 0; | ||
77 | if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0) | ||
78 | perror("tcsetattr"); | ||
79 | |||
80 | fatal_remove_cleanup((void (*)(void *))leave_raw_mode, NULL); | ||
81 | } | ||
82 | |||
83 | /* Puts the user\'s terminal in raw mode. */ | ||
84 | |||
85 | void enter_raw_mode() | ||
86 | { | ||
87 | struct termios tio; | ||
88 | |||
89 | if (tcgetattr(fileno(stdin), &tio) < 0) | ||
90 | perror("tcgetattr"); | ||
91 | saved_tio = tio; | ||
92 | tio.c_iflag |= IGNPAR; | ||
93 | tio.c_iflag &= ~(ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXANY|IXOFF); | ||
94 | tio.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHONL); | ||
95 | #ifdef IEXTEN | ||
96 | tio.c_lflag &= ~IEXTEN; | ||
97 | #endif /* IEXTEN */ | ||
98 | tio.c_oflag &= ~OPOST; | ||
99 | tio.c_cc[VMIN] = 1; | ||
100 | tio.c_cc[VTIME] = 0; | ||
101 | if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) | ||
102 | perror("tcsetattr"); | ||
103 | in_raw_mode = 1; | ||
104 | |||
105 | fatal_add_cleanup((void (*)(void *))leave_raw_mode, NULL); | ||
106 | } | ||
107 | |||
108 | /* Puts stdin terminal in non-blocking mode. */ | ||
109 | |||
110 | /* Restores stdin to blocking mode. */ | ||
111 | |||
112 | void leave_non_blocking() | ||
113 | { | ||
114 | if (in_non_blocking_mode) | ||
115 | { | ||
116 | (void)fcntl(fileno(stdin), F_SETFL, 0); | ||
117 | in_non_blocking_mode = 0; | ||
118 | fatal_remove_cleanup((void (*)(void *))leave_non_blocking, NULL); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | void enter_non_blocking() | ||
123 | { | ||
124 | in_non_blocking_mode = 1; | ||
125 | (void)fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); | ||
126 | fatal_add_cleanup((void (*)(void *))leave_non_blocking, NULL); | ||
127 | } | ||
128 | |||
129 | /* Signal handler for the window change signal (SIGWINCH). This just | ||
130 | sets a flag indicating that the window has changed. */ | ||
131 | |||
132 | void window_change_handler(int sig) | ||
133 | { | ||
134 | received_window_change_signal = 1; | ||
135 | signal(SIGWINCH, window_change_handler); | ||
136 | } | ||
137 | |||
138 | /* Signal handler for signals that cause the program to terminate. These | ||
139 | signals must be trapped to restore terminal modes. */ | ||
140 | |||
141 | void signal_handler(int sig) | ||
142 | { | ||
143 | if (in_raw_mode) | ||
144 | leave_raw_mode(); | ||
145 | if (in_non_blocking_mode) | ||
146 | leave_non_blocking(); | ||
147 | channel_stop_listening(); | ||
148 | packet_close(); | ||
149 | fatal("Killed by signal %d.", sig); | ||
150 | } | ||
151 | |||
152 | /* Returns current time in seconds from Jan 1, 1970 with the maximum available | ||
153 | resolution. */ | ||
154 | |||
155 | double get_current_time() | ||
156 | { | ||
157 | struct timeval tv; | ||
158 | gettimeofday(&tv, NULL); | ||
159 | return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; | ||
160 | } | ||
161 | |||
162 | /* This is called when the interactive is entered. This checks if there | ||
163 | is an EOF coming on stdin. We must check this explicitly, as select() | ||
164 | does not appear to wake up when redirecting from /dev/null. */ | ||
165 | |||
166 | void client_check_initial_eof_on_stdin() | ||
167 | { | ||
168 | int len; | ||
169 | char buf[1]; | ||
170 | |||
171 | /* If standard input is to be "redirected from /dev/null", we simply | ||
172 | mark that we have seen an EOF and send an EOF message to the server. | ||
173 | Otherwise, we try to read a single character; it appears that for some | ||
174 | files, such /dev/null, select() never wakes up for read for this | ||
175 | descriptor, which means that we never get EOF. This way we will get | ||
176 | the EOF if stdin comes from /dev/null or similar. */ | ||
177 | if (stdin_null_flag) | ||
178 | { | ||
179 | /* Fake EOF on stdin. */ | ||
180 | debug("Sending eof."); | ||
181 | stdin_eof = 1; | ||
182 | packet_start(SSH_CMSG_EOF); | ||
183 | packet_send(); | ||
184 | } | ||
185 | else | ||
186 | { | ||
187 | /* Enter non-blocking mode for stdin. */ | ||
188 | enter_non_blocking(); | ||
189 | |||
190 | /* Check for immediate EOF on stdin. */ | ||
191 | len = read(fileno(stdin), buf, 1); | ||
192 | if (len == 0) | ||
193 | { | ||
194 | /* EOF. Record that we have seen it and send EOF to server. */ | ||
195 | debug("Sending eof."); | ||
196 | stdin_eof = 1; | ||
197 | packet_start(SSH_CMSG_EOF); | ||
198 | packet_send(); | ||
199 | } | ||
200 | else | ||
201 | if (len > 0) | ||
202 | { | ||
203 | /* Got data. We must store the data in the buffer, and also | ||
204 | process it as an escape character if appropriate. */ | ||
205 | if ((unsigned char)buf[0] == escape_char) | ||
206 | escape_pending = 1; | ||
207 | else | ||
208 | { | ||
209 | buffer_append(&stdin_buffer, buf, 1); | ||
210 | stdin_bytes += 1; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | /* Leave non-blocking mode. */ | ||
215 | leave_non_blocking(); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | /* Get packets from the connection input buffer, and process them as long | ||
220 | as there are packets available. */ | ||
221 | |||
222 | void client_process_buffered_input_packets() | ||
223 | { | ||
224 | int type; | ||
225 | char *data; | ||
226 | unsigned int data_len; | ||
227 | int payload_len; | ||
228 | |||
229 | /* Process any buffered packets from the server. */ | ||
230 | while (!quit_pending && (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) | ||
231 | { | ||
232 | switch (type) | ||
233 | { | ||
234 | |||
235 | case SSH_SMSG_STDOUT_DATA: | ||
236 | data = packet_get_string(&data_len); | ||
237 | packet_integrity_check(payload_len, 4 + data_len, type); | ||
238 | buffer_append(&stdout_buffer, data, data_len); | ||
239 | stdout_bytes += data_len; | ||
240 | memset(data, 0, data_len); | ||
241 | xfree(data); | ||
242 | break; | ||
243 | |||
244 | case SSH_SMSG_STDERR_DATA: | ||
245 | data = packet_get_string(&data_len); | ||
246 | packet_integrity_check(payload_len, 4 + data_len, type); | ||
247 | buffer_append(&stderr_buffer, data, data_len); | ||
248 | stdout_bytes += data_len; | ||
249 | memset(data, 0, data_len); | ||
250 | xfree(data); | ||
251 | break; | ||
252 | |||
253 | case SSH_SMSG_EXITSTATUS: | ||
254 | packet_integrity_check(payload_len, 4, type); | ||
255 | exit_status = packet_get_int(); | ||
256 | /* Acknowledge the exit. */ | ||
257 | packet_start(SSH_CMSG_EXIT_CONFIRMATION); | ||
258 | packet_send(); | ||
259 | /* Must wait for packet to be sent since we are exiting the | ||
260 | loop. */ | ||
261 | packet_write_wait(); | ||
262 | /* Flag that we want to exit. */ | ||
263 | quit_pending = 1; | ||
264 | break; | ||
265 | |||
266 | case SSH_SMSG_X11_OPEN: | ||
267 | x11_input_open(payload_len); | ||
268 | break; | ||
269 | |||
270 | case SSH_MSG_PORT_OPEN: | ||
271 | channel_input_port_open(payload_len); | ||
272 | break; | ||
273 | |||
274 | case SSH_SMSG_AGENT_OPEN: | ||
275 | packet_integrity_check(payload_len, 4, type); | ||
276 | auth_input_open_request(); | ||
277 | break; | ||
278 | |||
279 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: | ||
280 | packet_integrity_check(payload_len, 4 + 4, type); | ||
281 | channel_input_open_confirmation(); | ||
282 | break; | ||
283 | |||
284 | case SSH_MSG_CHANNEL_OPEN_FAILURE: | ||
285 | packet_integrity_check(payload_len, 4, type); | ||
286 | channel_input_open_failure(); | ||
287 | break; | ||
288 | |||
289 | case SSH_MSG_CHANNEL_DATA: | ||
290 | channel_input_data(payload_len); | ||
291 | break; | ||
292 | |||
293 | case SSH_MSG_CHANNEL_CLOSE: | ||
294 | packet_integrity_check(payload_len, 4, type); | ||
295 | channel_input_close(); | ||
296 | break; | ||
297 | |||
298 | case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: | ||
299 | packet_integrity_check(payload_len, 4, type); | ||
300 | channel_input_close_confirmation(); | ||
301 | break; | ||
302 | |||
303 | default: | ||
304 | /* Any unknown packets received during the actual session | ||
305 | cause the session to terminate. This is intended to make | ||
306 | debugging easier since no confirmations are sent. Any | ||
307 | compatible protocol extensions must be negotiated during | ||
308 | the preparatory phase. */ | ||
309 | packet_disconnect("Protocol error during session: type %d", | ||
310 | type); | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | |||
315 | /* Make packets from buffered stdin data, and buffer them for sending to | ||
316 | the connection. */ | ||
317 | |||
318 | void client_make_packets_from_stdin_data() | ||
319 | { | ||
320 | unsigned int len; | ||
321 | |||
322 | /* Send buffered stdin data to the server. */ | ||
323 | while (buffer_len(&stdin_buffer) > 0 && | ||
324 | packet_not_very_much_data_to_write()) | ||
325 | { | ||
326 | len = buffer_len(&stdin_buffer); | ||
327 | if (len > 32768) | ||
328 | len = 32768; /* Keep the packets at reasonable size. */ | ||
329 | packet_start(SSH_CMSG_STDIN_DATA); | ||
330 | packet_put_string(buffer_ptr(&stdin_buffer), len); | ||
331 | packet_send(); | ||
332 | buffer_consume(&stdin_buffer, len); | ||
333 | /* If we have a pending EOF, send it now. */ | ||
334 | if (stdin_eof && buffer_len(&stdin_buffer) == 0) | ||
335 | { | ||
336 | packet_start(SSH_CMSG_EOF); | ||
337 | packet_send(); | ||
338 | } | ||
339 | } | ||
340 | } | ||
341 | |||
342 | /* Checks if the client window has changed, and sends a packet about it to | ||
343 | the server if so. The actual change is detected elsewhere (by a software | ||
344 | interrupt on Unix); this just checks the flag and sends a message if | ||
345 | appropriate. */ | ||
346 | |||
347 | void client_check_window_change() | ||
348 | { | ||
349 | /* Send possible window change message to the server. */ | ||
350 | if (received_window_change_signal) | ||
351 | { | ||
352 | struct winsize ws; | ||
353 | |||
354 | /* Clear the window change indicator. */ | ||
355 | received_window_change_signal = 0; | ||
356 | |||
357 | /* Read new window size. */ | ||
358 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) | ||
359 | { | ||
360 | /* Successful, send the packet now. */ | ||
361 | packet_start(SSH_CMSG_WINDOW_SIZE); | ||
362 | packet_put_int(ws.ws_row); | ||
363 | packet_put_int(ws.ws_col); | ||
364 | packet_put_int(ws.ws_xpixel); | ||
365 | packet_put_int(ws.ws_ypixel); | ||
366 | packet_send(); | ||
367 | } | ||
368 | } | ||
369 | } | ||
370 | |||
371 | /* Waits until the client can do something (some data becomes available on | ||
372 | one of the file descriptors). */ | ||
373 | |||
374 | void client_wait_until_can_do_something(fd_set *readset, fd_set *writeset) | ||
375 | { | ||
376 | /* Initialize select masks. */ | ||
377 | FD_ZERO(readset); | ||
378 | |||
379 | /* Read from the connection, unless our buffers are full. */ | ||
380 | if (buffer_len(&stdout_buffer) < buffer_high && | ||
381 | buffer_len(&stderr_buffer) < buffer_high && | ||
382 | channel_not_very_much_buffered_data()) | ||
383 | FD_SET(connection_in, readset); | ||
384 | |||
385 | /* Read from stdin, unless we have seen EOF or have very much buffered | ||
386 | data to send to the server. */ | ||
387 | if (!stdin_eof && packet_not_very_much_data_to_write()) | ||
388 | FD_SET(fileno(stdin), readset); | ||
389 | |||
390 | FD_ZERO(writeset); | ||
391 | |||
392 | /* Add any selections by the channel mechanism. */ | ||
393 | channel_prepare_select(readset, writeset); | ||
394 | |||
395 | /* Select server connection if have data to write to the server. */ | ||
396 | if (packet_have_data_to_write()) | ||
397 | FD_SET(connection_out, writeset); | ||
398 | |||
399 | /* Select stdout if have data in buffer. */ | ||
400 | if (buffer_len(&stdout_buffer) > 0) | ||
401 | FD_SET(fileno(stdout), writeset); | ||
402 | |||
403 | /* Select stderr if have data in buffer. */ | ||
404 | if (buffer_len(&stderr_buffer) > 0) | ||
405 | FD_SET(fileno(stderr), writeset); | ||
406 | |||
407 | /* Update maximum file descriptor number, if appropriate. */ | ||
408 | if (channel_max_fd() > max_fd) | ||
409 | max_fd = channel_max_fd(); | ||
410 | |||
411 | /* Wait for something to happen. This will suspend the process until | ||
412 | some selected descriptor can be read, written, or has some other | ||
413 | event pending. Note: if you want to implement SSH_MSG_IGNORE | ||
414 | messages to fool traffic analysis, this might be the place to do | ||
415 | it: just have a random timeout for the select, and send a random | ||
416 | SSH_MSG_IGNORE packet when the timeout expires. */ | ||
417 | if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) | ||
418 | { | ||
419 | char buf[100]; | ||
420 | /* Some systems fail to clear these automatically. */ | ||
421 | FD_ZERO(readset); | ||
422 | FD_ZERO(writeset); | ||
423 | if (errno == EINTR) | ||
424 | return; | ||
425 | /* Note: we might still have data in the buffers. */ | ||
426 | snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); | ||
427 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
428 | stderr_bytes += strlen(buf); | ||
429 | quit_pending = 1; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | void client_suspend_self() | ||
434 | { | ||
435 | struct winsize oldws, newws; | ||
436 | |||
437 | /* Flush stdout and stderr buffers. */ | ||
438 | if (buffer_len(&stdout_buffer) > 0) | ||
439 | write(fileno(stdout), | ||
440 | buffer_ptr(&stdout_buffer), | ||
441 | buffer_len(&stdout_buffer)); | ||
442 | if (buffer_len(&stderr_buffer) > 0) | ||
443 | write(fileno(stderr), | ||
444 | buffer_ptr(&stderr_buffer), | ||
445 | buffer_len(&stderr_buffer)); | ||
446 | |||
447 | /* Leave raw mode. */ | ||
448 | leave_raw_mode(); | ||
449 | |||
450 | /* Free (and clear) the buffer to reduce the | ||
451 | amount of data that gets written to swap. */ | ||
452 | buffer_free(&stdin_buffer); | ||
453 | buffer_free(&stdout_buffer); | ||
454 | buffer_free(&stderr_buffer); | ||
455 | |||
456 | /* Save old window size. */ | ||
457 | ioctl(fileno(stdin), TIOCGWINSZ, &oldws); | ||
458 | |||
459 | /* Send the suspend signal to the program | ||
460 | itself. */ | ||
461 | kill(getpid(), SIGTSTP); | ||
462 | |||
463 | /* Check if the window size has changed. */ | ||
464 | if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && | ||
465 | (oldws.ws_row != newws.ws_row || oldws.ws_col != newws.ws_col || | ||
466 | oldws.ws_xpixel != newws.ws_xpixel || | ||
467 | oldws.ws_ypixel != newws.ws_ypixel)) | ||
468 | received_window_change_signal = 1; | ||
469 | |||
470 | /* OK, we have been continued by the user. | ||
471 | Reinitialize buffers. */ | ||
472 | buffer_init(&stdin_buffer); | ||
473 | buffer_init(&stdout_buffer); | ||
474 | buffer_init(&stderr_buffer); | ||
475 | |||
476 | /* Re-enter raw mode. */ | ||
477 | enter_raw_mode(); | ||
478 | } | ||
479 | |||
480 | void client_process_input(fd_set *readset) | ||
481 | { | ||
482 | int len, pid; | ||
483 | char buf[8192], *s; | ||
484 | |||
485 | /* Read input from the server, and add any such data to the buffer of the | ||
486 | packet subsystem. */ | ||
487 | if (FD_ISSET(connection_in, readset)) | ||
488 | { | ||
489 | /* Read as much as possible. */ | ||
490 | len = read(connection_in, buf, sizeof(buf)); | ||
491 | if (len == 0) | ||
492 | { | ||
493 | /* Received EOF. The remote host has closed the connection. */ | ||
494 | snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", | ||
495 | host); | ||
496 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
497 | stderr_bytes += strlen(buf); | ||
498 | quit_pending = 1; | ||
499 | return; | ||
500 | } | ||
501 | |||
502 | /* There is a kernel bug on Solaris that causes select to sometimes | ||
503 | wake up even though there is no data available. */ | ||
504 | if (len < 0 && errno == EAGAIN) | ||
505 | len = 0; | ||
506 | |||
507 | if (len < 0) | ||
508 | { | ||
509 | /* An error has encountered. Perhaps there is a network | ||
510 | problem. */ | ||
511 | snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", | ||
512 | host, strerror(errno)); | ||
513 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
514 | stderr_bytes += strlen(buf); | ||
515 | quit_pending = 1; | ||
516 | return; | ||
517 | } | ||
518 | packet_process_incoming(buf, len); | ||
519 | } | ||
520 | |||
521 | /* Read input from stdin. */ | ||
522 | if (FD_ISSET(fileno(stdin), readset)) | ||
523 | { | ||
524 | /* Read as much as possible. */ | ||
525 | len = read(fileno(stdin), buf, sizeof(buf)); | ||
526 | if (len <= 0) | ||
527 | { | ||
528 | /* Received EOF or error. They are treated similarly, | ||
529 | except that an error message is printed if it was | ||
530 | an error condition. */ | ||
531 | if (len < 0) | ||
532 | { | ||
533 | snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); | ||
534 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
535 | stderr_bytes += strlen(buf); | ||
536 | } | ||
537 | /* Mark that we have seen EOF. */ | ||
538 | stdin_eof = 1; | ||
539 | /* Send an EOF message to the server unless there is data | ||
540 | in the buffer. If there is data in the buffer, no message | ||
541 | will be sent now. Code elsewhere will send the EOF | ||
542 | when the buffer becomes empty if stdin_eof is set. */ | ||
543 | if (buffer_len(&stdin_buffer) == 0) | ||
544 | { | ||
545 | packet_start(SSH_CMSG_EOF); | ||
546 | packet_send(); | ||
547 | } | ||
548 | } | ||
549 | else | ||
550 | if (escape_char == -1) | ||
551 | { | ||
552 | /* Normal successful read, and no escape character. Just | ||
553 | append the data to buffer. */ | ||
554 | buffer_append(&stdin_buffer, buf, len); | ||
555 | stdin_bytes += len; | ||
556 | } | ||
557 | else | ||
558 | { | ||
559 | /* Normal, successful read. But we have an escape character | ||
560 | and have to process the characters one by one. */ | ||
561 | unsigned int i; | ||
562 | for (i = 0; i < len; i++) | ||
563 | { | ||
564 | unsigned char ch; | ||
565 | /* Get one character at a time. */ | ||
566 | ch = buf[i]; | ||
567 | |||
568 | /* Check if we have a pending escape character. */ | ||
569 | if (escape_pending) | ||
570 | { | ||
571 | /* We have previously seen an escape character. */ | ||
572 | /* Clear the flag now. */ | ||
573 | escape_pending = 0; | ||
574 | /* Process the escaped character. */ | ||
575 | switch (ch) | ||
576 | { | ||
577 | case '.': | ||
578 | /* Terminate the connection. */ | ||
579 | snprintf(buf, sizeof buf, "%c.\r\n", escape_char); | ||
580 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
581 | stderr_bytes += strlen(buf); | ||
582 | quit_pending = 1; | ||
583 | return; | ||
584 | |||
585 | case 'Z' - 64: | ||
586 | /* Suspend the program. */ | ||
587 | /* Print a message to that effect to the user. */ | ||
588 | snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char); | ||
589 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
590 | stderr_bytes += strlen(buf); | ||
591 | |||
592 | /* Restore terminal modes and suspend. */ | ||
593 | client_suspend_self(); | ||
594 | |||
595 | /* We have been continued. */ | ||
596 | continue; | ||
597 | |||
598 | case '&': | ||
599 | /* Detach the program (continue to serve connections, | ||
600 | but put in background and no more new | ||
601 | connections). */ | ||
602 | if (!stdin_eof) | ||
603 | { | ||
604 | /* Sending SSH_CMSG_EOF alone does not always | ||
605 | appear to be enough. So we try to send an | ||
606 | EOF character first. */ | ||
607 | packet_start(SSH_CMSG_STDIN_DATA); | ||
608 | packet_put_string("\004", 1); | ||
609 | packet_send(); | ||
610 | /* Close stdin. */ | ||
611 | stdin_eof = 1; | ||
612 | if (buffer_len(&stdin_buffer) == 0) | ||
613 | { | ||
614 | packet_start(SSH_CMSG_EOF); | ||
615 | packet_send(); | ||
616 | } | ||
617 | } | ||
618 | /* Restore tty modes. */ | ||
619 | leave_raw_mode(); | ||
620 | |||
621 | /* Stop listening for new connections. */ | ||
622 | channel_stop_listening(); | ||
623 | |||
624 | printf("%c& [backgrounded]\n", escape_char); | ||
625 | |||
626 | /* Fork into background. */ | ||
627 | pid = fork(); | ||
628 | if (pid < 0) | ||
629 | { | ||
630 | error("fork: %.100s", strerror(errno)); | ||
631 | continue; | ||
632 | } | ||
633 | if (pid != 0) | ||
634 | { /* This is the parent. */ | ||
635 | /* The parent just exits. */ | ||
636 | exit(0); | ||
637 | } | ||
638 | |||
639 | /* The child continues serving connections. */ | ||
640 | continue; | ||
641 | |||
642 | case '?': | ||
643 | snprintf(buf, sizeof buf, "%c?\r\n\ | ||
644 | Supported escape sequences:\r\n\ | ||
645 | ~. - terminate connection\r\n\ | ||
646 | ~^Z - suspend ssh\r\n\ | ||
647 | ~# - list forwarded connections\r\n\ | ||
648 | ~& - background ssh (when waiting for connections to terminate)\r\n\ | ||
649 | ~? - this message\r\n\ | ||
650 | ~~ - send the escape character by typing it twice\r\n\ | ||
651 | (Note that escapes are only recognized immediately after newline.)\r\n", | ||
652 | escape_char); | ||
653 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
654 | continue; | ||
655 | |||
656 | case '#': | ||
657 | snprintf(buf, sizeof buf, "%c#\r\n", escape_char); | ||
658 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
659 | s = channel_open_message(); | ||
660 | buffer_append(&stderr_buffer, s, strlen(s)); | ||
661 | xfree(s); | ||
662 | continue; | ||
663 | |||
664 | default: | ||
665 | if (ch != escape_char) | ||
666 | { | ||
667 | /* Escape character followed by non-special | ||
668 | character. Append both to the input | ||
669 | buffer. */ | ||
670 | buf[0] = escape_char; | ||
671 | buf[1] = ch; | ||
672 | buffer_append(&stdin_buffer, buf, 2); | ||
673 | stdin_bytes += 2; | ||
674 | continue; | ||
675 | } | ||
676 | /* Note that escape character typed twice falls through | ||
677 | here; the latter gets processed as a normal | ||
678 | character below. */ | ||
679 | break; | ||
680 | } | ||
681 | } | ||
682 | else | ||
683 | { | ||
684 | /* The previous character was not an escape char. | ||
685 | Check if this is an escape. */ | ||
686 | if (last_was_cr && ch == escape_char) | ||
687 | { | ||
688 | /* It is. Set the flag and continue to next | ||
689 | character. */ | ||
690 | escape_pending = 1; | ||
691 | continue; | ||
692 | } | ||
693 | } | ||
694 | |||
695 | /* Normal character. Record whether it was a newline, | ||
696 | and append it to the buffer. */ | ||
697 | last_was_cr = (ch == '\r' || ch == '\n'); | ||
698 | buf[0] = ch; | ||
699 | buffer_append(&stdin_buffer, buf, 1); | ||
700 | stdin_bytes += 1; | ||
701 | continue; | ||
702 | } | ||
703 | } | ||
704 | } | ||
705 | } | ||
706 | |||
707 | void client_process_output(fd_set *writeset) | ||
708 | { | ||
709 | int len; | ||
710 | char buf[100]; | ||
711 | |||
712 | /* Write buffered output to stdout. */ | ||
713 | if (FD_ISSET(fileno(stdout), writeset)) | ||
714 | { | ||
715 | /* Write as much data as possible. */ | ||
716 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), | ||
717 | buffer_len(&stdout_buffer)); | ||
718 | if (len <= 0) | ||
719 | { | ||
720 | if (errno == EAGAIN) | ||
721 | len = 0; | ||
722 | else | ||
723 | { | ||
724 | /* An error or EOF was encountered. Put an error message | ||
725 | to stderr buffer. */ | ||
726 | snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); | ||
727 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
728 | stderr_bytes += strlen(buf); | ||
729 | quit_pending = 1; | ||
730 | return; | ||
731 | } | ||
732 | } | ||
733 | /* Consume printed data from the buffer. */ | ||
734 | buffer_consume(&stdout_buffer, len); | ||
735 | } | ||
736 | |||
737 | /* Write buffered output to stderr. */ | ||
738 | if (FD_ISSET(fileno(stderr), writeset)) | ||
739 | { | ||
740 | /* Write as much data as possible. */ | ||
741 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), | ||
742 | buffer_len(&stderr_buffer)); | ||
743 | if (len <= 0) { | ||
744 | if (errno == EAGAIN) | ||
745 | len = 0; | ||
746 | else | ||
747 | { | ||
748 | /* EOF or error, but can't even print error message. */ | ||
749 | quit_pending = 1; | ||
750 | return; | ||
751 | } | ||
752 | } | ||
753 | /* Consume printed characters from the buffer. */ | ||
754 | buffer_consume(&stderr_buffer, len); | ||
755 | } | ||
756 | } | ||
757 | |||
758 | /* Implements the interactive session with the server. This is called | ||
759 | after the user has been authenticated, and a command has been | ||
760 | started on the remote host. If escape_char != -1, it is the character | ||
761 | used as an escape character for terminating or suspending the | ||
762 | session. */ | ||
763 | |||
764 | int client_loop(int have_pty, int escape_char_arg) | ||
765 | { | ||
766 | double start_time, total_time; | ||
767 | int len; | ||
768 | char buf[100]; | ||
769 | |||
770 | debug("Entering interactive session."); | ||
771 | |||
772 | start_time = get_current_time(); | ||
773 | |||
774 | /* Initialize variables. */ | ||
775 | escape_pending = 0; | ||
776 | last_was_cr = 1; | ||
777 | exit_status = -1; | ||
778 | stdin_eof = 0; | ||
779 | buffer_high = 64 * 1024; | ||
780 | connection_in = packet_get_connection_in(); | ||
781 | connection_out = packet_get_connection_out(); | ||
782 | max_fd = connection_in; | ||
783 | if (connection_out > max_fd) | ||
784 | max_fd = connection_out; | ||
785 | stdin_bytes = 0; | ||
786 | stdout_bytes = 0; | ||
787 | stderr_bytes = 0; | ||
788 | quit_pending = 0; | ||
789 | escape_char = escape_char_arg; | ||
790 | |||
791 | /* Initialize buffers. */ | ||
792 | buffer_init(&stdin_buffer); | ||
793 | buffer_init(&stdout_buffer); | ||
794 | buffer_init(&stderr_buffer); | ||
795 | |||
796 | /* Set signal handlers to restore non-blocking mode. */ | ||
797 | signal(SIGINT, signal_handler); | ||
798 | signal(SIGQUIT, signal_handler); | ||
799 | signal(SIGTERM, signal_handler); | ||
800 | signal(SIGPIPE, SIG_IGN); | ||
801 | if (have_pty) | ||
802 | signal(SIGWINCH, window_change_handler); | ||
803 | |||
804 | /* Enter raw mode if have a pseudo terminal. */ | ||
805 | if (have_pty) | ||
806 | enter_raw_mode(); | ||
807 | |||
808 | /* Check if we should immediately send of on stdin. */ | ||
809 | client_check_initial_eof_on_stdin(); | ||
810 | |||
811 | /* Main loop of the client for the interactive session mode. */ | ||
812 | while (!quit_pending) | ||
813 | { | ||
814 | fd_set readset, writeset; | ||
815 | |||
816 | /* Precess buffered packets sent by the server. */ | ||
817 | client_process_buffered_input_packets(); | ||
818 | |||
819 | /* Make packets of buffered stdin data, and buffer them for sending | ||
820 | to the server. */ | ||
821 | client_make_packets_from_stdin_data(); | ||
822 | |||
823 | /* Make packets from buffered channel data, and buffer them for sending | ||
824 | to the server. */ | ||
825 | if (packet_not_very_much_data_to_write()) | ||
826 | channel_output_poll(); | ||
827 | |||
828 | /* Check if the window size has changed, and buffer a message about | ||
829 | it to the server if so. */ | ||
830 | client_check_window_change(); | ||
831 | |||
832 | if (quit_pending) | ||
833 | break; | ||
834 | |||
835 | /* Wait until we have something to do (something becomes available | ||
836 | on one of the descriptors). */ | ||
837 | client_wait_until_can_do_something(&readset, &writeset); | ||
838 | |||
839 | if (quit_pending) | ||
840 | break; | ||
841 | |||
842 | /* Do channel operations. */ | ||
843 | channel_after_select(&readset, &writeset); | ||
844 | |||
845 | /* Process input from the connection and from stdin. Buffer any data | ||
846 | that is available. */ | ||
847 | client_process_input(&readset); | ||
848 | |||
849 | /* Process output to stdout and stderr. Output to the connection | ||
850 | is processed elsewhere (above). */ | ||
851 | client_process_output(&writeset); | ||
852 | |||
853 | /* Send as much buffered packet data as possible to the sender. */ | ||
854 | if (FD_ISSET(connection_out, &writeset)) | ||
855 | packet_write_poll(); | ||
856 | } | ||
857 | |||
858 | /* Terminate the session. */ | ||
859 | |||
860 | /* Stop watching for window change. */ | ||
861 | if (have_pty) | ||
862 | signal(SIGWINCH, SIG_DFL); | ||
863 | |||
864 | /* Stop listening for connections. */ | ||
865 | channel_stop_listening(); | ||
866 | |||
867 | /* In interactive mode (with pseudo tty) display a message indicating that | ||
868 | the connection has been closed. */ | ||
869 | if (have_pty && !quiet_flag) | ||
870 | { | ||
871 | snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); | ||
872 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
873 | stderr_bytes += strlen(buf); | ||
874 | } | ||
875 | |||
876 | /* Output any buffered data for stdout. */ | ||
877 | while (buffer_len(&stdout_buffer) > 0) | ||
878 | { | ||
879 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), | ||
880 | buffer_len(&stdout_buffer)); | ||
881 | if (len <= 0) | ||
882 | { | ||
883 | error("Write failed flushing stdout buffer."); | ||
884 | break; | ||
885 | } | ||
886 | buffer_consume(&stdout_buffer, len); | ||
887 | } | ||
888 | |||
889 | /* Output any buffered data for stderr. */ | ||
890 | while (buffer_len(&stderr_buffer) > 0) | ||
891 | { | ||
892 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), | ||
893 | buffer_len(&stderr_buffer)); | ||
894 | if (len <= 0) | ||
895 | { | ||
896 | error("Write failed flushing stderr buffer."); | ||
897 | break; | ||
898 | } | ||
899 | buffer_consume(&stderr_buffer, len); | ||
900 | } | ||
901 | |||
902 | /* Leave raw mode. */ | ||
903 | if (have_pty) | ||
904 | leave_raw_mode(); | ||
905 | |||
906 | /* Clear and free any buffers. */ | ||
907 | memset(buf, 0, sizeof(buf)); | ||
908 | buffer_free(&stdin_buffer); | ||
909 | buffer_free(&stdout_buffer); | ||
910 | buffer_free(&stderr_buffer); | ||
911 | |||
912 | /* Report bytes transferred, and transfer rates. */ | ||
913 | total_time = get_current_time() - start_time; | ||
914 | debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", | ||
915 | stdin_bytes, stdout_bytes, stderr_bytes, total_time); | ||
916 | if (total_time > 0) | ||
917 | debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", | ||
918 | stdin_bytes / total_time, stdout_bytes / total_time, | ||
919 | stderr_bytes / total_time); | ||
920 | |||
921 | /* Return the exit status of the program. */ | ||
922 | debug("Exit status %d", exit_status); | ||
923 | return exit_status; | ||
924 | } | ||
diff --git a/compat.c b/compat.c new file mode 100644 index 000000000..4974b1cba --- /dev/null +++ b/compat.c | |||
@@ -0,0 +1,10 @@ | |||
1 | #include "includes.h" | ||
2 | RCSID("$Id: compat.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
3 | |||
4 | #include "ssh.h" | ||
5 | |||
6 | int compat13=0; | ||
7 | void enable_compat13(void){ | ||
8 | log("Enabling compatibility mode for protocol 1.3"); | ||
9 | compat13=1; | ||
10 | } | ||
diff --git a/compat.h b/compat.h new file mode 100644 index 000000000..9d896c7dd --- /dev/null +++ b/compat.h | |||
@@ -0,0 +1,7 @@ | |||
1 | /* RCSID("$Id: compat.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
2 | |||
3 | #ifndef COMPAT_H | ||
4 | #define COMPAT_H | ||
5 | void enable_compat13(void); | ||
6 | extern int compat13; | ||
7 | #endif | ||
diff --git a/compress.c b/compress.c new file mode 100644 index 000000000..c3267f73f --- /dev/null +++ b/compress.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | |||
3 | compress.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Oct 25 22:12:46 1995 ylo | ||
11 | |||
12 | Interface to packet compression for ssh. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: compress.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
18 | |||
19 | #include "ssh.h" | ||
20 | #include "buffer.h" | ||
21 | #include "zlib.h" | ||
22 | |||
23 | static z_stream incoming_stream; | ||
24 | static z_stream outgoing_stream; | ||
25 | |||
26 | /* Initializes compression; level is compression level from 1 to 9 (as in | ||
27 | gzip). */ | ||
28 | |||
29 | void buffer_compress_init(int level) | ||
30 | { | ||
31 | debug("Enabling compression at level %d.", level); | ||
32 | if (level < 1 || level > 9) | ||
33 | fatal("Bad compression level %d.", level); | ||
34 | inflateInit(&incoming_stream); | ||
35 | deflateInit(&outgoing_stream, level); | ||
36 | } | ||
37 | |||
38 | /* Frees any data structures allocated for compression. */ | ||
39 | |||
40 | void buffer_compress_uninit() | ||
41 | { | ||
42 | debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f", | ||
43 | outgoing_stream.total_in, outgoing_stream.total_out, | ||
44 | outgoing_stream.total_in == 0 ? 0.0 : | ||
45 | (double)outgoing_stream.total_out / outgoing_stream.total_in); | ||
46 | debug("compress incoming: raw data %lu, compressed %lu, factor %.2f", | ||
47 | incoming_stream.total_out, incoming_stream.total_in, | ||
48 | incoming_stream.total_out == 0 ? 0.0 : | ||
49 | (double)incoming_stream.total_in / incoming_stream.total_out); | ||
50 | inflateEnd(&incoming_stream); | ||
51 | deflateEnd(&outgoing_stream); | ||
52 | } | ||
53 | |||
54 | /* Compresses the contents of input_buffer into output_buffer. All | ||
55 | packets compressed using this function will form a single | ||
56 | compressed data stream; however, data will be flushed at the end of | ||
57 | every call so that each output_buffer can be decompressed | ||
58 | independently (but in the appropriate order since they together | ||
59 | form a single compression stream) by the receiver. This appends | ||
60 | the compressed data to the output buffer. */ | ||
61 | |||
62 | void buffer_compress(Buffer *input_buffer, Buffer *output_buffer) | ||
63 | { | ||
64 | char buf[4096]; | ||
65 | int status; | ||
66 | |||
67 | /* This case is not handled below. */ | ||
68 | if (buffer_len(input_buffer) == 0) | ||
69 | return; | ||
70 | |||
71 | /* Input is the contents of the input buffer. */ | ||
72 | outgoing_stream.next_in = buffer_ptr(input_buffer); | ||
73 | outgoing_stream.avail_in = buffer_len(input_buffer); | ||
74 | |||
75 | /* Loop compressing until deflate() returns with avail_out != 0. */ | ||
76 | do | ||
77 | { | ||
78 | /* Set up fixed-size output buffer. */ | ||
79 | outgoing_stream.next_out = buf; | ||
80 | outgoing_stream.avail_out = sizeof(buf); | ||
81 | |||
82 | /* Compress as much data into the buffer as possible. */ | ||
83 | status = deflate(&outgoing_stream, Z_PARTIAL_FLUSH); | ||
84 | switch (status) | ||
85 | { | ||
86 | case Z_OK: | ||
87 | /* Append compressed data to output_buffer. */ | ||
88 | buffer_append(output_buffer, buf, | ||
89 | sizeof(buf) - outgoing_stream.avail_out); | ||
90 | break; | ||
91 | case Z_STREAM_END: | ||
92 | fatal("buffer_compress: deflate returned Z_STREAM_END"); | ||
93 | /*NOTREACHED*/ | ||
94 | case Z_STREAM_ERROR: | ||
95 | fatal("buffer_compress: deflate returned Z_STREAM_ERROR"); | ||
96 | /*NOTREACHED*/ | ||
97 | case Z_BUF_ERROR: | ||
98 | fatal("buffer_compress: deflate returned Z_BUF_ERROR"); | ||
99 | /*NOTREACHED*/ | ||
100 | default: | ||
101 | fatal("buffer_compress: deflate returned %d", status); | ||
102 | /*NOTREACHED*/ | ||
103 | } | ||
104 | } | ||
105 | while (outgoing_stream.avail_out == 0); | ||
106 | } | ||
107 | |||
108 | /* Uncompresses the contents of input_buffer into output_buffer. All | ||
109 | packets uncompressed using this function will form a single | ||
110 | compressed data stream; however, data will be flushed at the end of | ||
111 | every call so that each output_buffer. This must be called for the | ||
112 | same size units that the buffer_compress was called, and in the | ||
113 | same order that buffers compressed with that. This appends the | ||
114 | uncompressed data to the output buffer. */ | ||
115 | |||
116 | void buffer_uncompress(Buffer *input_buffer, Buffer *output_buffer) | ||
117 | { | ||
118 | char buf[4096]; | ||
119 | int status; | ||
120 | |||
121 | incoming_stream.next_in = buffer_ptr(input_buffer); | ||
122 | incoming_stream.avail_in = buffer_len(input_buffer); | ||
123 | |||
124 | incoming_stream.next_out = buf; | ||
125 | incoming_stream.avail_out = sizeof(buf); | ||
126 | |||
127 | for (;;) | ||
128 | { | ||
129 | status = inflate(&incoming_stream, Z_PARTIAL_FLUSH); | ||
130 | switch (status) | ||
131 | { | ||
132 | case Z_OK: | ||
133 | buffer_append(output_buffer, buf, | ||
134 | sizeof(buf) - incoming_stream.avail_out); | ||
135 | incoming_stream.next_out = buf; | ||
136 | incoming_stream.avail_out = sizeof(buf); | ||
137 | break; | ||
138 | case Z_STREAM_END: | ||
139 | fatal("buffer_uncompress: inflate returned Z_STREAM_END"); | ||
140 | /*NOTREACHED*/ | ||
141 | case Z_DATA_ERROR: | ||
142 | fatal("buffer_uncompress: inflate returned Z_DATA_ERROR"); | ||
143 | /*NOTREACHED*/ | ||
144 | case Z_STREAM_ERROR: | ||
145 | fatal("buffer_uncompress: inflate returned Z_STREAM_ERROR"); | ||
146 | /*NOTREACHED*/ | ||
147 | case Z_BUF_ERROR: | ||
148 | /* Comments in zlib.h say that we should keep calling inflate() | ||
149 | until we get an error. This appears to be the error that we | ||
150 | get. */ | ||
151 | return; | ||
152 | case Z_MEM_ERROR: | ||
153 | fatal("buffer_uncompress: inflate returned Z_MEM_ERROR"); | ||
154 | /*NOTREACHED*/ | ||
155 | default: | ||
156 | fatal("buffer_uncompress: inflate returned %d", status); | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
diff --git a/compress.h b/compress.h new file mode 100644 index 000000000..b3144d621 --- /dev/null +++ b/compress.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | |||
3 | compress.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Oct 25 22:12:46 1995 ylo | ||
11 | |||
12 | Interface to packet compression for ssh. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: compress.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
17 | |||
18 | #ifndef COMPRESS_H | ||
19 | #define COMPRESS_H | ||
20 | |||
21 | /* Initializes compression; level is compression level from 1 to 9 (as in | ||
22 | gzip). */ | ||
23 | void buffer_compress_init(int level); | ||
24 | |||
25 | /* Frees any data structures allocated by buffer_compress_init. */ | ||
26 | void buffer_compress_uninit(); | ||
27 | |||
28 | /* Compresses the contents of input_buffer into output_buffer. All | ||
29 | packets compressed using this function will form a single | ||
30 | compressed data stream; however, data will be flushed at the end of | ||
31 | every call so that each output_buffer can be decompressed | ||
32 | independently (but in the appropriate order since they together | ||
33 | form a single compression stream) by the receiver. This appends | ||
34 | the compressed data to the output buffer. */ | ||
35 | void buffer_compress(Buffer *input_buffer, Buffer *output_buffer); | ||
36 | |||
37 | /* Uncompresses the contents of input_buffer into output_buffer. All | ||
38 | packets uncompressed using this function will form a single | ||
39 | compressed data stream; however, data will be flushed at the end of | ||
40 | every call so that each output_buffer. This must be called for the | ||
41 | same size units that the buffer_compress was called, and in the | ||
42 | same order that buffers compressed with that. This appends the | ||
43 | uncompressed data to the output buffer. */ | ||
44 | void buffer_uncompress(Buffer *input_buffer, Buffer *output_buffer); | ||
45 | |||
46 | #endif /* COMPRESS_H */ | ||
diff --git a/crc32.c b/crc32.c new file mode 100644 index 000000000..dbb1e6b7b --- /dev/null +++ b/crc32.c | |||
@@ -0,0 +1,120 @@ | |||
1 | /* The implementation here was originally done by Gary S. Brown. I have | ||
2 | borrowed the tables directly, and made some minor changes to the | ||
3 | crc32-function (including changing the interface). //ylo */ | ||
4 | |||
5 | #include "includes.h" | ||
6 | RCSID("$Id: crc32.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
7 | |||
8 | #include "crc32.h" | ||
9 | |||
10 | /* ============================================================= */ | ||
11 | /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ | ||
12 | /* code or tables extracted from it, as desired without restriction. */ | ||
13 | /* */ | ||
14 | /* First, the polynomial itself and its table of feedback terms. The */ | ||
15 | /* polynomial is */ | ||
16 | /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ | ||
17 | /* */ | ||
18 | /* Note that we take it "backwards" and put the highest-order term in */ | ||
19 | /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ | ||
20 | /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ | ||
21 | /* the MSB being 1. */ | ||
22 | /* */ | ||
23 | /* Note that the usual hardware shift register implementation, which */ | ||
24 | /* is what we're using (we're merely optimizing it by doing eight-bit */ | ||
25 | /* chunks at a time) shifts bits into the lowest-order term. In our */ | ||
26 | /* implementation, that means shifting towards the right. Why do we */ | ||
27 | /* do it this way? Because the calculated CRC must be transmitted in */ | ||
28 | /* order from highest-order term to lowest-order term. UARTs transmit */ | ||
29 | /* characters in order from LSB to MSB. By storing the CRC this way, */ | ||
30 | /* we hand it to the UART in the order low-byte to high-byte; the UART */ | ||
31 | /* sends each low-bit to hight-bit; and the result is transmission bit */ | ||
32 | /* by bit from highest- to lowest-order term without requiring any bit */ | ||
33 | /* shuffling on our part. Reception works similarly. */ | ||
34 | /* */ | ||
35 | /* The feedback terms table consists of 256, 32-bit entries. Notes: */ | ||
36 | /* */ | ||
37 | /* The table can be generated at runtime if desired; code to do so */ | ||
38 | /* is shown later. It might not be obvious, but the feedback */ | ||
39 | /* terms simply represent the results of eight shift/xor opera- */ | ||
40 | /* tions for all combinations of data and CRC register values. */ | ||
41 | /* */ | ||
42 | /* The values must be right-shifted by eight bits by the "updcrc" */ | ||
43 | /* logic; the shift must be unsigned (bring in zeroes). On some */ | ||
44 | /* hardware you could probably optimize the shift in assembler by */ | ||
45 | /* using byte-swap instructions. */ | ||
46 | /* polynomial $edb88320 */ | ||
47 | /* */ | ||
48 | /* -------------------------------------------------------------------- */ | ||
49 | |||
50 | static unsigned int crc32_tab[] = { | ||
51 | 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, | ||
52 | 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, | ||
53 | 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, | ||
54 | 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, | ||
55 | 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, | ||
56 | 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, | ||
57 | 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, | ||
58 | 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, | ||
59 | 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, | ||
60 | 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, | ||
61 | 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, | ||
62 | 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, | ||
63 | 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, | ||
64 | 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, | ||
65 | 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, | ||
66 | 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, | ||
67 | 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, | ||
68 | 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, | ||
69 | 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, | ||
70 | 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, | ||
71 | 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, | ||
72 | 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, | ||
73 | 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, | ||
74 | 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, | ||
75 | 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, | ||
76 | 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, | ||
77 | 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, | ||
78 | 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, | ||
79 | 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, | ||
80 | 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, | ||
81 | 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, | ||
82 | 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, | ||
83 | 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, | ||
84 | 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, | ||
85 | 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, | ||
86 | 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, | ||
87 | 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, | ||
88 | 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, | ||
89 | 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, | ||
90 | 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, | ||
91 | 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, | ||
92 | 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, | ||
93 | 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, | ||
94 | 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, | ||
95 | 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, | ||
96 | 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, | ||
97 | 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, | ||
98 | 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, | ||
99 | 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, | ||
100 | 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, | ||
101 | 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, | ||
102 | 0x2d02ef8dL | ||
103 | }; | ||
104 | |||
105 | /* Return a 32-bit CRC of the contents of the buffer. */ | ||
106 | |||
107 | unsigned int crc32(const unsigned char *s, unsigned int len) | ||
108 | { | ||
109 | unsigned int i; | ||
110 | unsigned int crc32val; | ||
111 | |||
112 | crc32val = 0; | ||
113 | for (i = 0; i < len; i ++) | ||
114 | { | ||
115 | crc32val = | ||
116 | crc32_tab[(crc32val ^ s[i]) & 0xff] ^ | ||
117 | (crc32val >> 8); | ||
118 | } | ||
119 | return crc32val; | ||
120 | } | ||
diff --git a/crc32.h b/crc32.h new file mode 100644 index 000000000..456b20b86 --- /dev/null +++ b/crc32.h | |||
@@ -0,0 +1,25 @@ | |||
1 | /* | ||
2 | |||
3 | crc32.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1992 Tatu Ylonen, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Tue Feb 11 14:37:27 1992 ylo | ||
11 | |||
12 | Functions for computing 32-bit CRC. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: crc32.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
17 | |||
18 | #ifndef CRC32_H | ||
19 | #define CRC32_H | ||
20 | |||
21 | /* This computes a 32 bit CRC of the data in the buffer, and returns the | ||
22 | CRC. The polynomial used is 0xedb88320. */ | ||
23 | unsigned int crc32(const unsigned char *buf, unsigned int len); | ||
24 | |||
25 | #endif /* CRC32_H */ | ||
diff --git a/deattack.c b/deattack.c new file mode 100644 index 000000000..d5f8608ca --- /dev/null +++ b/deattack.c | |||
@@ -0,0 +1,180 @@ | |||
1 | /* | ||
2 | * $Id: deattack.c,v 1.1 1999/10/27 03:42:44 damien Exp $ | ||
3 | * Cryptographic attack detector for ssh - source code | ||
4 | * | ||
5 | * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. | ||
6 | * | ||
7 | * All rights reserved. Redistribution and use in source and binary | ||
8 | * forms, with or without modification, are permitted provided that | ||
9 | * this copyright notice is retained. | ||
10 | * | ||
11 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
12 | * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE | ||
13 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR | ||
14 | * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS | ||
15 | * SOFTWARE. | ||
16 | * | ||
17 | * Ariel Futoransky <futo@core-sdi.com> | ||
18 | * <http://www.core-sdi.com> */ | ||
19 | |||
20 | #include "includes.h" | ||
21 | #include "deattack.h" | ||
22 | #include "ssh.h" | ||
23 | #include "crc32.h" | ||
24 | #include "getput.h" | ||
25 | #include "xmalloc.h" | ||
26 | |||
27 | /* SSH Constants */ | ||
28 | #define SSH_MAXBLOCKS (32 * 1024) | ||
29 | #define SSH_BLOCKSIZE (8) | ||
30 | |||
31 | /* Hashing constants */ | ||
32 | #define HASH_MINSIZE (8 * 1024) | ||
33 | #define HASH_ENTRYSIZE (2) | ||
34 | #define HASH_FACTOR(x) ((x)*3/2) | ||
35 | #define HASH_UNUSEDCHAR (0xff) | ||
36 | #define HASH_UNUSED (0xffff) | ||
37 | #define HASH_IV (0xfffe) | ||
38 | |||
39 | #define HASH_MINBLOCKS (7*SSH_BLOCKSIZE) | ||
40 | |||
41 | |||
42 | /* Hash function (Input keys are cipher results) */ | ||
43 | #define HASH(x) GET_32BIT(x) | ||
44 | |||
45 | #define CMP(a,b) (memcmp(a, b, SSH_BLOCKSIZE)) | ||
46 | |||
47 | |||
48 | void | ||
49 | crc_update(u_int32_t * a, u_int32_t b) | ||
50 | { | ||
51 | b ^= *a; | ||
52 | *a = crc32((unsigned char *) &b, sizeof(b)); | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | check_crc | ||
57 | detects if a block is used in a particular pattern | ||
58 | */ | ||
59 | |||
60 | int | ||
61 | check_crc(unsigned char *S, unsigned char *buf, u_int32_t len, unsigned char *IV) | ||
62 | { | ||
63 | u_int32_t crc; | ||
64 | unsigned char *c; | ||
65 | |||
66 | crc = 0; | ||
67 | if (IV && !CMP(S, IV)) | ||
68 | { | ||
69 | crc_update(&crc, 1); | ||
70 | crc_update(&crc, 0); | ||
71 | } | ||
72 | for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) | ||
73 | { | ||
74 | if (!CMP(S, c)) | ||
75 | { | ||
76 | crc_update(&crc, 1); | ||
77 | crc_update(&crc, 0); | ||
78 | } else | ||
79 | { | ||
80 | crc_update(&crc, 0); | ||
81 | crc_update(&crc, 0); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | return (crc == 0); | ||
86 | } | ||
87 | |||
88 | |||
89 | /* | ||
90 | detect_attack | ||
91 | Detects a crc32 compensation attack on a packet | ||
92 | */ | ||
93 | int | ||
94 | detect_attack(unsigned char *buf, u_int32_t len, unsigned char *IV) | ||
95 | { | ||
96 | static u_int16_t *h = (u_int16_t *) NULL; | ||
97 | static u_int16_t n = HASH_MINSIZE / HASH_ENTRYSIZE; | ||
98 | register u_int32_t i, j; | ||
99 | u_int32_t l; | ||
100 | register unsigned char *c; | ||
101 | unsigned char *d; | ||
102 | |||
103 | |||
104 | assert(len <= (SSH_MAXBLOCKS * SSH_BLOCKSIZE)); | ||
105 | assert(len % SSH_BLOCKSIZE == 0); | ||
106 | |||
107 | for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2); | ||
108 | |||
109 | if (h == NULL) | ||
110 | { | ||
111 | debug("Installing crc compensation attack detector."); | ||
112 | n = l; | ||
113 | h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE); | ||
114 | } else | ||
115 | { | ||
116 | if (l > n) | ||
117 | { | ||
118 | n = l; | ||
119 | h = (u_int16_t *) xrealloc(h, n * HASH_ENTRYSIZE); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | |||
124 | if (len <= HASH_MINBLOCKS) | ||
125 | { | ||
126 | for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) | ||
127 | { | ||
128 | if (IV && (!CMP(c, IV))) | ||
129 | { | ||
130 | if ((check_crc(c, buf, len, IV))) | ||
131 | return (DEATTACK_DETECTED); | ||
132 | else | ||
133 | break; | ||
134 | } | ||
135 | for (d = buf; d < c; d += SSH_BLOCKSIZE) | ||
136 | { | ||
137 | if (!CMP(c, d)) | ||
138 | { | ||
139 | if ((check_crc(c, buf, len, IV))) | ||
140 | return (DEATTACK_DETECTED); | ||
141 | else | ||
142 | break; | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | return (DEATTACK_OK); | ||
147 | } | ||
148 | memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE); | ||
149 | |||
150 | if (IV) | ||
151 | h[HASH(IV) & (n - 1)] = HASH_IV; | ||
152 | |||
153 | |||
154 | for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) | ||
155 | { | ||
156 | for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED; | ||
157 | i = (i + 1) & (n - 1)) | ||
158 | { | ||
159 | if (h[i] == HASH_IV) | ||
160 | { | ||
161 | if (!CMP(c, IV)) | ||
162 | { | ||
163 | if (check_crc(c, buf, len, IV)) | ||
164 | return (DEATTACK_DETECTED); | ||
165 | else | ||
166 | break; | ||
167 | } | ||
168 | } else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) | ||
169 | { | ||
170 | if (check_crc(c, buf, len, IV)) | ||
171 | return (DEATTACK_DETECTED); | ||
172 | else | ||
173 | break; | ||
174 | } | ||
175 | } | ||
176 | h[i] = j; | ||
177 | } | ||
178 | |||
179 | return (DEATTACK_OK); | ||
180 | } | ||
diff --git a/deattack.h b/deattack.h new file mode 100644 index 000000000..a0dcf5b6f --- /dev/null +++ b/deattack.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /* $Id: deattack.h,v 1.1 1999/10/27 03:42:44 damien Exp $ | ||
2 | * Cryptographic attack detector for ssh - Header file | ||
3 | * | ||
4 | * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. | ||
5 | * | ||
6 | * All rights reserved. Redistribution and use in source and binary | ||
7 | * forms, with or without modification, are permitted provided that | ||
8 | * this copyright notice is retained. | ||
9 | * | ||
10 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
11 | * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE | ||
12 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR | ||
13 | * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS | ||
14 | * SOFTWARE. | ||
15 | * | ||
16 | * Ariel Futoransky <futo@core-sdi.com> | ||
17 | * <http://www.core-sdi.com> */ | ||
18 | |||
19 | #ifndef _DEATTACK_H | ||
20 | #define _DEATTACK_H | ||
21 | |||
22 | /* Return codes */ | ||
23 | #define DEATTACK_OK 0 | ||
24 | #define DEATTACK_DETECTED 1 | ||
25 | |||
26 | int detect_attack(unsigned char *buf, u_int32_t len, unsigned char IV[8]); | ||
27 | #endif | ||
diff --git a/getput.h b/getput.h new file mode 100644 index 000000000..7b5d74252 --- /dev/null +++ b/getput.h | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | |||
3 | getput.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Jun 28 22:36:30 1995 ylo | ||
11 | |||
12 | Macros for storing and retrieving data in msb first and lsb first order. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: getput.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
17 | |||
18 | #ifndef GETPUT_H | ||
19 | #define GETPUT_H | ||
20 | |||
21 | /*------------ macros for storing/extracting msb first words -------------*/ | ||
22 | |||
23 | #define GET_32BIT(cp) (((unsigned long)(unsigned char)(cp)[0] << 24) | \ | ||
24 | ((unsigned long)(unsigned char)(cp)[1] << 16) | \ | ||
25 | ((unsigned long)(unsigned char)(cp)[2] << 8) | \ | ||
26 | ((unsigned long)(unsigned char)(cp)[3])) | ||
27 | |||
28 | #define GET_16BIT(cp) (((unsigned long)(unsigned char)(cp)[0] << 8) | \ | ||
29 | ((unsigned long)(unsigned char)(cp)[1])) | ||
30 | |||
31 | #define PUT_32BIT(cp, value) do { \ | ||
32 | (cp)[0] = (value) >> 24; \ | ||
33 | (cp)[1] = (value) >> 16; \ | ||
34 | (cp)[2] = (value) >> 8; \ | ||
35 | (cp)[3] = (value); } while (0) | ||
36 | |||
37 | #define PUT_16BIT(cp, value) do { \ | ||
38 | (cp)[0] = (value) >> 8; \ | ||
39 | (cp)[1] = (value); } while (0) | ||
40 | |||
41 | /*------------ macros for storing/extracting lsb first words -------------*/ | ||
42 | |||
43 | #define GET_32BIT_LSB_FIRST(cp) \ | ||
44 | (((unsigned long)(unsigned char)(cp)[0]) | \ | ||
45 | ((unsigned long)(unsigned char)(cp)[1] << 8) | \ | ||
46 | ((unsigned long)(unsigned char)(cp)[2] << 16) | \ | ||
47 | ((unsigned long)(unsigned char)(cp)[3] << 24)) | ||
48 | |||
49 | #define GET_16BIT_LSB_FIRST(cp) \ | ||
50 | (((unsigned long)(unsigned char)(cp)[0]) | \ | ||
51 | ((unsigned long)(unsigned char)(cp)[1] << 8)) | ||
52 | |||
53 | #define PUT_32BIT_LSB_FIRST(cp, value) do { \ | ||
54 | (cp)[0] = (value); \ | ||
55 | (cp)[1] = (value) >> 8; \ | ||
56 | (cp)[2] = (value) >> 16; \ | ||
57 | (cp)[3] = (value) >> 24; } while (0) | ||
58 | |||
59 | #define PUT_16BIT_LSB_FIRST(cp, value) do { \ | ||
60 | (cp)[0] = (value); \ | ||
61 | (cp)[1] = (value) >> 8; } while (0) | ||
62 | |||
63 | #endif /* GETPUT_H */ | ||
64 | |||
diff --git a/helper.c b/helper.c new file mode 100644 index 000000000..3b0402ecf --- /dev/null +++ b/helper.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | ** | ||
3 | ** OpenBSD emulation routines | ||
4 | ** | ||
5 | ** Damien Miller <djm@ibs.com.au> | ||
6 | ** | ||
7 | ** Copyright 1999 Internet Business Solutions | ||
8 | ** | ||
9 | ** Permission is hereby granted, free of charge, to any person | ||
10 | ** obtaining a copy of this software and associated documentation | ||
11 | ** files (the "Software"), to deal in the Software without | ||
12 | ** restriction, including without limitation the rights to use, copy, | ||
13 | ** modify, merge, publish, distribute, sublicense, and/or sell copies | ||
14 | ** of the Software, and to permit persons to whom the Software is | ||
15 | ** furnished to do so, subject to the following conditions: | ||
16 | ** | ||
17 | ** The above copyright notice and this permission notice shall be | ||
18 | ** included in all copies or substantial portions of the Software. | ||
19 | ** | ||
20 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | ||
21 | ** KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | ||
22 | ** WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE | ||
23 | ** AND NONINFRINGEMENT. IN NO EVENT SHALL DAMIEN MILLER OR INTERNET | ||
24 | ** BUSINESS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
25 | ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
26 | ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE | ||
27 | ** OR OTHER DEALINGS IN THE SOFTWARE. | ||
28 | ** | ||
29 | ** Except as contained in this notice, the name of Internet Business | ||
30 | ** Solutions shall not be used in advertising or otherwise to promote | ||
31 | ** the sale, use or other dealings in this Software without prior | ||
32 | ** written authorization from Internet Business Solutions. | ||
33 | ** | ||
34 | */ | ||
35 | |||
36 | #include <stdio.h> | ||
37 | #include <stdlib.h> | ||
38 | #include <string.h> | ||
39 | #include <errno.h> | ||
40 | #include <unistd.h> | ||
41 | |||
42 | #include <sys/types.h> | ||
43 | #include <sys/stat.h> | ||
44 | #include <fcntl.h> | ||
45 | |||
46 | #include "rc4.h" | ||
47 | #include "xmalloc.h" | ||
48 | |||
49 | #include "helper.h" | ||
50 | |||
51 | void get_random_bytes(unsigned char *buf, int len); | ||
52 | |||
53 | static rc4_t *rc4 = NULL; | ||
54 | |||
55 | void setproctitle(const char *fmt, ...) | ||
56 | { | ||
57 | /* FIXME */ | ||
58 | } | ||
59 | |||
60 | unsigned char arc4random(void) | ||
61 | { | ||
62 | unsigned char r; | ||
63 | |||
64 | if (rc4 == NULL) | ||
65 | arc4random_stir(); | ||
66 | |||
67 | rc4_getbytes(rc4, &r, 1); | ||
68 | |||
69 | return(r); | ||
70 | } | ||
71 | |||
72 | void arc4random_stir(void) | ||
73 | { | ||
74 | unsigned char rand_buf[32]; | ||
75 | |||
76 | if (rc4 == NULL) | ||
77 | rc4 = xmalloc(sizeof(*rc4)); | ||
78 | |||
79 | get_random_bytes(rand_buf, sizeof(rand_buf)); | ||
80 | rc4_key(rc4, rand_buf, sizeof(rand_buf)); | ||
81 | } | ||
82 | |||
83 | void get_random_bytes(unsigned char *buf, int len) | ||
84 | { | ||
85 | int urandom; | ||
86 | int c; | ||
87 | |||
88 | urandom = open("/dev/urandom", O_RDONLY); | ||
89 | if (urandom == -1) | ||
90 | { | ||
91 | fprintf(stderr, "Couldn't open /dev/urandom: %s", strerror(errno)); | ||
92 | exit(1); | ||
93 | } | ||
94 | |||
95 | c = read(urandom, buf, len); | ||
96 | if (c == -1) | ||
97 | { | ||
98 | fprintf(stderr, "Couldn't read from /dev/urandom: %s", strerror(errno)); | ||
99 | exit(1); | ||
100 | } | ||
101 | |||
102 | if (c != len) | ||
103 | { | ||
104 | fprintf(stderr, "Short read from /dev/urandom"); | ||
105 | exit(1); | ||
106 | } | ||
107 | } | ||
108 | |||
diff --git a/helper.h b/helper.h new file mode 100644 index 000000000..2f09daa8e --- /dev/null +++ b/helper.h | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | ** | ||
3 | ** OpenBSD emulation routines | ||
4 | ** | ||
5 | ** Damien Miller <djm@ibs.com.au> | ||
6 | ** | ||
7 | ** Copyright 1999 Internet Business Solutions | ||
8 | ** | ||
9 | ** Permission is hereby granted, free of charge, to any person | ||
10 | ** obtaining a copy of this software and associated documentation | ||
11 | ** files (the "Software"), to deal in the Software without | ||
12 | ** restriction, including without limitation the rights to use, copy, | ||
13 | ** modify, merge, publish, distribute, sublicense, and/or sell copies | ||
14 | ** of the Software, and to permit persons to whom the Software is | ||
15 | ** furnished to do so, subject to the following conditions: | ||
16 | ** | ||
17 | ** The above copyright notice and this permission notice shall be | ||
18 | ** included in all copies or substantial portions of the Software. | ||
19 | ** | ||
20 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | ||
21 | ** KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | ||
22 | ** WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE | ||
23 | ** AND NONINFRINGEMENT. IN NO EVENT SHALL DAMIEN MILLER OR INTERNET | ||
24 | ** BUSINESS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
25 | ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
26 | ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE | ||
27 | ** OR OTHER DEALINGS IN THE SOFTWARE. | ||
28 | ** | ||
29 | ** Except as contained in this notice, the name of Internet Business | ||
30 | ** Solutions shall not be used in advertising or otherwise to promote | ||
31 | ** the sale, use or other dealings in this Software without prior | ||
32 | ** written authorization from Internet Business Solutions. | ||
33 | ** | ||
34 | */ | ||
35 | |||
36 | #ifndef _HELPER_H | ||
37 | #define _HELPER_H | ||
38 | |||
39 | unsigned char arc4random(void); | ||
40 | void arc4random_stir(void); | ||
41 | void setproctitle(const char *fmt, ...); | ||
42 | |||
43 | #endif /* _HELPER_H */ | ||
diff --git a/hostfile.c b/hostfile.c new file mode 100644 index 000000000..ca0fe88a2 --- /dev/null +++ b/hostfile.c | |||
@@ -0,0 +1,279 @@ | |||
1 | /* | ||
2 | |||
3 | hostfile.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Thu Jun 29 07:10:56 1995 ylo | ||
11 | |||
12 | Functions for manipulating the known hosts files. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: hostfile.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
18 | |||
19 | #include "packet.h" | ||
20 | #include "ssh.h" | ||
21 | |||
22 | /* Reads a multiple-precision integer in hex from the buffer, and advances the | ||
23 | pointer. The integer must already be initialized. This function is | ||
24 | permitted to modify the buffer. This leaves *cpp to point just beyond | ||
25 | the last processed (and maybe modified) character. Note that this may | ||
26 | modify the buffer containing the number. */ | ||
27 | |||
28 | int | ||
29 | auth_rsa_read_bignum(char **cpp, BIGNUM *value) | ||
30 | { | ||
31 | char *cp = *cpp; | ||
32 | int len, old; | ||
33 | |||
34 | /* Skip any leading whitespace. */ | ||
35 | for (; *cp == ' ' || *cp == '\t'; cp++) | ||
36 | ; | ||
37 | |||
38 | /* Check that it begins with a hex digit. */ | ||
39 | if (*cp < '0' || *cp > '9') | ||
40 | return 0; | ||
41 | |||
42 | /* Save starting position. */ | ||
43 | *cpp = cp; | ||
44 | |||
45 | /* Move forward until all hex digits skipped. */ | ||
46 | for (; *cp >= '0' && *cp <= '9'; cp++) | ||
47 | ; | ||
48 | |||
49 | /* Compute the length of the hex number. */ | ||
50 | len = cp - *cpp; | ||
51 | |||
52 | /* Save the old terminating character, and replace it by \0. */ | ||
53 | old = *cp; | ||
54 | *cp = 0; | ||
55 | |||
56 | |||
57 | /* Parse the number. */ | ||
58 | if (BN_dec2bn(&value, *cpp) == 0) | ||
59 | return 0; | ||
60 | |||
61 | /* Restore old terminating character. */ | ||
62 | *cp = old; | ||
63 | |||
64 | /* Move beyond the number and return success. */ | ||
65 | *cpp = cp; | ||
66 | return 1; | ||
67 | } | ||
68 | |||
69 | /* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer | ||
70 | over the key. Skips any whitespace at the beginning and at end. */ | ||
71 | |||
72 | int | ||
73 | auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM *e, BIGNUM *n) | ||
74 | { | ||
75 | unsigned int bits; | ||
76 | char *cp; | ||
77 | |||
78 | /* Skip leading whitespace. */ | ||
79 | for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) | ||
80 | ; | ||
81 | |||
82 | /* Get number of bits. */ | ||
83 | if (*cp < '0' || *cp > '9') | ||
84 | return 0; /* Bad bit count... */ | ||
85 | for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) | ||
86 | bits = 10 * bits + *cp - '0'; | ||
87 | |||
88 | /* Get public exponent. */ | ||
89 | if (!auth_rsa_read_bignum(&cp, e)) | ||
90 | return 0; | ||
91 | |||
92 | /* Get public modulus. */ | ||
93 | if (!auth_rsa_read_bignum(&cp, n)) | ||
94 | return 0; | ||
95 | |||
96 | /* Skip trailing whitespace. */ | ||
97 | for (; *cp == ' ' || *cp == '\t'; cp++) | ||
98 | ; | ||
99 | |||
100 | /* Return results. */ | ||
101 | *cpp = cp; | ||
102 | *bitsp = bits; | ||
103 | return 1; | ||
104 | } | ||
105 | |||
106 | /* Tries to match the host name (which must be in all lowercase) against the | ||
107 | comma-separated sequence of subpatterns (each possibly preceded by ! to | ||
108 | indicate negation). Returns true if there is a positive match; zero | ||
109 | otherwise. */ | ||
110 | |||
111 | int | ||
112 | match_hostname(const char *host, const char *pattern, unsigned int len) | ||
113 | { | ||
114 | char sub[1024]; | ||
115 | int negated; | ||
116 | int got_positive; | ||
117 | unsigned int i, subi; | ||
118 | |||
119 | got_positive = 0; | ||
120 | for (i = 0; i < len;) | ||
121 | { | ||
122 | /* Check if the subpattern is negated. */ | ||
123 | if (pattern[i] == '!') | ||
124 | { | ||
125 | negated = 1; | ||
126 | i++; | ||
127 | } | ||
128 | else | ||
129 | negated = 0; | ||
130 | |||
131 | /* Extract the subpattern up to a comma or end. Convert the subpattern | ||
132 | to lowercase. */ | ||
133 | for (subi = 0; | ||
134 | i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; | ||
135 | subi++, i++) | ||
136 | sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i]; | ||
137 | /* If subpattern too long, return failure (no match). */ | ||
138 | if (subi >= sizeof(sub) - 1) | ||
139 | return 0; | ||
140 | |||
141 | /* If the subpattern was terminated by a comma, skip the comma. */ | ||
142 | if (i < len && pattern[i] == ',') | ||
143 | i++; | ||
144 | |||
145 | /* Null-terminate the subpattern. */ | ||
146 | sub[subi] = '\0'; | ||
147 | |||
148 | /* Try to match the subpattern against the host name. */ | ||
149 | if (match_pattern(host, sub)) { | ||
150 | if (negated) | ||
151 | return 0; /* Fail if host matches any negated subpattern. */ | ||
152 | else | ||
153 | got_positive = 1; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | /* Return success if got a positive match. If there was a negative match, | ||
158 | we have already returned zero and never get here. */ | ||
159 | return got_positive; | ||
160 | } | ||
161 | |||
162 | /* Checks whether the given host (which must be in all lowercase) is | ||
163 | already in the list of our known hosts. | ||
164 | Returns HOST_OK if the host is known and has the specified key, | ||
165 | HOST_NEW if the host is not known, and HOST_CHANGED if the host is known | ||
166 | but used to have a different host key. */ | ||
167 | |||
168 | HostStatus | ||
169 | check_host_in_hostfile(const char *filename, | ||
170 | const char *host, unsigned int bits, | ||
171 | BIGNUM *e, BIGNUM *n, | ||
172 | BIGNUM *ke, BIGNUM *kn) | ||
173 | { | ||
174 | FILE *f; | ||
175 | char line[8192]; | ||
176 | unsigned int kbits, hostlen; | ||
177 | char *cp, *cp2; | ||
178 | HostStatus end_return; | ||
179 | struct stat st; | ||
180 | |||
181 | /* Open the file containing the list of known hosts. */ | ||
182 | f = fopen(filename, "r"); | ||
183 | if (!f) | ||
184 | { | ||
185 | if (stat(filename, &st) >= 0) | ||
186 | { | ||
187 | packet_send_debug("Could not open %.900s for reading.", filename); | ||
188 | packet_send_debug("If your home directory is on an NFS volume, it may need to be world-readable."); | ||
189 | } | ||
190 | return HOST_NEW; | ||
191 | } | ||
192 | |||
193 | /* Cache the length of the host name. */ | ||
194 | hostlen = strlen(host); | ||
195 | |||
196 | /* Return value when the loop terminates. This is set to HOST_CHANGED if | ||
197 | we have seen a different key for the host and have not found the proper | ||
198 | one. */ | ||
199 | end_return = HOST_NEW; | ||
200 | |||
201 | /* Go trough the file. */ | ||
202 | while (fgets(line, sizeof(line), f)) | ||
203 | { | ||
204 | cp = line; | ||
205 | |||
206 | /* Skip any leading whitespace. */ | ||
207 | for (; *cp == ' ' || *cp == '\t'; cp++) | ||
208 | ; | ||
209 | |||
210 | /* Ignore comment lines and empty lines. */ | ||
211 | if (!*cp || *cp == '#' || *cp == '\n') | ||
212 | continue; | ||
213 | |||
214 | /* Find the end of the host name portion. */ | ||
215 | for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) | ||
216 | ; | ||
217 | |||
218 | /* Check if the host name matches. */ | ||
219 | if (!match_hostname(host, cp, (unsigned int)(cp2 - cp))) | ||
220 | continue; | ||
221 | |||
222 | /* Got a match. Skip host name. */ | ||
223 | cp = cp2; | ||
224 | |||
225 | /* Extract the key from the line. This will skip any leading | ||
226 | whitespace. Ignore badly formatted lines. */ | ||
227 | if (!auth_rsa_read_key(&cp, &kbits, ke, kn)) | ||
228 | continue; | ||
229 | |||
230 | /* Check if the current key is the same as the previous one. */ | ||
231 | if (kbits == bits && BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0) | ||
232 | { | ||
233 | /* Ok, they match. */ | ||
234 | fclose(f); | ||
235 | return HOST_OK; | ||
236 | } | ||
237 | |||
238 | /* They do not match. We will continue to go through the file; however, | ||
239 | we note that we will not return that it is new. */ | ||
240 | end_return = HOST_CHANGED; | ||
241 | } | ||
242 | /* Clear variables and close the file. */ | ||
243 | fclose(f); | ||
244 | |||
245 | /* Return either HOST_NEW or HOST_CHANGED, depending on whether we saw a | ||
246 | different key for the host. */ | ||
247 | return end_return; | ||
248 | } | ||
249 | |||
250 | /* Appends an entry to the host file. Returns false if the entry | ||
251 | could not be appended. */ | ||
252 | |||
253 | int | ||
254 | add_host_to_hostfile(const char *filename, const char *host, | ||
255 | unsigned int bits, BIGNUM *e, BIGNUM *n) | ||
256 | { | ||
257 | FILE *f; | ||
258 | char *buf; | ||
259 | |||
260 | /* Open the file for appending. */ | ||
261 | f = fopen(filename, "a"); | ||
262 | if (!f) | ||
263 | return 0; | ||
264 | |||
265 | /* Print the host name and key to the file. */ | ||
266 | fprintf(f, "%s %u ", host, bits); | ||
267 | buf = BN_bn2dec(e); | ||
268 | assert(buf != NULL); | ||
269 | fprintf(f, "%s ", buf); | ||
270 | free (buf); | ||
271 | buf = BN_bn2dec(n); | ||
272 | assert(buf != NULL); | ||
273 | fprintf(f, "%s\n", buf); | ||
274 | free (buf); | ||
275 | |||
276 | /* Close the file. */ | ||
277 | fclose(f); | ||
278 | return 1; | ||
279 | } | ||
diff --git a/includes.h b/includes.h new file mode 100644 index 000000000..862dbd64f --- /dev/null +++ b/includes.h | |||
@@ -0,0 +1,78 @@ | |||
1 | /* | ||
2 | |||
3 | includes.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Thu Mar 23 16:29:37 1995 ylo | ||
11 | |||
12 | This file includes most of the needed system headers. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #ifndef INCLUDES_H | ||
17 | #define INCLUDES_H | ||
18 | |||
19 | #define RCSID(msg) \ | ||
20 | static /**/const char *const rcsid[] = { (char *)rcsid, "\100(#)" msg } | ||
21 | |||
22 | #include <sys/types.h> | ||
23 | #include <sys/socket.h> | ||
24 | #include <sys/select.h> | ||
25 | #include <sys/param.h> | ||
26 | #include <sys/ioctl.h> | ||
27 | #include <sys/stat.h> | ||
28 | #include <sys/wait.h> | ||
29 | #include <sys/time.h> | ||
30 | #include <sys/un.h> | ||
31 | #include <sys/resource.h> | ||
32 | |||
33 | #include <netinet/in.h> | ||
34 | #include <netinet/in_systm.h> | ||
35 | #include <netinet/tcp.h> | ||
36 | #include <netinet/ip.h> | ||
37 | #include <arpa/inet.h> | ||
38 | #include <netdb.h> | ||
39 | |||
40 | #include <endian.h> | ||
41 | #include <stdio.h> | ||
42 | #include <ctype.h> | ||
43 | #include <errno.h> | ||
44 | #include <fcntl.h> | ||
45 | #include <assert.h> | ||
46 | #include <signal.h> | ||
47 | #include <termios.h> | ||
48 | #include <stdlib.h> | ||
49 | #include <string.h> | ||
50 | #include <stdarg.h> | ||
51 | #include <pwd.h> | ||
52 | #include <grp.h> | ||
53 | #include <unistd.h> | ||
54 | #include <time.h> | ||
55 | #include <paths.h> | ||
56 | #include <dirent.h> | ||
57 | |||
58 | #include "version.h" | ||
59 | |||
60 | #include "helper.h" | ||
61 | #include "mktemp.h" | ||
62 | #include "strlcpy.h" | ||
63 | |||
64 | /* Define this to be the path of the xauth program. */ | ||
65 | #ifndef XAUTH_PATH | ||
66 | #define XAUTH_PATH "/usr/X11R6/bin/xauth" | ||
67 | #endif /* XAUTH_PATH */ | ||
68 | |||
69 | /* Define this to be the path of the rsh program. */ | ||
70 | #ifndef _PATH_RSH | ||
71 | #define _PATH_RSH "/usr/bin/rsh" | ||
72 | #endif /* _PATH_RSH */ | ||
73 | |||
74 | /* Define this to use pipes instead of socketpairs for communicating with the | ||
75 | client program. Socketpairs do not seem to work on all systems. */ | ||
76 | #define USE_PIPES 1 | ||
77 | |||
78 | #endif /* INCLUDES_H */ | ||
diff --git a/log-client.c b/log-client.c new file mode 100644 index 000000000..1792ba847 --- /dev/null +++ b/log-client.c | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | |||
3 | log-client.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Mar 20 21:13:40 1995 ylo | ||
11 | |||
12 | Client-side versions of debug(), log(), etc. These print to stderr. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: log-client.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
18 | |||
19 | #include "xmalloc.h" | ||
20 | #include "ssh.h" | ||
21 | |||
22 | static int log_debug = 0; | ||
23 | static int log_quiet = 0; | ||
24 | |||
25 | void log_init(char *av0, int on_stderr, int debug, int quiet, | ||
26 | SyslogFacility facility) | ||
27 | { | ||
28 | log_debug = debug; | ||
29 | log_quiet = quiet; | ||
30 | } | ||
31 | |||
32 | void log(const char *fmt, ...) | ||
33 | { | ||
34 | va_list args; | ||
35 | |||
36 | if (log_quiet) | ||
37 | return; | ||
38 | va_start(args, fmt); | ||
39 | vfprintf(stderr, fmt, args); | ||
40 | fprintf(stderr, "\r\n"); | ||
41 | va_end(args); | ||
42 | } | ||
43 | |||
44 | void debug(const char *fmt, ...) | ||
45 | { | ||
46 | va_list args; | ||
47 | if (log_quiet || !log_debug) | ||
48 | return; | ||
49 | va_start(args, fmt); | ||
50 | fprintf(stderr, "debug: "); | ||
51 | vfprintf(stderr, fmt, args); | ||
52 | fprintf(stderr, "\r\n"); | ||
53 | va_end(args); | ||
54 | } | ||
55 | |||
56 | void error(const char *fmt, ...) | ||
57 | { | ||
58 | va_list args; | ||
59 | if (log_quiet) | ||
60 | return; | ||
61 | va_start(args, fmt); | ||
62 | vfprintf(stderr, fmt, args); | ||
63 | fprintf(stderr, "\r\n"); | ||
64 | va_end(args); | ||
65 | } | ||
66 | |||
67 | struct fatal_cleanup | ||
68 | { | ||
69 | struct fatal_cleanup *next; | ||
70 | void (*proc)(void *); | ||
71 | void *context; | ||
72 | }; | ||
73 | |||
74 | static struct fatal_cleanup *fatal_cleanups = NULL; | ||
75 | |||
76 | /* Registers a cleanup function to be called by fatal() before exiting. */ | ||
77 | |||
78 | void fatal_add_cleanup(void (*proc)(void *), void *context) | ||
79 | { | ||
80 | struct fatal_cleanup *cu; | ||
81 | |||
82 | cu = xmalloc(sizeof(*cu)); | ||
83 | cu->proc = proc; | ||
84 | cu->context = context; | ||
85 | cu->next = fatal_cleanups; | ||
86 | fatal_cleanups = cu; | ||
87 | } | ||
88 | |||
89 | /* Removes a cleanup frunction to be called at fatal(). */ | ||
90 | |||
91 | void fatal_remove_cleanup(void (*proc)(void *context), void *context) | ||
92 | { | ||
93 | struct fatal_cleanup **cup, *cu; | ||
94 | |||
95 | for (cup = &fatal_cleanups; *cup; cup = &cu->next) | ||
96 | { | ||
97 | cu = *cup; | ||
98 | if (cu->proc == proc && cu->context == context) | ||
99 | { | ||
100 | *cup = cu->next; | ||
101 | xfree(cu); | ||
102 | return; | ||
103 | } | ||
104 | } | ||
105 | fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx\n", | ||
106 | (unsigned long)proc, (unsigned long)context); | ||
107 | } | ||
108 | |||
109 | /* Function to display an error message and exit. This is in this file because | ||
110 | this needs to restore terminal modes before exiting. See log-client.c | ||
111 | for other related functions. */ | ||
112 | |||
113 | void fatal(const char *fmt, ...) | ||
114 | { | ||
115 | va_list args; | ||
116 | struct fatal_cleanup *cu, *next_cu; | ||
117 | static int fatal_called = 0; | ||
118 | |||
119 | if (!fatal_called) | ||
120 | { | ||
121 | fatal_called = 1; | ||
122 | |||
123 | /* Call cleanup functions. */ | ||
124 | for (cu = fatal_cleanups; cu; cu = next_cu) | ||
125 | { | ||
126 | next_cu = cu->next; | ||
127 | (*cu->proc)(cu->context); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | va_start(args, fmt); | ||
132 | vfprintf(stderr, fmt, args); | ||
133 | fprintf(stderr, "\r\n"); | ||
134 | va_end(args); | ||
135 | exit(255); | ||
136 | } | ||
137 | |||
138 | /* fatal() is in ssh.c so that it can properly reset terminal modes. */ | ||
diff --git a/log-server.c b/log-server.c new file mode 100644 index 000000000..fce96b01d --- /dev/null +++ b/log-server.c | |||
@@ -0,0 +1,233 @@ | |||
1 | /* | ||
2 | |||
3 | log-server.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Mar 20 21:19:30 1995 ylo | ||
11 | |||
12 | Server-side versions of debug(), log(), etc. These normally send the output | ||
13 | to the system log. | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: log-server.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
19 | |||
20 | #include <syslog.h> | ||
21 | #include "packet.h" | ||
22 | #include "xmalloc.h" | ||
23 | #include "ssh.h" | ||
24 | |||
25 | static int log_debug = 0; | ||
26 | static int log_quiet = 0; | ||
27 | static int log_on_stderr = 0; | ||
28 | |||
29 | /* Initialize the log. | ||
30 | av0 program name (should be argv[0]) | ||
31 | on_stderr print also on stderr | ||
32 | debug send debugging messages to system log | ||
33 | quiet don\'t log anything | ||
34 | */ | ||
35 | |||
36 | void log_init(char *av0, int on_stderr, int debug, int quiet, | ||
37 | SyslogFacility facility) | ||
38 | { | ||
39 | int log_facility; | ||
40 | |||
41 | switch (facility) | ||
42 | { | ||
43 | case SYSLOG_FACILITY_DAEMON: | ||
44 | log_facility = LOG_DAEMON; | ||
45 | break; | ||
46 | case SYSLOG_FACILITY_USER: | ||
47 | log_facility = LOG_USER; | ||
48 | break; | ||
49 | case SYSLOG_FACILITY_AUTH: | ||
50 | log_facility = LOG_AUTH; | ||
51 | break; | ||
52 | case SYSLOG_FACILITY_LOCAL0: | ||
53 | log_facility = LOG_LOCAL0; | ||
54 | break; | ||
55 | case SYSLOG_FACILITY_LOCAL1: | ||
56 | log_facility = LOG_LOCAL1; | ||
57 | break; | ||
58 | case SYSLOG_FACILITY_LOCAL2: | ||
59 | log_facility = LOG_LOCAL2; | ||
60 | break; | ||
61 | case SYSLOG_FACILITY_LOCAL3: | ||
62 | log_facility = LOG_LOCAL3; | ||
63 | break; | ||
64 | case SYSLOG_FACILITY_LOCAL4: | ||
65 | log_facility = LOG_LOCAL4; | ||
66 | break; | ||
67 | case SYSLOG_FACILITY_LOCAL5: | ||
68 | log_facility = LOG_LOCAL5; | ||
69 | break; | ||
70 | case SYSLOG_FACILITY_LOCAL6: | ||
71 | log_facility = LOG_LOCAL6; | ||
72 | break; | ||
73 | case SYSLOG_FACILITY_LOCAL7: | ||
74 | log_facility = LOG_LOCAL7; | ||
75 | break; | ||
76 | default: | ||
77 | fprintf(stderr, "Unrecognized internal syslog facility code %d\n", | ||
78 | (int)facility); | ||
79 | exit(1); | ||
80 | } | ||
81 | |||
82 | log_debug = debug; | ||
83 | log_quiet = quiet; | ||
84 | log_on_stderr = on_stderr; | ||
85 | closelog(); /* Close any previous log. */ | ||
86 | openlog(av0, LOG_PID, log_facility); | ||
87 | } | ||
88 | |||
89 | #define MSGBUFSIZE 1024 | ||
90 | |||
91 | #define DECL_MSGBUF char msgbuf[MSGBUFSIZE] | ||
92 | |||
93 | /* Log this message (information that usually should go to the log). */ | ||
94 | |||
95 | void log(const char *fmt, ...) | ||
96 | { | ||
97 | va_list args; | ||
98 | DECL_MSGBUF; | ||
99 | if (log_quiet) | ||
100 | return; | ||
101 | va_start(args, fmt); | ||
102 | vsnprintf(msgbuf, MSGBUFSIZE, fmt, args); | ||
103 | va_end(args); | ||
104 | if (log_on_stderr) | ||
105 | fprintf(stderr, "log: %s\n", msgbuf); | ||
106 | syslog(LOG_INFO, "log: %.500s", msgbuf); | ||
107 | } | ||
108 | |||
109 | /* Debugging messages that should not be logged during normal operation. */ | ||
110 | |||
111 | void debug(const char *fmt, ...) | ||
112 | { | ||
113 | va_list args; | ||
114 | DECL_MSGBUF; | ||
115 | if (!log_debug || log_quiet) | ||
116 | return; | ||
117 | va_start(args, fmt); | ||
118 | vsnprintf(msgbuf, MSGBUFSIZE, fmt, args); | ||
119 | va_end(args); | ||
120 | if (log_on_stderr) | ||
121 | fprintf(stderr, "debug: %s\n", msgbuf); | ||
122 | syslog(LOG_DEBUG, "debug: %.500s", msgbuf); | ||
123 | } | ||
124 | |||
125 | /* Error messages that should be logged. */ | ||
126 | |||
127 | void error(const char *fmt, ...) | ||
128 | { | ||
129 | va_list args; | ||
130 | DECL_MSGBUF; | ||
131 | if (log_quiet) | ||
132 | return; | ||
133 | va_start(args, fmt); | ||
134 | vsnprintf(msgbuf, MSGBUFSIZE, fmt, args); | ||
135 | va_end(args); | ||
136 | if (log_on_stderr) | ||
137 | fprintf(stderr, "error: %s\n", msgbuf); | ||
138 | syslog(LOG_ERR, "error: %.500s", msgbuf); | ||
139 | } | ||
140 | |||
141 | struct fatal_cleanup | ||
142 | { | ||
143 | struct fatal_cleanup *next; | ||
144 | void (*proc)(void *); | ||
145 | void *context; | ||
146 | }; | ||
147 | |||
148 | static struct fatal_cleanup *fatal_cleanups = NULL; | ||
149 | |||
150 | /* Registers a cleanup function to be called by fatal() before exiting. */ | ||
151 | |||
152 | void fatal_add_cleanup(void (*proc)(void *), void *context) | ||
153 | { | ||
154 | struct fatal_cleanup *cu; | ||
155 | |||
156 | cu = xmalloc(sizeof(*cu)); | ||
157 | cu->proc = proc; | ||
158 | cu->context = context; | ||
159 | cu->next = fatal_cleanups; | ||
160 | fatal_cleanups = cu; | ||
161 | } | ||
162 | |||
163 | /* Removes a cleanup frunction to be called at fatal(). */ | ||
164 | |||
165 | void fatal_remove_cleanup(void (*proc)(void *context), void *context) | ||
166 | { | ||
167 | struct fatal_cleanup **cup, *cu; | ||
168 | |||
169 | for (cup = &fatal_cleanups; *cup; cup = &cu->next) | ||
170 | { | ||
171 | cu = *cup; | ||
172 | if (cu->proc == proc && cu->context == context) | ||
173 | { | ||
174 | *cup = cu->next; | ||
175 | xfree(cu); | ||
176 | return; | ||
177 | } | ||
178 | } | ||
179 | fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx\n", | ||
180 | (unsigned long)proc, (unsigned long)context); | ||
181 | } | ||
182 | |||
183 | /* Fatal messages. This function never returns. */ | ||
184 | |||
185 | void fatal(const char *fmt, ...) | ||
186 | { | ||
187 | va_list args; | ||
188 | struct fatal_cleanup *cu, *next_cu; | ||
189 | static int fatal_called = 0; | ||
190 | #if defined(KRB4) | ||
191 | extern char *ticket; | ||
192 | #endif /* KRB4 */ | ||
193 | DECL_MSGBUF; | ||
194 | |||
195 | if (log_quiet) | ||
196 | exit(1); | ||
197 | va_start(args, fmt); | ||
198 | vsnprintf(msgbuf, MSGBUFSIZE, fmt, args); | ||
199 | va_end(args); | ||
200 | if (log_on_stderr) | ||
201 | fprintf(stderr, "fatal: %s\n", msgbuf); | ||
202 | syslog(LOG_ERR, "fatal: %.500s", msgbuf); | ||
203 | |||
204 | if (fatal_called) | ||
205 | exit(1); | ||
206 | fatal_called = 1; | ||
207 | |||
208 | /* Call cleanup functions. */ | ||
209 | for (cu = fatal_cleanups; cu; cu = next_cu) | ||
210 | { | ||
211 | next_cu = cu->next; | ||
212 | debug("Calling cleanup 0x%lx(0x%lx)", | ||
213 | (unsigned long)cu->proc, (unsigned long)cu->context); | ||
214 | (*cu->proc)(cu->context); | ||
215 | } | ||
216 | #if defined(KRB4) | ||
217 | /* If you forwarded a ticket you get one shot for proper | ||
218 | authentication. */ | ||
219 | /* If tgt was passed unlink file */ | ||
220 | if (ticket) | ||
221 | { | ||
222 | if (strcmp(ticket,"none")) | ||
223 | unlink(ticket); | ||
224 | else | ||
225 | ticket = NULL; | ||
226 | } | ||
227 | #endif /* KRB4 */ | ||
228 | |||
229 | /* If local XAUTHORITY was created, remove it. */ | ||
230 | if (xauthfile) unlink(xauthfile); | ||
231 | |||
232 | exit(1); | ||
233 | } | ||
diff --git a/login.c b/login.c new file mode 100644 index 000000000..0c1e61b77 --- /dev/null +++ b/login.c | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | |||
3 | login.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Fri Mar 24 14:51:08 1995 ylo | ||
11 | |||
12 | This file performs some of the things login(1) normally does. We cannot | ||
13 | easily use something like login -p -h host -f user, because there are | ||
14 | several different logins around, and it is hard to determined what kind of | ||
15 | login the current system has. Also, we want to be able to execute commands | ||
16 | on a tty. | ||
17 | |||
18 | */ | ||
19 | |||
20 | #include "includes.h" | ||
21 | RCSID("$Id: login.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
22 | |||
23 | #include <utmp.h> | ||
24 | #include "ssh.h" | ||
25 | |||
26 | /* Returns the time when the user last logged in. Returns 0 if the | ||
27 | information is not available. This must be called before record_login. | ||
28 | The host the user logged in from will be returned in buf. */ | ||
29 | |||
30 | /* Returns the time when the user last logged in (or 0 if no previous login | ||
31 | is found). The name of the host used last time is returned in buf. */ | ||
32 | |||
33 | unsigned long get_last_login_time(uid_t uid, const char *logname, | ||
34 | char *buf, unsigned int bufsize) | ||
35 | { | ||
36 | struct lastlog ll; | ||
37 | char *lastlog; | ||
38 | int fd; | ||
39 | |||
40 | lastlog = _PATH_LASTLOG; | ||
41 | |||
42 | buf[0] = '\0'; | ||
43 | |||
44 | fd = open(lastlog, O_RDONLY); | ||
45 | if (fd < 0) | ||
46 | return 0; | ||
47 | lseek(fd, (off_t)((long)uid * sizeof(ll)), SEEK_SET); | ||
48 | if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) | ||
49 | { | ||
50 | close(fd); | ||
51 | return 0; | ||
52 | } | ||
53 | close(fd); | ||
54 | if (bufsize > sizeof(ll.ll_host) + 1) | ||
55 | bufsize = sizeof(ll.ll_host) + 1; | ||
56 | strncpy(buf, ll.ll_host, bufsize - 1); | ||
57 | buf[bufsize - 1] = 0; | ||
58 | return ll.ll_time; | ||
59 | } | ||
60 | |||
61 | /* Records that the user has logged in. I these parts of operating systems | ||
62 | were more standardized. */ | ||
63 | |||
64 | void record_login(int pid, const char *ttyname, const char *user, uid_t uid, | ||
65 | const char *host, struct sockaddr_in *addr) | ||
66 | { | ||
67 | int fd; | ||
68 | struct lastlog ll; | ||
69 | char *lastlog; | ||
70 | |||
71 | struct utmp u; | ||
72 | const char *utmp, *wtmp; | ||
73 | |||
74 | /* Construct an utmp/wtmp entry. */ | ||
75 | memset(&u, 0, sizeof(u)); | ||
76 | strncpy(u.ut_line, ttyname + 5, sizeof(u.ut_line)); | ||
77 | u.ut_time = time(NULL); | ||
78 | strncpy(u.ut_name, user, sizeof(u.ut_name)); | ||
79 | strncpy(u.ut_host, host, sizeof(u.ut_host)); | ||
80 | |||
81 | /* Figure out the file names. */ | ||
82 | utmp = _PATH_UTMP; | ||
83 | wtmp = _PATH_WTMP; | ||
84 | |||
85 | login(&u); | ||
86 | |||
87 | lastlog = _PATH_LASTLOG; | ||
88 | |||
89 | /* Update lastlog unless actually recording a logout. */ | ||
90 | if (strcmp(user, "") != 0) | ||
91 | { | ||
92 | /* It is safer to bzero the lastlog structure first because some | ||
93 | systems might have some extra fields in it (e.g. SGI) */ | ||
94 | memset(&ll, 0, sizeof(ll)); | ||
95 | |||
96 | /* Update lastlog. */ | ||
97 | ll.ll_time = time(NULL); | ||
98 | strncpy(ll.ll_line, ttyname + 5, sizeof(ll.ll_line)); | ||
99 | strncpy(ll.ll_host, host, sizeof(ll.ll_host)); | ||
100 | fd = open(lastlog, O_RDWR); | ||
101 | if (fd >= 0) | ||
102 | { | ||
103 | lseek(fd, (off_t)((long)uid * sizeof(ll)), SEEK_SET); | ||
104 | if (write(fd, &ll, sizeof(ll)) != sizeof(ll)) | ||
105 | log("Could not write %.100s: %.100s", lastlog, strerror(errno)); | ||
106 | close(fd); | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | /* Records that the user has logged out. */ | ||
112 | |||
113 | void record_logout(int pid, const char *ttyname) | ||
114 | { | ||
115 | const char *line = ttyname + 5; /* /dev/ttyq8 -> ttyq8 */ | ||
116 | if (logout(line)) | ||
117 | logwtmp(line, "", ""); | ||
118 | } | ||
diff --git a/match.c b/match.c new file mode 100644 index 000000000..b7a7d3389 --- /dev/null +++ b/match.c | |||
@@ -0,0 +1,78 @@ | |||
1 | /* | ||
2 | |||
3 | match.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Thu Jun 22 01:17:50 1995 ylo | ||
11 | |||
12 | Simple pattern matching, with '*' and '?' as wildcards. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: match.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
18 | |||
19 | #include "ssh.h" | ||
20 | |||
21 | /* Returns true if the given string matches the pattern (which may contain | ||
22 | ? and * as wildcards), and zero if it does not match. */ | ||
23 | |||
24 | int match_pattern(const char *s, const char *pattern) | ||
25 | { | ||
26 | while (1) | ||
27 | { | ||
28 | /* If at end of pattern, accept if also at end of string. */ | ||
29 | if (!*pattern) | ||
30 | return !*s; | ||
31 | |||
32 | /* Process '*'. */ | ||
33 | if (*pattern == '*') | ||
34 | { | ||
35 | /* Skip the asterisk. */ | ||
36 | pattern++; | ||
37 | |||
38 | /* If at end of pattern, accept immediately. */ | ||
39 | if (!*pattern) | ||
40 | return 1; | ||
41 | |||
42 | /* If next character in pattern is known, optimize. */ | ||
43 | if (*pattern != '?' && *pattern != '*') | ||
44 | { | ||
45 | /* Look instances of the next character in pattern, and try | ||
46 | to match starting from those. */ | ||
47 | for (; *s; s++) | ||
48 | if (*s == *pattern && | ||
49 | match_pattern(s + 1, pattern + 1)) | ||
50 | return 1; | ||
51 | /* Failed. */ | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | /* Move ahead one character at a time and try to match at each | ||
56 | position. */ | ||
57 | for (; *s; s++) | ||
58 | if (match_pattern(s, pattern)) | ||
59 | return 1; | ||
60 | /* Failed. */ | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | /* There must be at least one more character in the string. If we are | ||
65 | at the end, fail. */ | ||
66 | if (!*s) | ||
67 | return 0; | ||
68 | |||
69 | /* Check if the next character of the string is acceptable. */ | ||
70 | if (*pattern != '?' && *pattern != *s) | ||
71 | return 0; | ||
72 | |||
73 | /* Move to the next character, both in string and in pattern. */ | ||
74 | s++; | ||
75 | pattern++; | ||
76 | } | ||
77 | /*NOTREACHED*/ | ||
78 | } | ||
diff --git a/mktemp.c b/mktemp.c new file mode 100644 index 000000000..919c53178 --- /dev/null +++ b/mktemp.c | |||
@@ -0,0 +1,181 @@ | |||
1 | /* THIS FILE HAS BEEN MODIFIED FROM THE ORIGINAL OPENBSD SOURCE */ | ||
2 | /* Changes: Removed mktemp */ | ||
3 | |||
4 | /* | ||
5 | * Copyright (c) 1987, 1993 | ||
6 | * The Regents of the University of California. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * 1. Redistributions of source code must retain the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer. | ||
13 | * 2. Redistributions in binary form must reproduce the above copyright | ||
14 | * notice, this list of conditions and the following disclaimer in the | ||
15 | * documentation and/or other materials provided with the distribution. | ||
16 | * 3. All advertising materials mentioning features or use of this software | ||
17 | * must display the following acknowledgement: | ||
18 | * This product includes software developed by the University of | ||
19 | * California, Berkeley and its contributors. | ||
20 | * 4. Neither the name of the University nor the names of its contributors | ||
21 | * may be used to endorse or promote products derived from this software | ||
22 | * without specific prior written permission. | ||
23 | * | ||
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
34 | * SUCH DAMAGE. | ||
35 | */ | ||
36 | |||
37 | #if defined(LIBC_SCCS) && !defined(lint) | ||
38 | static char rcsid[] = "$OpenBSD: mktemp.c,v 1.13 1998/06/30 23:03:13 deraadt Exp $"; | ||
39 | #endif /* LIBC_SCCS and not lint */ | ||
40 | |||
41 | #include <sys/types.h> | ||
42 | #include <sys/stat.h> | ||
43 | #include <fcntl.h> | ||
44 | #include <errno.h> | ||
45 | #include <stdio.h> | ||
46 | #include <stdlib.h> | ||
47 | #include <ctype.h> | ||
48 | #include <unistd.h> | ||
49 | |||
50 | static int _gettemp __P((char *, int *, int, int)); | ||
51 | |||
52 | int | ||
53 | mkstemps(path, slen) | ||
54 | char *path; | ||
55 | int slen; | ||
56 | { | ||
57 | int fd; | ||
58 | |||
59 | return (_gettemp(path, &fd, 0, slen) ? fd : -1); | ||
60 | } | ||
61 | |||
62 | int | ||
63 | mkstemp(path) | ||
64 | char *path; | ||
65 | { | ||
66 | int fd; | ||
67 | |||
68 | return (_gettemp(path, &fd, 0, 0) ? fd : -1); | ||
69 | } | ||
70 | |||
71 | char * | ||
72 | mkdtemp(path) | ||
73 | char *path; | ||
74 | { | ||
75 | return(_gettemp(path, (int *)NULL, 1, 0) ? path : (char *)NULL); | ||
76 | } | ||
77 | |||
78 | static int | ||
79 | _gettemp(path, doopen, domkdir, slen) | ||
80 | char *path; | ||
81 | register int *doopen; | ||
82 | int domkdir; | ||
83 | int slen; | ||
84 | { | ||
85 | register char *start, *trv, *suffp; | ||
86 | struct stat sbuf; | ||
87 | int pid, rval; | ||
88 | |||
89 | if (doopen && domkdir) { | ||
90 | errno = EINVAL; | ||
91 | return(0); | ||
92 | } | ||
93 | |||
94 | for (trv = path; *trv; ++trv) | ||
95 | ; | ||
96 | trv -= slen; | ||
97 | suffp = trv; | ||
98 | --trv; | ||
99 | if (trv < path) { | ||
100 | errno = EINVAL; | ||
101 | return (0); | ||
102 | } | ||
103 | pid = getpid(); | ||
104 | while (*trv == 'X' && pid != 0) { | ||
105 | *trv-- = (pid % 10) + '0'; | ||
106 | pid /= 10; | ||
107 | } | ||
108 | while (*trv == 'X') { | ||
109 | char c; | ||
110 | |||
111 | pid = (arc4random() & 0xffff) % (26+26); | ||
112 | if (pid < 26) | ||
113 | c = pid + 'A'; | ||
114 | else | ||
115 | c = (pid - 26) + 'a'; | ||
116 | *trv-- = c; | ||
117 | } | ||
118 | start = trv + 1; | ||
119 | |||
120 | /* | ||
121 | * check the target directory; if you have six X's and it | ||
122 | * doesn't exist this runs for a *very* long time. | ||
123 | */ | ||
124 | if (doopen || domkdir) { | ||
125 | for (;; --trv) { | ||
126 | if (trv <= path) | ||
127 | break; | ||
128 | if (*trv == '/') { | ||
129 | *trv = '\0'; | ||
130 | rval = stat(path, &sbuf); | ||
131 | *trv = '/'; | ||
132 | if (rval != 0) | ||
133 | return(0); | ||
134 | if (!S_ISDIR(sbuf.st_mode)) { | ||
135 | errno = ENOTDIR; | ||
136 | return(0); | ||
137 | } | ||
138 | break; | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | |||
143 | for (;;) { | ||
144 | if (doopen) { | ||
145 | if ((*doopen = | ||
146 | open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) | ||
147 | return(1); | ||
148 | if (errno != EEXIST) | ||
149 | return(0); | ||
150 | } else if (domkdir) { | ||
151 | if (mkdir(path, 0700) == 0) | ||
152 | return(1); | ||
153 | if (errno != EEXIST) | ||
154 | return(0); | ||
155 | } else if (lstat(path, &sbuf)) | ||
156 | return(errno == ENOENT ? 1 : 0); | ||
157 | |||
158 | /* tricky little algorithm for backward compatibility */ | ||
159 | for (trv = start;;) { | ||
160 | if (!*trv) | ||
161 | return (0); | ||
162 | if (*trv == 'Z') { | ||
163 | if (trv == suffp) | ||
164 | return (0); | ||
165 | *trv++ = 'a'; | ||
166 | } else { | ||
167 | if (isdigit(*trv)) | ||
168 | *trv = 'a'; | ||
169 | else if (*trv == 'z') /* inc from z to A */ | ||
170 | *trv = 'A'; | ||
171 | else { | ||
172 | if (trv == suffp) | ||
173 | return (0); | ||
174 | ++*trv; | ||
175 | } | ||
176 | break; | ||
177 | } | ||
178 | } | ||
179 | } | ||
180 | /*NOTREACHED*/ | ||
181 | } | ||
diff --git a/mktemp.h b/mktemp.h new file mode 100644 index 000000000..5d380058e --- /dev/null +++ b/mktemp.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef _MKTEMP_H | ||
2 | #define _MKTEMP_H | ||
3 | int mkstemps(char *path, int slen); | ||
4 | int mkstemp(char *path); | ||
5 | char *mkdtemp(char *path); | ||
6 | |||
7 | #endif /* _MKTEMP_H */ | ||
diff --git a/mpaux.c b/mpaux.c new file mode 100644 index 000000000..fd2c18031 --- /dev/null +++ b/mpaux.c | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | |||
3 | mpaux.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sun Jul 16 04:29:30 1995 ylo | ||
11 | |||
12 | This file contains various auxiliary functions related to multiple | ||
13 | precision integers. | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: mpaux.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
19 | |||
20 | #include <openssl/bn.h> | ||
21 | #include "getput.h" | ||
22 | #include "xmalloc.h" | ||
23 | |||
24 | #include <openssl/md5.h> | ||
25 | |||
26 | void | ||
27 | compute_session_id(unsigned char session_id[16], | ||
28 | unsigned char cookie[8], | ||
29 | unsigned int host_key_bits, | ||
30 | BIGNUM *host_key_n, | ||
31 | unsigned int session_key_bits, | ||
32 | BIGNUM *session_key_n) | ||
33 | { | ||
34 | unsigned int bytes = (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8 + 8; | ||
35 | unsigned char *buf = xmalloc(bytes); | ||
36 | MD5_CTX md; | ||
37 | |||
38 | BN_bn2bin(host_key_n, buf); | ||
39 | BN_bn2bin(session_key_n, buf + (host_key_bits + 7 ) / 8); | ||
40 | memcpy(buf + (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8, | ||
41 | cookie, 8); | ||
42 | MD5_Init(&md); | ||
43 | MD5_Update(&md, buf, bytes); | ||
44 | MD5_Final(session_id, &md); | ||
45 | xfree(buf); | ||
46 | } | ||
diff --git a/mpaux.h b/mpaux.h new file mode 100644 index 000000000..3ad06813b --- /dev/null +++ b/mpaux.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | |||
3 | mpaux.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sun Jul 16 04:29:30 1995 ylo | ||
11 | |||
12 | This file contains various auxiliary functions related to multiple | ||
13 | precision integers. | ||
14 | |||
15 | */ | ||
16 | |||
17 | /* RCSID("$Id: mpaux.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
18 | |||
19 | #ifndef MPAUX_H | ||
20 | #define MPAUX_H | ||
21 | |||
22 | /* Computes a 16-byte session id in the global variable session_id. | ||
23 | The session id is computed by concatenating the linearized, msb | ||
24 | first representations of host_key_n, session_key_n, and the cookie. */ | ||
25 | void compute_session_id(unsigned char session_id[16], | ||
26 | unsigned char cookie[8], | ||
27 | unsigned int host_key_bits, | ||
28 | BIGNUM *host_key_n, | ||
29 | unsigned int session_key_bits, | ||
30 | BIGNUM *session_key_n); | ||
31 | |||
32 | #endif /* MPAUX_H */ | ||
diff --git a/nchan.c b/nchan.c new file mode 100644 index 000000000..fcaeae405 --- /dev/null +++ b/nchan.c | |||
@@ -0,0 +1,187 @@ | |||
1 | #include "includes.h" | ||
2 | RCSID("$Id: nchan.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
3 | |||
4 | #include "ssh.h" | ||
5 | |||
6 | #include "buffer.h" | ||
7 | #include "packet.h" | ||
8 | #include "channels.h" | ||
9 | #include "nchan.h" | ||
10 | |||
11 | static void chan_send_ieof(Channel *c); | ||
12 | static void chan_send_oclose(Channel *c); | ||
13 | static void chan_shutdown_write(Channel *c); | ||
14 | static void chan_shutdown_read(Channel *c); | ||
15 | static void chan_delele_if_full_closed(Channel *c); | ||
16 | |||
17 | /* | ||
18 | * EVENTS: update channel input/output states | ||
19 | * execute ACTIONS | ||
20 | */ | ||
21 | /* events concerning the INPUT from socket for channel (istate) */ | ||
22 | void | ||
23 | chan_rcvd_oclose(Channel *c){ | ||
24 | switch(c->istate){ | ||
25 | case CHAN_INPUT_WAIT_OCLOSE: | ||
26 | debug("channel %d: INPUT_WAIT_OCLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self); | ||
27 | c->istate=CHAN_INPUT_CLOSED; | ||
28 | chan_delele_if_full_closed(c); | ||
29 | break; | ||
30 | case CHAN_INPUT_OPEN: | ||
31 | debug("channel %d: INPUT_OPEN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); | ||
32 | chan_shutdown_read(c); | ||
33 | chan_send_ieof(c); | ||
34 | c->istate=CHAN_INPUT_CLOSED; | ||
35 | chan_delele_if_full_closed(c); | ||
36 | break; | ||
37 | default: | ||
38 | debug("protocol error: chan_rcvd_oclose %d for istate %d",c->self,c->istate); | ||
39 | break; | ||
40 | } | ||
41 | } | ||
42 | void | ||
43 | chan_read_failed(Channel *c){ | ||
44 | switch(c->istate){ | ||
45 | case CHAN_INPUT_OPEN: | ||
46 | debug("channel %d: INPUT_OPEN -> INPUT_WAIT_DRAIN [read failed]", c->self); | ||
47 | chan_shutdown_read(c); | ||
48 | c->istate=CHAN_INPUT_WAIT_DRAIN; | ||
49 | break; | ||
50 | default: | ||
51 | debug("internal error: we do not read, but chan_read_failed %d for istate %d", | ||
52 | c->self,c->istate); | ||
53 | break; | ||
54 | } | ||
55 | } | ||
56 | void | ||
57 | chan_ibuf_empty(Channel *c){ | ||
58 | if(buffer_len(&c->input)){ | ||
59 | debug("internal error: chan_ibuf_empty %d for non empty buffer",c->self); | ||
60 | return; | ||
61 | } | ||
62 | switch(c->istate){ | ||
63 | case CHAN_INPUT_WAIT_DRAIN: | ||
64 | debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_WAIT_OCLOSE [inbuf empty, send IEOF]", c->self); | ||
65 | chan_send_ieof(c); | ||
66 | c->istate=CHAN_INPUT_WAIT_OCLOSE; | ||
67 | break; | ||
68 | default: | ||
69 | debug("internal error: chan_ibuf_empty %d for istate %d",c->self,c->istate); | ||
70 | break; | ||
71 | } | ||
72 | } | ||
73 | /* events concerning the OUTPUT from channel for socket (ostate) */ | ||
74 | void | ||
75 | chan_rcvd_ieof(Channel *c){ | ||
76 | switch(c->ostate){ | ||
77 | case CHAN_OUTPUT_OPEN: | ||
78 | debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_DRAIN [rvcd IEOF]", c->self); | ||
79 | c->ostate=CHAN_OUTPUT_WAIT_DRAIN; | ||
80 | break; | ||
81 | case CHAN_OUTPUT_WAIT_IEOF: | ||
82 | debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self); | ||
83 | c->ostate=CHAN_OUTPUT_CLOSED; | ||
84 | chan_delele_if_full_closed(c); | ||
85 | break; | ||
86 | default: | ||
87 | debug("protocol error: chan_rcvd_ieof %d for ostate %d", c->self,c->ostate); | ||
88 | break; | ||
89 | } | ||
90 | } | ||
91 | void | ||
92 | chan_write_failed(Channel *c){ | ||
93 | switch(c->ostate){ | ||
94 | case CHAN_OUTPUT_OPEN: | ||
95 | debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_IEOF [write failed]", c->self); | ||
96 | chan_send_oclose(c); | ||
97 | c->ostate=CHAN_OUTPUT_WAIT_IEOF; | ||
98 | break; | ||
99 | case CHAN_OUTPUT_WAIT_DRAIN: | ||
100 | debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self); | ||
101 | chan_send_oclose(c); | ||
102 | c->ostate=CHAN_OUTPUT_CLOSED; | ||
103 | chan_delele_if_full_closed(c); | ||
104 | break; | ||
105 | default: | ||
106 | debug("internal error: chan_write_failed %d for ostate %d",c->self,c->ostate); | ||
107 | break; | ||
108 | } | ||
109 | } | ||
110 | void | ||
111 | chan_obuf_empty(Channel *c){ | ||
112 | if(buffer_len(&c->output)){ | ||
113 | debug("internal error: chan_obuf_empty %d for non empty buffer",c->self); | ||
114 | return; | ||
115 | } | ||
116 | switch(c->ostate){ | ||
117 | case CHAN_OUTPUT_WAIT_DRAIN: | ||
118 | debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self); | ||
119 | chan_send_oclose(c); | ||
120 | c->ostate=CHAN_OUTPUT_CLOSED; | ||
121 | chan_delele_if_full_closed(c); | ||
122 | break; | ||
123 | default: | ||
124 | debug("internal error: chan_obuf_empty %d for ostate %d",c->self,c->ostate); | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | /* | ||
129 | * ACTIONS: should never update c->istate or c->ostate | ||
130 | */ | ||
131 | static void | ||
132 | chan_send_ieof(Channel *c){ | ||
133 | switch(c->istate){ | ||
134 | case CHAN_INPUT_OPEN: | ||
135 | case CHAN_INPUT_WAIT_DRAIN: | ||
136 | packet_start(SSH_MSG_CHANNEL_INPUT_EOF); | ||
137 | packet_put_int(c->remote_id); | ||
138 | packet_send(); | ||
139 | break; | ||
140 | default: | ||
141 | debug("internal error: channel %d: cannot send IEOF for istate %d",c->self,c->istate); | ||
142 | break; | ||
143 | } | ||
144 | } | ||
145 | static void | ||
146 | chan_send_oclose(Channel *c){ | ||
147 | switch(c->ostate){ | ||
148 | case CHAN_OUTPUT_OPEN: | ||
149 | case CHAN_OUTPUT_WAIT_DRAIN: | ||
150 | chan_shutdown_write(c); | ||
151 | buffer_consume(&c->output, buffer_len(&c->output)); | ||
152 | packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE); | ||
153 | packet_put_int(c->remote_id); | ||
154 | packet_send(); | ||
155 | break; | ||
156 | default: | ||
157 | debug("internal error: channel %d: cannot send OCLOSE for ostate %d",c->self,c->istate); | ||
158 | break; | ||
159 | } | ||
160 | } | ||
161 | /* helper */ | ||
162 | static void | ||
163 | chan_shutdown_write(Channel *c){ | ||
164 | debug("channel %d: shutdown_write", c->self); | ||
165 | if(shutdown(c->sock, SHUT_WR)<0) | ||
166 | error("chan_shutdown_write failed for #%d/fd%d: %.100s", | ||
167 | c->self, c->sock, strerror(errno)); | ||
168 | } | ||
169 | static void | ||
170 | chan_shutdown_read(Channel *c){ | ||
171 | debug("channel %d: shutdown_read", c->self); | ||
172 | if(shutdown(c->sock, SHUT_RD)<0) | ||
173 | error("chan_shutdown_read failed for #%d/fd%d: %.100s", | ||
174 | c->self, c->sock, strerror(errno)); | ||
175 | } | ||
176 | static void | ||
177 | chan_delele_if_full_closed(Channel *c){ | ||
178 | if(c->istate==CHAN_INPUT_CLOSED && c->ostate==CHAN_OUTPUT_CLOSED){ | ||
179 | debug("channel %d: closing", c->self); | ||
180 | channel_free(c->self); | ||
181 | } | ||
182 | } | ||
183 | void | ||
184 | chan_init_iostates(Channel *c){ | ||
185 | c->ostate=CHAN_OUTPUT_OPEN; | ||
186 | c->istate=CHAN_INPUT_OPEN; | ||
187 | } | ||
diff --git a/nchan.h b/nchan.h new file mode 100644 index 000000000..16d360d30 --- /dev/null +++ b/nchan.h | |||
@@ -0,0 +1,57 @@ | |||
1 | /* RCSID("$Id: nchan.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
2 | |||
3 | #ifndef NCHAN_H | ||
4 | #define NCHAN_H | ||
5 | |||
6 | /* | ||
7 | * SSH Protocol 1.5 aka New Channel Protocol | ||
8 | * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored. | ||
9 | * Written by Markus Friedl in October 1999 | ||
10 | * | ||
11 | * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the | ||
12 | * tear down of channels: | ||
13 | * | ||
14 | * 1.3: strict request-ack-protocol: | ||
15 | * CLOSE -> | ||
16 | * <- CLOSE_CONFIRM | ||
17 | * | ||
18 | * 1.5: uses variations of: | ||
19 | * IEOF -> | ||
20 | * <- OCLOSE | ||
21 | * <- IEOF | ||
22 | * OCLOSE -> | ||
23 | * i.e. both sides have to close the channel | ||
24 | * | ||
25 | * See the debugging output from 'ssh -v' and 'sshd -d' of | ||
26 | * ssh-1.2.27 as an example. | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | /* ssh-proto-1.5 overloads prot-1.3-message-types */ | ||
31 | #define SSH_MSG_CHANNEL_INPUT_EOF SSH_MSG_CHANNEL_CLOSE | ||
32 | #define SSH_MSG_CHANNEL_OUTPUT_CLOSE SSH_MSG_CHANNEL_CLOSE_CONFIRMATION | ||
33 | |||
34 | /* possible input states */ | ||
35 | #define CHAN_INPUT_OPEN 0x01 | ||
36 | #define CHAN_INPUT_WAIT_DRAIN 0x02 | ||
37 | #define CHAN_INPUT_WAIT_OCLOSE 0x04 | ||
38 | #define CHAN_INPUT_CLOSED 0x08 | ||
39 | |||
40 | /* possible output states */ | ||
41 | #define CHAN_OUTPUT_OPEN 0x10 | ||
42 | #define CHAN_OUTPUT_WAIT_DRAIN 0x20 | ||
43 | #define CHAN_OUTPUT_WAIT_IEOF 0x40 | ||
44 | #define CHAN_OUTPUT_CLOSED 0x80 | ||
45 | |||
46 | /* EVENTS for the input state */ | ||
47 | void chan_rcvd_oclose(Channel *c); | ||
48 | void chan_read_failed(Channel *c); | ||
49 | void chan_ibuf_empty(Channel *c); | ||
50 | |||
51 | /* EVENTS for the output state */ | ||
52 | void chan_rcvd_ieof(Channel *c); | ||
53 | void chan_write_failed(Channel *c); | ||
54 | void chan_obuf_empty(Channel *c); | ||
55 | |||
56 | void chan_init_iostates(Channel *c); | ||
57 | #endif | ||
diff --git a/nchan.ms b/nchan.ms new file mode 100644 index 000000000..b01512f78 --- /dev/null +++ b/nchan.ms | |||
@@ -0,0 +1,71 @@ | |||
1 | .TL | ||
2 | OpenSSH Channel Close Protocol 1.5 Implementation | ||
3 | .SH | ||
4 | Channel Input State Diagram | ||
5 | .PS | ||
6 | reset | ||
7 | l=1 | ||
8 | s=1.2 | ||
9 | ellipsewid=s*ellipsewid | ||
10 | boxwid=s*boxwid | ||
11 | ellipseht=s*ellipseht | ||
12 | S1: ellipse "INPUT" "OPEN" | ||
13 | move right 2*l from last ellipse.e | ||
14 | S4: ellipse "INPUT" "CLOSED" | ||
15 | move down l from last ellipse.s | ||
16 | S3: ellipse "INPUT" "WAIT" "OCLOSED" | ||
17 | move down l from 1st ellipse.s | ||
18 | S2: ellipse "INPUT" "WAIT" "DRAIN" | ||
19 | arrow "" "rcvd OCLOSE/" "shutdown_read" "send IEOF" from S1.e to S4.w | ||
20 | arrow "ibuf_empty/" "send IEOF" from S2.e to S3.w | ||
21 | arrow from S1.s to S2.n | ||
22 | box invis "read_failed/" "shutdown_read" with .e at last arrow.c | ||
23 | arrow from S3.n to S4.s | ||
24 | box invis "rcvd OCLOSE/" "-" with .w at last arrow.c | ||
25 | ellipse wid .9*ellipsewid ht .9*ellipseht at S4 | ||
26 | arrow "start" "" from S1.w+(-0.5,0) to S1.w | ||
27 | .PE | ||
28 | .SH | ||
29 | Channel Output State Diagram | ||
30 | .PS | ||
31 | S1: ellipse "OUTPUT" "OPEN" | ||
32 | move right 2*l from last ellipse.e | ||
33 | S3: ellipse "OUTPUT" "WAIT" "IEOF" | ||
34 | move down l from last ellipse.s | ||
35 | S4: ellipse "OUTPUT" "CLOSED" | ||
36 | move down l from 1st ellipse.s | ||
37 | S2: ellipse "OUTPUT" "WAIT" "DRAIN" | ||
38 | arrow "" "write_failed/" "shutdown_write" "send OCLOSE" from S1.e to S3.w | ||
39 | arrow "obuf_empty ||" "write_failed/" "shutdown_write" "send OCLOSE" from S2.e to S4.w | ||
40 | arrow from S1.s to S2.n | ||
41 | box invis "rcvd IEOF/" "-" with .e at last arrow.c | ||
42 | arrow from S3.s to S4.n | ||
43 | box invis "rcvd IEOF/" "-" with .w at last arrow.c | ||
44 | ellipse wid .9*ellipsewid ht .9*ellipseht at S4 | ||
45 | arrow "start" "" from S1.w+(-0.5,0) to S1.w | ||
46 | .PE | ||
47 | .SH | ||
48 | Notes | ||
49 | .PP | ||
50 | The input buffer is filled with data from the socket | ||
51 | (the socket represents the local comsumer/producer of the | ||
52 | forwarded channel). | ||
53 | The data is then sent over the INPUT-end of the channel to the | ||
54 | remote peer. | ||
55 | Data sent by the peer is received on the OUTPUT-end, | ||
56 | saved in the output buffer and written to the socket. | ||
57 | .PP | ||
58 | If the local protocol instance has forwarded all data on the | ||
59 | INPUT-end of the channel, it sends an IEOF message to the peer. | ||
60 | If the peer receives the IEOF and has comsumed all | ||
61 | data he replies with an OCLOSE. | ||
62 | When the local instance receives the OCLOSE | ||
63 | he considers the INPUT-half of the channel closed. | ||
64 | The peer has his OUTOUT-half closed. | ||
65 | .PP | ||
66 | A channel can be deallocated by a protocol instance | ||
67 | if both the INPUT- and the OUTOUT-half on his | ||
68 | side of the channel are closed. | ||
69 | Note that when an instance is unable to comsume the | ||
70 | received data, he is permitted to send an OCLOSE | ||
71 | before the matching IEOF is received. | ||
diff --git a/openssh.spec b/openssh.spec new file mode 100644 index 000000000..7ce58849f --- /dev/null +++ b/openssh.spec | |||
@@ -0,0 +1,105 @@ | |||
1 | Summary: OpenSSH free Secure Shell (SSH) implementation | ||
2 | Name: openssh | ||
3 | Version: 1.2pre3 | ||
4 | Release: 1 | ||
5 | Packager: Damien Miller <djm@ibs.com.au> | ||
6 | Source0: openssh-%{version}-linux.tar.gz | ||
7 | Copyright: BSD | ||
8 | Group: Applications/Internet | ||
9 | BuildRoot: /tmp/openssh-%{version}-buildroot | ||
10 | |||
11 | %description | ||
12 | Ssh (Secure Shell) a program for logging into a remote machine and for | ||
13 | executing commands in a remote machine. It is intended to replace | ||
14 | rlogin and rsh, and provide secure encrypted communications between | ||
15 | two untrusted hosts over an insecure network. X11 connections and | ||
16 | arbitrary TCP/IP ports can also be forwarded over the secure channel. | ||
17 | |||
18 | OpenSSH is OpenBSD's rework of the last free version of SSH, bringing it | ||
19 | up to date in terms of security and features, as well as removing all | ||
20 | patented algorithms to seperate libraries (OpenSSL). | ||
21 | |||
22 | %changelog | ||
23 | * Wed Oct 27 1999 Damien Miller <djm@ibs.com.au> | ||
24 | - Initial RPMification, based on Jan "Yenya" Kasprzak's <kas@fi.muni.cz> spec. | ||
25 | |||
26 | %prep | ||
27 | |||
28 | %setup -n openssh | ||
29 | |||
30 | %build | ||
31 | |||
32 | make -f Makefile.GNU OPT_FLAGS="$RPM_OPT_FLAGS" | ||
33 | |||
34 | %install | ||
35 | rm -rf $RPM_BUILD_ROOT | ||
36 | mkdir -p $RPM_BUILD_ROOT/usr/bin | ||
37 | mkdir -p $RPM_BUILD_ROOT/usr/sbin | ||
38 | mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d | ||
39 | mkdir -p $RPM_BUILD_ROOT/etc/pam.d | ||
40 | mkdir -p $RPM_BUILD_ROOT/etc/ssh | ||
41 | mkdir -p $RPM_BUILD_ROOT/usr/man/man1 | ||
42 | mkdir -p $RPM_BUILD_ROOT/usr/man/man8 | ||
43 | |||
44 | install -m644 ssh.pam $RPM_BUILD_ROOT/etc/pam.d/ssh | ||
45 | install -m755 sshd.init $RPM_BUILD_ROOT/etc/rc.d/init.d/sshd | ||
46 | install -m600 ssh_config $RPM_BUILD_ROOT/etc/ssh/ssh_config | ||
47 | install -m600 sshd_config $RPM_BUILD_ROOT/etc/ssh/sshd_config | ||
48 | |||
49 | install -s -m755 bin/sshd $RPM_BUILD_ROOT/usr/sbin | ||
50 | install -s -m755 bin/ssh $RPM_BUILD_ROOT/usr/bin | ||
51 | install -s -m755 bin/scp $RPM_BUILD_ROOT/usr/bin | ||
52 | install -s -m755 bin/ssh-agent $RPM_BUILD_ROOT/usr/bin | ||
53 | install -s -m755 bin/ssh-add $RPM_BUILD_ROOT/usr/bin | ||
54 | install -s -m755 bin/ssh-keygen $RPM_BUILD_ROOT/usr/bin | ||
55 | |||
56 | install -m644 sshd.8 $RPM_BUILD_ROOT/usr/man/man8 | ||
57 | install -m644 ssh.1 $RPM_BUILD_ROOT/usr/man/man1 | ||
58 | install -m644 scp.1 $RPM_BUILD_ROOT/usr/man/man1 | ||
59 | install -m644 ssh-agent.1 $RPM_BUILD_ROOT/usr/man/man1 | ||
60 | install -m644 ssh-add.1 $RPM_BUILD_ROOT/usr/man/man1 | ||
61 | install -m644 ssh-keygen.1 $RPM_BUILD_ROOT/usr/man/man1 | ||
62 | |||
63 | %clean | ||
64 | rm -rf $RPM_BUILD_ROOT | ||
65 | |||
66 | %post | ||
67 | /sbin/chkconfig --add sshd | ||
68 | if [ ! -f /etc/ssh/ssh_host_key -o ! -s /etc/ssh/ssh_host_key ]; then | ||
69 | /usr/bin/ssh-keygen -b 1024 -f /etc/ssh/ssh_host_key -N '' >&2 | ||
70 | fi | ||
71 | if test -r /var/run/sshd.pid | ||
72 | then | ||
73 | /etc/rc.d/init.d/sshd restart >&2 | ||
74 | fi | ||
75 | |||
76 | %preun | ||
77 | if [ "$1" = 0 ] | ||
78 | then | ||
79 | /etc/rc.d/init.d/sshd stop >&2 | ||
80 | /sbin/chkconfig --del sshd | ||
81 | fi | ||
82 | |||
83 | %files | ||
84 | %defattr(-,root,root) | ||
85 | %doc COPYING.Ylonen ChangeLog ChangeLog.linux OVERVIEW | ||
86 | %doc README README.openssh | ||
87 | %attr(0755,root,root) /usr/sbin/sshd | ||
88 | %attr(0755,root,root) /usr/bin/ssh | ||
89 | %attr(0755,root,root) /usr/bin/ssh-agent | ||
90 | %attr(0755,root,root) /usr/bin/ssh-keygen | ||
91 | %attr(0755,root,root) /usr/bin/ssh-add | ||
92 | %attr(0755,root,root) /usr/bin/scp | ||
93 | |||
94 | %attr(0755,root,root) /usr/man/man8/sshd.8 | ||
95 | %attr(0755,root,root) /usr/man/man1/ssh.1 | ||
96 | %attr(0755,root,root) /usr/man/man1/ssh-agent.1 | ||
97 | %attr(0755,root,root) /usr/man/man1/ssh-keygen.1 | ||
98 | %attr(0755,root,root) /usr/man/man1/ssh-add.1 | ||
99 | %attr(0755,root,root) /usr/man/man1/scp.1 | ||
100 | |||
101 | %attr(0600,root,root) %config /etc/ssh/sshd_config | ||
102 | %attr(0600,root,root) %config /etc/pam.d/ssh | ||
103 | %attr(0755,root,root) %config /etc/rc.d/init.d/sshd | ||
104 | %attr(0644,root,root) %config /etc/ssh/ssh_config | ||
105 | |||
diff --git a/packet.c b/packet.c new file mode 100644 index 000000000..7e74c73b3 --- /dev/null +++ b/packet.c | |||
@@ -0,0 +1,762 @@ | |||
1 | /* | ||
2 | |||
3 | packet.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Mar 18 02:40:40 1995 ylo | ||
11 | |||
12 | This file contains code implementing the packet protocol and communication | ||
13 | with the other side. This same code is used both on client and server side. | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: packet.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
19 | |||
20 | #include "xmalloc.h" | ||
21 | #include "buffer.h" | ||
22 | #include "packet.h" | ||
23 | #include "bufaux.h" | ||
24 | #include "ssh.h" | ||
25 | #include "crc32.h" | ||
26 | #include "cipher.h" | ||
27 | #include "getput.h" | ||
28 | |||
29 | #include "compress.h" | ||
30 | #include "deattack.h" | ||
31 | |||
32 | /* This variable contains the file descriptors used for communicating with | ||
33 | the other side. connection_in is used for reading; connection_out | ||
34 | for writing. These can be the same descriptor, in which case it is | ||
35 | assumed to be a socket. */ | ||
36 | static int connection_in = -1; | ||
37 | static int connection_out = -1; | ||
38 | |||
39 | /* Cipher type. This value is only used to determine whether to pad the | ||
40 | packets with zeroes or random data. */ | ||
41 | static int cipher_type = SSH_CIPHER_NONE; | ||
42 | |||
43 | /* Protocol flags for the remote side. */ | ||
44 | static unsigned int remote_protocol_flags = 0; | ||
45 | |||
46 | /* Encryption context for receiving data. This is only used for decryption. */ | ||
47 | static CipherContext receive_context; | ||
48 | /* Encryption coontext for sending data. This is only used for encryption. */ | ||
49 | static CipherContext send_context; | ||
50 | |||
51 | /* Buffer for raw input data from the socket. */ | ||
52 | static Buffer input; | ||
53 | |||
54 | /* Buffer for raw output data going to the socket. */ | ||
55 | static Buffer output; | ||
56 | |||
57 | /* Buffer for the partial outgoing packet being constructed. */ | ||
58 | static Buffer outgoing_packet; | ||
59 | |||
60 | /* Buffer for the incoming packet currently being processed. */ | ||
61 | static Buffer incoming_packet; | ||
62 | |||
63 | /* Scratch buffer for packet compression/decompression. */ | ||
64 | static Buffer compression_buffer; | ||
65 | |||
66 | /* Flag indicating whether packet compression/decompression is enabled. */ | ||
67 | static int packet_compression = 0; | ||
68 | |||
69 | /* Flag indicating whether this module has been initialized. */ | ||
70 | static int initialized = 0; | ||
71 | |||
72 | /* Set to true if the connection is interactive. */ | ||
73 | static int interactive_mode = 0; | ||
74 | |||
75 | /* Sets the descriptors used for communication. Disables encryption until | ||
76 | packet_set_encryption_key is called. */ | ||
77 | |||
78 | void | ||
79 | packet_set_connection(int fd_in, int fd_out) | ||
80 | { | ||
81 | connection_in = fd_in; | ||
82 | connection_out = fd_out; | ||
83 | cipher_type = SSH_CIPHER_NONE; | ||
84 | cipher_set_key(&send_context, SSH_CIPHER_NONE, (unsigned char *)"", 0, 1); | ||
85 | cipher_set_key(&receive_context, SSH_CIPHER_NONE, (unsigned char *)"", 0, 0); | ||
86 | if (!initialized) | ||
87 | { | ||
88 | initialized = 1; | ||
89 | buffer_init(&input); | ||
90 | buffer_init(&output); | ||
91 | buffer_init(&outgoing_packet); | ||
92 | buffer_init(&incoming_packet); | ||
93 | } | ||
94 | |||
95 | /* Kludge: arrange the close function to be called from fatal(). */ | ||
96 | fatal_add_cleanup((void (*)(void *))packet_close, NULL); | ||
97 | } | ||
98 | |||
99 | /* Sets the connection into non-blocking mode. */ | ||
100 | |||
101 | void | ||
102 | packet_set_nonblocking() | ||
103 | { | ||
104 | /* Set the socket into non-blocking mode. */ | ||
105 | if (fcntl(connection_in, F_SETFL, O_NONBLOCK) < 0) | ||
106 | error("fcntl O_NONBLOCK: %.100s", strerror(errno)); | ||
107 | |||
108 | if (connection_out != connection_in) | ||
109 | { | ||
110 | if (fcntl(connection_out, F_SETFL, O_NONBLOCK) < 0) | ||
111 | error("fcntl O_NONBLOCK: %.100s", strerror(errno)); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | /* Returns the socket used for reading. */ | ||
116 | |||
117 | int | ||
118 | packet_get_connection_in() | ||
119 | { | ||
120 | return connection_in; | ||
121 | } | ||
122 | |||
123 | /* Returns the descriptor used for writing. */ | ||
124 | |||
125 | int | ||
126 | packet_get_connection_out() | ||
127 | { | ||
128 | return connection_out; | ||
129 | } | ||
130 | |||
131 | /* Closes the connection and clears and frees internal data structures. */ | ||
132 | |||
133 | void | ||
134 | packet_close() | ||
135 | { | ||
136 | if (!initialized) | ||
137 | return; | ||
138 | initialized = 0; | ||
139 | if (connection_in == connection_out) | ||
140 | { | ||
141 | shutdown(connection_out, SHUT_RDWR); | ||
142 | close(connection_out); | ||
143 | } | ||
144 | else | ||
145 | { | ||
146 | close(connection_in); | ||
147 | close(connection_out); | ||
148 | } | ||
149 | buffer_free(&input); | ||
150 | buffer_free(&output); | ||
151 | buffer_free(&outgoing_packet); | ||
152 | buffer_free(&incoming_packet); | ||
153 | if (packet_compression) | ||
154 | { | ||
155 | buffer_free(&compression_buffer); | ||
156 | buffer_compress_uninit(); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | /* Sets remote side protocol flags. */ | ||
161 | |||
162 | void | ||
163 | packet_set_protocol_flags(unsigned int protocol_flags) | ||
164 | { | ||
165 | remote_protocol_flags = protocol_flags; | ||
166 | channel_set_options((protocol_flags & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) != 0); | ||
167 | } | ||
168 | |||
169 | /* Returns the remote protocol flags set earlier by the above function. */ | ||
170 | |||
171 | unsigned int | ||
172 | packet_get_protocol_flags() | ||
173 | { | ||
174 | return remote_protocol_flags; | ||
175 | } | ||
176 | |||
177 | /* Starts packet compression from the next packet on in both directions. | ||
178 | Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */ | ||
179 | |||
180 | void | ||
181 | packet_start_compression(int level) | ||
182 | { | ||
183 | if (packet_compression) | ||
184 | fatal("Compression already enabled."); | ||
185 | packet_compression = 1; | ||
186 | buffer_init(&compression_buffer); | ||
187 | buffer_compress_init(level); | ||
188 | } | ||
189 | |||
190 | /* Encrypts the given number of bytes, copying from src to dest. | ||
191 | bytes is known to be a multiple of 8. */ | ||
192 | |||
193 | void | ||
194 | packet_encrypt(CipherContext *cc, void *dest, void *src, | ||
195 | unsigned int bytes) | ||
196 | { | ||
197 | assert((bytes % 8) == 0); | ||
198 | cipher_encrypt(cc, dest, src, bytes); | ||
199 | } | ||
200 | |||
201 | /* Decrypts the given number of bytes, copying from src to dest. | ||
202 | bytes is known to be a multiple of 8. */ | ||
203 | |||
204 | void | ||
205 | packet_decrypt(CipherContext *cc, void *dest, void *src, | ||
206 | unsigned int bytes) | ||
207 | { | ||
208 | int i; | ||
209 | |||
210 | assert((bytes % 8) == 0); | ||
211 | |||
212 | /* | ||
213 | Cryptographic attack detector for ssh - Modifications for packet.c | ||
214 | (C)1998 CORE-SDI, Buenos Aires Argentina | ||
215 | Ariel Futoransky(futo@core-sdi.com) | ||
216 | */ | ||
217 | switch (cc->type) | ||
218 | { | ||
219 | case SSH_CIPHER_NONE: | ||
220 | i = DEATTACK_OK; | ||
221 | break; | ||
222 | default: | ||
223 | i = detect_attack(src, bytes, NULL); | ||
224 | break; | ||
225 | } | ||
226 | |||
227 | if (i == DEATTACK_DETECTED) | ||
228 | packet_disconnect("crc32 compensation attack: network attack detected"); | ||
229 | |||
230 | cipher_decrypt(cc, dest, src, bytes); | ||
231 | } | ||
232 | |||
233 | /* Causes any further packets to be encrypted using the given key. The same | ||
234 | key is used for both sending and reception. However, both directions | ||
235 | are encrypted independently of each other. */ | ||
236 | |||
237 | void | ||
238 | packet_set_encryption_key(const unsigned char *key, unsigned int keylen, | ||
239 | int cipher, int is_client) | ||
240 | { | ||
241 | cipher_type = cipher; | ||
242 | if (cipher == SSH_CIPHER_RC4) | ||
243 | { | ||
244 | if (is_client) | ||
245 | { /* In client: use first half for receiving, second for sending. */ | ||
246 | cipher_set_key(&receive_context, cipher, key, keylen / 2, 0); | ||
247 | cipher_set_key(&send_context, cipher, key + keylen / 2, | ||
248 | keylen / 2, 1); | ||
249 | } | ||
250 | else | ||
251 | { /* In server: use first half for sending, second for receiving. */ | ||
252 | cipher_set_key(&receive_context, cipher, key + keylen / 2, | ||
253 | keylen / 2, 0); | ||
254 | cipher_set_key(&send_context, cipher, key, keylen / 2, 1); | ||
255 | } | ||
256 | } | ||
257 | else | ||
258 | { | ||
259 | /* All other ciphers use the same key in both directions for now. */ | ||
260 | cipher_set_key(&receive_context, cipher, key, keylen, 0); | ||
261 | cipher_set_key(&send_context, cipher, key, keylen, 1); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | /* Starts constructing a packet to send. */ | ||
266 | |||
267 | void | ||
268 | packet_start(int type) | ||
269 | { | ||
270 | char buf[9]; | ||
271 | |||
272 | buffer_clear(&outgoing_packet); | ||
273 | memset(buf, 0, 8); | ||
274 | buf[8] = type; | ||
275 | buffer_append(&outgoing_packet, buf, 9); | ||
276 | } | ||
277 | |||
278 | /* Appends a character to the packet data. */ | ||
279 | |||
280 | void | ||
281 | packet_put_char(int value) | ||
282 | { | ||
283 | char ch = value; | ||
284 | buffer_append(&outgoing_packet, &ch, 1); | ||
285 | } | ||
286 | |||
287 | /* Appends an integer to the packet data. */ | ||
288 | |||
289 | void | ||
290 | packet_put_int(unsigned int value) | ||
291 | { | ||
292 | buffer_put_int(&outgoing_packet, value); | ||
293 | } | ||
294 | |||
295 | /* Appends a string to packet data. */ | ||
296 | |||
297 | void | ||
298 | packet_put_string(const char *buf, unsigned int len) | ||
299 | { | ||
300 | buffer_put_string(&outgoing_packet, buf, len); | ||
301 | } | ||
302 | |||
303 | /* Appends an arbitrary precision integer to packet data. */ | ||
304 | |||
305 | void | ||
306 | packet_put_bignum(BIGNUM *value) | ||
307 | { | ||
308 | buffer_put_bignum(&outgoing_packet, value); | ||
309 | } | ||
310 | |||
311 | /* Finalizes and sends the packet. If the encryption key has been set, | ||
312 | encrypts the packet before sending. */ | ||
313 | |||
314 | void | ||
315 | packet_send() | ||
316 | { | ||
317 | char buf[8], *cp; | ||
318 | int i, padding, len; | ||
319 | unsigned int checksum; | ||
320 | u_int32_t rand = 0; | ||
321 | |||
322 | /* If using packet compression, compress the payload of the outgoing | ||
323 | packet. */ | ||
324 | if (packet_compression) | ||
325 | { | ||
326 | buffer_clear(&compression_buffer); | ||
327 | buffer_consume(&outgoing_packet, 8); /* Skip padding. */ | ||
328 | buffer_append(&compression_buffer, "\0\0\0\0\0\0\0\0", 8); /* padding */ | ||
329 | buffer_compress(&outgoing_packet, &compression_buffer); | ||
330 | buffer_clear(&outgoing_packet); | ||
331 | buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), | ||
332 | buffer_len(&compression_buffer)); | ||
333 | } | ||
334 | |||
335 | /* Compute packet length without padding (add checksum, remove padding). */ | ||
336 | len = buffer_len(&outgoing_packet) + 4 - 8; | ||
337 | |||
338 | /* Insert padding. */ | ||
339 | padding = 8 - len % 8; | ||
340 | if (cipher_type != SSH_CIPHER_NONE) | ||
341 | { | ||
342 | cp = buffer_ptr(&outgoing_packet); | ||
343 | for (i = 0; i < padding; i++) { | ||
344 | if (i % 4 == 0) | ||
345 | rand = arc4random(); | ||
346 | cp[7 - i] = rand & 0xff; | ||
347 | rand >>= 8; | ||
348 | } | ||
349 | } | ||
350 | buffer_consume(&outgoing_packet, 8 - padding); | ||
351 | |||
352 | /* Add check bytes. */ | ||
353 | checksum = crc32((unsigned char *)buffer_ptr(&outgoing_packet), | ||
354 | buffer_len(&outgoing_packet)); | ||
355 | PUT_32BIT(buf, checksum); | ||
356 | buffer_append(&outgoing_packet, buf, 4); | ||
357 | |||
358 | #ifdef PACKET_DEBUG | ||
359 | fprintf(stderr, "packet_send plain: "); | ||
360 | buffer_dump(&outgoing_packet); | ||
361 | #endif | ||
362 | |||
363 | /* Append to output. */ | ||
364 | PUT_32BIT(buf, len); | ||
365 | buffer_append(&output, buf, 4); | ||
366 | buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); | ||
367 | packet_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), | ||
368 | buffer_len(&outgoing_packet)); | ||
369 | |||
370 | #ifdef PACKET_DEBUG | ||
371 | fprintf(stderr, "encrypted: "); buffer_dump(&output); | ||
372 | #endif | ||
373 | |||
374 | buffer_clear(&outgoing_packet); | ||
375 | |||
376 | /* Note that the packet is now only buffered in output. It won\'t be | ||
377 | actually sent until packet_write_wait or packet_write_poll is called. */ | ||
378 | } | ||
379 | |||
380 | /* Waits until a packet has been received, and returns its type. Note that | ||
381 | no other data is processed until this returns, so this function should | ||
382 | not be used during the interactive session. */ | ||
383 | |||
384 | int | ||
385 | packet_read(int *payload_len_ptr) | ||
386 | { | ||
387 | int type, len; | ||
388 | fd_set set; | ||
389 | char buf[8192]; | ||
390 | |||
391 | /* Since we are blocking, ensure that all written packets have been sent. */ | ||
392 | packet_write_wait(); | ||
393 | |||
394 | /* Stay in the loop until we have received a complete packet. */ | ||
395 | for (;;) | ||
396 | { | ||
397 | /* Try to read a packet from the buffer. */ | ||
398 | type = packet_read_poll(payload_len_ptr); | ||
399 | if (type == SSH_SMSG_SUCCESS | ||
400 | || type == SSH_SMSG_FAILURE | ||
401 | || type == SSH_CMSG_EOF | ||
402 | || type == SSH_CMSG_EXIT_CONFIRMATION) | ||
403 | packet_integrity_check(*payload_len_ptr, 0, type); | ||
404 | /* If we got a packet, return it. */ | ||
405 | if (type != SSH_MSG_NONE) | ||
406 | return type; | ||
407 | /* Otherwise, wait for some data to arrive, add it to the buffer, | ||
408 | and try again. */ | ||
409 | FD_ZERO(&set); | ||
410 | FD_SET(connection_in, &set); | ||
411 | /* Wait for some data to arrive. */ | ||
412 | select(connection_in + 1, &set, NULL, NULL, NULL); | ||
413 | /* Read data from the socket. */ | ||
414 | len = read(connection_in, buf, sizeof(buf)); | ||
415 | if (len == 0) | ||
416 | fatal("Connection closed by remote host."); | ||
417 | if (len < 0) | ||
418 | fatal("Read from socket failed: %.100s", strerror(errno)); | ||
419 | /* Append it to the buffer. */ | ||
420 | packet_process_incoming(buf, len); | ||
421 | } | ||
422 | /*NOTREACHED*/ | ||
423 | } | ||
424 | |||
425 | /* Waits until a packet has been received, verifies that its type matches | ||
426 | that given, and gives a fatal error and exits if there is a mismatch. */ | ||
427 | |||
428 | void | ||
429 | packet_read_expect(int *payload_len_ptr, int expected_type) | ||
430 | { | ||
431 | int type; | ||
432 | |||
433 | type = packet_read(payload_len_ptr); | ||
434 | if (type != expected_type) | ||
435 | packet_disconnect("Protocol error: expected packet type %d, got %d", | ||
436 | expected_type, type); | ||
437 | } | ||
438 | |||
439 | /* Checks if a full packet is available in the data received so far via | ||
440 | packet_process_incoming. If so, reads the packet; otherwise returns | ||
441 | SSH_MSG_NONE. This does not wait for data from the connection. | ||
442 | |||
443 | SSH_MSG_DISCONNECT is handled specially here. Also, | ||
444 | SSH_MSG_IGNORE messages are skipped by this function and are never returned | ||
445 | to higher levels. | ||
446 | |||
447 | The returned payload_len does include space consumed by: | ||
448 | Packet length | ||
449 | Padding | ||
450 | Packet type | ||
451 | Check bytes | ||
452 | |||
453 | |||
454 | */ | ||
455 | |||
456 | int | ||
457 | packet_read_poll(int *payload_len_ptr) | ||
458 | { | ||
459 | unsigned int len, padded_len; | ||
460 | unsigned char *ucp; | ||
461 | char buf[8], *cp; | ||
462 | unsigned int checksum, stored_checksum; | ||
463 | |||
464 | restart: | ||
465 | |||
466 | /* Check if input size is less than minimum packet size. */ | ||
467 | if (buffer_len(&input) < 4 + 8) | ||
468 | return SSH_MSG_NONE; | ||
469 | /* Get length of incoming packet. */ | ||
470 | ucp = (unsigned char *)buffer_ptr(&input); | ||
471 | len = GET_32BIT(ucp); | ||
472 | if (len < 1 + 2 + 2 || len > 256*1024) | ||
473 | packet_disconnect("Bad packet length %d.", len); | ||
474 | padded_len = (len + 8) & ~7; | ||
475 | |||
476 | /* Check if the packet has been entirely received. */ | ||
477 | if (buffer_len(&input) < 4 + padded_len) | ||
478 | return SSH_MSG_NONE; | ||
479 | |||
480 | /* The entire packet is in buffer. */ | ||
481 | |||
482 | /* Consume packet length. */ | ||
483 | buffer_consume(&input, 4); | ||
484 | |||
485 | /* Copy data to incoming_packet. */ | ||
486 | buffer_clear(&incoming_packet); | ||
487 | buffer_append_space(&incoming_packet, &cp, padded_len); | ||
488 | packet_decrypt(&receive_context, cp, buffer_ptr(&input), padded_len); | ||
489 | buffer_consume(&input, padded_len); | ||
490 | |||
491 | #ifdef PACKET_DEBUG | ||
492 | fprintf(stderr, "read_poll plain: "); buffer_dump(&incoming_packet); | ||
493 | #endif | ||
494 | |||
495 | /* Compute packet checksum. */ | ||
496 | checksum = crc32((unsigned char *)buffer_ptr(&incoming_packet), | ||
497 | buffer_len(&incoming_packet) - 4); | ||
498 | |||
499 | /* Skip padding. */ | ||
500 | buffer_consume(&incoming_packet, 8 - len % 8); | ||
501 | |||
502 | /* Test check bytes. */ | ||
503 | assert(len == buffer_len(&incoming_packet)); | ||
504 | ucp = (unsigned char *)buffer_ptr(&incoming_packet) + len - 4; | ||
505 | stored_checksum = GET_32BIT(ucp); | ||
506 | if (checksum != stored_checksum) | ||
507 | packet_disconnect("Corrupted check bytes on input."); | ||
508 | buffer_consume_end(&incoming_packet, 4); | ||
509 | |||
510 | /* If using packet compression, decompress the packet. */ | ||
511 | if (packet_compression) | ||
512 | { | ||
513 | buffer_clear(&compression_buffer); | ||
514 | buffer_uncompress(&incoming_packet, &compression_buffer); | ||
515 | buffer_clear(&incoming_packet); | ||
516 | buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), | ||
517 | buffer_len(&compression_buffer)); | ||
518 | } | ||
519 | |||
520 | /* Get packet type. */ | ||
521 | buffer_get(&incoming_packet, &buf[0], 1); | ||
522 | |||
523 | /* Return length of payload (without type field). */ | ||
524 | *payload_len_ptr = buffer_len(&incoming_packet); | ||
525 | |||
526 | /* Handle disconnect message. */ | ||
527 | if ((unsigned char)buf[0] == SSH_MSG_DISCONNECT) | ||
528 | fatal("%.900s", packet_get_string(NULL)); | ||
529 | |||
530 | /* Ignore ignore messages. */ | ||
531 | if ((unsigned char)buf[0] == SSH_MSG_IGNORE) | ||
532 | goto restart; | ||
533 | |||
534 | /* Send debug messages as debugging output. */ | ||
535 | if ((unsigned char)buf[0] == SSH_MSG_DEBUG) | ||
536 | { | ||
537 | debug("Remote: %.900s", packet_get_string(NULL)); | ||
538 | goto restart; | ||
539 | } | ||
540 | |||
541 | /* Return type. */ | ||
542 | return (unsigned char)buf[0]; | ||
543 | } | ||
544 | |||
545 | /* Buffers the given amount of input characters. This is intended to be | ||
546 | used together with packet_read_poll. */ | ||
547 | |||
548 | void | ||
549 | packet_process_incoming(const char *buf, unsigned int len) | ||
550 | { | ||
551 | buffer_append(&input, buf, len); | ||
552 | } | ||
553 | |||
554 | /* Returns a character from the packet. */ | ||
555 | |||
556 | unsigned int | ||
557 | packet_get_char() | ||
558 | { | ||
559 | char ch; | ||
560 | buffer_get(&incoming_packet, &ch, 1); | ||
561 | return (unsigned char)ch; | ||
562 | } | ||
563 | |||
564 | /* Returns an integer from the packet data. */ | ||
565 | |||
566 | unsigned int | ||
567 | packet_get_int() | ||
568 | { | ||
569 | return buffer_get_int(&incoming_packet); | ||
570 | } | ||
571 | |||
572 | /* Returns an arbitrary precision integer from the packet data. The integer | ||
573 | must have been initialized before this call. */ | ||
574 | |||
575 | void | ||
576 | packet_get_bignum(BIGNUM *value, int *length_ptr) | ||
577 | { | ||
578 | *length_ptr = buffer_get_bignum(&incoming_packet, value); | ||
579 | } | ||
580 | |||
581 | /* Returns a string from the packet data. The string is allocated using | ||
582 | xmalloc; it is the responsibility of the calling program to free it when | ||
583 | no longer needed. The length_ptr argument may be NULL, or point to an | ||
584 | integer into which the length of the string is stored. */ | ||
585 | |||
586 | char | ||
587 | *packet_get_string(unsigned int *length_ptr) | ||
588 | { | ||
589 | return buffer_get_string(&incoming_packet, length_ptr); | ||
590 | } | ||
591 | |||
592 | /* Sends a diagnostic message from the server to the client. This message | ||
593 | can be sent at any time (but not while constructing another message). | ||
594 | The message is printed immediately, but only if the client is being | ||
595 | executed in verbose mode. These messages are primarily intended to | ||
596 | ease debugging authentication problems. The length of the formatted | ||
597 | message must not exceed 1024 bytes. This will automatically call | ||
598 | packet_write_wait. */ | ||
599 | |||
600 | void | ||
601 | packet_send_debug(const char *fmt, ...) | ||
602 | { | ||
603 | char buf[1024]; | ||
604 | va_list args; | ||
605 | |||
606 | va_start(args, fmt); | ||
607 | vsnprintf(buf, sizeof(buf), fmt, args); | ||
608 | va_end(args); | ||
609 | |||
610 | packet_start(SSH_MSG_DEBUG); | ||
611 | packet_put_string(buf, strlen(buf)); | ||
612 | packet_send(); | ||
613 | packet_write_wait(); | ||
614 | } | ||
615 | |||
616 | /* Logs the error plus constructs and sends a disconnect | ||
617 | packet, closes the connection, and exits. This function never returns. | ||
618 | The error message should not contain a newline. The length of the | ||
619 | formatted message must not exceed 1024 bytes. */ | ||
620 | |||
621 | void | ||
622 | packet_disconnect(const char *fmt, ...) | ||
623 | { | ||
624 | char buf[1024]; | ||
625 | va_list args; | ||
626 | static int disconnecting = 0; | ||
627 | if (disconnecting) /* Guard against recursive invocations. */ | ||
628 | fatal("packet_disconnect called recursively."); | ||
629 | disconnecting = 1; | ||
630 | |||
631 | /* Format the message. Note that the caller must make sure the message | ||
632 | is of limited size. */ | ||
633 | va_start(args, fmt); | ||
634 | vsnprintf(buf, sizeof(buf), fmt, args); | ||
635 | va_end(args); | ||
636 | |||
637 | /* Send the disconnect message to the other side, and wait for it to get | ||
638 | sent. */ | ||
639 | packet_start(SSH_MSG_DISCONNECT); | ||
640 | packet_put_string(buf, strlen(buf)); | ||
641 | packet_send(); | ||
642 | packet_write_wait(); | ||
643 | |||
644 | /* Stop listening for connections. */ | ||
645 | channel_stop_listening(); | ||
646 | |||
647 | /* Close the connection. */ | ||
648 | packet_close(); | ||
649 | |||
650 | /* Display the error locally and exit. */ | ||
651 | fatal("Local: %.100s", buf); | ||
652 | } | ||
653 | |||
654 | /* Checks if there is any buffered output, and tries to write some of the | ||
655 | output. */ | ||
656 | |||
657 | void | ||
658 | packet_write_poll() | ||
659 | { | ||
660 | int len = buffer_len(&output); | ||
661 | if (len > 0) | ||
662 | { | ||
663 | len = write(connection_out, buffer_ptr(&output), len); | ||
664 | if (len <= 0) { | ||
665 | if (errno == EAGAIN) | ||
666 | return; | ||
667 | else | ||
668 | fatal("Write failed: %.100s", strerror(errno)); | ||
669 | } | ||
670 | buffer_consume(&output, len); | ||
671 | } | ||
672 | } | ||
673 | |||
674 | /* Calls packet_write_poll repeatedly until all pending output data has | ||
675 | been written. */ | ||
676 | |||
677 | void | ||
678 | packet_write_wait() | ||
679 | { | ||
680 | packet_write_poll(); | ||
681 | while (packet_have_data_to_write()) | ||
682 | { | ||
683 | fd_set set; | ||
684 | FD_ZERO(&set); | ||
685 | FD_SET(connection_out, &set); | ||
686 | select(connection_out + 1, NULL, &set, NULL, NULL); | ||
687 | packet_write_poll(); | ||
688 | } | ||
689 | } | ||
690 | |||
691 | /* Returns true if there is buffered data to write to the connection. */ | ||
692 | |||
693 | int | ||
694 | packet_have_data_to_write() | ||
695 | { | ||
696 | return buffer_len(&output) != 0; | ||
697 | } | ||
698 | |||
699 | /* Returns true if there is not too much data to write to the connection. */ | ||
700 | |||
701 | int | ||
702 | packet_not_very_much_data_to_write() | ||
703 | { | ||
704 | if (interactive_mode) | ||
705 | return buffer_len(&output) < 16384; | ||
706 | else | ||
707 | return buffer_len(&output) < 128*1024; | ||
708 | } | ||
709 | |||
710 | /* Informs that the current session is interactive. Sets IP flags for that. */ | ||
711 | |||
712 | void | ||
713 | packet_set_interactive(int interactive, int keepalives) | ||
714 | { | ||
715 | int on = 1; | ||
716 | |||
717 | /* Record that we are in interactive mode. */ | ||
718 | interactive_mode = interactive; | ||
719 | |||
720 | /* Only set socket options if using a socket (as indicated by the descriptors | ||
721 | being the same). */ | ||
722 | if (connection_in != connection_out) | ||
723 | return; | ||
724 | |||
725 | if (keepalives) | ||
726 | { | ||
727 | /* Set keepalives if requested. */ | ||
728 | if (setsockopt(connection_in, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, | ||
729 | sizeof(on)) < 0) | ||
730 | error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); | ||
731 | } | ||
732 | |||
733 | if (interactive) | ||
734 | { | ||
735 | /* Set IP options for an interactive connection. Use IPTOS_LOWDELAY | ||
736 | and TCP_NODELAY. */ | ||
737 | int lowdelay = IPTOS_LOWDELAY; | ||
738 | if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *)&lowdelay, | ||
739 | sizeof(lowdelay)) < 0) | ||
740 | error("setsockopt IPTOS_LOWDELAY: %.100s", strerror(errno)); | ||
741 | if (setsockopt(connection_in, IPPROTO_TCP, TCP_NODELAY, (void *)&on, | ||
742 | sizeof(on)) < 0) | ||
743 | error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); | ||
744 | } | ||
745 | else | ||
746 | { | ||
747 | /* Set IP options for a non-interactive connection. Use | ||
748 | IPTOS_THROUGHPUT. */ | ||
749 | int throughput = IPTOS_THROUGHPUT; | ||
750 | if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *)&throughput, | ||
751 | sizeof(throughput)) < 0) | ||
752 | error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno)); | ||
753 | } | ||
754 | } | ||
755 | |||
756 | /* Returns true if the current connection is interactive. */ | ||
757 | |||
758 | int | ||
759 | packet_is_interactive() | ||
760 | { | ||
761 | return interactive_mode; | ||
762 | } | ||
diff --git a/packet.h b/packet.h new file mode 100644 index 000000000..fb84e6c11 --- /dev/null +++ b/packet.h | |||
@@ -0,0 +1,166 @@ | |||
1 | /* | ||
2 | |||
3 | packet.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Mar 18 02:02:14 1995 ylo | ||
11 | |||
12 | Interface for the packet protocol functions. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: packet.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
17 | |||
18 | #ifndef PACKET_H | ||
19 | #define PACKET_H | ||
20 | |||
21 | #include <openssl/bn.h> | ||
22 | |||
23 | /* Sets the socket used for communication. Disables encryption until | ||
24 | packet_set_encryption_key is called. It is permissible that fd_in | ||
25 | and fd_out are the same descriptor; in that case it is assumed to | ||
26 | be a socket. */ | ||
27 | void packet_set_connection(int fd_in, int fd_out); | ||
28 | |||
29 | /* Puts the connection file descriptors into non-blocking mode. */ | ||
30 | void packet_set_nonblocking(void); | ||
31 | |||
32 | /* Returns the file descriptor used for input. */ | ||
33 | int packet_get_connection_in(void); | ||
34 | |||
35 | /* Returns the file descriptor used for output. */ | ||
36 | int packet_get_connection_out(void); | ||
37 | |||
38 | /* Closes the connection (both descriptors) and clears and frees | ||
39 | internal data structures. */ | ||
40 | void packet_close(void); | ||
41 | |||
42 | /* Causes any further packets to be encrypted using the given key. The same | ||
43 | key is used for both sending and reception. However, both directions | ||
44 | are encrypted independently of each other. Cipher types are | ||
45 | defined in ssh.h. */ | ||
46 | void packet_set_encryption_key(const unsigned char *key, unsigned int keylen, | ||
47 | int cipher_type, int is_client); | ||
48 | |||
49 | /* Sets remote side protocol flags for the current connection. This can | ||
50 | be called at any time. */ | ||
51 | void packet_set_protocol_flags(unsigned int flags); | ||
52 | |||
53 | /* Returns the remote protocol flags set earlier by the above function. */ | ||
54 | unsigned int packet_get_protocol_flags(void); | ||
55 | |||
56 | /* Enables compression in both directions starting from the next packet. */ | ||
57 | void packet_start_compression(int level); | ||
58 | |||
59 | /* Informs that the current session is interactive. Sets IP flags for optimal | ||
60 | performance in interactive use. */ | ||
61 | void packet_set_interactive(int interactive, int keepalives); | ||
62 | |||
63 | /* Returns true if the current connection is interactive. */ | ||
64 | int packet_is_interactive(void); | ||
65 | |||
66 | /* Starts constructing a packet to send. */ | ||
67 | void packet_start(int type); | ||
68 | |||
69 | /* Appends a character to the packet data. */ | ||
70 | void packet_put_char(int ch); | ||
71 | |||
72 | /* Appends an integer to the packet data. */ | ||
73 | void packet_put_int(unsigned int value); | ||
74 | |||
75 | /* Appends an arbitrary precision integer to packet data. */ | ||
76 | void packet_put_bignum(BIGNUM *value); | ||
77 | |||
78 | /* Appends a string to packet data. */ | ||
79 | void packet_put_string(const char *buf, unsigned int len); | ||
80 | |||
81 | /* Finalizes and sends the packet. If the encryption key has been set, | ||
82 | encrypts the packet before sending. */ | ||
83 | void packet_send(void); | ||
84 | |||
85 | /* Waits until a packet has been received, and returns its type. */ | ||
86 | int packet_read(int *payload_len_ptr); | ||
87 | |||
88 | /* Waits until a packet has been received, verifies that its type matches | ||
89 | that given, and gives a fatal error and exits if there is a mismatch. */ | ||
90 | void packet_read_expect(int *payload_len_ptr, int type); | ||
91 | |||
92 | /* Checks if a full packet is available in the data received so far via | ||
93 | packet_process_incoming. If so, reads the packet; otherwise returns | ||
94 | SSH_MSG_NONE. This does not wait for data from the connection. | ||
95 | |||
96 | SSH_MSG_DISCONNECT is handled specially here. Also, | ||
97 | SSH_MSG_IGNORE messages are skipped by this function and are never returned | ||
98 | to higher levels. */ | ||
99 | int packet_read_poll(int *packet_len_ptr); | ||
100 | |||
101 | /* Buffers the given amount of input characters. This is intended to be | ||
102 | used together with packet_read_poll. */ | ||
103 | void packet_process_incoming(const char *buf, unsigned int len); | ||
104 | |||
105 | /* Returns a character (0-255) from the packet data. */ | ||
106 | unsigned int packet_get_char(void); | ||
107 | |||
108 | /* Returns an integer from the packet data. */ | ||
109 | unsigned int packet_get_int(void); | ||
110 | |||
111 | /* Returns an arbitrary precision integer from the packet data. The integer | ||
112 | must have been initialized before this call. */ | ||
113 | void packet_get_bignum(BIGNUM *value, int *length_ptr); | ||
114 | |||
115 | /* Returns a string from the packet data. The string is allocated using | ||
116 | xmalloc; it is the responsibility of the calling program to free it when | ||
117 | no longer needed. The length_ptr argument may be NULL, or point to an | ||
118 | integer into which the length of the string is stored. */ | ||
119 | char *packet_get_string(unsigned int *length_ptr); | ||
120 | |||
121 | /* Logs the error in syslog using LOG_INFO, constructs and sends a disconnect | ||
122 | packet, closes the connection, and exits. This function never returns. | ||
123 | The error message should not contain a newline. The total length of the | ||
124 | message must not exceed 1024 bytes. */ | ||
125 | void packet_disconnect(const char *fmt, ...); | ||
126 | |||
127 | /* Sends a diagnostic message to the other side. This message | ||
128 | can be sent at any time (but not while constructing another message). | ||
129 | The message is printed immediately, but only if the client is being | ||
130 | executed in verbose mode. These messages are primarily intended to | ||
131 | ease debugging authentication problems. The total length of the message | ||
132 | must not exceed 1024 bytes. This will automatically call | ||
133 | packet_write_wait. If the remote side protocol flags do not indicate | ||
134 | that it supports SSH_MSG_DEBUG, this will do nothing. */ | ||
135 | void packet_send_debug(const char *fmt, ...); | ||
136 | |||
137 | /* Checks if there is any buffered output, and tries to write some of the | ||
138 | output. */ | ||
139 | void packet_write_poll(void); | ||
140 | |||
141 | /* Waits until all pending output data has been written. */ | ||
142 | void packet_write_wait(void); | ||
143 | |||
144 | /* Returns true if there is buffered data to write to the connection. */ | ||
145 | int packet_have_data_to_write(void); | ||
146 | |||
147 | /* Returns true if there is not too much data to write to the connection. */ | ||
148 | int packet_not_very_much_data_to_write(void); | ||
149 | |||
150 | /* Stores tty modes from the fd into current packet. */ | ||
151 | void tty_make_modes(int fd); | ||
152 | |||
153 | /* Parses tty modes for the fd from the current packet. */ | ||
154 | void tty_parse_modes(int fd, int *n_bytes_ptr); | ||
155 | |||
156 | #define packet_integrity_check(payload_len, expected_len, type) \ | ||
157 | do { \ | ||
158 | int _p = (payload_len), _e = (expected_len); \ | ||
159 | if (_p != _e) { \ | ||
160 | log("Packet integrity error (%d != %d) at %s:%d", \ | ||
161 | _p, _e, __FILE__, __LINE__); \ | ||
162 | packet_disconnect("Packet integrity error. (%d)", (type)); \ | ||
163 | } \ | ||
164 | } while (0) | ||
165 | |||
166 | #endif /* PACKET_H */ | ||
@@ -0,0 +1,264 @@ | |||
1 | /* | ||
2 | |||
3 | pty.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Fri Mar 17 04:37:25 1995 ylo | ||
11 | |||
12 | Allocating a pseudo-terminal, and making it the controlling tty. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: pty.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
18 | |||
19 | #include "pty.h" | ||
20 | #include "ssh.h" | ||
21 | |||
22 | /* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */ | ||
23 | #if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY) | ||
24 | #undef HAVE_DEV_PTMX | ||
25 | #endif | ||
26 | |||
27 | #ifndef O_NOCTTY | ||
28 | #define O_NOCTTY 0 | ||
29 | #endif | ||
30 | |||
31 | /* Allocates and opens a pty. Returns 0 if no pty could be allocated, | ||
32 | or nonzero if a pty was successfully allocated. On success, open file | ||
33 | descriptors for the pty and tty sides and the name of the tty side are | ||
34 | returned (the buffer must be able to hold at least 64 characters). */ | ||
35 | |||
36 | int pty_allocate(int *ptyfd, int *ttyfd, char *namebuf) | ||
37 | { | ||
38 | #ifdef HAVE_OPENPTY | ||
39 | |||
40 | /* openpty(3) exists in OSF/1 and some other os'es */ | ||
41 | |||
42 | int i; | ||
43 | |||
44 | i = openpty(ptyfd, ttyfd, namebuf, NULL, NULL); | ||
45 | |||
46 | if (i < 0) | ||
47 | { | ||
48 | error("openpty: %.100s", strerror(errno)); | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | return 1; | ||
53 | |||
54 | #else /* HAVE_OPENPTY */ | ||
55 | #ifdef HAVE__GETPTY | ||
56 | |||
57 | /* _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more | ||
58 | pty's automagically when needed */ | ||
59 | |||
60 | char *slave; | ||
61 | |||
62 | slave = _getpty(ptyfd, O_RDWR, 0622, 0); | ||
63 | if (slave == NULL) | ||
64 | { | ||
65 | error("_getpty: %.100s", strerror(errno)); | ||
66 | return 0; | ||
67 | } | ||
68 | strcpy(namebuf, slave); | ||
69 | /* Open the slave side. */ | ||
70 | *ttyfd = open(namebuf, O_RDWR|O_NOCTTY); | ||
71 | if (*ttyfd < 0) | ||
72 | { | ||
73 | error("%.200s: %.100s", namebuf, strerror(errno)); | ||
74 | close(*ptyfd); | ||
75 | return 0; | ||
76 | } | ||
77 | return 1; | ||
78 | |||
79 | #else /* HAVE__GETPTY */ | ||
80 | #ifdef HAVE_DEV_PTMX | ||
81 | /* This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 also has | ||
82 | bsd-style ptys, but they simply do not work.) */ | ||
83 | |||
84 | int ptm; | ||
85 | char *pts; | ||
86 | |||
87 | ptm = open("/dev/ptmx", O_RDWR|O_NOCTTY); | ||
88 | if (ptm < 0) | ||
89 | { | ||
90 | error("/dev/ptmx: %.100s", strerror(errno)); | ||
91 | return 0; | ||
92 | } | ||
93 | if (grantpt(ptm) < 0) | ||
94 | { | ||
95 | error("grantpt: %.100s", strerror(errno)); | ||
96 | return 0; | ||
97 | } | ||
98 | if (unlockpt(ptm) < 0) | ||
99 | { | ||
100 | error("unlockpt: %.100s", strerror(errno)); | ||
101 | return 0; | ||
102 | } | ||
103 | pts = ptsname(ptm); | ||
104 | if (pts == NULL) | ||
105 | error("Slave pty side name could not be obtained."); | ||
106 | strcpy(namebuf, pts); | ||
107 | *ptyfd = ptm; | ||
108 | |||
109 | /* Open the slave side. */ | ||
110 | *ttyfd = open(namebuf, O_RDWR|O_NOCTTY); | ||
111 | if (*ttyfd < 0) | ||
112 | { | ||
113 | error("%.100s: %.100s", namebuf, strerror(errno)); | ||
114 | close(*ptyfd); | ||
115 | return 0; | ||
116 | } | ||
117 | /* Push the appropriate streams modules, as described in Solaris pts(7). */ | ||
118 | if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) | ||
119 | error("ioctl I_PUSH ptem: %.100s", strerror(errno)); | ||
120 | if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) | ||
121 | error("ioctl I_PUSH ldterm: %.100s", strerror(errno)); | ||
122 | if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) | ||
123 | error("ioctl I_PUSH ttcompat: %.100s", strerror(errno)); | ||
124 | return 1; | ||
125 | |||
126 | #else /* HAVE_DEV_PTMX */ | ||
127 | #ifdef HAVE_DEV_PTS_AND_PTC | ||
128 | |||
129 | /* AIX-style pty code. */ | ||
130 | |||
131 | const char *name; | ||
132 | |||
133 | *ptyfd = open("/dev/ptc", O_RDWR|O_NOCTTY); | ||
134 | if (*ptyfd < 0) | ||
135 | { | ||
136 | error("Could not open /dev/ptc: %.100s", strerror(errno)); | ||
137 | return 0; | ||
138 | } | ||
139 | name = ttyname(*ptyfd); | ||
140 | if (!name) | ||
141 | fatal("Open of /dev/ptc returns device for which ttyname fails."); | ||
142 | strcpy(namebuf, name); | ||
143 | *ttyfd = open(name, O_RDWR|O_NOCTTY); | ||
144 | if (*ttyfd < 0) | ||
145 | { | ||
146 | error("Could not open pty slave side %.100s: %.100s", | ||
147 | name, strerror(errno)); | ||
148 | close(*ptyfd); | ||
149 | return 0; | ||
150 | } | ||
151 | return 1; | ||
152 | |||
153 | #else /* HAVE_DEV_PTS_AND_PTC */ | ||
154 | /* BSD-style pty code. */ | ||
155 | |||
156 | char buf[64]; | ||
157 | int i; | ||
158 | const char *ptymajors = | ||
159 | "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
160 | const char *ptyminors = "0123456789abcdef"; | ||
161 | int num_minors = strlen(ptyminors); | ||
162 | int num_ptys = strlen(ptymajors) * num_minors; | ||
163 | |||
164 | for (i = 0; i < num_ptys; i++) | ||
165 | { | ||
166 | snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], | ||
167 | ptyminors[i % num_minors]); | ||
168 | *ptyfd = open(buf, O_RDWR|O_NOCTTY); | ||
169 | if (*ptyfd < 0) | ||
170 | continue; | ||
171 | snprintf(namebuf, sizeof buf, "/dev/tty%c%c", ptymajors[i / num_minors], | ||
172 | ptyminors[i % num_minors]); | ||
173 | |||
174 | /* Open the slave side. */ | ||
175 | *ttyfd = open(namebuf, O_RDWR|O_NOCTTY); | ||
176 | if (*ttyfd < 0) | ||
177 | { | ||
178 | error("%.100s: %.100s", namebuf, strerror(errno)); | ||
179 | close(*ptyfd); | ||
180 | return 0; | ||
181 | } | ||
182 | return 1; | ||
183 | } | ||
184 | return 0; | ||
185 | #endif /* HAVE_DEV_PTS_AND_PTC */ | ||
186 | #endif /* HAVE_DEV_PTMX */ | ||
187 | #endif /* HAVE__GETPTY */ | ||
188 | #endif /* HAVE_OPENPTY */ | ||
189 | } | ||
190 | |||
191 | /* Releases the tty. Its ownership is returned to root, and permissions to | ||
192 | 0666. */ | ||
193 | |||
194 | void pty_release(const char *ttyname) | ||
195 | { | ||
196 | if (chown(ttyname, (uid_t)0, (gid_t)0) < 0) | ||
197 | debug("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno)); | ||
198 | if (chmod(ttyname, (mode_t)0666) < 0) | ||
199 | debug("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno)); | ||
200 | } | ||
201 | |||
202 | /* Makes the tty the processes controlling tty and sets it to sane modes. */ | ||
203 | |||
204 | void pty_make_controlling_tty(int *ttyfd, const char *ttyname) | ||
205 | { | ||
206 | int fd; | ||
207 | |||
208 | /* First disconnect from the old controlling tty. */ | ||
209 | #ifdef TIOCNOTTY | ||
210 | fd = open("/dev/tty", O_RDWR|O_NOCTTY); | ||
211 | if (fd >= 0) | ||
212 | { | ||
213 | (void)ioctl(fd, TIOCNOTTY, NULL); | ||
214 | close(fd); | ||
215 | } | ||
216 | #endif /* TIOCNOTTY */ | ||
217 | if (setsid() < 0) | ||
218 | error("setsid: %.100s", strerror(errno)); | ||
219 | |||
220 | /* Verify that we are successfully disconnected from the controlling tty. */ | ||
221 | fd = open("/dev/tty", O_RDWR|O_NOCTTY); | ||
222 | if (fd >= 0) | ||
223 | { | ||
224 | error("Failed to disconnect from controlling tty."); | ||
225 | close(fd); | ||
226 | } | ||
227 | |||
228 | /* Make it our controlling tty. */ | ||
229 | #ifdef TIOCSCTTY | ||
230 | debug("Setting controlling tty using TIOCSCTTY."); | ||
231 | /* We ignore errors from this, because HPSUX defines TIOCSCTTY, but returns | ||
232 | EINVAL with these arguments, and there is absolutely no documentation. */ | ||
233 | ioctl(*ttyfd, TIOCSCTTY, NULL); | ||
234 | #endif /* TIOCSCTTY */ | ||
235 | fd = open(ttyname, O_RDWR); | ||
236 | if (fd < 0) | ||
237 | error("%.100s: %.100s", ttyname, strerror(errno)); | ||
238 | else | ||
239 | close(fd); | ||
240 | |||
241 | /* Verify that we now have a controlling tty. */ | ||
242 | fd = open("/dev/tty", O_WRONLY); | ||
243 | if (fd < 0) | ||
244 | error("open /dev/tty failed - could not set controlling tty: %.100s", | ||
245 | strerror(errno)); | ||
246 | else | ||
247 | { | ||
248 | close(fd); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | /* Changes the window size associated with the pty. */ | ||
253 | |||
254 | void pty_change_window_size(int ptyfd, int row, int col, | ||
255 | int xpixel, int ypixel) | ||
256 | { | ||
257 | struct winsize w; | ||
258 | w.ws_row = row; | ||
259 | w.ws_col = col; | ||
260 | w.ws_xpixel = xpixel; | ||
261 | w.ws_ypixel = ypixel; | ||
262 | (void)ioctl(ptyfd, TIOCSWINSZ, &w); | ||
263 | } | ||
264 | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | |||
3 | pty.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Fri Mar 17 05:03:28 1995 ylo | ||
11 | |||
12 | Functions for allocating a pseudo-terminal and making it the controlling | ||
13 | tty. | ||
14 | |||
15 | */ | ||
16 | |||
17 | /* RCSID("$Id: pty.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
18 | |||
19 | #ifndef PTY_H | ||
20 | #define PTY_H | ||
21 | |||
22 | /* Allocates and opens a pty. Returns 0 if no pty could be allocated, | ||
23 | or nonzero if a pty was successfully allocated. On success, open file | ||
24 | descriptors for the pty and tty sides and the name of the tty side are | ||
25 | returned (the buffer must be able to hold at least 64 characters). */ | ||
26 | int pty_allocate(int *ptyfd, int *ttyfd, char *ttyname); | ||
27 | |||
28 | /* Releases the tty. Its ownership is returned to root, and permissions to | ||
29 | 0666. */ | ||
30 | void pty_release(const char *ttyname); | ||
31 | |||
32 | /* Makes the tty the processes controlling tty and sets it to sane modes. | ||
33 | This may need to reopen the tty to get rid of possible eavesdroppers. */ | ||
34 | void pty_make_controlling_tty(int *ttyfd, const char *ttyname); | ||
35 | |||
36 | /* Changes the window size associated with the pty. */ | ||
37 | void pty_change_window_size(int ptyfd, int row, int col, | ||
38 | int xpixel, int ypixel); | ||
39 | |||
40 | #endif /* PTY_H */ | ||
diff --git a/radix.c b/radix.c new file mode 100644 index 000000000..1c497945e --- /dev/null +++ b/radix.c | |||
@@ -0,0 +1,258 @@ | |||
1 | /* | ||
2 | radix.c | ||
3 | |||
4 | base-64 encoding pinched from lynx2-7-2, who pinched it from rpem. | ||
5 | Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991 | ||
6 | and placed in the public domain. | ||
7 | |||
8 | Dug Song <dugsong@UMICH.EDU> | ||
9 | */ | ||
10 | |||
11 | #include "includes.h" | ||
12 | |||
13 | #ifdef AFS | ||
14 | #include <krb.h> | ||
15 | |||
16 | char six2pr[64] = { | ||
17 | 'A','B','C','D','E','F','G','H','I','J','K','L','M', | ||
18 | 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', | ||
19 | 'a','b','c','d','e','f','g','h','i','j','k','l','m', | ||
20 | 'n','o','p','q','r','s','t','u','v','w','x','y','z', | ||
21 | '0','1','2','3','4','5','6','7','8','9','+','/' | ||
22 | }; | ||
23 | |||
24 | unsigned char pr2six[256]; | ||
25 | |||
26 | int uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) | ||
27 | { | ||
28 | /* ENC is the basic 1 character encoding function to make a char printing */ | ||
29 | #define ENC(c) six2pr[c] | ||
30 | |||
31 | register char *outptr = bufcoded; | ||
32 | unsigned int i; | ||
33 | |||
34 | for (i=0; i<nbytes; i += 3) { | ||
35 | *(outptr++) = ENC(*bufin >> 2); /* c1 */ | ||
36 | *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2*/ | ||
37 | *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03));/*c3*/ | ||
38 | *(outptr++) = ENC(bufin[2] & 077); /* c4 */ | ||
39 | bufin += 3; | ||
40 | } | ||
41 | if (i == nbytes+1) { | ||
42 | outptr[-1] = '='; | ||
43 | } else if (i == nbytes+2) { | ||
44 | outptr[-1] = '='; | ||
45 | outptr[-2] = '='; | ||
46 | } | ||
47 | *outptr = '\0'; | ||
48 | return(outptr - bufcoded); | ||
49 | } | ||
50 | |||
51 | int uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize) | ||
52 | { | ||
53 | /* single character decode */ | ||
54 | #define DEC(c) pr2six[(unsigned char)c] | ||
55 | #define MAXVAL 63 | ||
56 | |||
57 | static int first = 1; | ||
58 | int nbytesdecoded, j; | ||
59 | const char *bufin = bufcoded; | ||
60 | register unsigned char *bufout = bufplain; | ||
61 | register int nprbytes; | ||
62 | |||
63 | /* If this is the first call, initialize the mapping table. */ | ||
64 | if (first) { | ||
65 | first = 0; | ||
66 | for(j=0; j<256; j++) pr2six[j] = MAXVAL+1; | ||
67 | for(j=0; j<64; j++) pr2six[(unsigned char)six2pr[j]] = (unsigned char)j; | ||
68 | } | ||
69 | |||
70 | /* Strip leading whitespace. */ | ||
71 | while (*bufcoded==' ' || *bufcoded == '\t') bufcoded++; | ||
72 | |||
73 | /* Figure out how many characters are in the input buffer. | ||
74 | If this would decode into more bytes than would fit into | ||
75 | the output buffer, adjust the number of input bytes downwards. */ | ||
76 | bufin = bufcoded; | ||
77 | while (DEC(*(bufin++)) <= MAXVAL); | ||
78 | nprbytes = bufin - bufcoded - 1; | ||
79 | nbytesdecoded = ((nprbytes+3)/4) * 3; | ||
80 | if (nbytesdecoded > outbufsize) | ||
81 | nprbytes = (outbufsize*4)/3; | ||
82 | |||
83 | bufin = bufcoded; | ||
84 | |||
85 | while (nprbytes > 0) { | ||
86 | *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); | ||
87 | *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); | ||
88 | *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); | ||
89 | bufin += 4; | ||
90 | nprbytes -= 4; | ||
91 | } | ||
92 | if (nprbytes & 03) { | ||
93 | if (DEC(bufin[-2]) > MAXVAL) | ||
94 | nbytesdecoded -= 2; | ||
95 | else | ||
96 | nbytesdecoded -= 1; | ||
97 | } | ||
98 | return(nbytesdecoded); | ||
99 | } | ||
100 | |||
101 | typedef unsigned char my_u_char; | ||
102 | typedef unsigned int my_u_int32_t; | ||
103 | typedef unsigned short my_u_short; | ||
104 | |||
105 | /* Nasty macros from BIND-4.9.2 */ | ||
106 | |||
107 | #define GETSHORT(s, cp) { \ | ||
108 | register my_u_char *t_cp = (my_u_char*)(cp); \ | ||
109 | (s) = (((my_u_short)t_cp[0]) << 8) \ | ||
110 | | (((my_u_short)t_cp[1])) \ | ||
111 | ; \ | ||
112 | (cp) += 2; \ | ||
113 | } | ||
114 | |||
115 | #define GETLONG(l, cp) { \ | ||
116 | register my_u_char *t_cp = (my_u_char*)(cp); \ | ||
117 | (l) = (((my_u_int32_t)t_cp[0]) << 24) \ | ||
118 | | (((my_u_int32_t)t_cp[1]) << 16) \ | ||
119 | | (((my_u_int32_t)t_cp[2]) << 8) \ | ||
120 | | (((my_u_int32_t)t_cp[3])) \ | ||
121 | ; \ | ||
122 | (cp) += 4; \ | ||
123 | } | ||
124 | |||
125 | #define PUTSHORT(s, cp) { \ | ||
126 | register my_u_short t_s = (my_u_short)(s); \ | ||
127 | register my_u_char *t_cp = (my_u_char*)(cp); \ | ||
128 | *t_cp++ = t_s >> 8; \ | ||
129 | *t_cp = t_s; \ | ||
130 | (cp) += 2; \ | ||
131 | } | ||
132 | |||
133 | #define PUTLONG(l, cp) { \ | ||
134 | register my_u_int32_t t_l = (my_u_int32_t)(l); \ | ||
135 | register my_u_char *t_cp = (my_u_char*)(cp); \ | ||
136 | *t_cp++ = t_l >> 24; \ | ||
137 | *t_cp++ = t_l >> 16; \ | ||
138 | *t_cp++ = t_l >> 8; \ | ||
139 | *t_cp = t_l; \ | ||
140 | (cp) += 4; \ | ||
141 | } | ||
142 | |||
143 | #define GETSTRING(s, p, p_l) { \ | ||
144 | register char* p_targ = (p) + p_l; \ | ||
145 | register char* s_c = (s); \ | ||
146 | register char* p_c = (p); \ | ||
147 | while (*p_c && (p_c < p_targ)) { \ | ||
148 | *s_c++ = *p_c++; \ | ||
149 | } \ | ||
150 | if (p_c == p_targ) { \ | ||
151 | return 1; \ | ||
152 | } \ | ||
153 | *s_c = *p_c++; \ | ||
154 | (p_l) = (p_l) - (p_c - (p)); \ | ||
155 | (p) = p_c; \ | ||
156 | } | ||
157 | |||
158 | |||
159 | int creds_to_radix(CREDENTIALS *creds, unsigned char *buf) | ||
160 | { | ||
161 | char *p, *s; | ||
162 | int len; | ||
163 | char temp[2048]; | ||
164 | |||
165 | p = temp; | ||
166 | *p++ = 1; /* version */ | ||
167 | s = creds->service; while (*s) *p++ = *s++; *p++ = *s; | ||
168 | s = creds->instance; while (*s) *p++ = *s++; *p++ = *s; | ||
169 | s = creds->realm; while (*s) *p++ = *s++; *p++ = *s; | ||
170 | |||
171 | s = creds->pname; while (*s) *p++ = *s++; *p++ = *s; | ||
172 | s = creds->pinst; while (*s) *p++ = *s++; *p++ = *s; | ||
173 | /* Null string to repeat the realm. */ | ||
174 | *p++ = '\0'; | ||
175 | |||
176 | PUTLONG(creds->issue_date,p); | ||
177 | { | ||
178 | unsigned int endTime ; | ||
179 | endTime = (unsigned int)krb_life_to_time(creds->issue_date, | ||
180 | creds->lifetime); | ||
181 | PUTLONG(endTime,p); | ||
182 | } | ||
183 | |||
184 | memcpy(p,&creds->session, sizeof(creds->session)); | ||
185 | p += sizeof(creds->session); | ||
186 | |||
187 | PUTSHORT(creds->kvno,p); | ||
188 | PUTLONG(creds->ticket_st.length,p); | ||
189 | |||
190 | memcpy(p,creds->ticket_st.dat, creds->ticket_st.length); | ||
191 | p += creds->ticket_st.length; | ||
192 | len = p - temp; | ||
193 | |||
194 | return(uuencode(temp, len, buf)); | ||
195 | } | ||
196 | |||
197 | int radix_to_creds(const char *buf, CREDENTIALS *creds) | ||
198 | { | ||
199 | |||
200 | char *p; | ||
201 | int len, tl; | ||
202 | char version; | ||
203 | char temp[2048]; | ||
204 | |||
205 | if (!(len = uudecode(buf, temp, sizeof(temp)))) | ||
206 | return 0; | ||
207 | |||
208 | p = temp; | ||
209 | |||
210 | /* check version and length! */ | ||
211 | if (len < 1) return 0; | ||
212 | version = *p; p++; len--; | ||
213 | |||
214 | GETSTRING(creds->service, p, len); | ||
215 | GETSTRING(creds->instance, p, len); | ||
216 | GETSTRING(creds->realm, p, len); | ||
217 | |||
218 | GETSTRING(creds->pname, p, len); | ||
219 | GETSTRING(creds->pinst, p, len); | ||
220 | /* Ignore possibly different realm. */ | ||
221 | while (*p && len) p++, len--; | ||
222 | if (len == 0) return 0; | ||
223 | p++, len--; | ||
224 | |||
225 | /* Enough space for remaining fixed-length parts? */ | ||
226 | if (len < (4 + 4 + sizeof(creds->session) + 2 + 4)) | ||
227 | return 0; | ||
228 | |||
229 | GETLONG(creds->issue_date,p); | ||
230 | len -= 4; | ||
231 | { | ||
232 | unsigned int endTime; | ||
233 | GETLONG(endTime,p); | ||
234 | len -= 4; | ||
235 | creds->lifetime = krb_time_to_life(creds->issue_date, endTime); | ||
236 | } | ||
237 | |||
238 | memcpy(&creds->session, p, sizeof(creds->session)); | ||
239 | p += sizeof(creds->session); | ||
240 | len -= sizeof(creds->session); | ||
241 | |||
242 | GETSHORT(creds->kvno,p); | ||
243 | len -= 2; | ||
244 | GETLONG(creds->ticket_st.length,p); | ||
245 | len -= 4; | ||
246 | |||
247 | tl = creds->ticket_st.length; | ||
248 | if (tl < 0 || tl > len || tl > sizeof(creds->ticket_st.dat)) | ||
249 | return 0; | ||
250 | |||
251 | memcpy(creds->ticket_st.dat, p, tl); | ||
252 | p += tl; | ||
253 | len -= tl; | ||
254 | |||
255 | return 1; | ||
256 | } | ||
257 | |||
258 | #endif /* AFS */ | ||
@@ -0,0 +1,105 @@ | |||
1 | /*! \file rc4.c | ||
2 | \brief Source file for RC4 stream cipher routines | ||
3 | \author Damien Miller <djm@mindrot.org> | ||
4 | \version 0.0.0 | ||
5 | \date 1999 | ||
6 | |||
7 | A simple implementation of the RC4 stream cipher, based on the | ||
8 | description given in _Bruce Schneier's_ "Applied Cryptography" | ||
9 | 2nd edition. | ||
10 | |||
11 | Copyright 1999 Damien Miller | ||
12 | |||
13 | Permission is hereby granted, free of charge, to any person | ||
14 | obtaining a copy of this software and associated documentation | ||
15 | files (the "Software"), to deal in the Software without | ||
16 | restriction, including without limitation the rights to use, copy, | ||
17 | modify, merge, publish, distribute, sublicense, and/or sell copies | ||
18 | of the Software, and to permit persons to whom the Software is | ||
19 | furnished to do so, subject to the following conditions: | ||
20 | |||
21 | The above copyright notice and this permission notice shall be | ||
22 | included in all copies or substantial portions of the Software. | ||
23 | |||
24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | ||
25 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | ||
26 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE | ||
27 | AND NONINFRINGEMENT. IN NO EVENT SHALL DAMIEN MILLER BE LIABLE | ||
28 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | ||
29 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
30 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
31 | |||
32 | \warning None of these functions clears its memory after use. It | ||
33 | \warning is the responsability of the calling routines to ensure | ||
34 | \warning that any sensitive data (keystream, key or plaintext) is | ||
35 | \warning properly erased after use. | ||
36 | |||
37 | \warning The name "RC4" is trademarked in the United States, | ||
38 | \warning you may need to use "RC4 compatible" or "ARC4" | ||
39 | \warning (Alleged RC4). | ||
40 | */ | ||
41 | |||
42 | /* $Id: rc4.c,v 1.1.1.1 1999/10/26 05:48:13 damien Exp $ */ | ||
43 | |||
44 | #include "rc4.h" | ||
45 | |||
46 | |||
47 | void rc4_key(rc4_t *r, unsigned char *key, int len) | ||
48 | { | ||
49 | int t; | ||
50 | |||
51 | for(r->i = 0; r->i < 256; r->i++) | ||
52 | r->s[r->i] = r->i; | ||
53 | |||
54 | r->j = 0; | ||
55 | for(r->i = 0; r->i < 256; r->i++) | ||
56 | { | ||
57 | r->j = (r->j + r->s[r->i] + key[r->i % len]) % 256; | ||
58 | t = r->s[r->i]; | ||
59 | r->s[r->i] = r->s[r->j]; | ||
60 | r->s[r->j] = t; | ||
61 | } | ||
62 | r->i = r->j = 0; | ||
63 | } | ||
64 | |||
65 | void rc4_crypt(rc4_t *r, unsigned char *plaintext, int len) | ||
66 | { | ||
67 | int t; | ||
68 | int c; | ||
69 | |||
70 | c = 0; | ||
71 | while(c < len) | ||
72 | { | ||
73 | r->i = (r->i + 1) % 256; | ||
74 | r->j = (r->j + r->s[r->i]) % 256; | ||
75 | t = r->s[r->i]; | ||
76 | r->s[r->i] = r->s[r->j]; | ||
77 | r->s[r->j] = t; | ||
78 | |||
79 | t = (r->s[r->i] + r->s[r->j]) % 256; | ||
80 | |||
81 | plaintext[c] ^= r->s[t]; | ||
82 | c++; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | void rc4_getbytes(rc4_t *r, unsigned char *buffer, int len) | ||
87 | { | ||
88 | int t; | ||
89 | int c; | ||
90 | |||
91 | c = 0; | ||
92 | while(c < len) | ||
93 | { | ||
94 | r->i = (r->i + 1) % 256; | ||
95 | r->j = (r->j + r->s[r->i]) % 256; | ||
96 | t = r->s[r->i]; | ||
97 | r->s[r->i] = r->s[r->j]; | ||
98 | r->s[r->j] = t; | ||
99 | |||
100 | t = (r->s[r->i] + r->s[r->j]) % 256; | ||
101 | |||
102 | buffer[c] = r->s[t]; | ||
103 | c++; | ||
104 | } | ||
105 | } | ||
@@ -0,0 +1,110 @@ | |||
1 | /*! \file rc4.h | ||
2 | \brief Header file for RC4 stream cipher routines | ||
3 | \author Damien Miller <djm@mindrot.org> | ||
4 | \version 0.0.0 | ||
5 | \date 1999 | ||
6 | |||
7 | A simple implementation of the RC4 stream cipher, based on the | ||
8 | description given in _Bruce Schneier's_ "Applied Cryptography" | ||
9 | 2nd edition. | ||
10 | |||
11 | Copyright 1999 Damien Miller | ||
12 | |||
13 | Permission is hereby granted, free of charge, to any person | ||
14 | obtaining a copy of this software and associated documentation | ||
15 | files (the "Software"), to deal in the Software without | ||
16 | restriction, including without limitation the rights to use, copy, | ||
17 | modify, merge, publish, distribute, sublicense, and/or sell copies | ||
18 | of the Software, and to permit persons to whom the Software is | ||
19 | furnished to do so, subject to the following conditions: | ||
20 | |||
21 | The above copyright notice and this permission notice shall be | ||
22 | included in all copies or substantial portions of the Software. | ||
23 | |||
24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | ||
25 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | ||
26 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE | ||
27 | AND NONINFRINGEMENT. IN NO EVENT SHALL DAMIEN MILLER BE LIABLE | ||
28 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | ||
29 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
30 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
31 | |||
32 | \warning None of these functions clears its memory after use. It | ||
33 | \warning is the responsability of the calling routines to ensure | ||
34 | \warning that any sensitive data (keystream, key or plaintext) is | ||
35 | \warning properly erased after use. | ||
36 | |||
37 | \warning The name "RC4" is trademarked in the United States, | ||
38 | \warning you may need to use "RC4 compatible" or "ARC4" | ||
39 | \warning (Alleged RC4). | ||
40 | */ | ||
41 | |||
42 | /* $Id: rc4.h,v 1.1.1.1 1999/10/26 05:48:13 damien Exp $ */ | ||
43 | |||
44 | #ifndef _RC4_H | ||
45 | #define _RC4_H | ||
46 | |||
47 | /*! \struct rc4_t | ||
48 | \brief RC4 stream cipher state object | ||
49 | \var s State array | ||
50 | \var i Monotonic index | ||
51 | \var j Randomised index | ||
52 | |||
53 | \warning This structure should not be accessed directly. To | ||
54 | \warning initialise a rc4_t object, you should use the rc4_key() | ||
55 | \warning function | ||
56 | |||
57 | This structure holds the current state of the RC4 algorithm. | ||
58 | */ | ||
59 | typedef struct | ||
60 | { | ||
61 | unsigned int s[256]; | ||
62 | int i; | ||
63 | int j; | ||
64 | } rc4_t; | ||
65 | |||
66 | /*! \fn void rc4_key(rc4_t *r, unsigned char *key, int len); | ||
67 | \brief Set up key structure of RC4 stream cipher | ||
68 | \param r pointer to RC4 structure to be seeded | ||
69 | \param key pointer to buffer containing raw key | ||
70 | \param len length of key | ||
71 | |||
72 | This function set the internal state of the RC4 data structure | ||
73 | pointed to by \a r using the specified \a key of length \a len. | ||
74 | |||
75 | This function can use up to 256 bytes of key, any more are ignored. | ||
76 | |||
77 | \warning Stream ciphers (such as RC4) can be insecure if the same | ||
78 | \warning key is used repeatedly. Ensure that any key specified has | ||
79 | \warning an reasonably sized Initialisation Vector component. | ||
80 | */ | ||
81 | void rc4_key(rc4_t *r, unsigned char *key, int len); | ||
82 | |||
83 | /*! \fn rc4_crypt(rc4_t *r, unsigned char *plaintext, int len); | ||
84 | \brief Crypt bytes using RC4 algorithm | ||
85 | \param r pointer to RC4 structure to be used | ||
86 | \param plaintext Pointer to bytes to encrypt | ||
87 | \param len number of bytes to crypt | ||
88 | |||
89 | This function encrypts one or more bytes (pointed to by \a plaintext) | ||
90 | using the RC4 algorithm. \a r is a state structure that must be | ||
91 | initialiased using the rc4_key() function prior to use. | ||
92 | |||
93 | Since RC4 XORs each byte of plaintext with a byte of keystream, | ||
94 | this function can be used for both encryption and decryption. | ||
95 | */ | ||
96 | void rc4_crypt(rc4_t *r, unsigned char *plaintext, int len); | ||
97 | |||
98 | /*! \fn rc4_getbytes(rc4_t *r, unsigned char *buffer, int len); | ||
99 | \brief Generate key stream using the RC4 stream cipher | ||
100 | \param r pointer to RC4 structure to be used | ||
101 | \param buffer pointer to buffer in which to deposit keystream | ||
102 | \param len number of bytes to deposit | ||
103 | |||
104 | This function gives access to the raw RC4 key stream. In this | ||
105 | consiguration RC4 can be used as a fast, strong pseudo-random | ||
106 | number generator with a very long period. | ||
107 | */ | ||
108 | void rc4_getbytes(rc4_t *r, unsigned char *buffer, int len); | ||
109 | |||
110 | #endif /* _RC4_H */ | ||
diff --git a/readconf.c b/readconf.c new file mode 100644 index 000000000..281548d2a --- /dev/null +++ b/readconf.c | |||
@@ -0,0 +1,684 @@ | |||
1 | /* | ||
2 | |||
3 | readconf.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Apr 22 00:03:10 1995 ylo | ||
11 | |||
12 | Functions for reading the configuration files. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: readconf.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
18 | |||
19 | #include "ssh.h" | ||
20 | #include "cipher.h" | ||
21 | #include "readconf.h" | ||
22 | #include "xmalloc.h" | ||
23 | |||
24 | /* Format of the configuration file: | ||
25 | |||
26 | # Configuration data is parsed as follows: | ||
27 | # 1. command line options | ||
28 | # 2. user-specific file | ||
29 | # 3. system-wide file | ||
30 | # Any configuration value is only changed the first time it is set. | ||
31 | # Thus, host-specific definitions should be at the beginning of the | ||
32 | # configuration file, and defaults at the end. | ||
33 | |||
34 | # Host-specific declarations. These may override anything above. A single | ||
35 | # host may match multiple declarations; these are processed in the order | ||
36 | # that they are given in. | ||
37 | |||
38 | Host *.ngs.fi ngs.fi | ||
39 | FallBackToRsh no | ||
40 | |||
41 | Host fake.com | ||
42 | HostName another.host.name.real.org | ||
43 | User blaah | ||
44 | Port 34289 | ||
45 | ForwardX11 no | ||
46 | ForwardAgent no | ||
47 | |||
48 | Host books.com | ||
49 | RemoteForward 9999 shadows.cs.hut.fi:9999 | ||
50 | Cipher 3des | ||
51 | |||
52 | Host fascist.blob.com | ||
53 | Port 23123 | ||
54 | User tylonen | ||
55 | RhostsAuthentication no | ||
56 | PasswordAuthentication no | ||
57 | |||
58 | Host puukko.hut.fi | ||
59 | User t35124p | ||
60 | ProxyCommand ssh-proxy %h %p | ||
61 | |||
62 | Host *.fr | ||
63 | UseRsh yes | ||
64 | |||
65 | Host *.su | ||
66 | Cipher none | ||
67 | PasswordAuthentication no | ||
68 | |||
69 | # Defaults for various options | ||
70 | Host * | ||
71 | ForwardAgent no | ||
72 | ForwardX11 yes | ||
73 | RhostsAuthentication yes | ||
74 | PasswordAuthentication yes | ||
75 | RSAAuthentication yes | ||
76 | RhostsRSAAuthentication yes | ||
77 | FallBackToRsh no | ||
78 | UseRsh no | ||
79 | StrictHostKeyChecking yes | ||
80 | KeepAlives no | ||
81 | IdentityFile ~/.ssh/identity | ||
82 | Port 22 | ||
83 | EscapeChar ~ | ||
84 | |||
85 | */ | ||
86 | |||
87 | /* Keyword tokens. */ | ||
88 | |||
89 | typedef enum | ||
90 | { | ||
91 | oForwardAgent, oForwardX11, oGatewayPorts, oRhostsAuthentication, | ||
92 | oPasswordAuthentication, oRSAAuthentication, oFallBackToRsh, oUseRsh, | ||
93 | #ifdef KRB4 | ||
94 | oKerberosAuthentication, | ||
95 | #endif /* KRB4 */ | ||
96 | #ifdef AFS | ||
97 | oKerberosTgtPassing, oAFSTokenPassing, | ||
98 | #endif | ||
99 | oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, | ||
100 | oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, | ||
101 | oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, | ||
102 | oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, | ||
103 | oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, | ||
104 | oUsePrivilegedPort | ||
105 | } OpCodes; | ||
106 | |||
107 | /* Textual representations of the tokens. */ | ||
108 | |||
109 | static struct | ||
110 | { | ||
111 | const char *name; | ||
112 | OpCodes opcode; | ||
113 | } keywords[] = | ||
114 | { | ||
115 | { "forwardagent", oForwardAgent }, | ||
116 | { "forwardx11", oForwardX11 }, | ||
117 | { "gatewayports", oGatewayPorts }, | ||
118 | { "useprivilegedport", oUsePrivilegedPort }, | ||
119 | { "rhostsauthentication", oRhostsAuthentication }, | ||
120 | { "passwordauthentication", oPasswordAuthentication }, | ||
121 | { "rsaauthentication", oRSAAuthentication }, | ||
122 | #ifdef KRB4 | ||
123 | { "kerberosauthentication", oKerberosAuthentication }, | ||
124 | #endif /* KRB4 */ | ||
125 | #ifdef AFS | ||
126 | { "kerberostgtpassing", oKerberosTgtPassing }, | ||
127 | { "afstokenpassing", oAFSTokenPassing }, | ||
128 | #endif | ||
129 | { "fallbacktorsh", oFallBackToRsh }, | ||
130 | { "usersh", oUseRsh }, | ||
131 | { "identityfile", oIdentityFile }, | ||
132 | { "hostname", oHostName }, | ||
133 | { "proxycommand", oProxyCommand }, | ||
134 | { "port", oPort }, | ||
135 | { "cipher", oCipher }, | ||
136 | { "remoteforward", oRemoteForward }, | ||
137 | { "localforward", oLocalForward }, | ||
138 | { "user", oUser }, | ||
139 | { "host", oHost }, | ||
140 | { "escapechar", oEscapeChar }, | ||
141 | { "rhostsrsaauthentication", oRhostsRSAAuthentication }, | ||
142 | { "globalknownhostsfile", oGlobalKnownHostsFile }, | ||
143 | { "userknownhostsfile", oUserKnownHostsFile }, | ||
144 | { "connectionattempts", oConnectionAttempts }, | ||
145 | { "batchmode", oBatchMode }, | ||
146 | { "checkhostip", oCheckHostIP }, | ||
147 | { "stricthostkeychecking", oStrictHostKeyChecking }, | ||
148 | { "compression", oCompression }, | ||
149 | { "compressionlevel", oCompressionLevel }, | ||
150 | { "keepalive", oKeepAlives }, | ||
151 | { "numberofpasswordprompts", oNumberOfPasswordPrompts }, | ||
152 | { "tisauthentication", oTISAuthentication }, | ||
153 | { NULL, 0 } | ||
154 | }; | ||
155 | |||
156 | /* Characters considered whitespace in strtok calls. */ | ||
157 | #define WHITESPACE " \t\r\n" | ||
158 | |||
159 | |||
160 | /* Adds a local TCP/IP port forward to options. Never returns if there | ||
161 | is an error. */ | ||
162 | |||
163 | void add_local_forward(Options *options, int port, const char *host, | ||
164 | int host_port) | ||
165 | { | ||
166 | Forward *fwd; | ||
167 | extern uid_t original_real_uid; | ||
168 | if ((port & 0xffff) != port) | ||
169 | fatal("Requested forwarding of nonexistent port %d.", port); | ||
170 | if (port < IPPORT_RESERVED && original_real_uid != 0) | ||
171 | fatal("Privileged ports can only be forwarded by root.\n"); | ||
172 | if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) | ||
173 | fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); | ||
174 | fwd = &options->local_forwards[options->num_local_forwards++]; | ||
175 | fwd->port = port; | ||
176 | fwd->host = xstrdup(host); | ||
177 | fwd->host_port = host_port; | ||
178 | } | ||
179 | |||
180 | /* Adds a remote TCP/IP port forward to options. Never returns if there | ||
181 | is an error. */ | ||
182 | |||
183 | void add_remote_forward(Options *options, int port, const char *host, | ||
184 | int host_port) | ||
185 | { | ||
186 | Forward *fwd; | ||
187 | if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) | ||
188 | fatal("Too many remote forwards (max %d).", | ||
189 | SSH_MAX_FORWARDS_PER_DIRECTION); | ||
190 | fwd = &options->remote_forwards[options->num_remote_forwards++]; | ||
191 | fwd->port = port; | ||
192 | fwd->host = xstrdup(host); | ||
193 | fwd->host_port = host_port; | ||
194 | } | ||
195 | |||
196 | /* Returns the number of the token pointed to by cp of length len. | ||
197 | Never returns if the token is not known. */ | ||
198 | |||
199 | static OpCodes parse_token(const char *cp, const char *filename, int linenum) | ||
200 | { | ||
201 | unsigned int i; | ||
202 | |||
203 | for (i = 0; keywords[i].name; i++) | ||
204 | if (strcmp(cp, keywords[i].name) == 0) | ||
205 | return keywords[i].opcode; | ||
206 | |||
207 | fatal("%.200s line %d: Bad configuration option.", | ||
208 | filename, linenum); | ||
209 | /*NOTREACHED*/ | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | /* Processes a single option line as used in the configuration files. | ||
214 | This only sets those values that have not already been set. */ | ||
215 | |||
216 | void process_config_line(Options *options, const char *host, | ||
217 | char *line, const char *filename, int linenum, | ||
218 | int *activep) | ||
219 | { | ||
220 | char buf[256], *cp, *string, **charptr; | ||
221 | int opcode, *intptr, value, fwd_port, fwd_host_port; | ||
222 | |||
223 | /* Skip leading whitespace. */ | ||
224 | cp = line + strspn(line, WHITESPACE); | ||
225 | if (!*cp || *cp == '\n' || *cp == '#') | ||
226 | return; | ||
227 | |||
228 | /* Get the keyword. (Each line is supposed to begin with a keyword). */ | ||
229 | cp = strtok(cp, WHITESPACE); | ||
230 | { | ||
231 | char *t = cp; | ||
232 | for (; *t != 0; t++) | ||
233 | if ('A' <= *t && *t <= 'Z') | ||
234 | *t = *t - 'A' + 'a'; /* tolower */ | ||
235 | |||
236 | } | ||
237 | opcode = parse_token(cp, filename, linenum); | ||
238 | |||
239 | switch (opcode) | ||
240 | { | ||
241 | |||
242 | case oForwardAgent: | ||
243 | intptr = &options->forward_agent; | ||
244 | parse_flag: | ||
245 | cp = strtok(NULL, WHITESPACE); | ||
246 | if (!cp) | ||
247 | fatal("%.200s line %d: Missing yes/no argument.", filename, linenum); | ||
248 | value = 0; /* To avoid compiler warning... */ | ||
249 | if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0) | ||
250 | value = 1; | ||
251 | else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0) | ||
252 | value = 0; | ||
253 | else | ||
254 | fatal("%.200s line %d: Bad yes/no argument.", filename, linenum); | ||
255 | if (*activep && *intptr == -1) | ||
256 | *intptr = value; | ||
257 | break; | ||
258 | |||
259 | case oForwardX11: | ||
260 | intptr = &options->forward_x11; | ||
261 | goto parse_flag; | ||
262 | |||
263 | case oGatewayPorts: | ||
264 | intptr = &options->gateway_ports; | ||
265 | goto parse_flag; | ||
266 | |||
267 | case oUsePrivilegedPort: | ||
268 | intptr = &options->use_privileged_port; | ||
269 | goto parse_flag; | ||
270 | |||
271 | case oRhostsAuthentication: | ||
272 | intptr = &options->rhosts_authentication; | ||
273 | goto parse_flag; | ||
274 | |||
275 | case oPasswordAuthentication: | ||
276 | intptr = &options->password_authentication; | ||
277 | goto parse_flag; | ||
278 | |||
279 | case oRSAAuthentication: | ||
280 | intptr = &options->rsa_authentication; | ||
281 | goto parse_flag; | ||
282 | |||
283 | case oRhostsRSAAuthentication: | ||
284 | intptr = &options->rhosts_rsa_authentication; | ||
285 | goto parse_flag; | ||
286 | |||
287 | #ifdef KRB4 | ||
288 | case oKerberosAuthentication: | ||
289 | intptr = &options->kerberos_authentication; | ||
290 | goto parse_flag; | ||
291 | #endif /* KRB4 */ | ||
292 | |||
293 | #ifdef AFS | ||
294 | case oKerberosTgtPassing: | ||
295 | intptr = &options->kerberos_tgt_passing; | ||
296 | goto parse_flag; | ||
297 | |||
298 | case oAFSTokenPassing: | ||
299 | intptr = &options->afs_token_passing; | ||
300 | goto parse_flag; | ||
301 | #endif | ||
302 | |||
303 | case oFallBackToRsh: | ||
304 | intptr = &options->fallback_to_rsh; | ||
305 | goto parse_flag; | ||
306 | |||
307 | case oUseRsh: | ||
308 | intptr = &options->use_rsh; | ||
309 | goto parse_flag; | ||
310 | |||
311 | case oBatchMode: | ||
312 | intptr = &options->batch_mode; | ||
313 | goto parse_flag; | ||
314 | |||
315 | case oCheckHostIP: | ||
316 | intptr = &options->check_host_ip; | ||
317 | goto parse_flag; | ||
318 | |||
319 | case oStrictHostKeyChecking: | ||
320 | intptr = &options->strict_host_key_checking; | ||
321 | cp = strtok(NULL, WHITESPACE); | ||
322 | if (!cp) | ||
323 | fatal("%.200s line %d: Missing yes/no argument.", | ||
324 | filename, linenum); | ||
325 | value = 0; /* To avoid compiler warning... */ | ||
326 | if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0) | ||
327 | value = 1; | ||
328 | else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0) | ||
329 | value = 0; | ||
330 | else if (strcmp(cp, "ask") == 0) | ||
331 | value = 2; | ||
332 | else | ||
333 | fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum); | ||
334 | if (*activep && *intptr == -1) | ||
335 | *intptr = value; | ||
336 | break; | ||
337 | |||
338 | case oCompression: | ||
339 | intptr = &options->compression; | ||
340 | goto parse_flag; | ||
341 | |||
342 | case oKeepAlives: | ||
343 | intptr = &options->keepalives; | ||
344 | goto parse_flag; | ||
345 | |||
346 | case oNumberOfPasswordPrompts: | ||
347 | intptr = &options->number_of_password_prompts; | ||
348 | goto parse_int; | ||
349 | |||
350 | case oTISAuthentication: | ||
351 | cp = strtok(NULL, WHITESPACE); | ||
352 | if (cp != 0 && (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0)) | ||
353 | fprintf(stderr, | ||
354 | "%.99s line %d: Warning, TIS is not supported.\n", | ||
355 | filename, | ||
356 | linenum); | ||
357 | break; | ||
358 | |||
359 | case oCompressionLevel: | ||
360 | intptr = &options->compression_level; | ||
361 | goto parse_int; | ||
362 | |||
363 | case oIdentityFile: | ||
364 | cp = strtok(NULL, WHITESPACE); | ||
365 | if (!cp) | ||
366 | fatal("%.200s line %d: Missing argument.", filename, linenum); | ||
367 | if (*activep) | ||
368 | { | ||
369 | if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) | ||
370 | fatal("%.200s line %d: Too many identity files specified (max %d).", | ||
371 | filename, linenum, SSH_MAX_IDENTITY_FILES); | ||
372 | options->identity_files[options->num_identity_files++] = xstrdup(cp); | ||
373 | } | ||
374 | break; | ||
375 | |||
376 | case oUser: | ||
377 | charptr = &options->user; | ||
378 | parse_string: | ||
379 | cp = strtok(NULL, WHITESPACE); | ||
380 | if (!cp) | ||
381 | fatal("%.200s line %d: Missing argument.", filename, linenum); | ||
382 | if (*activep && *charptr == NULL) | ||
383 | *charptr = xstrdup(cp); | ||
384 | break; | ||
385 | |||
386 | case oGlobalKnownHostsFile: | ||
387 | charptr = &options->system_hostfile; | ||
388 | goto parse_string; | ||
389 | |||
390 | case oUserKnownHostsFile: | ||
391 | charptr = &options->user_hostfile; | ||
392 | goto parse_string; | ||
393 | |||
394 | case oHostName: | ||
395 | charptr = &options->hostname; | ||
396 | goto parse_string; | ||
397 | |||
398 | case oProxyCommand: | ||
399 | charptr = &options->proxy_command; | ||
400 | string = xstrdup(""); | ||
401 | while ((cp = strtok(NULL, WHITESPACE)) != NULL) | ||
402 | { | ||
403 | string = xrealloc(string, strlen(string) + strlen(cp) + 2); | ||
404 | strcat(string, " "); | ||
405 | strcat(string, cp); | ||
406 | } | ||
407 | if (*activep && *charptr == NULL) | ||
408 | *charptr = string; | ||
409 | else | ||
410 | xfree(string); | ||
411 | return; | ||
412 | |||
413 | case oPort: | ||
414 | intptr = &options->port; | ||
415 | parse_int: | ||
416 | cp = strtok(NULL, WHITESPACE); | ||
417 | if (!cp) | ||
418 | fatal("%.200s line %d: Missing argument.", filename, linenum); | ||
419 | if (cp[0] < '0' || cp[0] > '9') | ||
420 | fatal("%.200s line %d: Bad number.", filename, linenum); | ||
421 | #if 0 | ||
422 | value = atoi(cp); | ||
423 | #else | ||
424 | { | ||
425 | char *ptr; | ||
426 | value = strtol(cp, &ptr, 0); /* Octal, decimal, or hex format? */ | ||
427 | if (cp == ptr) | ||
428 | fatal("%.200s line %d: Bad number.", filename, linenum); | ||
429 | } | ||
430 | #endif | ||
431 | if (*activep && *intptr == -1) | ||
432 | *intptr = value; | ||
433 | break; | ||
434 | |||
435 | case oConnectionAttempts: | ||
436 | intptr = &options->connection_attempts; | ||
437 | goto parse_int; | ||
438 | |||
439 | case oCipher: | ||
440 | intptr = &options->cipher; | ||
441 | cp = strtok(NULL, WHITESPACE); | ||
442 | value = cipher_number(cp); | ||
443 | if (value == -1) | ||
444 | fatal("%.200s line %d: Bad cipher.", filename, linenum); | ||
445 | if (*activep && *intptr == -1) | ||
446 | *intptr = value; | ||
447 | break; | ||
448 | |||
449 | case oRemoteForward: | ||
450 | cp = strtok(NULL, WHITESPACE); | ||
451 | if (!cp) | ||
452 | fatal("%.200s line %d: Missing argument.", filename, linenum); | ||
453 | if (cp[0] < '0' || cp[0] > '9') | ||
454 | fatal("%.200s line %d: Badly formatted port number.", | ||
455 | filename, linenum); | ||
456 | fwd_port = atoi(cp); | ||
457 | cp = strtok(NULL, WHITESPACE); | ||
458 | if (!cp) | ||
459 | fatal("%.200s line %d: Missing second argument.", | ||
460 | filename, linenum); | ||
461 | if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2) | ||
462 | fatal("%.200s line %d: Badly formatted host:port.", | ||
463 | filename, linenum); | ||
464 | if (*activep) | ||
465 | add_remote_forward(options, fwd_port, buf, fwd_host_port); | ||
466 | break; | ||
467 | |||
468 | case oLocalForward: | ||
469 | cp = strtok(NULL, WHITESPACE); | ||
470 | if (!cp) | ||
471 | fatal("%.200s line %d: Missing argument.", filename, linenum); | ||
472 | if (cp[0] < '0' || cp[0] > '9') | ||
473 | fatal("%.200s line %d: Badly formatted port number.", | ||
474 | filename, linenum); | ||
475 | fwd_port = atoi(cp); | ||
476 | cp = strtok(NULL, WHITESPACE); | ||
477 | if (!cp) | ||
478 | fatal("%.200s line %d: Missing second argument.", | ||
479 | filename, linenum); | ||
480 | if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2) | ||
481 | fatal("%.200s line %d: Badly formatted host:port.", | ||
482 | filename, linenum); | ||
483 | if (*activep) | ||
484 | add_local_forward(options, fwd_port, buf, fwd_host_port); | ||
485 | break; | ||
486 | |||
487 | case oHost: | ||
488 | *activep = 0; | ||
489 | while ((cp = strtok(NULL, WHITESPACE)) != NULL) | ||
490 | if (match_pattern(host, cp)) | ||
491 | { | ||
492 | debug("Applying options for %.100s", cp); | ||
493 | *activep = 1; | ||
494 | break; | ||
495 | } | ||
496 | /* Avoid garbage check below, as strtok already returned NULL. */ | ||
497 | return; | ||
498 | |||
499 | case oEscapeChar: | ||
500 | intptr = &options->escape_char; | ||
501 | cp = strtok(NULL, WHITESPACE); | ||
502 | if (!cp) | ||
503 | fatal("%.200s line %d: Missing argument.", filename, linenum); | ||
504 | if (cp[0] == '^' && cp[2] == 0 && | ||
505 | (unsigned char)cp[1] >= 64 && (unsigned char)cp[1] < 128) | ||
506 | value = (unsigned char)cp[1] & 31; | ||
507 | else | ||
508 | if (strlen(cp) == 1) | ||
509 | value = (unsigned char)cp[0]; | ||
510 | else | ||
511 | if (strcmp(cp, "none") == 0) | ||
512 | value = -2; | ||
513 | else | ||
514 | { | ||
515 | fatal("%.200s line %d: Bad escape character.", | ||
516 | filename, linenum); | ||
517 | /*NOTREACHED*/ | ||
518 | value = 0; /* Avoid compiler warning. */ | ||
519 | } | ||
520 | if (*activep && *intptr == -1) | ||
521 | *intptr = value; | ||
522 | break; | ||
523 | |||
524 | default: | ||
525 | fatal("parse_config_file: Unimplemented opcode %d", opcode); | ||
526 | } | ||
527 | |||
528 | /* Check that there is no garbage at end of line. */ | ||
529 | if (strtok(NULL, WHITESPACE) != NULL) | ||
530 | fatal("%.200s line %d: garbage at end of line.", | ||
531 | filename, linenum); | ||
532 | } | ||
533 | |||
534 | |||
535 | /* Reads the config file and modifies the options accordingly. Options should | ||
536 | already be initialized before this call. This never returns if there | ||
537 | is an error. If the file does not exist, this returns immediately. */ | ||
538 | |||
539 | void read_config_file(const char *filename, const char *host, Options *options) | ||
540 | { | ||
541 | FILE *f; | ||
542 | char line[1024]; | ||
543 | int active, linenum; | ||
544 | |||
545 | /* Open the file. */ | ||
546 | f = fopen(filename, "r"); | ||
547 | if (!f) | ||
548 | return; | ||
549 | |||
550 | debug("Reading configuration data %.200s", filename); | ||
551 | |||
552 | /* Mark that we are now processing the options. This flag is turned on/off | ||
553 | by Host specifications. */ | ||
554 | active = 1; | ||
555 | linenum = 0; | ||
556 | while (fgets(line, sizeof(line), f)) | ||
557 | { | ||
558 | /* Update line number counter. */ | ||
559 | linenum++; | ||
560 | |||
561 | process_config_line(options, host, line, filename, linenum, &active); | ||
562 | } | ||
563 | fclose(f); | ||
564 | } | ||
565 | |||
566 | /* Initializes options to special values that indicate that they have not | ||
567 | yet been set. Read_config_file will only set options with this value. | ||
568 | Options are processed in the following order: command line, user config | ||
569 | file, system config file. Last, fill_default_options is called. */ | ||
570 | |||
571 | void initialize_options(Options *options) | ||
572 | { | ||
573 | memset(options, 'X', sizeof(*options)); | ||
574 | options->forward_agent = -1; | ||
575 | options->forward_x11 = -1; | ||
576 | options->gateway_ports = -1; | ||
577 | options->use_privileged_port = -1; | ||
578 | options->rhosts_authentication = -1; | ||
579 | options->rsa_authentication = -1; | ||
580 | #ifdef KRB4 | ||
581 | options->kerberos_authentication = -1; | ||
582 | #endif | ||
583 | #ifdef AFS | ||
584 | options->kerberos_tgt_passing = -1; | ||
585 | options->afs_token_passing = -1; | ||
586 | #endif | ||
587 | options->password_authentication = -1; | ||
588 | options->rhosts_rsa_authentication = -1; | ||
589 | options->fallback_to_rsh = -1; | ||
590 | options->use_rsh = -1; | ||
591 | options->batch_mode = -1; | ||
592 | options->check_host_ip = -1; | ||
593 | options->strict_host_key_checking = -1; | ||
594 | options->compression = -1; | ||
595 | options->keepalives = -1; | ||
596 | options->compression_level = -1; | ||
597 | options->port = -1; | ||
598 | options->connection_attempts = -1; | ||
599 | options->number_of_password_prompts = -1; | ||
600 | options->cipher = -1; | ||
601 | options->num_identity_files = 0; | ||
602 | options->hostname = NULL; | ||
603 | options->proxy_command = NULL; | ||
604 | options->user = NULL; | ||
605 | options->escape_char = -1; | ||
606 | options->system_hostfile = NULL; | ||
607 | options->user_hostfile = NULL; | ||
608 | options->num_local_forwards = 0; | ||
609 | options->num_remote_forwards = 0; | ||
610 | } | ||
611 | |||
612 | /* Called after processing other sources of option data, this fills those | ||
613 | options for which no value has been specified with their default values. */ | ||
614 | |||
615 | void fill_default_options(Options *options) | ||
616 | { | ||
617 | if (options->forward_agent == -1) | ||
618 | options->forward_agent = 1; | ||
619 | if (options->forward_x11 == -1) | ||
620 | options->forward_x11 = 1; | ||
621 | if (options->gateway_ports == -1) | ||
622 | options->gateway_ports = 0; | ||
623 | if (options->use_privileged_port == -1) | ||
624 | options->use_privileged_port = 1; | ||
625 | if (options->rhosts_authentication == -1) | ||
626 | options->rhosts_authentication = 1; | ||
627 | if (options->rsa_authentication == -1) | ||
628 | options->rsa_authentication = 1; | ||
629 | #ifdef KRB4 | ||
630 | if (options->kerberos_authentication == -1) | ||
631 | options->kerberos_authentication = 1; | ||
632 | #endif /* KRB4 */ | ||
633 | #ifdef AFS | ||
634 | if (options->kerberos_tgt_passing == -1) | ||
635 | options->kerberos_tgt_passing = 1; | ||
636 | if (options->afs_token_passing == -1) | ||
637 | options->afs_token_passing = 1; | ||
638 | #endif /* AFS */ | ||
639 | if (options->password_authentication == -1) | ||
640 | options->password_authentication = 1; | ||
641 | if (options->rhosts_rsa_authentication == -1) | ||
642 | options->rhosts_rsa_authentication = 1; | ||
643 | if (options->fallback_to_rsh == -1) | ||
644 | options->fallback_to_rsh = 1; | ||
645 | if (options->use_rsh == -1) | ||
646 | options->use_rsh = 0; | ||
647 | if (options->batch_mode == -1) | ||
648 | options->batch_mode = 0; | ||
649 | if (options->check_host_ip == -1) | ||
650 | options->check_host_ip = 1; | ||
651 | if (options->strict_host_key_checking == -1) | ||
652 | options->strict_host_key_checking = 2; /* 2 is default */ | ||
653 | if (options->compression == -1) | ||
654 | options->compression = 0; | ||
655 | if (options->keepalives == -1) | ||
656 | options->keepalives = 1; | ||
657 | if (options->compression_level == -1) | ||
658 | options->compression_level = 6; | ||
659 | if (options->port == -1) | ||
660 | options->port = 0; /* Filled in ssh_connect. */ | ||
661 | if (options->connection_attempts == -1) | ||
662 | options->connection_attempts = 4; | ||
663 | if (options->number_of_password_prompts == -1) | ||
664 | options->number_of_password_prompts = 3; | ||
665 | if (options->cipher == -1) | ||
666 | options->cipher = SSH_CIPHER_NOT_SET; /* Selected in ssh_login(). */ | ||
667 | if (options->num_identity_files == 0) | ||
668 | { | ||
669 | options->identity_files[0] = | ||
670 | xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1); | ||
671 | sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY); | ||
672 | options->num_identity_files = 1; | ||
673 | } | ||
674 | if (options->escape_char == -1) | ||
675 | options->escape_char = '~'; | ||
676 | if (options->system_hostfile == NULL) | ||
677 | options->system_hostfile = SSH_SYSTEM_HOSTFILE; | ||
678 | if (options->user_hostfile == NULL) | ||
679 | options->user_hostfile = SSH_USER_HOSTFILE; | ||
680 | /* options->proxy_command should not be set by default */ | ||
681 | /* options->user will be set in the main program if appropriate */ | ||
682 | /* options->hostname will be set in the main program if appropriate */ | ||
683 | } | ||
684 | |||
diff --git a/readconf.h b/readconf.h new file mode 100644 index 000000000..71655bd28 --- /dev/null +++ b/readconf.h | |||
@@ -0,0 +1,116 @@ | |||
1 | /* | ||
2 | |||
3 | readconf.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Apr 22 00:25:29 1995 ylo | ||
11 | |||
12 | Functions for reading the configuration file. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: readconf.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
17 | |||
18 | #ifndef READCONF_H | ||
19 | #define READCONF_H | ||
20 | |||
21 | /* Data structure for representing a forwarding request. */ | ||
22 | |||
23 | typedef struct | ||
24 | { | ||
25 | int port; /* Port to forward. */ | ||
26 | char *host; /* Host to connect. */ | ||
27 | int host_port; /* Port to connect on host. */ | ||
28 | } Forward; | ||
29 | |||
30 | /* Data structure for representing option data. */ | ||
31 | |||
32 | typedef struct | ||
33 | { | ||
34 | int forward_agent; /* Forward authentication agent. */ | ||
35 | int forward_x11; /* Forward X11 display. */ | ||
36 | int gateway_ports; /* Allow remote connects to forwarded ports. */ | ||
37 | int use_privileged_port; /* Don't use privileged port if false. */ | ||
38 | int rhosts_authentication; /* Try rhosts authentication. */ | ||
39 | int rhosts_rsa_authentication;/* Try rhosts with RSA authentication. */ | ||
40 | int rsa_authentication; /* Try RSA authentication. */ | ||
41 | #ifdef KRB4 | ||
42 | int kerberos_authentication; /* Try Kerberos authentication. */ | ||
43 | #endif | ||
44 | #ifdef AFS | ||
45 | int kerberos_tgt_passing; /* Try Kerberos tgt passing. */ | ||
46 | int afs_token_passing; /* Try AFS token passing. */ | ||
47 | #endif | ||
48 | int password_authentication; /* Try password authentication. */ | ||
49 | int fallback_to_rsh; /* Use rsh if cannot connect with ssh. */ | ||
50 | int use_rsh; /* Always use rsh (don\'t try ssh). */ | ||
51 | int batch_mode; /* Batch mode: do not ask for passwords. */ | ||
52 | int check_host_ip; /* Also keep track of keys for IP address */ | ||
53 | int strict_host_key_checking; /* Strict host key checking. */ | ||
54 | int compression; /* Compress packets in both directions. */ | ||
55 | int compression_level; /* Compression level 1 (fast) to 9 (best). */ | ||
56 | int keepalives; /* Set SO_KEEPALIVE. */ | ||
57 | |||
58 | int port; /* Port to connect. */ | ||
59 | int connection_attempts; /* Max attempts (seconds) before giving up */ | ||
60 | int number_of_password_prompts; /* Max number of password prompts. */ | ||
61 | int cipher; /* Cipher to use. */ | ||
62 | char *hostname; /* Real host to connect. */ | ||
63 | char *proxy_command; /* Proxy command for connecting the host. */ | ||
64 | char *user; /* User to log in as. */ | ||
65 | int escape_char; /* Escape character; -2 = none */ | ||
66 | |||
67 | char *system_hostfile; /* Path for /etc/ssh_known_hosts. */ | ||
68 | char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ | ||
69 | |||
70 | int num_identity_files; /* Number of files for RSA identities. */ | ||
71 | char *identity_files[SSH_MAX_IDENTITY_FILES]; | ||
72 | |||
73 | /* Local TCP/IP forward requests. */ | ||
74 | int num_local_forwards; | ||
75 | Forward local_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; | ||
76 | |||
77 | /* Remote TCP/IP forward requests. */ | ||
78 | int num_remote_forwards; | ||
79 | Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; | ||
80 | } Options; | ||
81 | |||
82 | |||
83 | /* Initializes options to special values that indicate that they have not | ||
84 | yet been set. Read_config_file will only set options with this value. | ||
85 | Options are processed in the following order: command line, user config | ||
86 | file, system config file. Last, fill_default_options is called. */ | ||
87 | void initialize_options(Options *options); | ||
88 | |||
89 | /* Called after processing other sources of option data, this fills those | ||
90 | options for which no value has been specified with their default values. */ | ||
91 | void fill_default_options(Options *options); | ||
92 | |||
93 | /* Processes a single option line as used in the configuration files. | ||
94 | This only sets those values that have not already been set. */ | ||
95 | void process_config_line(Options *options, const char *host, | ||
96 | char *line, const char *filename, int linenum, | ||
97 | int *activep); | ||
98 | |||
99 | /* Reads the config file and modifies the options accordingly. Options should | ||
100 | already be initialized before this call. This never returns if there | ||
101 | is an error. If the file does not exist, this returns immediately. */ | ||
102 | void read_config_file(const char *filename, const char *host, | ||
103 | Options *options); | ||
104 | |||
105 | /* Adds a local TCP/IP port forward to options. Never returns if there | ||
106 | is an error. */ | ||
107 | void add_local_forward(Options *options, int port, const char *host, | ||
108 | int host_port); | ||
109 | |||
110 | /* Adds a remote TCP/IP port forward to options. Never returns if there | ||
111 | is an error. */ | ||
112 | void add_remote_forward(Options *options, int port, const char *host, | ||
113 | int host_port); | ||
114 | |||
115 | |||
116 | #endif /* READCONF_H */ | ||
diff --git a/readpass.c b/readpass.c new file mode 100644 index 000000000..3031825e5 --- /dev/null +++ b/readpass.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | |||
3 | readpass.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Jul 10 22:08:59 1995 ylo | ||
11 | |||
12 | Functions for reading passphrases and passwords. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: readpass.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
18 | |||
19 | #include "xmalloc.h" | ||
20 | #include "ssh.h" | ||
21 | |||
22 | /* Saved old terminal mode for read_passphrase. */ | ||
23 | static struct termios saved_tio; | ||
24 | |||
25 | /* Old interrupt signal handler for read_passphrase. */ | ||
26 | static void (*old_handler)(int sig) = NULL; | ||
27 | |||
28 | /* Interrupt signal handler for read_passphrase. */ | ||
29 | |||
30 | void intr_handler(int sig) | ||
31 | { | ||
32 | /* Restore terminal modes. */ | ||
33 | tcsetattr(fileno(stdin), TCSANOW, &saved_tio); | ||
34 | /* Restore the old signal handler. */ | ||
35 | signal(sig, old_handler); | ||
36 | /* Resend the signal, with the old handler. */ | ||
37 | kill(getpid(), sig); | ||
38 | } | ||
39 | |||
40 | /* Reads a passphrase from /dev/tty with echo turned off. Returns the | ||
41 | passphrase (allocated with xmalloc). Exits if EOF is encountered. | ||
42 | The passphrase if read from stdin if from_stdin is true (as is the | ||
43 | case with ssh-keygen). */ | ||
44 | |||
45 | char *read_passphrase(const char *prompt, int from_stdin) | ||
46 | { | ||
47 | char buf[1024], *cp; | ||
48 | struct termios tio; | ||
49 | FILE *f; | ||
50 | |||
51 | if (from_stdin) | ||
52 | f = stdin; | ||
53 | else | ||
54 | { | ||
55 | /* Read the passphrase from /dev/tty to make it possible to ask it even | ||
56 | when stdin has been redirected. */ | ||
57 | f = fopen("/dev/tty", "r"); | ||
58 | if (!f) | ||
59 | { | ||
60 | /* No controlling terminal and no DISPLAY. Nowhere to read. */ | ||
61 | fprintf(stderr, "You have no controlling tty and no DISPLAY. Cannot read passphrase.\n"); | ||
62 | exit(1); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | /* Display the prompt (on stderr because stdout might be redirected). */ | ||
67 | fflush(stdout); | ||
68 | fprintf(stderr, "%s", prompt); | ||
69 | fflush(stderr); | ||
70 | |||
71 | /* Get terminal modes. */ | ||
72 | tcgetattr(fileno(f), &tio); | ||
73 | saved_tio = tio; | ||
74 | /* Save signal handler and set the new handler. */ | ||
75 | old_handler = signal(SIGINT, intr_handler); | ||
76 | |||
77 | /* Set new terminal modes disabling all echo. */ | ||
78 | tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); | ||
79 | tcsetattr(fileno(f), TCSANOW, &tio); | ||
80 | |||
81 | /* Read the passphrase from the terminal. */ | ||
82 | if (fgets(buf, sizeof(buf), f) == NULL) | ||
83 | { | ||
84 | /* Got EOF. Just exit. */ | ||
85 | /* Restore terminal modes. */ | ||
86 | tcsetattr(fileno(f), TCSANOW, &saved_tio); | ||
87 | /* Restore the signal handler. */ | ||
88 | signal(SIGINT, old_handler); | ||
89 | /* Print a newline (the prompt probably didn\'t have one). */ | ||
90 | fprintf(stderr, "\n"); | ||
91 | /* Close the file. */ | ||
92 | if (f != stdin) | ||
93 | fclose(f); | ||
94 | exit(1); | ||
95 | } | ||
96 | /* Restore terminal modes. */ | ||
97 | tcsetattr(fileno(f), TCSANOW, &saved_tio); | ||
98 | /* Restore the signal handler. */ | ||
99 | (void)signal(SIGINT, old_handler); | ||
100 | /* Remove newline from the passphrase. */ | ||
101 | if (strchr(buf, '\n')) | ||
102 | *strchr(buf, '\n') = 0; | ||
103 | /* Allocate a copy of the passphrase. */ | ||
104 | cp = xstrdup(buf); | ||
105 | /* Clear the buffer so we don\'t leave copies of the passphrase laying | ||
106 | around. */ | ||
107 | memset(buf, 0, sizeof(buf)); | ||
108 | /* Print a newline since the prompt probably didn\'t have one. */ | ||
109 | fprintf(stderr, "\n"); | ||
110 | /* Close the file. */ | ||
111 | if (f != stdin) | ||
112 | fclose(f); | ||
113 | return cp; | ||
114 | } | ||
@@ -0,0 +1,164 @@ | |||
1 | /* | ||
2 | |||
3 | rsa.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Fri Mar 3 22:07:06 1995 ylo | ||
11 | |||
12 | Description of the RSA algorithm can be found e.g. from the following sources: | ||
13 | |||
14 | Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1994. | ||
15 | |||
16 | Jennifer Seberry and Josed Pieprzyk: Cryptography: An Introduction to | ||
17 | Computer Security. Prentice-Hall, 1989. | ||
18 | |||
19 | Man Young Rhee: Cryptography and Secure Data Communications. McGraw-Hill, | ||
20 | 1994. | ||
21 | |||
22 | R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic Communications | ||
23 | System and Method. US Patent 4,405,829, 1983. | ||
24 | |||
25 | Hans Riesel: Prime Numbers and Computer Methods for Factorization. | ||
26 | Birkhauser, 1994. | ||
27 | |||
28 | The RSA Frequently Asked Questions document by RSA Data Security, Inc., 1995. | ||
29 | |||
30 | RSA in 3 lines of perl by Adam Back <aba@atlax.ex.ac.uk>, 1995, as included | ||
31 | below: | ||
32 | |||
33 | gone - had to be deleted - what a pity | ||
34 | |||
35 | */ | ||
36 | |||
37 | #include "includes.h" | ||
38 | RCSID("$Id: rsa.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
39 | |||
40 | #include "rsa.h" | ||
41 | #include "ssh.h" | ||
42 | #include "xmalloc.h" | ||
43 | |||
44 | int rsa_verbose = 1; | ||
45 | |||
46 | int | ||
47 | rsa_alive() | ||
48 | { | ||
49 | RSA *key; | ||
50 | |||
51 | key = RSA_generate_key(32, 3, NULL, NULL); | ||
52 | if (key == NULL) | ||
53 | return (0); | ||
54 | RSA_free(key); | ||
55 | return (1); | ||
56 | } | ||
57 | |||
58 | /* Generates RSA public and private keys. This initializes the data | ||
59 | structures; they should be freed with rsa_clear_private_key and | ||
60 | rsa_clear_public_key. */ | ||
61 | |||
62 | void | ||
63 | rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits) | ||
64 | { | ||
65 | RSA *key; | ||
66 | |||
67 | if (rsa_verbose) { | ||
68 | printf("Generating RSA keys: "); | ||
69 | fflush(stdout); | ||
70 | } | ||
71 | |||
72 | key = RSA_generate_key(bits, 35, NULL, NULL); | ||
73 | |||
74 | assert(key != NULL); | ||
75 | |||
76 | /* Copy public key parameters */ | ||
77 | pub->n = BN_new(); | ||
78 | BN_copy(pub->n, key->n); | ||
79 | pub->e = BN_new(); | ||
80 | BN_copy(pub->e, key->e); | ||
81 | |||
82 | /* Copy private key parameters */ | ||
83 | prv->n = BN_new(); | ||
84 | BN_copy(prv->n, key->n); | ||
85 | prv->e = BN_new(); | ||
86 | BN_copy(prv->e, key->e); | ||
87 | prv->d = BN_new(); | ||
88 | BN_copy(prv->d, key->d); | ||
89 | prv->p = BN_new(); | ||
90 | BN_copy(prv->p, key->p); | ||
91 | prv->q = BN_new(); | ||
92 | BN_copy(prv->q, key->q); | ||
93 | |||
94 | prv->dmp1 = BN_new(); | ||
95 | BN_copy(prv->dmp1, key->dmp1); | ||
96 | |||
97 | prv->dmq1 = BN_new(); | ||
98 | BN_copy(prv->dmq1, key->dmq1); | ||
99 | |||
100 | prv->iqmp = BN_new(); | ||
101 | BN_copy(prv->iqmp, key->iqmp); | ||
102 | |||
103 | RSA_free(key); | ||
104 | |||
105 | if (rsa_verbose) | ||
106 | printf("Key generation complete.\n"); | ||
107 | } | ||
108 | |||
109 | void | ||
110 | rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA* key) | ||
111 | { | ||
112 | char *inbuf, *outbuf; | ||
113 | int len; | ||
114 | |||
115 | if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e)) | ||
116 | fatal("rsa_public_encrypt() exponent too small or not odd"); | ||
117 | |||
118 | len = BN_num_bytes(key->n); | ||
119 | outbuf = xmalloc(len); | ||
120 | |||
121 | len = BN_num_bytes(in); | ||
122 | inbuf = xmalloc(len); | ||
123 | BN_bn2bin(in, inbuf); | ||
124 | |||
125 | if ((len = RSA_public_encrypt(len, inbuf, outbuf, key, | ||
126 | RSA_PKCS1_PADDING)) <= 0) | ||
127 | fatal("rsa_public_encrypt() failed"); | ||
128 | |||
129 | BN_bin2bn(outbuf, len, out); | ||
130 | |||
131 | xfree(outbuf); | ||
132 | xfree(inbuf); | ||
133 | } | ||
134 | |||
135 | void | ||
136 | rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) | ||
137 | { | ||
138 | char *inbuf, *outbuf; | ||
139 | int len; | ||
140 | |||
141 | len = BN_num_bytes(key->n); | ||
142 | outbuf = xmalloc(len); | ||
143 | |||
144 | len = BN_num_bytes(in); | ||
145 | inbuf = xmalloc(len); | ||
146 | BN_bn2bin(in, inbuf); | ||
147 | |||
148 | if ((len = RSA_private_decrypt(len, inbuf, outbuf, key, | ||
149 | RSA_SSLV23_PADDING)) <= 0) | ||
150 | fatal("rsa_private_decrypt() failed"); | ||
151 | |||
152 | BN_bin2bn(outbuf, len, out); | ||
153 | |||
154 | xfree(outbuf); | ||
155 | xfree(inbuf); | ||
156 | } | ||
157 | |||
158 | /* Set whether to output verbose messages during key generation. */ | ||
159 | |||
160 | void | ||
161 | rsa_set_verbose(int verbose) | ||
162 | { | ||
163 | rsa_verbose = verbose; | ||
164 | } | ||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | |||
3 | rsa.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Fri Mar 3 22:01:06 1995 ylo | ||
11 | |||
12 | RSA key generation, encryption and decryption. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: rsa.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
17 | |||
18 | #ifndef RSA_H | ||
19 | #define RSA_H | ||
20 | |||
21 | #include <openssl/bn.h> | ||
22 | #include <openssl/rsa.h> | ||
23 | |||
24 | /* Calls SSL RSA_generate_key, only copies to prv and pub */ | ||
25 | void rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits); | ||
26 | |||
27 | /* Indicates whether the rsa module is permitted to show messages on | ||
28 | the terminal. */ | ||
29 | void rsa_set_verbose __P((int verbose)); | ||
30 | |||
31 | int rsa_alive __P((void)); | ||
32 | |||
33 | void rsa_public_encrypt __P((BIGNUM *out, BIGNUM *in, RSA *prv)); | ||
34 | void rsa_private_decrypt __P((BIGNUM *out, BIGNUM *in, RSA *prv)); | ||
35 | |||
36 | #endif /* RSA_H */ | ||
@@ -0,0 +1,110 @@ | |||
1 | .\" -*- nroff -*- | ||
2 | .\" | ||
3 | .\" scp.1 | ||
4 | .\" | ||
5 | .\" Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | .\" | ||
7 | .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | .\" All rights reserved | ||
9 | .\" | ||
10 | .\" Created: Sun May 7 00:14:37 1995 ylo | ||
11 | .\" | ||
12 | .\" $Id: scp.1,v 1.1 1999/10/27 03:42:44 damien Exp $ | ||
13 | .\" | ||
14 | .Dd September 25, 1999 | ||
15 | .Dt SCP 1 | ||
16 | .Os | ||
17 | .Sh NAME | ||
18 | .Nm scp | ||
19 | .Nd secure copy (remote file copy program) | ||
20 | .Sh SYNOPSIS | ||
21 | .Nm scp | ||
22 | .Op Fl pqrvC | ||
23 | .Op Fl P Ar port | ||
24 | .Op Fl c Ar cipher | ||
25 | .Op Fl i Ar identity_file | ||
26 | .Sm off | ||
27 | .Oo | ||
28 | .Op Ar user@ | ||
29 | .Ar host1 No : | ||
30 | .Oc Ns Ar file1 | ||
31 | .Sm on | ||
32 | .Op Ar ... | ||
33 | .Sm off | ||
34 | .Oo | ||
35 | .Op Ar user@ | ||
36 | .Ar host2 No : | ||
37 | .Oc Ar file2 | ||
38 | .Sm on | ||
39 | .Sh DESCRIPTION | ||
40 | .Nm | ||
41 | copies files between hosts on a network. It uses | ||
42 | .Xr ssh 1 | ||
43 | for data transfer, and uses the same authentication and provides the | ||
44 | same security as | ||
45 | .Xr ssh 1 . | ||
46 | Unlike | ||
47 | .Xr rcp 1 , | ||
48 | .Nm | ||
49 | will ask for passwords or passphrases if they are needed for | ||
50 | authentication. | ||
51 | .Pp | ||
52 | Any file name may contain a host and user specification to indicate | ||
53 | that the file is to be copied to/from that host. Copies between two | ||
54 | remote hosts are permitted. | ||
55 | .Pp | ||
56 | The options are as follows: | ||
57 | .Bl -tag -width Ds | ||
58 | .It Fl c Ar cipher | ||
59 | Selects the cipher to use for encrypting the data transfer. This | ||
60 | option is directly passed to | ||
61 | .Xr ssh 1 . | ||
62 | .It Fl i Ar identity_file | ||
63 | Selects the file from which the identity (private key) for RSA | ||
64 | authentication is read. This option is directly passed to | ||
65 | .Xr ssh 1 . | ||
66 | .It Fl p | ||
67 | Preserves modification times, access times, and modes from the | ||
68 | original file. | ||
69 | .It Fl r | ||
70 | Recursively copy entire directories. | ||
71 | .It Fl v | ||
72 | Verbose mode. Causes | ||
73 | .Nm | ||
74 | and | ||
75 | .Xr ssh 1 | ||
76 | to print debugging messages about their progress. This is helpful in | ||
77 | debugging connection, authentication, and configuration problems. | ||
78 | .It Fl B | ||
79 | Selects batch mode (prevents asking for passwords or passphrases). | ||
80 | .It Fl q | ||
81 | Disables the progress meter. | ||
82 | .It Fl C | ||
83 | Compression enable. Passes the | ||
84 | .Fl C | ||
85 | flag to | ||
86 | .Xr ssh 1 | ||
87 | to enable compression. | ||
88 | .It Fl P Ar port | ||
89 | Specifies the port to connect to on the remote host. Note that this | ||
90 | option is written with a capital | ||
91 | .Sq P , | ||
92 | because | ||
93 | .Fl p | ||
94 | is already reserved for preserving the times and modes of the file in | ||
95 | .Xr rcp 1 . | ||
96 | .Sh AUTHORS | ||
97 | Timo Rinne <tri@iki.fi> and Tatu Ylonen <ylo@cs.hut.fi> | ||
98 | .Sh HISTORY | ||
99 | .Nm | ||
100 | is based on the | ||
101 | .Xr rcp 1 | ||
102 | program in BSD source code from the Regents of the University of | ||
103 | California. | ||
104 | .Sh SEE ALSO | ||
105 | .Xr rcp 1 , | ||
106 | .Xr ssh 1 , | ||
107 | .Xr ssh-add 1 , | ||
108 | .Xr ssh-agent 1 , | ||
109 | .Xr ssh-keygen 1 , | ||
110 | .Xr sshd 8 | ||
@@ -0,0 +1,1220 @@ | |||
1 | /* | ||
2 | |||
3 | scp - secure remote copy. This is basically patched BSD rcp which uses ssh | ||
4 | to do the data transfer (instead of using rcmd). | ||
5 | |||
6 | NOTE: This version should NOT be suid root. (This uses ssh to do the transfer | ||
7 | and ssh has the necessary privileges.) | ||
8 | |||
9 | 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> | ||
10 | |||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * Copyright (c) 1983, 1990, 1992, 1993, 1995 | ||
15 | * The Regents of the University of California. All rights reserved. | ||
16 | * | ||
17 | * Redistribution and use in source and binary forms, with or without | ||
18 | * modification, are permitted provided that the following conditions | ||
19 | * are met: | ||
20 | * 1. Redistributions of source code must retain the above copyright | ||
21 | * notice, this list of conditions and the following disclaimer. | ||
22 | * 2. Redistributions in binary form must reproduce the above copyright | ||
23 | * notice, this list of conditions and the following disclaimer in the | ||
24 | * documentation and/or other materials provided with the distribution. | ||
25 | * 3. All advertising materials mentioning features or use of this software | ||
26 | * must display the following acknowledgement: | ||
27 | * This product includes software developed by the University of | ||
28 | * California, Berkeley and its contributors. | ||
29 | * 4. Neither the name of the University nor the names of its contributors | ||
30 | * may be used to endorse or promote products derived from this software | ||
31 | * without specific prior written permission. | ||
32 | * | ||
33 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
34 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
35 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
36 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
37 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
38 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
39 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
40 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
41 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
42 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
43 | * SUCH DAMAGE. | ||
44 | * | ||
45 | * $Id: scp.c,v 1.1 1999/10/27 03:42:45 damien Exp $ | ||
46 | */ | ||
47 | |||
48 | #include "includes.h" | ||
49 | RCSID("$Id: scp.c,v 1.1 1999/10/27 03:42:45 damien Exp $"); | ||
50 | |||
51 | #include "ssh.h" | ||
52 | #include "xmalloc.h" | ||
53 | #include <utime.h> | ||
54 | |||
55 | #define _PATH_CP "cp" | ||
56 | |||
57 | /* For progressmeter() -- number of seconds before xfer considered "stalled" */ | ||
58 | #define STALLTIME 5 | ||
59 | |||
60 | /* Visual statistics about files as they are transferred. */ | ||
61 | void progressmeter(int); | ||
62 | |||
63 | /* Returns width of the terminal (for progress meter calculations). */ | ||
64 | int getttywidth(void); | ||
65 | |||
66 | /* Time a transfer started. */ | ||
67 | static struct timeval start; | ||
68 | |||
69 | /* Number of bytes of current file transferred so far. */ | ||
70 | volatile unsigned long statbytes; | ||
71 | |||
72 | /* Total size of current file. */ | ||
73 | unsigned long totalbytes = 0; | ||
74 | |||
75 | /* Name of current file being transferred. */ | ||
76 | char *curfile; | ||
77 | |||
78 | /* This is set to non-zero to enable verbose mode. */ | ||
79 | int verbose = 0; | ||
80 | |||
81 | /* This is set to non-zero if compression is desired. */ | ||
82 | int compress = 0; | ||
83 | |||
84 | /* This is set to zero if the progressmeter is not desired. */ | ||
85 | int showprogress = 1; | ||
86 | |||
87 | /* This is set to non-zero if running in batch mode (that is, password | ||
88 | and passphrase queries are not allowed). */ | ||
89 | int batchmode = 0; | ||
90 | |||
91 | /* This is set to the cipher type string if given on the command line. */ | ||
92 | char *cipher = NULL; | ||
93 | |||
94 | /* This is set to the RSA authentication identity file name if given on | ||
95 | the command line. */ | ||
96 | char *identity = NULL; | ||
97 | |||
98 | /* This is the port to use in contacting the remote site (is non-NULL). */ | ||
99 | char *port = NULL; | ||
100 | |||
101 | /* This function executes the given command as the specified user on the given | ||
102 | host. This returns < 0 if execution fails, and >= 0 otherwise. | ||
103 | This assigns the input and output file descriptors on success. */ | ||
104 | |||
105 | int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | ||
106 | { | ||
107 | int pin[2], pout[2], reserved[2]; | ||
108 | |||
109 | if (verbose) | ||
110 | fprintf(stderr, "Executing: host %s, user %s, command %s\n", | ||
111 | host, remuser ? remuser : "(unspecified)", cmd); | ||
112 | |||
113 | /* Reserve two descriptors so that the real pipes won't get descriptors | ||
114 | 0 and 1 because that will screw up dup2 below. */ | ||
115 | pipe(reserved); | ||
116 | |||
117 | /* Create a socket pair for communicating with ssh. */ | ||
118 | if (pipe(pin) < 0) | ||
119 | fatal("pipe: %s", strerror(errno)); | ||
120 | if (pipe(pout) < 0) | ||
121 | fatal("pipe: %s", strerror(errno)); | ||
122 | |||
123 | /* Free the reserved descriptors. */ | ||
124 | close(reserved[0]); | ||
125 | close(reserved[1]); | ||
126 | |||
127 | /* For a child to execute the command on the remote host using ssh. */ | ||
128 | if (fork() == 0) | ||
129 | { | ||
130 | char *args[100]; | ||
131 | unsigned int i; | ||
132 | |||
133 | /* Child. */ | ||
134 | close(pin[1]); | ||
135 | close(pout[0]); | ||
136 | dup2(pin[0], 0); | ||
137 | dup2(pout[1], 1); | ||
138 | close(pin[0]); | ||
139 | close(pout[1]); | ||
140 | |||
141 | i = 0; | ||
142 | args[i++] = SSH_PROGRAM; | ||
143 | args[i++] = "-x"; | ||
144 | args[i++] = "-oFallBackToRsh no"; | ||
145 | if (verbose) | ||
146 | args[i++] = "-v"; | ||
147 | if (compress) | ||
148 | args[i++] = "-C"; | ||
149 | if (batchmode) | ||
150 | args[i++] = "-oBatchMode yes"; | ||
151 | if (cipher != NULL) | ||
152 | { | ||
153 | args[i++] = "-c"; | ||
154 | args[i++] = cipher; | ||
155 | } | ||
156 | if (identity != NULL) | ||
157 | { | ||
158 | args[i++] = "-i"; | ||
159 | args[i++] = identity; | ||
160 | } | ||
161 | if (port != NULL) | ||
162 | { | ||
163 | args[i++] = "-p"; | ||
164 | args[i++] = port; | ||
165 | } | ||
166 | if (remuser != NULL) | ||
167 | { | ||
168 | args[i++] = "-l"; | ||
169 | args[i++] = remuser; | ||
170 | } | ||
171 | args[i++] = host; | ||
172 | args[i++] = cmd; | ||
173 | args[i++] = NULL; | ||
174 | |||
175 | execvp(SSH_PROGRAM, args); | ||
176 | perror(SSH_PROGRAM); | ||
177 | exit(1); | ||
178 | } | ||
179 | /* Parent. Close the other side, and return the local side. */ | ||
180 | close(pin[0]); | ||
181 | *fdout = pin[1]; | ||
182 | close(pout[1]); | ||
183 | *fdin = pout[0]; | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | void fatal(const char *fmt, ...) | ||
188 | { | ||
189 | va_list ap; | ||
190 | char buf[1024]; | ||
191 | |||
192 | va_start(ap, fmt); | ||
193 | vsnprintf(buf, sizeof(buf), fmt, ap); | ||
194 | va_end(ap); | ||
195 | fprintf(stderr, "%s\n", buf); | ||
196 | exit(255); | ||
197 | } | ||
198 | |||
199 | /* This stuff used to be in BSD rcp extern.h. */ | ||
200 | |||
201 | typedef struct { | ||
202 | int cnt; | ||
203 | char *buf; | ||
204 | } BUF; | ||
205 | |||
206 | extern int iamremote; | ||
207 | |||
208 | BUF *allocbuf(BUF *, int, int); | ||
209 | char *colon(char *); | ||
210 | void lostconn(int); | ||
211 | void nospace(void); | ||
212 | int okname(char *); | ||
213 | void run_err(const char *, ...); | ||
214 | void verifydir(char *); | ||
215 | |||
216 | /* Stuff from BSD rcp.c continues. */ | ||
217 | |||
218 | struct passwd *pwd; | ||
219 | uid_t userid; | ||
220 | int errs, remin, remout; | ||
221 | int pflag, iamremote, iamrecursive, targetshouldbedirectory; | ||
222 | |||
223 | #define CMDNEEDS 64 | ||
224 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ | ||
225 | |||
226 | int response(void); | ||
227 | void rsource(char *, struct stat *); | ||
228 | void sink(int, char *[]); | ||
229 | void source(int, char *[]); | ||
230 | void tolocal(int, char *[]); | ||
231 | void toremote(char *, int, char *[]); | ||
232 | void usage(void); | ||
233 | |||
234 | int | ||
235 | main(argc, argv) | ||
236 | int argc; | ||
237 | char *argv[]; | ||
238 | { | ||
239 | int ch, fflag, tflag; | ||
240 | char *targ; | ||
241 | extern char *optarg; | ||
242 | extern int optind; | ||
243 | |||
244 | fflag = tflag = 0; | ||
245 | while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q")) != EOF) | ||
246 | switch(ch) { /* User-visible flags. */ | ||
247 | case 'p': | ||
248 | pflag = 1; | ||
249 | break; | ||
250 | case 'P': | ||
251 | port = optarg; | ||
252 | break; | ||
253 | case 'r': | ||
254 | iamrecursive = 1; | ||
255 | break; | ||
256 | /* Server options. */ | ||
257 | case 'd': | ||
258 | targetshouldbedirectory = 1; | ||
259 | break; | ||
260 | case 'f': /* "from" */ | ||
261 | iamremote = 1; | ||
262 | fflag = 1; | ||
263 | break; | ||
264 | case 't': /* "to" */ | ||
265 | iamremote = 1; | ||
266 | tflag = 1; | ||
267 | break; | ||
268 | case 'c': | ||
269 | cipher = optarg; | ||
270 | break; | ||
271 | case 'i': | ||
272 | identity = optarg; | ||
273 | break; | ||
274 | case 'v': | ||
275 | verbose = 1; | ||
276 | break; | ||
277 | case 'B': | ||
278 | batchmode = 1; | ||
279 | break; | ||
280 | case 'C': | ||
281 | compress = 1; | ||
282 | break; | ||
283 | case 'q': | ||
284 | showprogress = 0; | ||
285 | break; | ||
286 | case '?': | ||
287 | default: | ||
288 | usage(); | ||
289 | } | ||
290 | argc -= optind; | ||
291 | argv += optind; | ||
292 | |||
293 | if ((pwd = getpwuid(userid = getuid())) == NULL) | ||
294 | fatal("unknown user %d", (int)userid); | ||
295 | |||
296 | if (! isatty(STDERR_FILENO)) | ||
297 | showprogress = 0; | ||
298 | |||
299 | remin = STDIN_FILENO; | ||
300 | remout = STDOUT_FILENO; | ||
301 | |||
302 | if (fflag) { /* Follow "protocol", send data. */ | ||
303 | (void)response(); | ||
304 | source(argc, argv); | ||
305 | exit(errs != 0); | ||
306 | } | ||
307 | |||
308 | if (tflag) { /* Receive data. */ | ||
309 | sink(argc, argv); | ||
310 | exit(errs != 0); | ||
311 | } | ||
312 | |||
313 | if (argc < 2) | ||
314 | usage(); | ||
315 | if (argc > 2) | ||
316 | targetshouldbedirectory = 1; | ||
317 | |||
318 | remin = remout = -1; | ||
319 | /* Command to be executed on remote system using "ssh". */ | ||
320 | (void)sprintf(cmd, "scp%s%s%s%s", verbose ? " -v" : "", | ||
321 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | ||
322 | targetshouldbedirectory ? " -d" : ""); | ||
323 | |||
324 | (void)signal(SIGPIPE, lostconn); | ||
325 | |||
326 | if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ | ||
327 | toremote(targ, argc, argv); | ||
328 | else { | ||
329 | tolocal(argc, argv); /* Dest is local host. */ | ||
330 | if (targetshouldbedirectory) | ||
331 | verifydir(argv[argc - 1]); | ||
332 | } | ||
333 | exit(errs != 0); | ||
334 | } | ||
335 | |||
336 | void | ||
337 | toremote(targ, argc, argv) | ||
338 | char *targ, *argv[]; | ||
339 | int argc; | ||
340 | { | ||
341 | int i, len; | ||
342 | char *bp, *host, *src, *suser, *thost, *tuser; | ||
343 | |||
344 | *targ++ = 0; | ||
345 | if (*targ == 0) | ||
346 | targ = "."; | ||
347 | |||
348 | if ((thost = strchr(argv[argc - 1], '@'))) { | ||
349 | /* user@host */ | ||
350 | *thost++ = 0; | ||
351 | tuser = argv[argc - 1]; | ||
352 | if (*tuser == '\0') | ||
353 | tuser = NULL; | ||
354 | else if (!okname(tuser)) | ||
355 | exit(1); | ||
356 | } else { | ||
357 | thost = argv[argc - 1]; | ||
358 | tuser = NULL; | ||
359 | } | ||
360 | |||
361 | for (i = 0; i < argc - 1; i++) { | ||
362 | src = colon(argv[i]); | ||
363 | if (src) { /* remote to remote */ | ||
364 | *src++ = 0; | ||
365 | if (*src == 0) | ||
366 | src = "."; | ||
367 | host = strchr(argv[i], '@'); | ||
368 | len = strlen(SSH_PROGRAM) + strlen(argv[i]) + | ||
369 | strlen(src) + (tuser ? strlen(tuser) : 0) + | ||
370 | strlen(thost) + strlen(targ) + CMDNEEDS + 32; | ||
371 | bp = xmalloc(len); | ||
372 | if (host) { | ||
373 | *host++ = 0; | ||
374 | suser = argv[i]; | ||
375 | if (*suser == '\0') | ||
376 | suser = pwd->pw_name; | ||
377 | else if (!okname(suser)) | ||
378 | continue; | ||
379 | (void)sprintf(bp, | ||
380 | "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", | ||
381 | SSH_PROGRAM, verbose ? " -v" : "", | ||
382 | suser, host, cmd, src, | ||
383 | tuser ? tuser : "", tuser ? "@" : "", | ||
384 | thost, targ); | ||
385 | } else | ||
386 | (void)sprintf(bp, | ||
387 | "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", | ||
388 | SSH_PROGRAM, verbose ? " -v" : "", | ||
389 | argv[i], cmd, src, | ||
390 | tuser ? tuser : "", tuser ? "@" : "", | ||
391 | thost, targ); | ||
392 | if (verbose) | ||
393 | fprintf(stderr, "Executing: %s\n", bp); | ||
394 | (void)system(bp); | ||
395 | (void)xfree(bp); | ||
396 | } else { /* local to remote */ | ||
397 | if (remin == -1) { | ||
398 | len = strlen(targ) + CMDNEEDS + 20; | ||
399 | bp = xmalloc(len); | ||
400 | (void)sprintf(bp, "%s -t %s", cmd, targ); | ||
401 | host = thost; | ||
402 | if (do_cmd(host, tuser, | ||
403 | bp, &remin, &remout) < 0) | ||
404 | exit(1); | ||
405 | if (response() < 0) | ||
406 | exit(1); | ||
407 | (void)xfree(bp); | ||
408 | } | ||
409 | source(1, argv+i); | ||
410 | } | ||
411 | } | ||
412 | } | ||
413 | |||
414 | void | ||
415 | tolocal(argc, argv) | ||
416 | int argc; | ||
417 | char *argv[]; | ||
418 | { | ||
419 | int i, len; | ||
420 | char *bp, *host, *src, *suser; | ||
421 | |||
422 | for (i = 0; i < argc - 1; i++) { | ||
423 | if (!(src = colon(argv[i]))) { /* Local to local. */ | ||
424 | len = strlen(_PATH_CP) + strlen(argv[i]) + | ||
425 | strlen(argv[argc - 1]) + 20; | ||
426 | bp = xmalloc(len); | ||
427 | (void)sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, | ||
428 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | ||
429 | argv[i], argv[argc - 1]); | ||
430 | if (verbose) | ||
431 | fprintf(stderr, "Executing: %s\n", bp); | ||
432 | if (system(bp)) | ||
433 | ++errs; | ||
434 | (void)xfree(bp); | ||
435 | continue; | ||
436 | } | ||
437 | *src++ = 0; | ||
438 | if (*src == 0) | ||
439 | src = "."; | ||
440 | if ((host = strchr(argv[i], '@')) == NULL) { | ||
441 | host = argv[i]; | ||
442 | suser = NULL; | ||
443 | } else { | ||
444 | *host++ = 0; | ||
445 | suser = argv[i]; | ||
446 | if (*suser == '\0') | ||
447 | suser = pwd->pw_name; | ||
448 | else if (!okname(suser)) | ||
449 | continue; | ||
450 | } | ||
451 | len = strlen(src) + CMDNEEDS + 20; | ||
452 | bp = xmalloc(len); | ||
453 | (void)sprintf(bp, "%s -f %s", cmd, src); | ||
454 | if (do_cmd(host, suser, bp, &remin, &remout) < 0) { | ||
455 | (void)xfree(bp); | ||
456 | ++errs; | ||
457 | continue; | ||
458 | } | ||
459 | xfree(bp); | ||
460 | sink(1, argv + argc - 1); | ||
461 | (void)close(remin); | ||
462 | remin = remout = -1; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | void | ||
467 | source(argc, argv) | ||
468 | int argc; | ||
469 | char *argv[]; | ||
470 | { | ||
471 | struct stat stb; | ||
472 | static BUF buffer; | ||
473 | BUF *bp; | ||
474 | off_t i; | ||
475 | int amt, fd, haderr, indx, result; | ||
476 | char *last, *name, buf[2048]; | ||
477 | |||
478 | for (indx = 0; indx < argc; ++indx) { | ||
479 | name = argv[indx]; | ||
480 | statbytes = 0; | ||
481 | if ((fd = open(name, O_RDONLY, 0)) < 0) | ||
482 | goto syserr; | ||
483 | if (fstat(fd, &stb) < 0) { | ||
484 | syserr: run_err("%s: %s", name, strerror(errno)); | ||
485 | goto next; | ||
486 | } | ||
487 | switch (stb.st_mode & S_IFMT) { | ||
488 | case S_IFREG: | ||
489 | break; | ||
490 | case S_IFDIR: | ||
491 | if (iamrecursive) { | ||
492 | rsource(name, &stb); | ||
493 | goto next; | ||
494 | } | ||
495 | /* FALLTHROUGH */ | ||
496 | default: | ||
497 | run_err("%s: not a regular file", name); | ||
498 | goto next; | ||
499 | } | ||
500 | if ((last = strrchr(name, '/')) == NULL) | ||
501 | last = name; | ||
502 | else | ||
503 | ++last; | ||
504 | curfile = last; | ||
505 | if (pflag) { | ||
506 | /* | ||
507 | * Make it compatible with possible future | ||
508 | * versions expecting microseconds. | ||
509 | */ | ||
510 | (void)sprintf(buf, "T%lu 0 %lu 0\n", | ||
511 | (unsigned long)stb.st_mtime, | ||
512 | (unsigned long)stb.st_atime); | ||
513 | (void)write(remout, buf, strlen(buf)); | ||
514 | if (response() < 0) | ||
515 | goto next; | ||
516 | } | ||
517 | #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) | ||
518 | (void)sprintf(buf, "C%04o %lu %s\n", | ||
519 | (unsigned int)(stb.st_mode & FILEMODEMASK), | ||
520 | (unsigned long)stb.st_size, | ||
521 | last); | ||
522 | if (verbose) | ||
523 | { | ||
524 | fprintf(stderr, "Sending file modes: %s", buf); | ||
525 | fflush(stderr); | ||
526 | } | ||
527 | (void)write(remout, buf, strlen(buf)); | ||
528 | if (response() < 0) | ||
529 | goto next; | ||
530 | if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { | ||
531 | next: (void)close(fd); | ||
532 | continue; | ||
533 | } | ||
534 | |||
535 | if (showprogress) { | ||
536 | totalbytes = stb.st_size; | ||
537 | progressmeter(-1); | ||
538 | } | ||
539 | |||
540 | /* Keep writing after an error so that we stay sync'd up. */ | ||
541 | for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { | ||
542 | amt = bp->cnt; | ||
543 | if (i + amt > stb.st_size) | ||
544 | amt = stb.st_size - i; | ||
545 | if (!haderr) { | ||
546 | result = read(fd, bp->buf, amt); | ||
547 | if (result != amt) | ||
548 | haderr = result >= 0 ? EIO : errno; | ||
549 | } | ||
550 | if (haderr) | ||
551 | (void)write(remout, bp->buf, amt); | ||
552 | else { | ||
553 | result = write(remout, bp->buf, amt); | ||
554 | if (result != amt) | ||
555 | haderr = result >= 0 ? EIO : errno; | ||
556 | statbytes += result; | ||
557 | } | ||
558 | } | ||
559 | if(showprogress) | ||
560 | progressmeter(1); | ||
561 | |||
562 | if (close(fd) < 0 && !haderr) | ||
563 | haderr = errno; | ||
564 | if (!haderr) | ||
565 | (void)write(remout, "", 1); | ||
566 | else | ||
567 | run_err("%s: %s", name, strerror(haderr)); | ||
568 | (void)response(); | ||
569 | } | ||
570 | } | ||
571 | |||
572 | void | ||
573 | rsource(name, statp) | ||
574 | char *name; | ||
575 | struct stat *statp; | ||
576 | { | ||
577 | DIR *dirp; | ||
578 | struct dirent *dp; | ||
579 | char *last, *vect[1], path[1100]; | ||
580 | |||
581 | if (!(dirp = opendir(name))) { | ||
582 | run_err("%s: %s", name, strerror(errno)); | ||
583 | return; | ||
584 | } | ||
585 | last = strrchr(name, '/'); | ||
586 | if (last == 0) | ||
587 | last = name; | ||
588 | else | ||
589 | last++; | ||
590 | if (pflag) { | ||
591 | (void)sprintf(path, "T%lu 0 %lu 0\n", | ||
592 | (unsigned long)statp->st_mtime, | ||
593 | (unsigned long)statp->st_atime); | ||
594 | (void)write(remout, path, strlen(path)); | ||
595 | if (response() < 0) { | ||
596 | closedir(dirp); | ||
597 | return; | ||
598 | } | ||
599 | } | ||
600 | (void)sprintf(path, | ||
601 | "D%04o %d %.1024s\n", (unsigned int)(statp->st_mode & FILEMODEMASK), | ||
602 | 0, last); | ||
603 | if (verbose) | ||
604 | fprintf(stderr, "Entering directory: %s", path); | ||
605 | (void)write(remout, path, strlen(path)); | ||
606 | if (response() < 0) { | ||
607 | closedir(dirp); | ||
608 | return; | ||
609 | } | ||
610 | while ((dp = readdir(dirp))) { | ||
611 | if (dp->d_ino == 0) | ||
612 | continue; | ||
613 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | ||
614 | continue; | ||
615 | if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { | ||
616 | run_err("%s/%s: name too long", name, dp->d_name); | ||
617 | continue; | ||
618 | } | ||
619 | (void)sprintf(path, "%s/%s", name, dp->d_name); | ||
620 | vect[0] = path; | ||
621 | source(1, vect); | ||
622 | } | ||
623 | (void)closedir(dirp); | ||
624 | (void)write(remout, "E\n", 2); | ||
625 | (void)response(); | ||
626 | } | ||
627 | |||
628 | void | ||
629 | sink(argc, argv) | ||
630 | int argc; | ||
631 | char *argv[]; | ||
632 | { | ||
633 | static BUF buffer; | ||
634 | struct stat stb; | ||
635 | enum { YES, NO, DISPLAYED } wrerr; | ||
636 | BUF *bp; | ||
637 | off_t i, j; | ||
638 | int amt, count, exists, first, mask, mode, ofd, omode; | ||
639 | int setimes, size, targisdir, wrerrno = 0; | ||
640 | char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; | ||
641 | struct utimbuf ut; | ||
642 | int dummy_usec; | ||
643 | |||
644 | #define SCREWUP(str) { why = str; goto screwup; } | ||
645 | |||
646 | setimes = targisdir = 0; | ||
647 | mask = umask(0); | ||
648 | if (!pflag) | ||
649 | (void)umask(mask); | ||
650 | if (argc != 1) { | ||
651 | run_err("ambiguous target"); | ||
652 | exit(1); | ||
653 | } | ||
654 | targ = *argv; | ||
655 | if (targetshouldbedirectory) | ||
656 | verifydir(targ); | ||
657 | |||
658 | (void)write(remout, "", 1); | ||
659 | if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) | ||
660 | targisdir = 1; | ||
661 | for (first = 1;; first = 0) { | ||
662 | cp = buf; | ||
663 | if (read(remin, cp, 1) <= 0) | ||
664 | return; | ||
665 | if (*cp++ == '\n') | ||
666 | SCREWUP("unexpected <newline>"); | ||
667 | do { | ||
668 | if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) | ||
669 | SCREWUP("lost connection"); | ||
670 | *cp++ = ch; | ||
671 | } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); | ||
672 | *cp = 0; | ||
673 | |||
674 | if (buf[0] == '\01' || buf[0] == '\02') { | ||
675 | if (iamremote == 0) | ||
676 | (void)write(STDERR_FILENO, | ||
677 | buf + 1, strlen(buf + 1)); | ||
678 | if (buf[0] == '\02') | ||
679 | exit(1); | ||
680 | ++errs; | ||
681 | continue; | ||
682 | } | ||
683 | if (buf[0] == 'E') { | ||
684 | (void)write(remout, "", 1); | ||
685 | return; | ||
686 | } | ||
687 | |||
688 | if (ch == '\n') | ||
689 | *--cp = 0; | ||
690 | |||
691 | #define getnum(t) (t) = 0; \ | ||
692 | while (*cp >= '0' && *cp <= '9') (t) = (t) * 10 + (*cp++ - '0'); | ||
693 | cp = buf; | ||
694 | if (*cp == 'T') { | ||
695 | setimes++; | ||
696 | cp++; | ||
697 | getnum(ut.modtime); | ||
698 | if (*cp++ != ' ') | ||
699 | SCREWUP("mtime.sec not delimited"); | ||
700 | getnum(dummy_usec); | ||
701 | if (*cp++ != ' ') | ||
702 | SCREWUP("mtime.usec not delimited"); | ||
703 | getnum(ut.actime); | ||
704 | if (*cp++ != ' ') | ||
705 | SCREWUP("atime.sec not delimited"); | ||
706 | getnum(dummy_usec); | ||
707 | if (*cp++ != '\0') | ||
708 | SCREWUP("atime.usec not delimited"); | ||
709 | (void)write(remout, "", 1); | ||
710 | continue; | ||
711 | } | ||
712 | if (*cp != 'C' && *cp != 'D') { | ||
713 | /* | ||
714 | * Check for the case "rcp remote:foo\* local:bar". | ||
715 | * In this case, the line "No match." can be returned | ||
716 | * by the shell before the rcp command on the remote is | ||
717 | * executed so the ^Aerror_message convention isn't | ||
718 | * followed. | ||
719 | */ | ||
720 | if (first) { | ||
721 | run_err("%s", cp); | ||
722 | exit(1); | ||
723 | } | ||
724 | SCREWUP("expected control record"); | ||
725 | } | ||
726 | mode = 0; | ||
727 | for (++cp; cp < buf + 5; cp++) { | ||
728 | if (*cp < '0' || *cp > '7') | ||
729 | SCREWUP("bad mode"); | ||
730 | mode = (mode << 3) | (*cp - '0'); | ||
731 | } | ||
732 | if (*cp++ != ' ') | ||
733 | SCREWUP("mode not delimited"); | ||
734 | |||
735 | for (size = 0; *cp >= '0' && *cp <= '9';) | ||
736 | size = size * 10 + (*cp++ - '0'); | ||
737 | if (*cp++ != ' ') | ||
738 | SCREWUP("size not delimited"); | ||
739 | if (targisdir) { | ||
740 | static char *namebuf; | ||
741 | static int cursize; | ||
742 | size_t need; | ||
743 | |||
744 | need = strlen(targ) + strlen(cp) + 250; | ||
745 | if (need > cursize) | ||
746 | namebuf = xmalloc(need); | ||
747 | (void)sprintf(namebuf, "%s%s%s", targ, | ||
748 | *targ ? "/" : "", cp); | ||
749 | np = namebuf; | ||
750 | } else | ||
751 | np = targ; | ||
752 | curfile = cp; | ||
753 | exists = stat(np, &stb) == 0; | ||
754 | if (buf[0] == 'D') { | ||
755 | int mod_flag = pflag; | ||
756 | if (exists) { | ||
757 | if (!S_ISDIR(stb.st_mode)) { | ||
758 | errno = ENOTDIR; | ||
759 | goto bad; | ||
760 | } | ||
761 | if (pflag) | ||
762 | (void)chmod(np, mode); | ||
763 | } else { | ||
764 | /* Handle copying from a read-only directory */ | ||
765 | mod_flag = 1; | ||
766 | if (mkdir(np, mode | S_IRWXU) < 0) | ||
767 | goto bad; | ||
768 | } | ||
769 | vect[0] = np; | ||
770 | sink(1, vect); | ||
771 | if (setimes) { | ||
772 | setimes = 0; | ||
773 | if (utime(np, &ut) < 0) | ||
774 | run_err("%s: set times: %s", | ||
775 | np, strerror(errno)); | ||
776 | } | ||
777 | if (mod_flag) | ||
778 | (void)chmod(np, mode); | ||
779 | continue; | ||
780 | } | ||
781 | omode = mode; | ||
782 | mode |= S_IWRITE; | ||
783 | if ((ofd = open(np, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) { | ||
784 | bad: run_err("%s: %s", np, strerror(errno)); | ||
785 | continue; | ||
786 | } | ||
787 | (void)write(remout, "", 1); | ||
788 | if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { | ||
789 | (void)close(ofd); | ||
790 | continue; | ||
791 | } | ||
792 | cp = bp->buf; | ||
793 | wrerr = NO; | ||
794 | |||
795 | if (showprogress) { | ||
796 | totalbytes = size; | ||
797 | progressmeter(-1); | ||
798 | } | ||
799 | statbytes = 0; | ||
800 | for (count = i = 0; i < size; i += 4096) { | ||
801 | amt = 4096; | ||
802 | if (i + amt > size) | ||
803 | amt = size - i; | ||
804 | count += amt; | ||
805 | do { | ||
806 | j = read(remin, cp, amt); | ||
807 | if (j <= 0) { | ||
808 | run_err("%s", j ? strerror(errno) : | ||
809 | "dropped connection"); | ||
810 | exit(1); | ||
811 | } | ||
812 | amt -= j; | ||
813 | cp += j; | ||
814 | statbytes += j; | ||
815 | } while (amt > 0); | ||
816 | if (count == bp->cnt) { | ||
817 | /* Keep reading so we stay sync'd up. */ | ||
818 | if (wrerr == NO) { | ||
819 | j = write(ofd, bp->buf, count); | ||
820 | if (j != count) { | ||
821 | wrerr = YES; | ||
822 | wrerrno = j >= 0 ? EIO : errno; | ||
823 | } | ||
824 | } | ||
825 | count = 0; | ||
826 | cp = bp->buf; | ||
827 | } | ||
828 | } | ||
829 | if (showprogress) | ||
830 | progressmeter(1); | ||
831 | if (count != 0 && wrerr == NO && | ||
832 | (j = write(ofd, bp->buf, count)) != count) { | ||
833 | wrerr = YES; | ||
834 | wrerrno = j >= 0 ? EIO : errno; | ||
835 | } | ||
836 | #if 0 | ||
837 | if (ftruncate(ofd, size)) { | ||
838 | run_err("%s: truncate: %s", np, strerror(errno)); | ||
839 | wrerr = DISPLAYED; | ||
840 | } | ||
841 | #endif | ||
842 | if (pflag) { | ||
843 | if (exists || omode != mode) | ||
844 | if (fchmod(ofd, omode)) | ||
845 | run_err("%s: set mode: %s", | ||
846 | np, strerror(errno)); | ||
847 | } else { | ||
848 | if (!exists && omode != mode) | ||
849 | if (fchmod(ofd, omode & ~mask)) | ||
850 | run_err("%s: set mode: %s", | ||
851 | np, strerror(errno)); | ||
852 | } | ||
853 | (void)close(ofd); | ||
854 | (void)response(); | ||
855 | if (setimes && wrerr == NO) { | ||
856 | setimes = 0; | ||
857 | if (utime(np, &ut) < 0) { | ||
858 | run_err("%s: set times: %s", | ||
859 | np, strerror(errno)); | ||
860 | wrerr = DISPLAYED; | ||
861 | } | ||
862 | } | ||
863 | switch(wrerr) { | ||
864 | case YES: | ||
865 | run_err("%s: %s", np, strerror(wrerrno)); | ||
866 | break; | ||
867 | case NO: | ||
868 | (void)write(remout, "", 1); | ||
869 | break; | ||
870 | case DISPLAYED: | ||
871 | break; | ||
872 | } | ||
873 | } | ||
874 | screwup: | ||
875 | run_err("protocol error: %s", why); | ||
876 | exit(1); | ||
877 | } | ||
878 | |||
879 | int | ||
880 | response() | ||
881 | { | ||
882 | char ch, *cp, resp, rbuf[2048]; | ||
883 | |||
884 | if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) | ||
885 | lostconn(0); | ||
886 | |||
887 | cp = rbuf; | ||
888 | switch(resp) { | ||
889 | case 0: /* ok */ | ||
890 | return (0); | ||
891 | default: | ||
892 | *cp++ = resp; | ||
893 | /* FALLTHROUGH */ | ||
894 | case 1: /* error, followed by error msg */ | ||
895 | case 2: /* fatal error, "" */ | ||
896 | do { | ||
897 | if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) | ||
898 | lostconn(0); | ||
899 | *cp++ = ch; | ||
900 | } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); | ||
901 | |||
902 | if (!iamremote) | ||
903 | (void)write(STDERR_FILENO, rbuf, cp - rbuf); | ||
904 | ++errs; | ||
905 | if (resp == 1) | ||
906 | return (-1); | ||
907 | exit(1); | ||
908 | } | ||
909 | /* NOTREACHED */ | ||
910 | } | ||
911 | |||
912 | void | ||
913 | usage() | ||
914 | { | ||
915 | (void)fprintf(stderr, | ||
916 | "usage: scp [-pqrvC] [-P port] [-c cipher] [-i identity] f1 f2; or:\n scp [options] f1 ... fn directory\n"); | ||
917 | exit(1); | ||
918 | } | ||
919 | |||
920 | void | ||
921 | run_err(const char *fmt, ...) | ||
922 | { | ||
923 | static FILE *fp; | ||
924 | va_list ap; | ||
925 | va_start(ap, fmt); | ||
926 | |||
927 | ++errs; | ||
928 | if (fp == NULL && !(fp = fdopen(remout, "w"))) | ||
929 | return; | ||
930 | (void)fprintf(fp, "%c", 0x01); | ||
931 | (void)fprintf(fp, "scp: "); | ||
932 | (void)vfprintf(fp, fmt, ap); | ||
933 | (void)fprintf(fp, "\n"); | ||
934 | (void)fflush(fp); | ||
935 | |||
936 | if (!iamremote) | ||
937 | { | ||
938 | vfprintf(stderr, fmt, ap); | ||
939 | fprintf(stderr, "\n"); | ||
940 | } | ||
941 | |||
942 | va_end(ap); | ||
943 | } | ||
944 | |||
945 | /* Stuff below is from BSD rcp util.c. */ | ||
946 | |||
947 | /*- | ||
948 | * Copyright (c) 1992, 1993 | ||
949 | * The Regents of the University of California. All rights reserved. | ||
950 | * | ||
951 | * Redistribution and use in source and binary forms, with or without | ||
952 | * modification, are permitted provided that the following conditions | ||
953 | * are met: | ||
954 | * 1. Redistributions of source code must retain the above copyright | ||
955 | * notice, this list of conditions and the following disclaimer. | ||
956 | * 2. Redistributions in binary form must reproduce the above copyright | ||
957 | * notice, this list of conditions and the following disclaimer in the | ||
958 | * documentation and/or other materials provided with the distribution. | ||
959 | * 3. All advertising materials mentioning features or use of this software | ||
960 | * must display the following acknowledgement: | ||
961 | * This product includes software developed by the University of | ||
962 | * California, Berkeley and its contributors. | ||
963 | * 4. Neither the name of the University nor the names of its contributors | ||
964 | * may be used to endorse or promote products derived from this software | ||
965 | * without specific prior written permission. | ||
966 | * | ||
967 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
968 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
969 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
970 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
971 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
972 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
973 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
974 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
975 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
976 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
977 | * SUCH DAMAGE. | ||
978 | * | ||
979 | * $Id: scp.c,v 1.1 1999/10/27 03:42:45 damien Exp $ | ||
980 | */ | ||
981 | |||
982 | char * | ||
983 | colon(cp) | ||
984 | char *cp; | ||
985 | { | ||
986 | if (*cp == ':') /* Leading colon is part of file name. */ | ||
987 | return (0); | ||
988 | |||
989 | for (; *cp; ++cp) { | ||
990 | if (*cp == ':') | ||
991 | return (cp); | ||
992 | if (*cp == '/') | ||
993 | return (0); | ||
994 | } | ||
995 | return (0); | ||
996 | } | ||
997 | |||
998 | void | ||
999 | verifydir(cp) | ||
1000 | char *cp; | ||
1001 | { | ||
1002 | struct stat stb; | ||
1003 | |||
1004 | if (!stat(cp, &stb)) { | ||
1005 | if (S_ISDIR(stb.st_mode)) | ||
1006 | return; | ||
1007 | errno = ENOTDIR; | ||
1008 | } | ||
1009 | run_err("%s: %s", cp, strerror(errno)); | ||
1010 | exit(1); | ||
1011 | } | ||
1012 | |||
1013 | int | ||
1014 | okname(cp0) | ||
1015 | char *cp0; | ||
1016 | { | ||
1017 | int c; | ||
1018 | char *cp; | ||
1019 | |||
1020 | cp = cp0; | ||
1021 | do { | ||
1022 | c = *cp; | ||
1023 | if (c & 0200) | ||
1024 | goto bad; | ||
1025 | if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') | ||
1026 | goto bad; | ||
1027 | } while (*++cp); | ||
1028 | return (1); | ||
1029 | |||
1030 | bad: fprintf(stderr, "%s: invalid user name", cp0); | ||
1031 | return (0); | ||
1032 | } | ||
1033 | |||
1034 | BUF * | ||
1035 | allocbuf(bp, fd, blksize) | ||
1036 | BUF *bp; | ||
1037 | int fd, blksize; | ||
1038 | { | ||
1039 | size_t size; | ||
1040 | struct stat stb; | ||
1041 | |||
1042 | if (fstat(fd, &stb) < 0) { | ||
1043 | run_err("fstat: %s", strerror(errno)); | ||
1044 | return (0); | ||
1045 | } | ||
1046 | if (stb.st_blksize == 0) | ||
1047 | size = blksize; | ||
1048 | else | ||
1049 | size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % | ||
1050 | stb.st_blksize; | ||
1051 | if (bp->cnt >= size) | ||
1052 | return (bp); | ||
1053 | if (bp->buf == NULL) | ||
1054 | bp->buf = xmalloc(size); | ||
1055 | else | ||
1056 | bp->buf = xrealloc(bp->buf, size); | ||
1057 | bp->cnt = size; | ||
1058 | return (bp); | ||
1059 | } | ||
1060 | |||
1061 | void | ||
1062 | lostconn(signo) | ||
1063 | int signo; | ||
1064 | { | ||
1065 | if (!iamremote) | ||
1066 | fprintf(stderr, "lost connection\n"); | ||
1067 | exit(1); | ||
1068 | } | ||
1069 | |||
1070 | /* | ||
1071 | * ensure all of data on socket comes through. f==read || f==write | ||
1072 | */ | ||
1073 | int | ||
1074 | atomicio(f, fd, s, n) | ||
1075 | int (*f)(); | ||
1076 | char *s; | ||
1077 | { | ||
1078 | int res, pos = 0; | ||
1079 | |||
1080 | while (n>pos) { | ||
1081 | res = (f)(fd, s+pos, n-pos); | ||
1082 | switch (res) { | ||
1083 | case -1: | ||
1084 | if (errno==EINTR || errno==EAGAIN) | ||
1085 | continue; | ||
1086 | case 0: | ||
1087 | return (res); | ||
1088 | default: | ||
1089 | pos += res; | ||
1090 | } | ||
1091 | } | ||
1092 | return (pos); | ||
1093 | } | ||
1094 | |||
1095 | void | ||
1096 | alarmtimer(int wait) | ||
1097 | { | ||
1098 | struct itimerval itv; | ||
1099 | |||
1100 | itv.it_value.tv_sec = wait; | ||
1101 | itv.it_value.tv_usec = 0; | ||
1102 | itv.it_interval = itv.it_value; | ||
1103 | setitimer(ITIMER_REAL, &itv, NULL); | ||
1104 | } | ||
1105 | |||
1106 | void | ||
1107 | updateprogressmeter(void) | ||
1108 | { | ||
1109 | int save_errno = errno; | ||
1110 | |||
1111 | progressmeter(0); | ||
1112 | errno = save_errno; | ||
1113 | } | ||
1114 | |||
1115 | void | ||
1116 | progressmeter(int flag) | ||
1117 | { | ||
1118 | static const char prefixes[] = " KMGTP"; | ||
1119 | static struct timeval lastupdate; | ||
1120 | static off_t lastsize; | ||
1121 | struct timeval now, td, wait; | ||
1122 | off_t cursize, abbrevsize; | ||
1123 | double elapsed; | ||
1124 | int ratio, barlength, i, remaining; | ||
1125 | char buf[256]; | ||
1126 | |||
1127 | if (flag == -1) { | ||
1128 | (void)gettimeofday(&start, (struct timezone *)0); | ||
1129 | lastupdate = start; | ||
1130 | lastsize = 0; | ||
1131 | } | ||
1132 | (void)gettimeofday(&now, (struct timezone *)0); | ||
1133 | cursize = statbytes; | ||
1134 | if (totalbytes != 0) { | ||
1135 | ratio = cursize * 100 / totalbytes; | ||
1136 | ratio = MAX(ratio, 0); | ||
1137 | ratio = MIN(ratio, 100); | ||
1138 | } | ||
1139 | else | ||
1140 | ratio = 100; | ||
1141 | |||
1142 | snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); | ||
1143 | |||
1144 | barlength = getttywidth() - 51; | ||
1145 | if (barlength > 0) { | ||
1146 | i = barlength * ratio / 100; | ||
1147 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1148 | "|%.*s%*s|", i, | ||
1149 | "*****************************************************************************" | ||
1150 | "*****************************************************************************", | ||
1151 | barlength - i, ""); | ||
1152 | } | ||
1153 | |||
1154 | i = 0; | ||
1155 | abbrevsize = cursize; | ||
1156 | while (abbrevsize >= 100000 && i < sizeof(prefixes)) { | ||
1157 | i++; | ||
1158 | abbrevsize >>= 10; | ||
1159 | } | ||
1160 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ", | ||
1161 | (quad_t)abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : | ||
1162 | 'B'); | ||
1163 | |||
1164 | timersub(&now, &lastupdate, &wait); | ||
1165 | if (cursize > lastsize) { | ||
1166 | lastupdate = now; | ||
1167 | lastsize = cursize; | ||
1168 | if (wait.tv_sec >= STALLTIME) { | ||
1169 | start.tv_sec += wait.tv_sec; | ||
1170 | start.tv_usec += wait.tv_usec; | ||
1171 | } | ||
1172 | wait.tv_sec = 0; | ||
1173 | } | ||
1174 | |||
1175 | timersub(&now, &start, &td); | ||
1176 | elapsed = td.tv_sec + (td.tv_usec / 1000000.0); | ||
1177 | |||
1178 | if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { | ||
1179 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1180 | " --:-- ETA"); | ||
1181 | } else if (wait.tv_sec >= STALLTIME) { | ||
1182 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1183 | " - stalled -"); | ||
1184 | } else { | ||
1185 | remaining = (int)(totalbytes / (statbytes / elapsed) - elapsed); | ||
1186 | i = elapsed / 3600; | ||
1187 | if (i) | ||
1188 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1189 | "%2d:", i); | ||
1190 | else | ||
1191 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1192 | " "); | ||
1193 | i = remaining % 3600; | ||
1194 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1195 | "%02d:%02d ETA", i / 60, i % 60); | ||
1196 | } | ||
1197 | atomicio(write, fileno(stdout), buf, strlen(buf)); | ||
1198 | |||
1199 | if (flag == -1) { | ||
1200 | signal(SIGALRM, (void *)updateprogressmeter); | ||
1201 | alarmtimer(1); | ||
1202 | } else if (flag == 1) { | ||
1203 | alarmtimer(0); | ||
1204 | write(fileno(stdout), "\n", 1); | ||
1205 | statbytes = 0; | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | int | ||
1210 | getttywidth(void) | ||
1211 | { | ||
1212 | struct winsize winsize; | ||
1213 | |||
1214 | if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) | ||
1215 | return(winsize.ws_col ? winsize.ws_col : 80); | ||
1216 | else | ||
1217 | return(80); | ||
1218 | } | ||
1219 | |||
1220 | |||
diff --git a/servconf.c b/servconf.c new file mode 100644 index 000000000..5fa48790f --- /dev/null +++ b/servconf.c | |||
@@ -0,0 +1,567 @@ | |||
1 | /* | ||
2 | |||
3 | servconf.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Aug 21 15:48:58 1995 ylo | ||
11 | |||
12 | */ | ||
13 | |||
14 | #include "includes.h" | ||
15 | RCSID("$Id: servconf.c,v 1.1 1999/10/27 03:42:45 damien Exp $"); | ||
16 | |||
17 | #include "ssh.h" | ||
18 | #include "servconf.h" | ||
19 | #include "xmalloc.h" | ||
20 | |||
21 | /* Initializes the server options to their default values. */ | ||
22 | |||
23 | void initialize_server_options(ServerOptions *options) | ||
24 | { | ||
25 | memset(options, 0, sizeof(*options)); | ||
26 | options->port = -1; | ||
27 | options->listen_addr.s_addr = htonl(INADDR_ANY); | ||
28 | options->host_key_file = NULL; | ||
29 | options->server_key_bits = -1; | ||
30 | options->login_grace_time = -1; | ||
31 | options->key_regeneration_time = -1; | ||
32 | options->permit_root_login = -1; | ||
33 | options->ignore_rhosts = -1; | ||
34 | options->quiet_mode = -1; | ||
35 | options->fascist_logging = -1; | ||
36 | options->print_motd = -1; | ||
37 | options->check_mail = -1; | ||
38 | options->x11_forwarding = -1; | ||
39 | options->x11_display_offset = -1; | ||
40 | options->strict_modes = -1; | ||
41 | options->keepalives = -1; | ||
42 | options->log_facility = (SyslogFacility)-1; | ||
43 | options->rhosts_authentication = -1; | ||
44 | options->rhosts_rsa_authentication = -1; | ||
45 | options->rsa_authentication = -1; | ||
46 | #ifdef KRB4 | ||
47 | options->kerberos_authentication = -1; | ||
48 | options->kerberos_or_local_passwd = -1; | ||
49 | options->kerberos_ticket_cleanup = -1; | ||
50 | #endif | ||
51 | #ifdef AFS | ||
52 | options->kerberos_tgt_passing = -1; | ||
53 | options->afs_token_passing = -1; | ||
54 | #endif | ||
55 | options->password_authentication = -1; | ||
56 | #ifdef SKEY | ||
57 | options->skey_authentication = -1; | ||
58 | #endif | ||
59 | options->permit_empty_passwd = -1; | ||
60 | options->use_login = -1; | ||
61 | options->num_allow_users = 0; | ||
62 | options->num_deny_users = 0; | ||
63 | options->num_allow_groups = 0; | ||
64 | options->num_deny_groups = 0; | ||
65 | } | ||
66 | |||
67 | void fill_default_server_options(ServerOptions *options) | ||
68 | { | ||
69 | if (options->port == -1) | ||
70 | { | ||
71 | struct servent *sp; | ||
72 | |||
73 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | ||
74 | if (sp) | ||
75 | options->port = ntohs(sp->s_port); | ||
76 | else | ||
77 | options->port = SSH_DEFAULT_PORT; | ||
78 | endservent(); | ||
79 | } | ||
80 | if (options->host_key_file == NULL) | ||
81 | options->host_key_file = HOST_KEY_FILE; | ||
82 | if (options->server_key_bits == -1) | ||
83 | options->server_key_bits = 768; | ||
84 | if (options->login_grace_time == -1) | ||
85 | options->login_grace_time = 600; | ||
86 | if (options->key_regeneration_time == -1) | ||
87 | options->key_regeneration_time = 3600; | ||
88 | if (options->permit_root_login == -1) | ||
89 | options->permit_root_login = 1; /* yes */ | ||
90 | if (options->ignore_rhosts == -1) | ||
91 | options->ignore_rhosts = 0; | ||
92 | if (options->quiet_mode == -1) | ||
93 | options->quiet_mode = 0; | ||
94 | if (options->check_mail == -1) | ||
95 | options->check_mail = 0; | ||
96 | if (options->fascist_logging == -1) | ||
97 | options->fascist_logging = 1; | ||
98 | if (options->print_motd == -1) | ||
99 | options->print_motd = 1; | ||
100 | if (options->x11_forwarding == -1) | ||
101 | options->x11_forwarding = 1; | ||
102 | if (options->x11_display_offset == -1) | ||
103 | options->x11_display_offset = 1; | ||
104 | if (options->strict_modes == -1) | ||
105 | options->strict_modes = 1; | ||
106 | if (options->keepalives == -1) | ||
107 | options->keepalives = 1; | ||
108 | if (options->log_facility == (SyslogFacility)(-1)) | ||
109 | options->log_facility = SYSLOG_FACILITY_AUTH; | ||
110 | if (options->rhosts_authentication == -1) | ||
111 | options->rhosts_authentication = 0; | ||
112 | if (options->rhosts_rsa_authentication == -1) | ||
113 | options->rhosts_rsa_authentication = 1; | ||
114 | if (options->rsa_authentication == -1) | ||
115 | options->rsa_authentication = 1; | ||
116 | #ifdef KRB4 | ||
117 | if (options->kerberos_authentication == -1) | ||
118 | options->kerberos_authentication = (access(KEYFILE, R_OK) == 0); | ||
119 | if (options->kerberos_or_local_passwd == -1) | ||
120 | options->kerberos_or_local_passwd = 1; | ||
121 | if (options->kerberos_ticket_cleanup == -1) | ||
122 | options->kerberos_ticket_cleanup = 1; | ||
123 | #endif /* KRB4 */ | ||
124 | #ifdef AFS | ||
125 | if (options->kerberos_tgt_passing == -1) | ||
126 | options->kerberos_tgt_passing = 0; | ||
127 | if (options->afs_token_passing == -1) | ||
128 | options->afs_token_passing = k_hasafs(); | ||
129 | #endif /* AFS */ | ||
130 | if (options->password_authentication == -1) | ||
131 | options->password_authentication = 1; | ||
132 | #ifdef SKEY | ||
133 | if (options->skey_authentication == -1) | ||
134 | options->skey_authentication = 1; | ||
135 | #endif | ||
136 | if (options->permit_empty_passwd == -1) | ||
137 | options->permit_empty_passwd = 1; | ||
138 | if (options->use_login == -1) | ||
139 | options->use_login = 0; | ||
140 | } | ||
141 | |||
142 | #define WHITESPACE " \t\r\n" | ||
143 | |||
144 | /* Keyword tokens. */ | ||
145 | typedef enum | ||
146 | { | ||
147 | sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, | ||
148 | sPermitRootLogin, sQuietMode, sFascistLogging, sLogFacility, | ||
149 | sRhostsAuthentication, sRhostsRSAAuthentication, sRSAAuthentication, | ||
150 | #ifdef KRB4 | ||
151 | sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, | ||
152 | #endif | ||
153 | #ifdef AFS | ||
154 | sKerberosTgtPassing, sAFSTokenPassing, | ||
155 | #endif | ||
156 | #ifdef SKEY | ||
157 | sSkeyAuthentication, | ||
158 | #endif | ||
159 | sPasswordAuthentication, sListenAddress, | ||
160 | sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, | ||
161 | sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail, | ||
162 | sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups | ||
163 | |||
164 | } ServerOpCodes; | ||
165 | |||
166 | /* Textual representation of the tokens. */ | ||
167 | static struct | ||
168 | { | ||
169 | const char *name; | ||
170 | ServerOpCodes opcode; | ||
171 | } keywords[] = | ||
172 | { | ||
173 | { "port", sPort }, | ||
174 | { "hostkey", sHostKeyFile }, | ||
175 | { "serverkeybits", sServerKeyBits }, | ||
176 | { "logingracetime", sLoginGraceTime }, | ||
177 | { "keyregenerationinterval", sKeyRegenerationTime }, | ||
178 | { "permitrootlogin", sPermitRootLogin }, | ||
179 | { "quietmode", sQuietMode }, | ||
180 | { "fascistlogging", sFascistLogging }, | ||
181 | { "syslogfacility", sLogFacility }, | ||
182 | { "rhostsauthentication", sRhostsAuthentication }, | ||
183 | { "rhostsrsaauthentication", sRhostsRSAAuthentication }, | ||
184 | { "rsaauthentication", sRSAAuthentication }, | ||
185 | #ifdef KRB4 | ||
186 | { "kerberosauthentication", sKerberosAuthentication }, | ||
187 | { "kerberosorlocalpasswd", sKerberosOrLocalPasswd }, | ||
188 | { "kerberosticketcleanup", sKerberosTicketCleanup }, | ||
189 | #endif | ||
190 | #ifdef AFS | ||
191 | { "kerberostgtpassing", sKerberosTgtPassing }, | ||
192 | { "afstokenpassing", sAFSTokenPassing }, | ||
193 | #endif | ||
194 | { "passwordauthentication", sPasswordAuthentication }, | ||
195 | #ifdef SKEY | ||
196 | { "skeyauthentication", sSkeyAuthentication }, | ||
197 | #endif | ||
198 | { "checkmail", sCheckMail }, | ||
199 | { "listenaddress", sListenAddress }, | ||
200 | { "printmotd", sPrintMotd }, | ||
201 | { "ignorerhosts", sIgnoreRhosts }, | ||
202 | { "x11forwarding", sX11Forwarding }, | ||
203 | { "x11displayoffset", sX11DisplayOffset }, | ||
204 | { "strictmodes", sStrictModes }, | ||
205 | { "permitemptypasswords", sEmptyPasswd }, | ||
206 | { "uselogin", sUseLogin }, | ||
207 | { "randomseed", sRandomSeedFile }, | ||
208 | { "keepalive", sKeepAlives }, | ||
209 | { "allowusers", sAllowUsers }, | ||
210 | { "denyusers", sDenyUsers }, | ||
211 | { "allowgroups", sAllowGroups }, | ||
212 | { "denygroups", sDenyGroups }, | ||
213 | { NULL, 0 } | ||
214 | }; | ||
215 | |||
216 | static struct | ||
217 | { | ||
218 | const char *name; | ||
219 | SyslogFacility facility; | ||
220 | } log_facilities[] = | ||
221 | { | ||
222 | { "DAEMON", SYSLOG_FACILITY_DAEMON }, | ||
223 | { "USER", SYSLOG_FACILITY_USER }, | ||
224 | { "AUTH", SYSLOG_FACILITY_AUTH }, | ||
225 | { "LOCAL0", SYSLOG_FACILITY_LOCAL0 }, | ||
226 | { "LOCAL1", SYSLOG_FACILITY_LOCAL1 }, | ||
227 | { "LOCAL2", SYSLOG_FACILITY_LOCAL2 }, | ||
228 | { "LOCAL3", SYSLOG_FACILITY_LOCAL3 }, | ||
229 | { "LOCAL4", SYSLOG_FACILITY_LOCAL4 }, | ||
230 | { "LOCAL5", SYSLOG_FACILITY_LOCAL5 }, | ||
231 | { "LOCAL6", SYSLOG_FACILITY_LOCAL6 }, | ||
232 | { "LOCAL7", SYSLOG_FACILITY_LOCAL7 }, | ||
233 | { NULL, 0 } | ||
234 | }; | ||
235 | |||
236 | /* Returns the number of the token pointed to by cp of length len. | ||
237 | Never returns if the token is not known. */ | ||
238 | |||
239 | static ServerOpCodes parse_token(const char *cp, const char *filename, | ||
240 | int linenum) | ||
241 | { | ||
242 | unsigned int i; | ||
243 | |||
244 | for (i = 0; keywords[i].name; i++) | ||
245 | if (strcmp(cp, keywords[i].name) == 0) | ||
246 | return keywords[i].opcode; | ||
247 | |||
248 | fprintf(stderr, "%s line %d: Bad configuration option: %s\n", | ||
249 | filename, linenum, cp); | ||
250 | exit(1); | ||
251 | } | ||
252 | |||
253 | /* Reads the server configuration file. */ | ||
254 | |||
255 | void read_server_config(ServerOptions *options, const char *filename) | ||
256 | { | ||
257 | FILE *f; | ||
258 | char line[1024]; | ||
259 | char *cp, **charptr; | ||
260 | int linenum, *intptr, i, value; | ||
261 | ServerOpCodes opcode; | ||
262 | |||
263 | f = fopen(filename, "r"); | ||
264 | if (!f) | ||
265 | { | ||
266 | perror(filename); | ||
267 | exit(1); | ||
268 | } | ||
269 | |||
270 | linenum = 0; | ||
271 | while (fgets(line, sizeof(line), f)) | ||
272 | { | ||
273 | linenum++; | ||
274 | cp = line + strspn(line, WHITESPACE); | ||
275 | if (!*cp || *cp == '#') | ||
276 | continue; | ||
277 | cp = strtok(cp, WHITESPACE); | ||
278 | { | ||
279 | char *t = cp; | ||
280 | for (; *t != 0; t++) | ||
281 | if ('A' <= *t && *t <= 'Z') | ||
282 | *t = *t - 'A' + 'a'; /* tolower */ | ||
283 | |||
284 | } | ||
285 | opcode = parse_token(cp, filename, linenum); | ||
286 | switch (opcode) | ||
287 | { | ||
288 | case sPort: | ||
289 | intptr = &options->port; | ||
290 | parse_int: | ||
291 | cp = strtok(NULL, WHITESPACE); | ||
292 | if (!cp) | ||
293 | { | ||
294 | fprintf(stderr, "%s line %d: missing integer value.\n", | ||
295 | filename, linenum); | ||
296 | exit(1); | ||
297 | } | ||
298 | value = atoi(cp); | ||
299 | if (*intptr == -1) | ||
300 | *intptr = value; | ||
301 | break; | ||
302 | |||
303 | case sServerKeyBits: | ||
304 | intptr = &options->server_key_bits; | ||
305 | goto parse_int; | ||
306 | |||
307 | case sLoginGraceTime: | ||
308 | intptr = &options->login_grace_time; | ||
309 | goto parse_int; | ||
310 | |||
311 | case sKeyRegenerationTime: | ||
312 | intptr = &options->key_regeneration_time; | ||
313 | goto parse_int; | ||
314 | |||
315 | case sListenAddress: | ||
316 | cp = strtok(NULL, WHITESPACE); | ||
317 | if (!cp) | ||
318 | { | ||
319 | fprintf(stderr, "%s line %d: missing inet addr.\n", | ||
320 | filename, linenum); | ||
321 | exit(1); | ||
322 | } | ||
323 | options->listen_addr.s_addr = inet_addr(cp); | ||
324 | break; | ||
325 | |||
326 | case sHostKeyFile: | ||
327 | charptr = &options->host_key_file; | ||
328 | cp = strtok(NULL, WHITESPACE); | ||
329 | if (!cp) | ||
330 | { | ||
331 | fprintf(stderr, "%s line %d: missing file name.\n", | ||
332 | filename, linenum); | ||
333 | exit(1); | ||
334 | } | ||
335 | if (*charptr == NULL) | ||
336 | *charptr = tilde_expand_filename(cp, getuid()); | ||
337 | break; | ||
338 | |||
339 | case sRandomSeedFile: | ||
340 | fprintf(stderr, "%s line %d: \"randomseed\" option is obsolete.\n", | ||
341 | filename, linenum); | ||
342 | cp = strtok(NULL, WHITESPACE); | ||
343 | break; | ||
344 | |||
345 | case sPermitRootLogin: | ||
346 | intptr = &options->permit_root_login; | ||
347 | cp = strtok(NULL, WHITESPACE); | ||
348 | if (!cp) | ||
349 | { | ||
350 | fprintf(stderr, "%s line %d: missing yes/without-password/no argument.\n", | ||
351 | filename, linenum); | ||
352 | exit(1); | ||
353 | } | ||
354 | if (strcmp(cp, "without-password") == 0) | ||
355 | value = 2; | ||
356 | else if (strcmp(cp, "yes") == 0) | ||
357 | value = 1; | ||
358 | else if (strcmp(cp, "no") == 0) | ||
359 | value = 0; | ||
360 | else | ||
361 | { | ||
362 | fprintf(stderr, "%s line %d: Bad yes/without-password/no argument: %s\n", | ||
363 | filename, linenum, cp); | ||
364 | exit(1); | ||
365 | } | ||
366 | if (*intptr == -1) | ||
367 | *intptr = value; | ||
368 | break; | ||
369 | |||
370 | case sIgnoreRhosts: | ||
371 | intptr = &options->ignore_rhosts; | ||
372 | parse_flag: | ||
373 | cp = strtok(NULL, WHITESPACE); | ||
374 | if (!cp) | ||
375 | { | ||
376 | fprintf(stderr, "%s line %d: missing yes/no argument.\n", | ||
377 | filename, linenum); | ||
378 | exit(1); | ||
379 | } | ||
380 | if (strcmp(cp, "yes") == 0) | ||
381 | value = 1; | ||
382 | else | ||
383 | if (strcmp(cp, "no") == 0) | ||
384 | value = 0; | ||
385 | else | ||
386 | { | ||
387 | fprintf(stderr, "%s line %d: Bad yes/no argument: %s\n", | ||
388 | filename, linenum, cp); | ||
389 | exit(1); | ||
390 | } | ||
391 | if (*intptr == -1) | ||
392 | *intptr = value; | ||
393 | break; | ||
394 | |||
395 | case sQuietMode: | ||
396 | intptr = &options->quiet_mode; | ||
397 | goto parse_flag; | ||
398 | |||
399 | case sFascistLogging: | ||
400 | intptr = &options->fascist_logging; | ||
401 | goto parse_flag; | ||
402 | |||
403 | case sRhostsAuthentication: | ||
404 | intptr = &options->rhosts_authentication; | ||
405 | goto parse_flag; | ||
406 | |||
407 | case sRhostsRSAAuthentication: | ||
408 | intptr = &options->rhosts_rsa_authentication; | ||
409 | goto parse_flag; | ||
410 | |||
411 | case sRSAAuthentication: | ||
412 | intptr = &options->rsa_authentication; | ||
413 | goto parse_flag; | ||
414 | |||
415 | #ifdef KRB4 | ||
416 | case sKerberosAuthentication: | ||
417 | intptr = &options->kerberos_authentication; | ||
418 | goto parse_flag; | ||
419 | |||
420 | case sKerberosOrLocalPasswd: | ||
421 | intptr = &options->kerberos_or_local_passwd; | ||
422 | goto parse_flag; | ||
423 | |||
424 | case sKerberosTicketCleanup: | ||
425 | intptr = &options->kerberos_ticket_cleanup; | ||
426 | goto parse_flag; | ||
427 | #endif | ||
428 | |||
429 | #ifdef AFS | ||
430 | case sKerberosTgtPassing: | ||
431 | intptr = &options->kerberos_tgt_passing; | ||
432 | goto parse_flag; | ||
433 | |||
434 | case sAFSTokenPassing: | ||
435 | intptr = &options->afs_token_passing; | ||
436 | goto parse_flag; | ||
437 | #endif | ||
438 | |||
439 | case sPasswordAuthentication: | ||
440 | intptr = &options->password_authentication; | ||
441 | goto parse_flag; | ||
442 | |||
443 | case sCheckMail: | ||
444 | intptr = &options->check_mail; | ||
445 | goto parse_flag; | ||
446 | |||
447 | #ifdef SKEY | ||
448 | case sSkeyAuthentication: | ||
449 | intptr = &options->skey_authentication; | ||
450 | goto parse_flag; | ||
451 | #endif | ||
452 | |||
453 | case sPrintMotd: | ||
454 | intptr = &options->print_motd; | ||
455 | goto parse_flag; | ||
456 | |||
457 | case sX11Forwarding: | ||
458 | intptr = &options->x11_forwarding; | ||
459 | goto parse_flag; | ||
460 | |||
461 | case sX11DisplayOffset: | ||
462 | intptr = &options->x11_display_offset; | ||
463 | goto parse_int; | ||
464 | |||
465 | case sStrictModes: | ||
466 | intptr = &options->strict_modes; | ||
467 | goto parse_flag; | ||
468 | |||
469 | case sKeepAlives: | ||
470 | intptr = &options->keepalives; | ||
471 | goto parse_flag; | ||
472 | |||
473 | case sEmptyPasswd: | ||
474 | intptr = &options->permit_empty_passwd; | ||
475 | goto parse_flag; | ||
476 | |||
477 | case sUseLogin: | ||
478 | intptr = &options->use_login; | ||
479 | goto parse_flag; | ||
480 | |||
481 | case sLogFacility: | ||
482 | cp = strtok(NULL, WHITESPACE); | ||
483 | if (!cp) | ||
484 | { | ||
485 | fprintf(stderr, "%s line %d: missing facility name.\n", | ||
486 | filename, linenum); | ||
487 | exit(1); | ||
488 | } | ||
489 | for (i = 0; log_facilities[i].name; i++) | ||
490 | if (strcmp(log_facilities[i].name, cp) == 0) | ||
491 | break; | ||
492 | if (!log_facilities[i].name) | ||
493 | { | ||
494 | fprintf(stderr, "%s line %d: unsupported log facility %s\n", | ||
495 | filename, linenum, cp); | ||
496 | exit(1); | ||
497 | } | ||
498 | if (options->log_facility == (SyslogFacility)(-1)) | ||
499 | options->log_facility = log_facilities[i].facility; | ||
500 | break; | ||
501 | |||
502 | case sAllowUsers: | ||
503 | while ((cp = strtok(NULL, WHITESPACE))) | ||
504 | { | ||
505 | if (options->num_allow_users >= MAX_ALLOW_USERS) | ||
506 | { | ||
507 | fprintf(stderr, "%s line %d: too many allow users.\n", | ||
508 | filename, linenum); | ||
509 | exit(1); | ||
510 | } | ||
511 | options->allow_users[options->num_allow_users++] = xstrdup(cp); | ||
512 | } | ||
513 | break; | ||
514 | |||
515 | case sDenyUsers: | ||
516 | while ((cp = strtok(NULL, WHITESPACE))) | ||
517 | { | ||
518 | if (options->num_deny_users >= MAX_DENY_USERS) | ||
519 | { | ||
520 | fprintf(stderr, "%s line %d: too many deny users.\n", | ||
521 | filename, linenum); | ||
522 | exit(1); | ||
523 | } | ||
524 | options->deny_users[options->num_deny_users++] = xstrdup(cp); | ||
525 | } | ||
526 | break; | ||
527 | |||
528 | case sAllowGroups: | ||
529 | while ((cp = strtok(NULL, WHITESPACE))) | ||
530 | { | ||
531 | if (options->num_allow_groups >= MAX_ALLOW_GROUPS) | ||
532 | { | ||
533 | fprintf(stderr, "%s line %d: too many allow groups.\n", | ||
534 | filename, linenum); | ||
535 | exit(1); | ||
536 | } | ||
537 | options->allow_groups[options->num_allow_groups++] = xstrdup(cp); | ||
538 | } | ||
539 | break; | ||
540 | |||
541 | case sDenyGroups: | ||
542 | while ((cp = strtok(NULL, WHITESPACE))) | ||
543 | { | ||
544 | if (options->num_deny_groups >= MAX_DENY_GROUPS) | ||
545 | { | ||
546 | fprintf(stderr, "%s line %d: too many deny groups.\n", | ||
547 | filename, linenum); | ||
548 | exit(1); | ||
549 | } | ||
550 | options->deny_groups[options->num_deny_groups++] = xstrdup(cp); | ||
551 | } | ||
552 | break; | ||
553 | |||
554 | default: | ||
555 | fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n", | ||
556 | filename, linenum, cp, opcode); | ||
557 | exit(1); | ||
558 | } | ||
559 | if (strtok(NULL, WHITESPACE) != NULL) | ||
560 | { | ||
561 | fprintf(stderr, "%s line %d: garbage at end of line.\n", | ||
562 | filename, linenum); | ||
563 | exit(1); | ||
564 | } | ||
565 | } | ||
566 | fclose(f); | ||
567 | } | ||
diff --git a/servconf.h b/servconf.h new file mode 100644 index 000000000..22c73fd73 --- /dev/null +++ b/servconf.h | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | |||
3 | servconf.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Aug 21 15:35:03 1995 ylo | ||
11 | |||
12 | Definitions for server configuration data and for the functions reading it. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: servconf.h,v 1.1 1999/10/27 03:42:45 damien Exp $"); */ | ||
17 | |||
18 | #ifndef SERVCONF_H | ||
19 | #define SERVCONF_H | ||
20 | |||
21 | #define MAX_ALLOW_USERS 256 /* Max # users on allow list. */ | ||
22 | #define MAX_DENY_USERS 256 /* Max # users on deny list. */ | ||
23 | #define MAX_ALLOW_GROUPS 256 /* Max # groups on allow list. */ | ||
24 | #define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */ | ||
25 | |||
26 | typedef struct | ||
27 | { | ||
28 | int port; /* Port number to listen on. */ | ||
29 | struct in_addr listen_addr; /* Address on which the server listens. */ | ||
30 | char *host_key_file; /* File containing host key. */ | ||
31 | int server_key_bits; /* Size of the server key. */ | ||
32 | int login_grace_time; /* Disconnect if no auth in this time (sec). */ | ||
33 | int key_regeneration_time; /* Server key lifetime (seconds). */ | ||
34 | int permit_root_login; /* If true, permit root login. */ | ||
35 | int ignore_rhosts; /* Ignore .rhosts and .shosts. */ | ||
36 | int quiet_mode; /* If true, don't log anything but fatals. */ | ||
37 | int fascist_logging; /* Perform very verbose logging. */ | ||
38 | int print_motd; /* If true, print /etc/motd. */ | ||
39 | int check_mail; /* If true, check for new mail. */ | ||
40 | int x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */ | ||
41 | int x11_display_offset; /* What DISPLAY number to start searching at */ | ||
42 | int strict_modes; /* If true, require string home dir modes. */ | ||
43 | int keepalives; /* If true, set SO_KEEPALIVE. */ | ||
44 | SyslogFacility log_facility; /* Facility for system logging. */ | ||
45 | int rhosts_authentication; /* If true, permit rhosts authentication. */ | ||
46 | int rhosts_rsa_authentication;/* If true, permit rhosts RSA authentication.*/ | ||
47 | int rsa_authentication; /* If true, permit RSA authentication. */ | ||
48 | #ifdef KRB4 | ||
49 | int kerberos_authentication; /* If true, permit Kerberos authentication. */ | ||
50 | int kerberos_or_local_passwd; /* If true, permit kerberos and any other | ||
51 | password authentication mechanism, such | ||
52 | as SecurID or /etc/passwd */ | ||
53 | int kerberos_ticket_cleanup; /* If true, destroy ticket file on logout. */ | ||
54 | #endif | ||
55 | #ifdef AFS | ||
56 | int kerberos_tgt_passing; /* If true, permit Kerberos tgt passing. */ | ||
57 | int afs_token_passing; /* If true, permit AFS token passing. */ | ||
58 | #endif | ||
59 | int password_authentication; /* If true, permit password authentication. */ | ||
60 | #ifdef SKEY | ||
61 | int skey_authentication; /* If true, permit s/key authentication. */ | ||
62 | #endif | ||
63 | int permit_empty_passwd; /* If false, do not permit empty passwords. */ | ||
64 | int use_login; /* If true, login(1) is used */ | ||
65 | unsigned int num_allow_users; | ||
66 | char *allow_users[MAX_ALLOW_USERS]; | ||
67 | unsigned int num_deny_users; | ||
68 | char *deny_users[MAX_DENY_USERS]; | ||
69 | unsigned int num_allow_groups; | ||
70 | char *allow_groups[MAX_ALLOW_GROUPS]; | ||
71 | unsigned int num_deny_groups; | ||
72 | char *deny_groups[MAX_DENY_GROUPS]; | ||
73 | } ServerOptions; | ||
74 | |||
75 | /* Initializes the server options to special values that indicate that they | ||
76 | have not yet been set. */ | ||
77 | void initialize_server_options(ServerOptions *options); | ||
78 | |||
79 | /* Reads the server configuration file. This only sets the values for those | ||
80 | options that have the special value indicating they have not been set. */ | ||
81 | void read_server_config(ServerOptions *options, const char *filename); | ||
82 | |||
83 | /* Sets values for those values that have not yet been set. */ | ||
84 | void fill_default_server_options(ServerOptions *options); | ||
85 | |||
86 | #endif /* SERVCONF_H */ | ||
diff --git a/serverloop.c b/serverloop.c new file mode 100644 index 000000000..552c69c29 --- /dev/null +++ b/serverloop.c | |||
@@ -0,0 +1,644 @@ | |||
1 | /* | ||
2 | |||
3 | serverloop.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sun Sep 10 00:30:37 1995 ylo | ||
11 | |||
12 | Server main loop for handling the interactive session. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | #include "xmalloc.h" | ||
18 | #include "ssh.h" | ||
19 | #include "packet.h" | ||
20 | #include "buffer.h" | ||
21 | #include "servconf.h" | ||
22 | #include "pty.h" | ||
23 | |||
24 | static Buffer stdin_buffer; /* Buffer for stdin data. */ | ||
25 | static Buffer stdout_buffer; /* Buffer for stdout data. */ | ||
26 | static Buffer stderr_buffer; /* Buffer for stderr data. */ | ||
27 | static int fdin; /* Descriptor for stdin (for writing) */ | ||
28 | static int fdout; /* Descriptor for stdout (for reading); | ||
29 | May be same number as fdin. */ | ||
30 | static int fderr; /* Descriptor for stderr. May be -1. */ | ||
31 | static long stdin_bytes = 0; /* Number of bytes written to stdin. */ | ||
32 | static long stdout_bytes = 0; /* Number of stdout bytes sent to client. */ | ||
33 | static long stderr_bytes = 0; /* Number of stderr bytes sent to client. */ | ||
34 | static long fdout_bytes = 0; /* Number of stdout bytes read from program. */ | ||
35 | static int stdin_eof = 0; /* EOF message received from client. */ | ||
36 | static int fdout_eof = 0; /* EOF encountered reading from fdout. */ | ||
37 | static int fderr_eof = 0; /* EOF encountered readung from fderr. */ | ||
38 | static int connection_in; /* Connection to client (input). */ | ||
39 | static int connection_out; /* Connection to client (output). */ | ||
40 | static unsigned int buffer_high;/* "Soft" max buffer size. */ | ||
41 | static int max_fd; /* Max file descriptor number for select(). */ | ||
42 | |||
43 | /* This SIGCHLD kludge is used to detect when the child exits. The server | ||
44 | will exit after that, as soon as forwarded connections have terminated. */ | ||
45 | |||
46 | static int child_pid; /* Pid of the child. */ | ||
47 | static volatile int child_terminated; /* The child has terminated. */ | ||
48 | static volatile int child_wait_status; /* Status from wait(). */ | ||
49 | |||
50 | void sigchld_handler(int sig) | ||
51 | { | ||
52 | int save_errno = errno; | ||
53 | int wait_pid; | ||
54 | debug("Received SIGCHLD."); | ||
55 | wait_pid = wait((int *)&child_wait_status); | ||
56 | if (wait_pid != -1) | ||
57 | { | ||
58 | if (wait_pid != child_pid) | ||
59 | error("Strange, got SIGCHLD and wait returned pid %d but child is %d", | ||
60 | wait_pid, child_pid); | ||
61 | if (WIFEXITED(child_wait_status) || | ||
62 | WIFSIGNALED(child_wait_status)) | ||
63 | child_terminated = 1; | ||
64 | } | ||
65 | signal(SIGCHLD, sigchld_handler); | ||
66 | errno = save_errno; | ||
67 | } | ||
68 | |||
69 | /* Process any buffered packets that have been received from the client. */ | ||
70 | |||
71 | void process_buffered_input_packets() | ||
72 | { | ||
73 | int type; | ||
74 | char *data; | ||
75 | unsigned int data_len; | ||
76 | int row, col, xpixel, ypixel; | ||
77 | int payload_len; | ||
78 | |||
79 | /* Process buffered packets from the client. */ | ||
80 | while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) | ||
81 | { | ||
82 | switch (type) | ||
83 | { | ||
84 | case SSH_CMSG_STDIN_DATA: | ||
85 | /* Stdin data from the client. Append it to the buffer. */ | ||
86 | if (fdin == -1) | ||
87 | break; /* Ignore any data if the client has closed stdin. */ | ||
88 | data = packet_get_string(&data_len); | ||
89 | packet_integrity_check(payload_len, (4 + data_len), type); | ||
90 | buffer_append(&stdin_buffer, data, data_len); | ||
91 | memset(data, 0, data_len); | ||
92 | xfree(data); | ||
93 | break; | ||
94 | |||
95 | case SSH_CMSG_EOF: | ||
96 | /* Eof from the client. The stdin descriptor to the program | ||
97 | will be closed when all buffered data has drained. */ | ||
98 | debug("EOF received for stdin."); | ||
99 | packet_integrity_check(payload_len, 0, type); | ||
100 | stdin_eof = 1; | ||
101 | break; | ||
102 | |||
103 | case SSH_CMSG_WINDOW_SIZE: | ||
104 | debug("Window change received."); | ||
105 | packet_integrity_check(payload_len, 4*4, type); | ||
106 | row = packet_get_int(); | ||
107 | col = packet_get_int(); | ||
108 | xpixel = packet_get_int(); | ||
109 | ypixel = packet_get_int(); | ||
110 | if (fdin != -1) | ||
111 | pty_change_window_size(fdin, row, col, xpixel, ypixel); | ||
112 | break; | ||
113 | |||
114 | case SSH_MSG_PORT_OPEN: | ||
115 | debug("Received port open request."); | ||
116 | channel_input_port_open(payload_len); | ||
117 | break; | ||
118 | |||
119 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: | ||
120 | debug("Received channel open confirmation."); | ||
121 | packet_integrity_check(payload_len, 4 + 4, type); | ||
122 | channel_input_open_confirmation(); | ||
123 | break; | ||
124 | |||
125 | case SSH_MSG_CHANNEL_OPEN_FAILURE: | ||
126 | debug("Received channel open failure."); | ||
127 | packet_integrity_check(payload_len, 4, type); | ||
128 | channel_input_open_failure(); | ||
129 | break; | ||
130 | |||
131 | case SSH_MSG_CHANNEL_DATA: | ||
132 | channel_input_data(payload_len); | ||
133 | break; | ||
134 | |||
135 | case SSH_MSG_CHANNEL_CLOSE: | ||
136 | debug("Received channel close."); | ||
137 | packet_integrity_check(payload_len, 4, type); | ||
138 | channel_input_close(); | ||
139 | break; | ||
140 | |||
141 | case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: | ||
142 | debug("Received channel close confirmation."); | ||
143 | packet_integrity_check(payload_len, 4, type); | ||
144 | channel_input_close_confirmation(); | ||
145 | break; | ||
146 | |||
147 | default: | ||
148 | /* In this phase, any unexpected messages cause a protocol | ||
149 | error. This is to ease debugging; also, since no | ||
150 | confirmations are sent messages, unprocessed unknown | ||
151 | messages could cause strange problems. Any compatible | ||
152 | protocol extensions must be negotiated before entering the | ||
153 | interactive session. */ | ||
154 | packet_disconnect("Protocol error during session: type %d", | ||
155 | type); | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | /* Make packets from buffered stderr data, and buffer it for sending | ||
161 | to the client. */ | ||
162 | |||
163 | void make_packets_from_stderr_data() | ||
164 | { | ||
165 | int len; | ||
166 | |||
167 | /* Send buffered stderr data to the client. */ | ||
168 | while (buffer_len(&stderr_buffer) > 0 && | ||
169 | packet_not_very_much_data_to_write()) | ||
170 | { | ||
171 | len = buffer_len(&stderr_buffer); | ||
172 | if (packet_is_interactive()) | ||
173 | { | ||
174 | if (len > 512) | ||
175 | len = 512; | ||
176 | } | ||
177 | else | ||
178 | { | ||
179 | if (len > 32768) | ||
180 | len = 32768; /* Keep the packets at reasonable size. */ | ||
181 | } | ||
182 | packet_start(SSH_SMSG_STDERR_DATA); | ||
183 | packet_put_string(buffer_ptr(&stderr_buffer), len); | ||
184 | packet_send(); | ||
185 | buffer_consume(&stderr_buffer, len); | ||
186 | stderr_bytes += len; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | /* Make packets from buffered stdout data, and buffer it for sending to the | ||
191 | client. */ | ||
192 | |||
193 | void make_packets_from_stdout_data() | ||
194 | { | ||
195 | int len; | ||
196 | |||
197 | /* Send buffered stdout data to the client. */ | ||
198 | while (buffer_len(&stdout_buffer) > 0 && | ||
199 | packet_not_very_much_data_to_write()) | ||
200 | { | ||
201 | len = buffer_len(&stdout_buffer); | ||
202 | if (packet_is_interactive()) | ||
203 | { | ||
204 | if (len > 512) | ||
205 | len = 512; | ||
206 | } | ||
207 | else | ||
208 | { | ||
209 | if (len > 32768) | ||
210 | len = 32768; /* Keep the packets at reasonable size. */ | ||
211 | } | ||
212 | packet_start(SSH_SMSG_STDOUT_DATA); | ||
213 | packet_put_string(buffer_ptr(&stdout_buffer), len); | ||
214 | packet_send(); | ||
215 | buffer_consume(&stdout_buffer, len); | ||
216 | stdout_bytes += len; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | /* Sleep in select() until we can do something. This will initialize the | ||
221 | select masks. Upon return, the masks will indicate which descriptors | ||
222 | have data or can accept data. Optionally, a maximum time can be specified | ||
223 | for the duration of the wait (0 = infinite). */ | ||
224 | |||
225 | void wait_until_can_do_something(fd_set *readset, fd_set *writeset, | ||
226 | unsigned int max_time_milliseconds) | ||
227 | { | ||
228 | struct timeval tv, *tvp; | ||
229 | int ret; | ||
230 | |||
231 | /* When select fails we restart from here. */ | ||
232 | retry_select: | ||
233 | |||
234 | /* Initialize select() masks. */ | ||
235 | FD_ZERO(readset); | ||
236 | |||
237 | /* Read packets from the client unless we have too much buffered stdin | ||
238 | or channel data. */ | ||
239 | if (buffer_len(&stdin_buffer) < 4096 && | ||
240 | channel_not_very_much_buffered_data()) | ||
241 | FD_SET(connection_in, readset); | ||
242 | |||
243 | /* If there is not too much data already buffered going to the client, | ||
244 | try to get some more data from the program. */ | ||
245 | if (packet_not_very_much_data_to_write()) | ||
246 | { | ||
247 | if (!fdout_eof) | ||
248 | FD_SET(fdout, readset); | ||
249 | if (!fderr_eof) | ||
250 | FD_SET(fderr, readset); | ||
251 | } | ||
252 | |||
253 | FD_ZERO(writeset); | ||
254 | |||
255 | /* Set masks for channel descriptors. */ | ||
256 | channel_prepare_select(readset, writeset); | ||
257 | |||
258 | /* If we have buffered packet data going to the client, mark that | ||
259 | descriptor. */ | ||
260 | if (packet_have_data_to_write()) | ||
261 | FD_SET(connection_out, writeset); | ||
262 | |||
263 | /* If we have buffered data, try to write some of that data to the | ||
264 | program. */ | ||
265 | if (fdin != -1 && buffer_len(&stdin_buffer) > 0) | ||
266 | FD_SET(fdin, writeset); | ||
267 | |||
268 | /* Update the maximum descriptor number if appropriate. */ | ||
269 | if (channel_max_fd() > max_fd) | ||
270 | max_fd = channel_max_fd(); | ||
271 | |||
272 | /* If child has terminated, read as much as is available and then exit. */ | ||
273 | if (child_terminated) | ||
274 | if (max_time_milliseconds == 0) | ||
275 | max_time_milliseconds = 100; | ||
276 | |||
277 | if (max_time_milliseconds == 0) | ||
278 | tvp = NULL; | ||
279 | else | ||
280 | { | ||
281 | tv.tv_sec = max_time_milliseconds / 1000; | ||
282 | tv.tv_usec = 1000 * (max_time_milliseconds % 1000); | ||
283 | tvp = &tv; | ||
284 | } | ||
285 | |||
286 | /* Wait for something to happen, or the timeout to expire. */ | ||
287 | ret = select(max_fd + 1, readset, writeset, NULL, tvp); | ||
288 | |||
289 | if (ret < 0) | ||
290 | { | ||
291 | if (errno != EINTR) | ||
292 | error("select: %.100s", strerror(errno)); | ||
293 | else | ||
294 | goto retry_select; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | /* Processes input from the client and the program. Input data is stored | ||
299 | in buffers and processed later. */ | ||
300 | |||
301 | void process_input(fd_set *readset) | ||
302 | { | ||
303 | int len; | ||
304 | char buf[16384]; | ||
305 | |||
306 | /* Read and buffer any input data from the client. */ | ||
307 | if (FD_ISSET(connection_in, readset)) | ||
308 | { | ||
309 | len = read(connection_in, buf, sizeof(buf)); | ||
310 | if (len == 0) | ||
311 | fatal("Connection closed by remote host."); | ||
312 | |||
313 | /* There is a kernel bug on Solaris that causes select to sometimes | ||
314 | wake up even though there is no data available. */ | ||
315 | if (len < 0 && errno == EAGAIN) | ||
316 | len = 0; | ||
317 | |||
318 | if (len < 0) | ||
319 | fatal("Read error from remote host: %.100s", strerror(errno)); | ||
320 | |||
321 | /* Buffer any received data. */ | ||
322 | packet_process_incoming(buf, len); | ||
323 | } | ||
324 | |||
325 | /* Read and buffer any available stdout data from the program. */ | ||
326 | if (!fdout_eof && FD_ISSET(fdout, readset)) | ||
327 | { | ||
328 | len = read(fdout, buf, sizeof(buf)); | ||
329 | if (len <= 0) | ||
330 | fdout_eof = 1; | ||
331 | else | ||
332 | { | ||
333 | buffer_append(&stdout_buffer, buf, len); | ||
334 | fdout_bytes += len; | ||
335 | } | ||
336 | } | ||
337 | |||
338 | /* Read and buffer any available stderr data from the program. */ | ||
339 | if (!fderr_eof && FD_ISSET(fderr, readset)) | ||
340 | { | ||
341 | len = read(fderr, buf, sizeof(buf)); | ||
342 | if (len <= 0) | ||
343 | fderr_eof = 1; | ||
344 | else | ||
345 | buffer_append(&stderr_buffer, buf, len); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | /* Sends data from internal buffers to client program stdin. */ | ||
350 | |||
351 | void process_output(fd_set *writeset) | ||
352 | { | ||
353 | int len; | ||
354 | |||
355 | /* Write buffered data to program stdin. */ | ||
356 | if (fdin != -1 && FD_ISSET(fdin, writeset)) | ||
357 | { | ||
358 | len = write(fdin, buffer_ptr(&stdin_buffer), | ||
359 | buffer_len(&stdin_buffer)); | ||
360 | if (len <= 0) | ||
361 | { | ||
362 | #ifdef USE_PIPES | ||
363 | close(fdin); | ||
364 | #else | ||
365 | if (fdout == -1) | ||
366 | close(fdin); | ||
367 | else | ||
368 | shutdown(fdin, SHUT_WR); /* We will no longer send. */ | ||
369 | #endif | ||
370 | fdin = -1; | ||
371 | } | ||
372 | else | ||
373 | { | ||
374 | /* Successful write. Consume the data from the buffer. */ | ||
375 | buffer_consume(&stdin_buffer, len); | ||
376 | /* Update the count of bytes written to the program. */ | ||
377 | stdin_bytes += len; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | /* Send any buffered packet data to the client. */ | ||
382 | if (FD_ISSET(connection_out, writeset)) | ||
383 | packet_write_poll(); | ||
384 | } | ||
385 | |||
386 | /* Wait until all buffered output has been sent to the client. | ||
387 | This is used when the program terminates. */ | ||
388 | |||
389 | void drain_output() | ||
390 | { | ||
391 | /* Send any buffered stdout data to the client. */ | ||
392 | if (buffer_len(&stdout_buffer) > 0) | ||
393 | { | ||
394 | packet_start(SSH_SMSG_STDOUT_DATA); | ||
395 | packet_put_string(buffer_ptr(&stdout_buffer), | ||
396 | buffer_len(&stdout_buffer)); | ||
397 | packet_send(); | ||
398 | /* Update the count of sent bytes. */ | ||
399 | stdout_bytes += buffer_len(&stdout_buffer); | ||
400 | } | ||
401 | |||
402 | /* Send any buffered stderr data to the client. */ | ||
403 | if (buffer_len(&stderr_buffer) > 0) | ||
404 | { | ||
405 | packet_start(SSH_SMSG_STDERR_DATA); | ||
406 | packet_put_string(buffer_ptr(&stderr_buffer), | ||
407 | buffer_len(&stderr_buffer)); | ||
408 | packet_send(); | ||
409 | /* Update the count of sent bytes. */ | ||
410 | stderr_bytes += buffer_len(&stderr_buffer); | ||
411 | } | ||
412 | |||
413 | /* Wait until all buffered data has been written to the client. */ | ||
414 | packet_write_wait(); | ||
415 | } | ||
416 | |||
417 | /* Performs the interactive session. This handles data transmission between | ||
418 | the client and the program. Note that the notion of stdin, stdout, and | ||
419 | stderr in this function is sort of reversed: this function writes to | ||
420 | stdin (of the child program), and reads from stdout and stderr (of the | ||
421 | child program). */ | ||
422 | |||
423 | void server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) | ||
424 | { | ||
425 | int wait_status, wait_pid; /* Status and pid returned by wait(). */ | ||
426 | int waiting_termination = 0; /* Have displayed waiting close message. */ | ||
427 | unsigned int max_time_milliseconds; | ||
428 | unsigned int previous_stdout_buffer_bytes; | ||
429 | unsigned int stdout_buffer_bytes; | ||
430 | int type; | ||
431 | |||
432 | debug("Entering interactive session."); | ||
433 | |||
434 | /* Initialize the SIGCHLD kludge. */ | ||
435 | child_pid = pid; | ||
436 | child_terminated = 0; | ||
437 | signal(SIGCHLD, sigchld_handler); | ||
438 | |||
439 | /* Initialize our global variables. */ | ||
440 | fdin = fdin_arg; | ||
441 | fdout = fdout_arg; | ||
442 | fderr = fderr_arg; | ||
443 | connection_in = packet_get_connection_in(); | ||
444 | connection_out = packet_get_connection_out(); | ||
445 | |||
446 | previous_stdout_buffer_bytes = 0; | ||
447 | |||
448 | /* Set approximate I/O buffer size. */ | ||
449 | if (packet_is_interactive()) | ||
450 | buffer_high = 4096; | ||
451 | else | ||
452 | buffer_high = 64 * 1024; | ||
453 | |||
454 | /* Initialize max_fd to the maximum of the known file descriptors. */ | ||
455 | max_fd = fdin; | ||
456 | if (fdout > max_fd) | ||
457 | max_fd = fdout; | ||
458 | if (fderr != -1 && fderr > max_fd) | ||
459 | max_fd = fderr; | ||
460 | if (connection_in > max_fd) | ||
461 | max_fd = connection_in; | ||
462 | if (connection_out > max_fd) | ||
463 | max_fd = connection_out; | ||
464 | |||
465 | /* Initialize Initialize buffers. */ | ||
466 | buffer_init(&stdin_buffer); | ||
467 | buffer_init(&stdout_buffer); | ||
468 | buffer_init(&stderr_buffer); | ||
469 | |||
470 | /* If we have no separate fderr (which is the case when we have a pty - there | ||
471 | we cannot make difference between data sent to stdout and stderr), | ||
472 | indicate that we have seen an EOF from stderr. This way we don\'t | ||
473 | need to check the descriptor everywhere. */ | ||
474 | if (fderr == -1) | ||
475 | fderr_eof = 1; | ||
476 | |||
477 | /* Main loop of the server for the interactive session mode. */ | ||
478 | for (;;) | ||
479 | { | ||
480 | fd_set readset, writeset; | ||
481 | |||
482 | /* Process buffered packets from the client. */ | ||
483 | process_buffered_input_packets(); | ||
484 | |||
485 | /* If we have received eof, and there is no more pending input data, | ||
486 | cause a real eof by closing fdin. */ | ||
487 | if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) | ||
488 | { | ||
489 | #ifdef USE_PIPES | ||
490 | close(fdin); | ||
491 | #else | ||
492 | if (fdout == -1) | ||
493 | close(fdin); | ||
494 | else | ||
495 | shutdown(fdin, SHUT_WR); /* We will no longer send. */ | ||
496 | #endif | ||
497 | fdin = -1; | ||
498 | } | ||
499 | |||
500 | /* Make packets from buffered stderr data to send to the client. */ | ||
501 | make_packets_from_stderr_data(); | ||
502 | |||
503 | /* Make packets from buffered stdout data to send to the client. | ||
504 | If there is very little to send, this arranges to not send them | ||
505 | now, but to wait a short while to see if we are getting more data. | ||
506 | This is necessary, as some systems wake up readers from a pty after | ||
507 | each separate character. */ | ||
508 | max_time_milliseconds = 0; | ||
509 | stdout_buffer_bytes = buffer_len(&stdout_buffer); | ||
510 | if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && | ||
511 | stdout_buffer_bytes != previous_stdout_buffer_bytes) | ||
512 | max_time_milliseconds = 10; /* try again after a while */ | ||
513 | else | ||
514 | make_packets_from_stdout_data(); /* Send it now. */ | ||
515 | previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); | ||
516 | |||
517 | /* Send channel data to the client. */ | ||
518 | if (packet_not_very_much_data_to_write()) | ||
519 | channel_output_poll(); | ||
520 | |||
521 | /* Bail out of the loop if the program has closed its output descriptors, | ||
522 | and we have no more data to send to the client, and there is no | ||
523 | pending buffered data. */ | ||
524 | if (fdout_eof && fderr_eof && !packet_have_data_to_write() && | ||
525 | buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) | ||
526 | { | ||
527 | if (!channel_still_open()) | ||
528 | goto quit; | ||
529 | if (!waiting_termination) | ||
530 | { | ||
531 | const char *s = | ||
532 | "Waiting for forwarded connections to terminate...\r\n"; | ||
533 | char *cp; | ||
534 | waiting_termination = 1; | ||
535 | buffer_append(&stderr_buffer, s, strlen(s)); | ||
536 | |||
537 | /* Display list of open channels. */ | ||
538 | cp = channel_open_message(); | ||
539 | buffer_append(&stderr_buffer, cp, strlen(cp)); | ||
540 | xfree(cp); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | /* Sleep in select() until we can do something. */ | ||
545 | wait_until_can_do_something(&readset, &writeset, | ||
546 | max_time_milliseconds); | ||
547 | |||
548 | /* Process any channel events. */ | ||
549 | channel_after_select(&readset, &writeset); | ||
550 | |||
551 | /* Process input from the client and from program stdout/stderr. */ | ||
552 | process_input(&readset); | ||
553 | |||
554 | /* Process output to the client and to program stdin. */ | ||
555 | process_output(&writeset); | ||
556 | } | ||
557 | |||
558 | quit: | ||
559 | /* Cleanup and termination code. */ | ||
560 | |||
561 | /* Wait until all output has been sent to the client. */ | ||
562 | drain_output(); | ||
563 | |||
564 | debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", | ||
565 | stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); | ||
566 | |||
567 | /* Free and clear the buffers. */ | ||
568 | buffer_free(&stdin_buffer); | ||
569 | buffer_free(&stdout_buffer); | ||
570 | buffer_free(&stderr_buffer); | ||
571 | |||
572 | /* Close the file descriptors. */ | ||
573 | if (fdout != -1) | ||
574 | close(fdout); | ||
575 | fdout = -1; | ||
576 | fdout_eof = 1; | ||
577 | if (fderr != -1) | ||
578 | close(fderr); | ||
579 | fderr = -1; | ||
580 | fderr_eof = 1; | ||
581 | if (fdin != -1) | ||
582 | close(fdin); | ||
583 | fdin = -1; | ||
584 | |||
585 | /* Stop listening for channels; this removes unix domain sockets. */ | ||
586 | channel_stop_listening(); | ||
587 | |||
588 | /* Wait for the child to exit. Get its exit status. */ | ||
589 | wait_pid = wait(&wait_status); | ||
590 | if (wait_pid < 0) | ||
591 | { | ||
592 | /* It is possible that the wait was handled by SIGCHLD handler. This | ||
593 | may result in either: this call returning with EINTR, or: this | ||
594 | call returning ECHILD. */ | ||
595 | if (child_terminated) | ||
596 | wait_status = child_wait_status; | ||
597 | else | ||
598 | packet_disconnect("wait: %.100s", strerror(errno)); | ||
599 | } | ||
600 | else | ||
601 | { | ||
602 | /* Check if it matches the process we forked. */ | ||
603 | if (wait_pid != pid) | ||
604 | error("Strange, wait returned pid %d, expected %d", wait_pid, pid); | ||
605 | } | ||
606 | |||
607 | /* We no longer want our SIGCHLD handler to be called. */ | ||
608 | signal(SIGCHLD, SIG_DFL); | ||
609 | |||
610 | /* Check if it exited normally. */ | ||
611 | if (WIFEXITED(wait_status)) | ||
612 | { | ||
613 | /* Yes, normal exit. Get exit status and send it to the client. */ | ||
614 | debug("Command exited with status %d.", WEXITSTATUS(wait_status)); | ||
615 | packet_start(SSH_SMSG_EXITSTATUS); | ||
616 | packet_put_int(WEXITSTATUS(wait_status)); | ||
617 | packet_send(); | ||
618 | packet_write_wait(); | ||
619 | |||
620 | /* Wait for exit confirmation. Note that there might be other | ||
621 | packets coming before it; however, the program has already died | ||
622 | so we just ignore them. The client is supposed to respond with | ||
623 | the confirmation when it receives the exit status. */ | ||
624 | do | ||
625 | { | ||
626 | int plen; | ||
627 | type = packet_read(&plen); | ||
628 | } | ||
629 | while (type != SSH_CMSG_EXIT_CONFIRMATION); | ||
630 | |||
631 | debug("Received exit confirmation."); | ||
632 | return; | ||
633 | } | ||
634 | |||
635 | /* Check if the program terminated due to a signal. */ | ||
636 | if (WIFSIGNALED(wait_status)) | ||
637 | packet_disconnect("Command terminated on signal %d.", | ||
638 | WTERMSIG(wait_status)); | ||
639 | |||
640 | /* Some weird exit cause. Just exit. */ | ||
641 | packet_disconnect("wait returned status %04x.", wait_status); | ||
642 | /*NOTREACHED*/ | ||
643 | } | ||
644 | |||
diff --git a/ssh-add.1 b/ssh-add.1 new file mode 100644 index 000000000..4c64ab2b3 --- /dev/null +++ b/ssh-add.1 | |||
@@ -0,0 +1,116 @@ | |||
1 | .\" -*- nroff -*- | ||
2 | .\" | ||
3 | .\" ssh-add.1 | ||
4 | .\" | ||
5 | .\" Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | .\" | ||
7 | .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | .\" All rights reserved | ||
9 | .\" | ||
10 | .\" Created: Sat Apr 22 23:55:14 1995 ylo | ||
11 | .\" | ||
12 | .\" $Id: ssh-add.1,v 1.1 1999/10/27 03:42:45 damien Exp $ | ||
13 | .\" | ||
14 | .Dd September 25, 1999 | ||
15 | .Dt SSH-ADD 1 | ||
16 | .Os | ||
17 | .Sh NAME | ||
18 | .Nm ssh-add | ||
19 | .Nd adds identities for the authentication agent | ||
20 | .Sh SYNOPSIS | ||
21 | .Nm ssh-add | ||
22 | .Op Fl ldD | ||
23 | .Op Ar | ||
24 | .Sh DESCRIPTION | ||
25 | .Nm | ||
26 | adds identities to the authentication agent, | ||
27 | .Xr ssh-agent 1 . | ||
28 | When run without arguments, it adds the file | ||
29 | .Pa $HOME/.ssh/identity . | ||
30 | Alternative file names can be given on the | ||
31 | command line. If any file requires a passphrase, | ||
32 | .Nm | ||
33 | asks for the passphrase from the user. | ||
34 | The Passphrase it is read from the user's tty. | ||
35 | .Pp | ||
36 | The authentication agent must be running and must be an ancestor of | ||
37 | the current process for | ||
38 | .Nm | ||
39 | to work. | ||
40 | .Pp | ||
41 | The options are as follows: | ||
42 | .Bl -tag -width Ds | ||
43 | .It Fl l | ||
44 | Lists all identities currently represented by the agent. | ||
45 | .It Fl d | ||
46 | Instead of adding the identity, removes the identity from the agent. | ||
47 | .It Fl D | ||
48 | Deletes all identities from the agent. | ||
49 | .El | ||
50 | .Sh FILES | ||
51 | .Bl -tag -width Ds | ||
52 | .Pa $HOME/.ssh/identity | ||
53 | Contains the RSA authentication identity of the user. This file | ||
54 | should not be readable by anyone but the user. | ||
55 | Note that | ||
56 | .Nm | ||
57 | ignores this file if it is accessible by others. | ||
58 | It is possible to | ||
59 | specify a passphrase when generating the key; that passphrase will be | ||
60 | used to encrypt the private part of this file. This is the | ||
61 | default file added by | ||
62 | .Nm | ||
63 | when no other files have been specified. | ||
64 | .Pp | ||
65 | If | ||
66 | .Nm | ||
67 | needs a passphrase, it will read the passphrase from the current | ||
68 | terminal if it was run from a terminal. If | ||
69 | .Nm | ||
70 | does not have a terminal associated with it but | ||
71 | .Ev DISPLAY | ||
72 | is set, it | ||
73 | will open an X11 window to read the passphrase. This is particularly | ||
74 | useful when calling | ||
75 | .Nm | ||
76 | from a | ||
77 | .Pa .Xsession | ||
78 | or related script. (Note that on some machines it | ||
79 | may be necessary to redirect the input from | ||
80 | .Pa /dev/null | ||
81 | to make this work.) | ||
82 | .Sh AUTHOR | ||
83 | Tatu Ylonen <ylo@cs.hut.fi> | ||
84 | .Pp | ||
85 | OpenSSH | ||
86 | is a derivative of the original (free) ssh 1.2.12 release, but with bugs | ||
87 | removed and newer features re-added. Rapidly after the 1.2.12 release, | ||
88 | newer versions bore successively more restrictive licenses. This version | ||
89 | of OpenSSH | ||
90 | .Bl -bullet | ||
91 | .It | ||
92 | has all components of a restrictive nature (ie. patents, see | ||
93 | .Xr ssl 8 ) | ||
94 | directly removed from the source code; any licensed or patented components | ||
95 | are chosen from | ||
96 | external libraries. | ||
97 | .It | ||
98 | has been updated to support ssh protocol 1.5. | ||
99 | .It | ||
100 | contains added support for | ||
101 | .Xr kerberos 8 | ||
102 | authentication and ticket passing. | ||
103 | .It | ||
104 | supports one-time password authentication with | ||
105 | .Xr skey 1 . | ||
106 | .El | ||
107 | .Pp | ||
108 | The libraries described in | ||
109 | .Xr ssl 8 | ||
110 | are required for proper operation. | ||
111 | .Sh SEE ALSO | ||
112 | .Xr ssh 1 , | ||
113 | .Xr ssh-agent 1 , | ||
114 | .Xr ssh-keygen 1 , | ||
115 | .Xr sshd 8 , | ||
116 | .Xr ssl 8 | ||
diff --git a/ssh-add.c b/ssh-add.c new file mode 100644 index 000000000..5ac3c303a --- /dev/null +++ b/ssh-add.c | |||
@@ -0,0 +1,254 @@ | |||
1 | /* | ||
2 | |||
3 | ssh-add.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Thu Apr 6 00:52:24 1995 ylo | ||
11 | |||
12 | Adds an identity to the authentication server, or removes an identity. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: ssh-add.c,v 1.1 1999/10/27 03:42:45 damien Exp $"); | ||
18 | |||
19 | #include "rsa.h" | ||
20 | #include "ssh.h" | ||
21 | #include "xmalloc.h" | ||
22 | #include "authfd.h" | ||
23 | |||
24 | void | ||
25 | delete_file(const char *filename) | ||
26 | { | ||
27 | RSA *key; | ||
28 | char *comment; | ||
29 | AuthenticationConnection *ac; | ||
30 | |||
31 | key = RSA_new(); | ||
32 | if (!load_public_key(filename, key, &comment)) | ||
33 | { | ||
34 | printf("Bad key file %s: %s\n", filename, strerror(errno)); | ||
35 | return; | ||
36 | } | ||
37 | |||
38 | /* Send the request to the authentication agent. */ | ||
39 | ac = ssh_get_authentication_connection(); | ||
40 | if (!ac) | ||
41 | { | ||
42 | fprintf(stderr, | ||
43 | "Could not open a connection to your authentication agent.\n"); | ||
44 | RSA_free(key); | ||
45 | xfree(comment); | ||
46 | return; | ||
47 | } | ||
48 | if (ssh_remove_identity(ac, key)) | ||
49 | fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); | ||
50 | else | ||
51 | fprintf(stderr, "Could not remove identity: %s\n", filename); | ||
52 | RSA_free(key); | ||
53 | xfree(comment); | ||
54 | ssh_close_authentication_connection(ac); | ||
55 | } | ||
56 | |||
57 | void | ||
58 | delete_all() | ||
59 | { | ||
60 | AuthenticationConnection *ac; | ||
61 | |||
62 | /* Get a connection to the agent. */ | ||
63 | ac = ssh_get_authentication_connection(); | ||
64 | if (!ac) | ||
65 | { | ||
66 | fprintf(stderr, | ||
67 | "Could not open a connection to your authentication agent.\n"); | ||
68 | return; | ||
69 | } | ||
70 | |||
71 | /* Send a request to remove all identities. */ | ||
72 | if (ssh_remove_all_identities(ac)) | ||
73 | fprintf(stderr, "All identities removed.\n"); | ||
74 | else | ||
75 | fprintf(stderr, "Failed to remove all identitities.\n"); | ||
76 | |||
77 | /* Close the connection to the agent. */ | ||
78 | ssh_close_authentication_connection(ac); | ||
79 | } | ||
80 | |||
81 | void | ||
82 | add_file(const char *filename) | ||
83 | { | ||
84 | RSA *key; | ||
85 | RSA *public_key; | ||
86 | AuthenticationConnection *ac; | ||
87 | char *saved_comment, *comment, *pass; | ||
88 | int first; | ||
89 | |||
90 | key = RSA_new(); | ||
91 | public_key = RSA_new(); | ||
92 | if (!load_public_key(filename, public_key, &saved_comment)) | ||
93 | { | ||
94 | printf("Bad key file %s: %s\n", filename, strerror(errno)); | ||
95 | return; | ||
96 | } | ||
97 | RSA_free(public_key); | ||
98 | |||
99 | pass = xstrdup(""); | ||
100 | first = 1; | ||
101 | while (!load_private_key(filename, pass, key, &comment)) | ||
102 | { | ||
103 | /* Free the old passphrase. */ | ||
104 | memset(pass, 0, strlen(pass)); | ||
105 | xfree(pass); | ||
106 | |||
107 | /* Ask for a passphrase. */ | ||
108 | if (getenv("DISPLAY") && !isatty(fileno(stdin))) | ||
109 | { | ||
110 | xfree(saved_comment); | ||
111 | return; | ||
112 | } | ||
113 | else | ||
114 | { | ||
115 | if (first) | ||
116 | printf("Need passphrase for %s (%s).\n", filename, saved_comment); | ||
117 | else | ||
118 | printf("Bad passphrase.\n"); | ||
119 | pass = read_passphrase("Enter passphrase: ", 1); | ||
120 | if (strcmp(pass, "") == 0) | ||
121 | { | ||
122 | xfree(saved_comment); | ||
123 | xfree(pass); | ||
124 | return; | ||
125 | } | ||
126 | } | ||
127 | first = 0; | ||
128 | } | ||
129 | memset(pass, 0, strlen(pass)); | ||
130 | xfree(pass); | ||
131 | |||
132 | xfree(saved_comment); | ||
133 | |||
134 | /* Send the key to the authentication agent. */ | ||
135 | ac = ssh_get_authentication_connection(); | ||
136 | if (!ac) | ||
137 | { | ||
138 | fprintf(stderr, | ||
139 | "Could not open a connection to your authentication agent.\n"); | ||
140 | RSA_free(key); | ||
141 | xfree(comment); | ||
142 | return; | ||
143 | } | ||
144 | if (ssh_add_identity(ac, key, comment)) | ||
145 | fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); | ||
146 | else | ||
147 | fprintf(stderr, "Could not add identity: %s\n", filename); | ||
148 | RSA_free(key); | ||
149 | xfree(comment); | ||
150 | ssh_close_authentication_connection(ac); | ||
151 | } | ||
152 | |||
153 | void | ||
154 | list_identities() | ||
155 | { | ||
156 | AuthenticationConnection *ac; | ||
157 | BIGNUM *e, *n; | ||
158 | int bits, status; | ||
159 | char *comment; | ||
160 | int had_identities; | ||
161 | |||
162 | ac = ssh_get_authentication_connection(); | ||
163 | if (!ac) | ||
164 | { | ||
165 | fprintf(stderr, "Could not connect to authentication server.\n"); | ||
166 | return; | ||
167 | } | ||
168 | e = BN_new(); | ||
169 | n = BN_new(); | ||
170 | had_identities = 0; | ||
171 | for (status = ssh_get_first_identity(ac, &bits, e, n, &comment); | ||
172 | status; | ||
173 | status = ssh_get_next_identity(ac, &bits, e, n, &comment)) | ||
174 | { | ||
175 | char *buf; | ||
176 | had_identities = 1; | ||
177 | printf("%d ", bits); | ||
178 | buf = BN_bn2dec(e); | ||
179 | assert(buf != NULL); | ||
180 | printf("%s ", buf); | ||
181 | free (buf); | ||
182 | buf = BN_bn2dec(n); | ||
183 | assert(buf != NULL); | ||
184 | printf("%s %s\n", buf, comment); | ||
185 | free (buf); | ||
186 | xfree(comment); | ||
187 | } | ||
188 | BN_clear_free(e); | ||
189 | BN_clear_free(n); | ||
190 | if (!had_identities) | ||
191 | printf("The agent has no identities.\n"); | ||
192 | ssh_close_authentication_connection(ac); | ||
193 | } | ||
194 | |||
195 | int | ||
196 | main(int ac, char **av) | ||
197 | { | ||
198 | struct passwd *pw; | ||
199 | char buf[1024]; | ||
200 | int no_files = 1; | ||
201 | int i; | ||
202 | int deleting = 0; | ||
203 | |||
204 | /* check if RSA support exists */ | ||
205 | if (rsa_alive() == 0) { | ||
206 | extern char *__progname; | ||
207 | |||
208 | fprintf(stderr, | ||
209 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | ||
210 | __progname); | ||
211 | exit(1); | ||
212 | } | ||
213 | |||
214 | for (i = 1; i < ac; i++) | ||
215 | { | ||
216 | if (strcmp(av[i], "-l") == 0) | ||
217 | { | ||
218 | list_identities(); | ||
219 | no_files = 0; /* Don't default-add/delete if -l. */ | ||
220 | continue; | ||
221 | } | ||
222 | if (strcmp(av[i], "-d") == 0) | ||
223 | { | ||
224 | deleting = 1; | ||
225 | continue; | ||
226 | } | ||
227 | if (strcmp(av[i], "-D") == 0) | ||
228 | { | ||
229 | delete_all(); | ||
230 | no_files = 0; | ||
231 | continue; | ||
232 | } | ||
233 | no_files = 0; | ||
234 | if (deleting) | ||
235 | delete_file(av[i]); | ||
236 | else | ||
237 | add_file(av[i]); | ||
238 | } | ||
239 | if (no_files) | ||
240 | { | ||
241 | pw = getpwuid(getuid()); | ||
242 | if (!pw) | ||
243 | { | ||
244 | fprintf(stderr, "No user found with uid %d\n", (int)getuid()); | ||
245 | exit(1); | ||
246 | } | ||
247 | snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); | ||
248 | if (deleting) | ||
249 | delete_file(buf); | ||
250 | else | ||
251 | add_file(buf); | ||
252 | } | ||
253 | exit(0); | ||
254 | } | ||
diff --git a/ssh-agent.1 b/ssh-agent.1 new file mode 100644 index 000000000..01c43cdee --- /dev/null +++ b/ssh-agent.1 | |||
@@ -0,0 +1,124 @@ | |||
1 | .\" -*- nroff -*- | ||
2 | .\" | ||
3 | .\" ssh-agent.1 | ||
4 | .\" | ||
5 | .\" Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | .\" | ||
7 | .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | .\" All rights reserved | ||
9 | .\" | ||
10 | .\" Created: Sat Apr 23 20:10:43 1995 ylo | ||
11 | .\" | ||
12 | .\" $Id: ssh-agent.1,v 1.1 1999/10/27 03:42:45 damien Exp $ | ||
13 | .\" | ||
14 | .Dd September 25, 1999 | ||
15 | .Dt SSH-AGENT 1 | ||
16 | .Os | ||
17 | .Sh NAME | ||
18 | .Nm ssh-agent | ||
19 | .Nd authentication agent | ||
20 | .Sh SYNOPSIS | ||
21 | .Nm ssh-agent | ||
22 | .Ar command | ||
23 | .Sh DESCRIPTION | ||
24 | .Nm | ||
25 | is a program to hold authentication private keys. The | ||
26 | idea is that | ||
27 | .Nm | ||
28 | is started in the beginning of an X-session or a login session, and | ||
29 | all other windows or programs are started as children of the ssh-agent | ||
30 | program (the | ||
31 | .Ar command | ||
32 | normally starts X or is the user shell). Programs started under | ||
33 | the agent inherit a connection to the agent, and the agent is | ||
34 | automatically used for RSA authentication when logging to other | ||
35 | machines using | ||
36 | .Xr ssh 1 . | ||
37 | .Pp | ||
38 | The agent initially does not have any private keys. Keys are added | ||
39 | using | ||
40 | .Xr ssh-add 1 . | ||
41 | When executed without arguments, | ||
42 | .Xr ssh-add 1 | ||
43 | adds the | ||
44 | .Pa $HOME/.ssh/identity | ||
45 | file. If the identity has a passphrase, | ||
46 | .Xr ssh-add 1 | ||
47 | asks for the passphrase (using a small X11 application if running | ||
48 | under X11, or from the terminal if running without X). It then sends | ||
49 | the identity to the agent. Several identities can be stored in the | ||
50 | agent; the agent can automatically use any of these identities. | ||
51 | .Ic ssh-add -l | ||
52 | displays the identities currently held by the agent. | ||
53 | .Pp | ||
54 | The idea is that the agent is run in the user's local PC, laptop, or | ||
55 | terminal. Authentication data need not be stored on any other | ||
56 | machine, and authentication passphrases never go over the network. | ||
57 | However, the connection to the agent is forwarded over SSH | ||
58 | remote logins, and the user can thus use the privileges given by the | ||
59 | identities anywhere in the network in a secure way. | ||
60 | .Pp | ||
61 | A connection to the agent is inherited by child programs: | ||
62 | A unix-domain socket is created | ||
63 | .Pq Pa /tmp/ssh-XXXX/agent.<pid> , | ||
64 | and the name of this socket is stored in the | ||
65 | .Ev SSH_AUTH_SOCK | ||
66 | environment | ||
67 | variable. The socket is made accessible only to the current user. | ||
68 | This method is easily abused by root or another instance of the same | ||
69 | user. | ||
70 | .Pp | ||
71 | The agent exits automatically when the command given on the command | ||
72 | line terminates. | ||
73 | .Sh FILES | ||
74 | .Bl -tag -width Ds | ||
75 | .It Pa $HOME/.ssh/identity | ||
76 | Contains the RSA authentication identity of the user. This file | ||
77 | should not be readable by anyone but the user. It is possible to | ||
78 | specify a passphrase when generating the key; that passphrase will be | ||
79 | used to encrypt the private part of this file. This file | ||
80 | is not used by | ||
81 | .Nm | ||
82 | but is normally added to the agent using | ||
83 | .Xr ssh-add 1 | ||
84 | at login time. | ||
85 | .It Pa /tmp/ssh-XXXX/agent.<pid> , | ||
86 | Unix-domain sockets used to contain the connection to the | ||
87 | authentication agent. These sockets should only be readable by the | ||
88 | owner. The sockets should get automatically removed when the agent | ||
89 | exits. | ||
90 | .Sh AUTHOR | ||
91 | Tatu Ylonen <ylo@cs.hut.fi> | ||
92 | .Pp | ||
93 | OpenSSH | ||
94 | is a derivative of the original (free) ssh 1.2.12 release, but with bugs | ||
95 | removed and newer features re-added. Rapidly after the 1.2.12 release, | ||
96 | newer versions bore successively more restrictive licenses. This version | ||
97 | of OpenSSH | ||
98 | .Bl -bullet | ||
99 | .It | ||
100 | has all components of a restrictive nature (ie. patents, see | ||
101 | .Xr ssl 8 ) | ||
102 | directly removed from the source code; any licensed or patented components | ||
103 | are chosen from | ||
104 | external libraries. | ||
105 | .It | ||
106 | has been updated to support ssh protocol 1.5. | ||
107 | .It | ||
108 | contains added support for | ||
109 | .Xr kerberos 8 | ||
110 | authentication and ticket passing. | ||
111 | .It | ||
112 | supports one-time password authentication with | ||
113 | .Xr skey 1 . | ||
114 | .El | ||
115 | .Pp | ||
116 | The libraries described in | ||
117 | .Xr ssl 8 | ||
118 | are required for proper operation. | ||
119 | .Sh SEE ALSO | ||
120 | .Xr ssh 1 , | ||
121 | .Xr ssh-add 1 , | ||
122 | .Xr ssh-keygen 1 , | ||
123 | .Xr sshd 8 , | ||
124 | .Xr ssl 8 | ||
diff --git a/ssh-agent.c b/ssh-agent.c new file mode 100644 index 000000000..19165b8f1 --- /dev/null +++ b/ssh-agent.c | |||
@@ -0,0 +1,572 @@ | |||
1 | /* | ||
2 | |||
3 | ssh-agent.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Mar 29 03:46:59 1995 ylo | ||
11 | |||
12 | The authentication agent program. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: ssh-agent.c,v 1.1 1999/10/27 03:42:45 damien Exp $"); | ||
18 | |||
19 | #include "ssh.h" | ||
20 | #include "rsa.h" | ||
21 | #include "authfd.h" | ||
22 | #include "buffer.h" | ||
23 | #include "bufaux.h" | ||
24 | #include "xmalloc.h" | ||
25 | #include "packet.h" | ||
26 | #include "getput.h" | ||
27 | #include "mpaux.h" | ||
28 | |||
29 | #include <openssl/md5.h> | ||
30 | |||
31 | typedef struct | ||
32 | { | ||
33 | int fd; | ||
34 | enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type; | ||
35 | Buffer input; | ||
36 | Buffer output; | ||
37 | } SocketEntry; | ||
38 | |||
39 | unsigned int sockets_alloc = 0; | ||
40 | SocketEntry *sockets = NULL; | ||
41 | |||
42 | typedef struct | ||
43 | { | ||
44 | RSA *key; | ||
45 | char *comment; | ||
46 | } Identity; | ||
47 | |||
48 | unsigned int num_identities = 0; | ||
49 | Identity *identities = NULL; | ||
50 | |||
51 | int max_fd = 0; | ||
52 | |||
53 | /* pid of shell == parent of agent */ | ||
54 | int parent_pid = -1; | ||
55 | |||
56 | /* pathname and directory for AUTH_SOCKET */ | ||
57 | char socket_name[1024]; | ||
58 | char socket_dir[1024]; | ||
59 | |||
60 | void | ||
61 | process_request_identity(SocketEntry *e) | ||
62 | { | ||
63 | Buffer msg; | ||
64 | int i; | ||
65 | |||
66 | buffer_init(&msg); | ||
67 | buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER); | ||
68 | buffer_put_int(&msg, num_identities); | ||
69 | for (i = 0; i < num_identities; i++) | ||
70 | { | ||
71 | buffer_put_int(&msg, BN_num_bits(identities[i].key->n)); | ||
72 | buffer_put_bignum(&msg, identities[i].key->e); | ||
73 | buffer_put_bignum(&msg, identities[i].key->n); | ||
74 | buffer_put_string(&msg, identities[i].comment, | ||
75 | strlen(identities[i].comment)); | ||
76 | } | ||
77 | buffer_put_int(&e->output, buffer_len(&msg)); | ||
78 | buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); | ||
79 | buffer_free(&msg); | ||
80 | } | ||
81 | |||
82 | void | ||
83 | process_authentication_challenge(SocketEntry *e) | ||
84 | { | ||
85 | int i, pub_bits, len; | ||
86 | BIGNUM *pub_e, *pub_n, *challenge; | ||
87 | Buffer msg; | ||
88 | MD5_CTX md; | ||
89 | unsigned char buf[32], mdbuf[16], session_id[16]; | ||
90 | unsigned int response_type; | ||
91 | |||
92 | buffer_init(&msg); | ||
93 | pub_e = BN_new(); | ||
94 | pub_n = BN_new(); | ||
95 | challenge = BN_new(); | ||
96 | pub_bits = buffer_get_int(&e->input); | ||
97 | buffer_get_bignum(&e->input, pub_e); | ||
98 | buffer_get_bignum(&e->input, pub_n); | ||
99 | buffer_get_bignum(&e->input, challenge); | ||
100 | if (buffer_len(&e->input) == 0) | ||
101 | { | ||
102 | /* Compatibility code for old servers. */ | ||
103 | memset(session_id, 0, 16); | ||
104 | response_type = 0; | ||
105 | } | ||
106 | else | ||
107 | { | ||
108 | /* New code. */ | ||
109 | buffer_get(&e->input, (char *)session_id, 16); | ||
110 | response_type = buffer_get_int(&e->input); | ||
111 | } | ||
112 | for (i = 0; i < num_identities; i++) | ||
113 | if (pub_bits == BN_num_bits(identities[i].key->n) && | ||
114 | BN_cmp(pub_e, identities[i].key->e) == 0 && | ||
115 | BN_cmp(pub_n, identities[i].key->n) == 0) | ||
116 | { | ||
117 | /* Decrypt the challenge using the private key. */ | ||
118 | rsa_private_decrypt(challenge, challenge, identities[i].key); | ||
119 | |||
120 | /* Compute the desired response. */ | ||
121 | switch (response_type) | ||
122 | { | ||
123 | case 0: /* As of protocol 1.0 */ | ||
124 | /* This response type is no longer supported. */ | ||
125 | log("Compatibility with ssh protocol 1.0 no longer supported."); | ||
126 | buffer_put_char(&msg, SSH_AGENT_FAILURE); | ||
127 | goto send; | ||
128 | |||
129 | case 1: /* As of protocol 1.1 */ | ||
130 | /* The response is MD5 of decrypted challenge plus session id. */ | ||
131 | len = BN_num_bytes(challenge); | ||
132 | assert(len <= 32 && len); | ||
133 | memset(buf, 0, 32); | ||
134 | BN_bn2bin(challenge, buf + 32 - len); | ||
135 | MD5_Init(&md); | ||
136 | MD5_Update(&md, buf, 32); | ||
137 | MD5_Update(&md, session_id, 16); | ||
138 | MD5_Final(mdbuf, &md); | ||
139 | break; | ||
140 | |||
141 | default: | ||
142 | fatal("process_authentication_challenge: bad response_type %d", | ||
143 | response_type); | ||
144 | break; | ||
145 | } | ||
146 | |||
147 | /* Send the response. */ | ||
148 | buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); | ||
149 | for (i = 0; i < 16; i++) | ||
150 | buffer_put_char(&msg, mdbuf[i]); | ||
151 | |||
152 | goto send; | ||
153 | } | ||
154 | /* Unknown identity. Send failure. */ | ||
155 | buffer_put_char(&msg, SSH_AGENT_FAILURE); | ||
156 | send: | ||
157 | buffer_put_int(&e->output, buffer_len(&msg)); | ||
158 | buffer_append(&e->output, buffer_ptr(&msg), | ||
159 | buffer_len(&msg)); | ||
160 | buffer_free(&msg); | ||
161 | BN_clear_free(pub_e); | ||
162 | BN_clear_free(pub_n); | ||
163 | BN_clear_free(challenge); | ||
164 | } | ||
165 | |||
166 | void | ||
167 | process_remove_identity(SocketEntry *e) | ||
168 | { | ||
169 | unsigned int bits; | ||
170 | unsigned int i; | ||
171 | BIGNUM *dummy, *n; | ||
172 | |||
173 | dummy = BN_new(); | ||
174 | n = BN_new(); | ||
175 | |||
176 | /* Get the key from the packet. */ | ||
177 | bits = buffer_get_int(&e->input); | ||
178 | buffer_get_bignum(&e->input, dummy); | ||
179 | buffer_get_bignum(&e->input, n); | ||
180 | |||
181 | /* Check if we have the key. */ | ||
182 | for (i = 0; i < num_identities; i++) | ||
183 | if (BN_cmp(identities[i].key->n, n) == 0) | ||
184 | { | ||
185 | /* We have this key. Free the old key. Since we don\'t want to leave | ||
186 | empty slots in the middle of the array, we actually free the | ||
187 | key there and copy data from the last entry. */ | ||
188 | RSA_free(identities[i].key); | ||
189 | xfree(identities[i].comment); | ||
190 | if (i < num_identities - 1) | ||
191 | identities[i] = identities[num_identities - 1]; | ||
192 | num_identities--; | ||
193 | BN_clear_free(dummy); | ||
194 | BN_clear_free(n); | ||
195 | |||
196 | /* Send success. */ | ||
197 | buffer_put_int(&e->output, 1); | ||
198 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | ||
199 | return; | ||
200 | } | ||
201 | /* We did not have the key. */ | ||
202 | BN_clear(dummy); | ||
203 | BN_clear(n); | ||
204 | |||
205 | /* Send failure. */ | ||
206 | buffer_put_int(&e->output, 1); | ||
207 | buffer_put_char(&e->output, SSH_AGENT_FAILURE); | ||
208 | } | ||
209 | |||
210 | /* Removes all identities from the agent. */ | ||
211 | |||
212 | void | ||
213 | process_remove_all_identities(SocketEntry *e) | ||
214 | { | ||
215 | unsigned int i; | ||
216 | |||
217 | /* Loop over all identities and clear the keys. */ | ||
218 | for (i = 0; i < num_identities; i++) | ||
219 | { | ||
220 | RSA_free(identities[i].key); | ||
221 | xfree(identities[i].comment); | ||
222 | } | ||
223 | |||
224 | /* Mark that there are no identities. */ | ||
225 | num_identities = 0; | ||
226 | |||
227 | /* Send success. */ | ||
228 | buffer_put_int(&e->output, 1); | ||
229 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | ||
230 | return; | ||
231 | } | ||
232 | |||
233 | /* Adds an identity to the agent. */ | ||
234 | |||
235 | void | ||
236 | process_add_identity(SocketEntry *e) | ||
237 | { | ||
238 | RSA *k; | ||
239 | int i; | ||
240 | BIGNUM *aux; | ||
241 | BN_CTX *ctx; | ||
242 | |||
243 | if (num_identities == 0) | ||
244 | identities = xmalloc(sizeof(Identity)); | ||
245 | else | ||
246 | identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity)); | ||
247 | |||
248 | identities[num_identities].key = RSA_new(); | ||
249 | k = identities[num_identities].key; | ||
250 | buffer_get_int(&e->input); /* bits */ | ||
251 | k->n = BN_new(); | ||
252 | buffer_get_bignum(&e->input, k->n); | ||
253 | k->e = BN_new(); | ||
254 | buffer_get_bignum(&e->input, k->e); | ||
255 | k->d = BN_new(); | ||
256 | buffer_get_bignum(&e->input, k->d); | ||
257 | k->iqmp = BN_new(); | ||
258 | buffer_get_bignum(&e->input, k->iqmp); | ||
259 | /* SSH and SSL have p and q swapped */ | ||
260 | k->q = BN_new(); | ||
261 | buffer_get_bignum(&e->input, k->q); /* p */ | ||
262 | k->p = BN_new(); | ||
263 | buffer_get_bignum(&e->input, k->p); /* q */ | ||
264 | |||
265 | /* Generate additional parameters */ | ||
266 | aux = BN_new(); | ||
267 | ctx = BN_CTX_new(); | ||
268 | |||
269 | BN_sub(aux, k->q, BN_value_one()); | ||
270 | k->dmq1 = BN_new(); | ||
271 | BN_mod(k->dmq1, k->d, aux, ctx); | ||
272 | |||
273 | BN_sub(aux, k->p, BN_value_one()); | ||
274 | k->dmp1 = BN_new(); | ||
275 | BN_mod(k->dmp1, k->d, aux, ctx); | ||
276 | |||
277 | BN_clear_free(aux); | ||
278 | BN_CTX_free(ctx); | ||
279 | |||
280 | identities[num_identities].comment = buffer_get_string(&e->input, NULL); | ||
281 | |||
282 | /* Check if we already have the key. */ | ||
283 | for (i = 0; i < num_identities; i++) | ||
284 | if (BN_cmp(identities[i].key->n, k->n) == 0) | ||
285 | { | ||
286 | /* We already have this key. Clear and free the new data and | ||
287 | return success. */ | ||
288 | RSA_free(k); | ||
289 | xfree(identities[num_identities].comment); | ||
290 | |||
291 | /* Send success. */ | ||
292 | buffer_put_int(&e->output, 1); | ||
293 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | ||
294 | return; | ||
295 | } | ||
296 | |||
297 | /* Increment the number of identities. */ | ||
298 | num_identities++; | ||
299 | |||
300 | /* Send a success message. */ | ||
301 | buffer_put_int(&e->output, 1); | ||
302 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | ||
303 | } | ||
304 | |||
305 | void | ||
306 | process_message(SocketEntry *e) | ||
307 | { | ||
308 | unsigned int msg_len; | ||
309 | unsigned int type; | ||
310 | unsigned char *cp; | ||
311 | if (buffer_len(&e->input) < 5) | ||
312 | return; /* Incomplete message. */ | ||
313 | cp = (unsigned char *)buffer_ptr(&e->input); | ||
314 | msg_len = GET_32BIT(cp); | ||
315 | if (msg_len > 256 * 1024) | ||
316 | { | ||
317 | shutdown(e->fd, SHUT_RDWR); | ||
318 | close(e->fd); | ||
319 | e->type = AUTH_UNUSED; | ||
320 | return; | ||
321 | } | ||
322 | if (buffer_len(&e->input) < msg_len + 4) | ||
323 | return; | ||
324 | buffer_consume(&e->input, 4); | ||
325 | type = buffer_get_char(&e->input); | ||
326 | |||
327 | switch (type) | ||
328 | { | ||
329 | case SSH_AGENTC_REQUEST_RSA_IDENTITIES: | ||
330 | process_request_identity(e); | ||
331 | break; | ||
332 | case SSH_AGENTC_RSA_CHALLENGE: | ||
333 | process_authentication_challenge(e); | ||
334 | break; | ||
335 | case SSH_AGENTC_ADD_RSA_IDENTITY: | ||
336 | process_add_identity(e); | ||
337 | break; | ||
338 | case SSH_AGENTC_REMOVE_RSA_IDENTITY: | ||
339 | process_remove_identity(e); | ||
340 | break; | ||
341 | case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: | ||
342 | process_remove_all_identities(e); | ||
343 | break; | ||
344 | default: | ||
345 | /* Unknown message. Respond with failure. */ | ||
346 | error("Unknown message %d", type); | ||
347 | buffer_clear(&e->input); | ||
348 | buffer_put_int(&e->output, 1); | ||
349 | buffer_put_char(&e->output, SSH_AGENT_FAILURE); | ||
350 | break; | ||
351 | } | ||
352 | } | ||
353 | |||
354 | void | ||
355 | new_socket(int type, int fd) | ||
356 | { | ||
357 | unsigned int i, old_alloc; | ||
358 | if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) | ||
359 | error("fcntl O_NONBLOCK: %s", strerror(errno)); | ||
360 | |||
361 | if (fd > max_fd) | ||
362 | max_fd = fd; | ||
363 | |||
364 | for (i = 0; i < sockets_alloc; i++) | ||
365 | if (sockets[i].type == AUTH_UNUSED) | ||
366 | { | ||
367 | sockets[i].fd = fd; | ||
368 | sockets[i].type = type; | ||
369 | buffer_init(&sockets[i].input); | ||
370 | buffer_init(&sockets[i].output); | ||
371 | return; | ||
372 | } | ||
373 | old_alloc = sockets_alloc; | ||
374 | sockets_alloc += 10; | ||
375 | if (sockets) | ||
376 | sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0])); | ||
377 | else | ||
378 | sockets = xmalloc(sockets_alloc * sizeof(sockets[0])); | ||
379 | for (i = old_alloc; i < sockets_alloc; i++) | ||
380 | sockets[i].type = AUTH_UNUSED; | ||
381 | sockets[old_alloc].type = type; | ||
382 | sockets[old_alloc].fd = fd; | ||
383 | buffer_init(&sockets[old_alloc].input); | ||
384 | buffer_init(&sockets[old_alloc].output); | ||
385 | } | ||
386 | |||
387 | void | ||
388 | prepare_select(fd_set *readset, fd_set *writeset) | ||
389 | { | ||
390 | unsigned int i; | ||
391 | for (i = 0; i < sockets_alloc; i++) | ||
392 | switch (sockets[i].type) | ||
393 | { | ||
394 | case AUTH_SOCKET: | ||
395 | case AUTH_CONNECTION: | ||
396 | FD_SET(sockets[i].fd, readset); | ||
397 | if (buffer_len(&sockets[i].output) > 0) | ||
398 | FD_SET(sockets[i].fd, writeset); | ||
399 | break; | ||
400 | case AUTH_UNUSED: | ||
401 | break; | ||
402 | default: | ||
403 | fatal("Unknown socket type %d", sockets[i].type); | ||
404 | break; | ||
405 | } | ||
406 | } | ||
407 | |||
408 | void after_select(fd_set *readset, fd_set *writeset) | ||
409 | { | ||
410 | unsigned int i; | ||
411 | int len, sock; | ||
412 | char buf[1024]; | ||
413 | struct sockaddr_un sunaddr; | ||
414 | |||
415 | for (i = 0; i < sockets_alloc; i++) | ||
416 | switch (sockets[i].type) | ||
417 | { | ||
418 | case AUTH_UNUSED: | ||
419 | break; | ||
420 | case AUTH_SOCKET: | ||
421 | if (FD_ISSET(sockets[i].fd, readset)) | ||
422 | { | ||
423 | len = sizeof(sunaddr); | ||
424 | sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len); | ||
425 | if (sock < 0) | ||
426 | { | ||
427 | perror("accept from AUTH_SOCKET"); | ||
428 | break; | ||
429 | } | ||
430 | new_socket(AUTH_CONNECTION, sock); | ||
431 | } | ||
432 | break; | ||
433 | case AUTH_CONNECTION: | ||
434 | if (buffer_len(&sockets[i].output) > 0 && | ||
435 | FD_ISSET(sockets[i].fd, writeset)) | ||
436 | { | ||
437 | len = write(sockets[i].fd, buffer_ptr(&sockets[i].output), | ||
438 | buffer_len(&sockets[i].output)); | ||
439 | if (len <= 0) | ||
440 | { | ||
441 | shutdown(sockets[i].fd, SHUT_RDWR); | ||
442 | close(sockets[i].fd); | ||
443 | sockets[i].type = AUTH_UNUSED; | ||
444 | break; | ||
445 | } | ||
446 | buffer_consume(&sockets[i].output, len); | ||
447 | } | ||
448 | if (FD_ISSET(sockets[i].fd, readset)) | ||
449 | { | ||
450 | len = read(sockets[i].fd, buf, sizeof(buf)); | ||
451 | if (len <= 0) | ||
452 | { | ||
453 | shutdown(sockets[i].fd, SHUT_RDWR); | ||
454 | close(sockets[i].fd); | ||
455 | sockets[i].type = AUTH_UNUSED; | ||
456 | break; | ||
457 | } | ||
458 | buffer_append(&sockets[i].input, buf, len); | ||
459 | process_message(&sockets[i]); | ||
460 | } | ||
461 | break; | ||
462 | default: | ||
463 | fatal("Unknown type %d", sockets[i].type); | ||
464 | } | ||
465 | } | ||
466 | |||
467 | void | ||
468 | check_parent_exists(int sig) | ||
469 | { | ||
470 | if (kill(parent_pid, 0) < 0) | ||
471 | { | ||
472 | /* printf("Parent has died - Authentication agent exiting.\n"); */ | ||
473 | exit(1); | ||
474 | } | ||
475 | signal(SIGALRM, check_parent_exists); | ||
476 | alarm(10); | ||
477 | } | ||
478 | |||
479 | void cleanup_socket(void) { | ||
480 | remove(socket_name); | ||
481 | rmdir(socket_dir); | ||
482 | } | ||
483 | |||
484 | int | ||
485 | main(int ac, char **av) | ||
486 | { | ||
487 | fd_set readset, writeset; | ||
488 | int sock; | ||
489 | struct sockaddr_un sunaddr; | ||
490 | |||
491 | /* check if RSA support exists */ | ||
492 | if (rsa_alive() == 0) { | ||
493 | extern char *__progname; | ||
494 | fprintf(stderr, | ||
495 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | ||
496 | __progname); | ||
497 | exit(1); | ||
498 | } | ||
499 | |||
500 | if (ac < 2) | ||
501 | { | ||
502 | fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION); | ||
503 | fprintf(stderr, "Usage: %s command\n", av[0]); | ||
504 | exit(1); | ||
505 | } | ||
506 | |||
507 | parent_pid = getpid(); | ||
508 | |||
509 | /* Create private directory for agent socket */ | ||
510 | strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir); | ||
511 | if (mkdtemp(socket_dir) == NULL) { | ||
512 | perror("mkdtemp: private socket dir"); | ||
513 | exit(1); | ||
514 | } | ||
515 | snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, parent_pid); | ||
516 | |||
517 | /* Fork, and have the parent execute the command. The child continues as | ||
518 | the authentication agent. */ | ||
519 | if (fork() != 0) | ||
520 | { /* Parent - execute the given command. */ | ||
521 | setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1); | ||
522 | execvp(av[1], av + 1); | ||
523 | perror(av[1]); | ||
524 | exit(1); | ||
525 | } | ||
526 | |||
527 | if (atexit(cleanup_socket) < 0) { | ||
528 | perror("atexit"); | ||
529 | cleanup_socket(); | ||
530 | exit(1); | ||
531 | } | ||
532 | |||
533 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | ||
534 | if (sock < 0) | ||
535 | { | ||
536 | perror("socket"); | ||
537 | exit(1); | ||
538 | } | ||
539 | memset(&sunaddr, 0, sizeof(sunaddr)); | ||
540 | sunaddr.sun_family = AF_UNIX; | ||
541 | strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); | ||
542 | if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) | ||
543 | { | ||
544 | perror("bind"); | ||
545 | exit(1); | ||
546 | } | ||
547 | if (listen(sock, 5) < 0) | ||
548 | { | ||
549 | perror("listen"); | ||
550 | exit(1); | ||
551 | } | ||
552 | new_socket(AUTH_SOCKET, sock); | ||
553 | signal(SIGALRM, check_parent_exists); | ||
554 | alarm(10); | ||
555 | |||
556 | signal(SIGINT, SIG_IGN); | ||
557 | while (1) | ||
558 | { | ||
559 | FD_ZERO(&readset); | ||
560 | FD_ZERO(&writeset); | ||
561 | prepare_select(&readset, &writeset); | ||
562 | if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) | ||
563 | { | ||
564 | if (errno == EINTR) | ||
565 | continue; | ||
566 | perror("select"); | ||
567 | exit(1); | ||
568 | } | ||
569 | after_select(&readset, &writeset); | ||
570 | } | ||
571 | /*NOTREACHED*/ | ||
572 | } | ||
diff --git a/ssh-keygen.1 b/ssh-keygen.1 new file mode 100644 index 000000000..67fbfd2c7 --- /dev/null +++ b/ssh-keygen.1 | |||
@@ -0,0 +1,155 @@ | |||
1 | .\" -*- nroff -*- | ||
2 | .\" | ||
3 | .\" ssh-keygen.1 | ||
4 | .\" | ||
5 | .\" Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | .\" | ||
7 | .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | .\" All rights reserved | ||
9 | .\" | ||
10 | .\" Created: Sat Apr 22 23:55:14 1995 ylo | ||
11 | .\" | ||
12 | .\" $Id: ssh-keygen.1,v 1.1 1999/10/27 03:42:45 damien Exp $ | ||
13 | .\" | ||
14 | .Dd September 25, 1999 | ||
15 | .Dt SSH-KEYGEN 1 | ||
16 | .Os | ||
17 | .Sh NAME | ||
18 | .Nm ssh-keygen | ||
19 | .Nd authentication key generation | ||
20 | .Sh SYNOPSIS | ||
21 | .Nm ssh-keygen | ||
22 | .Op Fl q | ||
23 | .Op Fl b Ar bits | ||
24 | .Op Fl N Ar new_passphrase | ||
25 | .Op Fl C Ar comment | ||
26 | .Nm ssh-keygen | ||
27 | .Fl p | ||
28 | .Op Fl P Ar old_passphrase | ||
29 | .Op Fl N Ar new_passphrase | ||
30 | .Nm ssh-keygen | ||
31 | .Fl c | ||
32 | .Op Fl P Ar passphrase | ||
33 | .Op Fl C Ar comment | ||
34 | .Sh DESCRIPTION | ||
35 | .Nm | ||
36 | generates and manages authentication keys for | ||
37 | .Xr ssh 1 . | ||
38 | Normally each user wishing to use SSH | ||
39 | with RSA authentication runs this once to create the authentication | ||
40 | key in | ||
41 | .Pa $HOME/.ssh/identity . | ||
42 | Additionally, the system administrator may use this to generate host keys. | ||
43 | .Pp | ||
44 | Normally this program generates the key and asks for a file in which | ||
45 | to store the private key. The public key is stored in a file with the | ||
46 | same name but | ||
47 | .Dq .pub | ||
48 | appended. The program also asks for a | ||
49 | passphrase. The passphrase may be empty to indicate no passphrase | ||
50 | (host keys must have empty passphrase), or it may be a string of | ||
51 | arbitrary length. Good passphrases are 10-30 characters long and are | ||
52 | not simple sentences or otherwise easily guessable (English | ||
53 | prose has only 1-2 bits of entropy per word, and provides very bad | ||
54 | passphrases). The passphrase can be changed later by using the | ||
55 | .Fl p | ||
56 | option. | ||
57 | .Pp | ||
58 | There is no way to recover a lost passphrase. If the passphrase is | ||
59 | lost or forgotten, you will have to generate a new key and copy the | ||
60 | corresponding public key to other machines. | ||
61 | .Pp | ||
62 | There is also a comment field in the key file that is only for | ||
63 | convenience to the user to help identify the key. The comment can | ||
64 | tell what the key is for, or whatever is useful. The comment is | ||
65 | initialized to | ||
66 | .Dq user@host | ||
67 | when the key is created, but can be changed using the | ||
68 | .Fl c | ||
69 | option. | ||
70 | .Pp | ||
71 | The options are as follows: | ||
72 | .Bl -tag -width Ds | ||
73 | .It Fl b Ar bits | ||
74 | Specifies the number of bits in the key to create. Minimum is 512 | ||
75 | bits. Generally 1024 bits is considered sufficient, and key sizes | ||
76 | above that no longer improve security but make things slower. The | ||
77 | default is 1024 bits. | ||
78 | .It Fl c | ||
79 | Requests changing the comment in the private and public key files. | ||
80 | The program will prompt for the file containing the private keys, for | ||
81 | passphrase if the key has one, and for the new comment. | ||
82 | .It Fl p | ||
83 | Requests changing the passphrase of a private key file instead of | ||
84 | creating a new private key. The program will prompt for the file | ||
85 | containing the private key, for the old passphrase, and twice for the | ||
86 | new passphrase. | ||
87 | .It Fl q | ||
88 | Silence | ||
89 | .Nm ssh-keygen . | ||
90 | Used by | ||
91 | .Pa /etc/rc | ||
92 | when creating a new key. | ||
93 | .It Fl C Ar comment | ||
94 | Provides the new comment. | ||
95 | .It Fl N Ar new_passphrase | ||
96 | Provides the new passphrase. | ||
97 | .It Fl P Ar passphrase | ||
98 | Provides the (old) passphrase. | ||
99 | .El | ||
100 | .Sh FILES | ||
101 | .Bl -tag -width Ds | ||
102 | .It Pa $HOME/.ssh/random_seed | ||
103 | Used for seeding the random number generator. This file should not be | ||
104 | readable by anyone but the user. This file is created the first time | ||
105 | the program is run, and is updated every time. | ||
106 | .It Pa $HOME/.ssh/identity | ||
107 | Contains the RSA authentication identity of the user. This file | ||
108 | should not be readable by anyone but the user. It is possible to | ||
109 | specify a passphrase when generating the key; that passphrase will be | ||
110 | used to encrypt the private part of this file using 3DES. This file | ||
111 | is not automatically accessed by | ||
112 | .Nm | ||
113 | but it is offered as the default file for the private key. | ||
114 | .It Pa $HOME/.ssh/identity.pub | ||
115 | Contains the public key for authentication. The contents of this file | ||
116 | should be added to | ||
117 | .Pa $HOME/.ssh/authorized_keys | ||
118 | on all machines | ||
119 | where you wish to log in using RSA authentication. There is no | ||
120 | need to keep the contents of this file secret. | ||
121 | .Sh AUTHOR | ||
122 | Tatu Ylonen <ylo@cs.hut.fi> | ||
123 | .Pp | ||
124 | OpenSSH | ||
125 | is a derivative of the original (free) ssh 1.2.12 release, but with bugs | ||
126 | removed and newer features re-added. Rapidly after the 1.2.12 release, | ||
127 | newer versions bore successively more restrictive licenses. This version | ||
128 | of OpenSSH | ||
129 | .Bl -bullet | ||
130 | .It | ||
131 | has all components of a restrictive nature (ie. patents, see | ||
132 | .Xr ssl 8 ) | ||
133 | directly removed from the source code; any licensed or patented components | ||
134 | are chosen from | ||
135 | external libraries. | ||
136 | .It | ||
137 | has been updated to support ssh protocol 1.5. | ||
138 | .It | ||
139 | contains added support for | ||
140 | .Xr kerberos 8 | ||
141 | authentication and ticket passing. | ||
142 | .It | ||
143 | supports one-time password authentication with | ||
144 | .Xr skey 1 . | ||
145 | .El | ||
146 | .Pp | ||
147 | The libraries described in | ||
148 | .Xr ssl 8 | ||
149 | are required for proper operation. | ||
150 | .Sh SEE ALSO | ||
151 | .Xr ssh 1 , | ||
152 | .Xr ssh-add 1 , | ||
153 | .Xr ssh-agent 1, | ||
154 | .Xr sshd 8 , | ||
155 | .Xr ssl 8 | ||
diff --git a/ssh-keygen.c b/ssh-keygen.c new file mode 100644 index 000000000..2ba64e756 --- /dev/null +++ b/ssh-keygen.c | |||
@@ -0,0 +1,552 @@ | |||
1 | /* | ||
2 | |||
3 | ssh-keygen.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Mar 27 02:26:40 1995 ylo | ||
11 | |||
12 | Identity and host key generation and maintenance. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: ssh-keygen.c,v 1.1 1999/10/27 03:42:45 damien Exp $"); | ||
18 | |||
19 | #include "rsa.h" | ||
20 | #include "ssh.h" | ||
21 | #include "xmalloc.h" | ||
22 | |||
23 | /* Generated private key. */ | ||
24 | RSA *private_key; | ||
25 | |||
26 | /* Generated public key. */ | ||
27 | RSA *public_key; | ||
28 | |||
29 | /* Number of bits in the RSA key. This value can be changed on the command | ||
30 | line. */ | ||
31 | int bits = 1024; | ||
32 | |||
33 | /* Flag indicating that we just want to change the passphrase. This can be | ||
34 | set on the command line. */ | ||
35 | int change_passphrase = 0; | ||
36 | |||
37 | /* Flag indicating that we just want to change the comment. This can be set | ||
38 | on the command line. */ | ||
39 | int change_comment = 0; | ||
40 | |||
41 | int quiet = 0; | ||
42 | |||
43 | /* This is set to the identity file name if given on the command line. */ | ||
44 | char *identity_file = NULL; | ||
45 | |||
46 | /* This is set to the passphrase if given on the command line. */ | ||
47 | char *identity_passphrase = NULL; | ||
48 | |||
49 | /* This is set to the new passphrase if given on the command line. */ | ||
50 | char *identity_new_passphrase = NULL; | ||
51 | |||
52 | /* This is set to the new comment if given on the command line. */ | ||
53 | char *identity_comment = NULL; | ||
54 | |||
55 | /* Perform changing a passphrase. The argument is the passwd structure | ||
56 | for the current user. */ | ||
57 | |||
58 | void | ||
59 | do_change_passphrase(struct passwd *pw) | ||
60 | { | ||
61 | char buf[1024], *comment; | ||
62 | char *old_passphrase, *passphrase1, *passphrase2; | ||
63 | struct stat st; | ||
64 | RSA *private_key; | ||
65 | |||
66 | /* Read key file name. */ | ||
67 | if (identity_file != NULL) { | ||
68 | strncpy(buf, identity_file, sizeof(buf)); | ||
69 | buf[sizeof(buf) - 1] = '\0'; | ||
70 | } else { | ||
71 | printf("Enter file in which the key is ($HOME/%s): ", SSH_CLIENT_IDENTITY); | ||
72 | fflush(stdout); | ||
73 | if (fgets(buf, sizeof(buf), stdin) == NULL) | ||
74 | exit(1); | ||
75 | if (strchr(buf, '\n')) | ||
76 | *strchr(buf, '\n') = 0; | ||
77 | if (strcmp(buf, "") == 0) | ||
78 | snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); | ||
79 | } | ||
80 | |||
81 | /* Check if the file exists. */ | ||
82 | if (stat(buf, &st) < 0) | ||
83 | { | ||
84 | perror(buf); | ||
85 | exit(1); | ||
86 | } | ||
87 | |||
88 | /* Try to load the public key from the file the verify that it is | ||
89 | readable and of the proper format. */ | ||
90 | public_key = RSA_new(); | ||
91 | if (!load_public_key(buf, public_key, NULL)) | ||
92 | { | ||
93 | printf("%s is not a valid key file.\n", buf); | ||
94 | exit(1); | ||
95 | } | ||
96 | /* Clear the public key since we are just about to load the whole file. */ | ||
97 | RSA_free(public_key); | ||
98 | |||
99 | /* Try to load the file with empty passphrase. */ | ||
100 | private_key = RSA_new(); | ||
101 | if (!load_private_key(buf, "", private_key, &comment)) { | ||
102 | /* Read passphrase from the user. */ | ||
103 | if (identity_passphrase) | ||
104 | old_passphrase = xstrdup(identity_passphrase); | ||
105 | else | ||
106 | old_passphrase = read_passphrase("Enter old passphrase: ", 1); | ||
107 | /* Try to load using the passphrase. */ | ||
108 | if (!load_private_key(buf, old_passphrase, private_key, &comment)) | ||
109 | { | ||
110 | memset(old_passphrase, 0, strlen(old_passphrase)); | ||
111 | xfree(old_passphrase); | ||
112 | printf("Bad passphrase.\n"); | ||
113 | exit(1); | ||
114 | } | ||
115 | /* Destroy the passphrase. */ | ||
116 | memset(old_passphrase, 0, strlen(old_passphrase)); | ||
117 | xfree(old_passphrase); | ||
118 | } | ||
119 | printf("Key has comment '%s'\n", comment); | ||
120 | |||
121 | /* Ask the new passphrase (twice). */ | ||
122 | if (identity_new_passphrase) | ||
123 | { | ||
124 | passphrase1 = xstrdup(identity_new_passphrase); | ||
125 | passphrase2 = NULL; | ||
126 | } | ||
127 | else | ||
128 | { | ||
129 | passphrase1 = | ||
130 | read_passphrase("Enter new passphrase (empty for no passphrase): ", 1); | ||
131 | passphrase2 = read_passphrase("Enter same passphrase again: ", 1); | ||
132 | |||
133 | /* Verify that they are the same. */ | ||
134 | if (strcmp(passphrase1, passphrase2) != 0) | ||
135 | { | ||
136 | memset(passphrase1, 0, strlen(passphrase1)); | ||
137 | memset(passphrase2, 0, strlen(passphrase2)); | ||
138 | xfree(passphrase1); | ||
139 | xfree(passphrase2); | ||
140 | printf("Pass phrases do not match. Try again.\n"); | ||
141 | exit(1); | ||
142 | } | ||
143 | /* Destroy the other copy. */ | ||
144 | memset(passphrase2, 0, strlen(passphrase2)); | ||
145 | xfree(passphrase2); | ||
146 | } | ||
147 | |||
148 | /* Save the file using the new passphrase. */ | ||
149 | if (!save_private_key(buf, passphrase1, private_key, comment)) | ||
150 | { | ||
151 | printf("Saving the key failed: %s: %s.\n", | ||
152 | buf, strerror(errno)); | ||
153 | memset(passphrase1, 0, strlen(passphrase1)); | ||
154 | xfree(passphrase1); | ||
155 | RSA_free(private_key); | ||
156 | xfree(comment); | ||
157 | exit(1); | ||
158 | } | ||
159 | /* Destroy the passphrase and the copy of the key in memory. */ | ||
160 | memset(passphrase1, 0, strlen(passphrase1)); | ||
161 | xfree(passphrase1); | ||
162 | RSA_free(private_key); /* Destroys contents */ | ||
163 | xfree(comment); | ||
164 | |||
165 | printf("Your identification has been saved with the new passphrase.\n"); | ||
166 | exit(0); | ||
167 | } | ||
168 | |||
169 | /* Change the comment of a private key file. */ | ||
170 | |||
171 | void | ||
172 | do_change_comment(struct passwd *pw) | ||
173 | { | ||
174 | char buf[1024], new_comment[1024], *comment; | ||
175 | RSA *private_key; | ||
176 | char *passphrase; | ||
177 | struct stat st; | ||
178 | FILE *f; | ||
179 | char *tmpbuf; | ||
180 | |||
181 | /* Read key file name. */ | ||
182 | if (identity_file) | ||
183 | { | ||
184 | strncpy(buf, identity_file, sizeof(buf)); | ||
185 | buf[sizeof(buf) - 1] = '\0'; | ||
186 | } | ||
187 | else | ||
188 | { | ||
189 | printf("Enter file in which the key is ($HOME/%s): ", | ||
190 | SSH_CLIENT_IDENTITY); | ||
191 | fflush(stdout); | ||
192 | if (fgets(buf, sizeof(buf), stdin) == NULL) | ||
193 | exit(1); | ||
194 | if (strchr(buf, '\n')) | ||
195 | *strchr(buf, '\n') = 0; | ||
196 | if (strcmp(buf, "") == 0) | ||
197 | snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); | ||
198 | } | ||
199 | |||
200 | /* Check if the file exists. */ | ||
201 | if (stat(buf, &st) < 0) | ||
202 | { | ||
203 | perror(buf); | ||
204 | exit(1); | ||
205 | } | ||
206 | |||
207 | /* Try to load the public key from the file the verify that it is | ||
208 | readable and of the proper format. */ | ||
209 | public_key = RSA_new(); | ||
210 | if (!load_public_key(buf, public_key, NULL)) | ||
211 | { | ||
212 | printf("%s is not a valid key file.\n", buf); | ||
213 | exit(1); | ||
214 | } | ||
215 | |||
216 | private_key = RSA_new(); | ||
217 | /* Try to load the file with empty passphrase. */ | ||
218 | if (load_private_key(buf, "", private_key, &comment)) | ||
219 | passphrase = xstrdup(""); | ||
220 | else | ||
221 | { | ||
222 | /* Read passphrase from the user. */ | ||
223 | if (identity_passphrase) | ||
224 | passphrase = xstrdup(identity_passphrase); | ||
225 | else | ||
226 | if (identity_new_passphrase) | ||
227 | passphrase = xstrdup(identity_new_passphrase); | ||
228 | else | ||
229 | passphrase = read_passphrase("Enter passphrase: ", 1); | ||
230 | /* Try to load using the passphrase. */ | ||
231 | if (!load_private_key(buf, passphrase, private_key, &comment)) | ||
232 | { | ||
233 | memset(passphrase, 0, strlen(passphrase)); | ||
234 | xfree(passphrase); | ||
235 | printf("Bad passphrase.\n"); | ||
236 | exit(1); | ||
237 | } | ||
238 | } | ||
239 | printf("Key now has comment '%s'\n", comment); | ||
240 | |||
241 | if (identity_comment) | ||
242 | { | ||
243 | strncpy(new_comment, identity_comment, sizeof(new_comment)); | ||
244 | new_comment[sizeof(new_comment) - 1] = '\0'; | ||
245 | } | ||
246 | else | ||
247 | { | ||
248 | printf("Enter new comment: "); | ||
249 | fflush(stdout); | ||
250 | if (!fgets(new_comment, sizeof(new_comment), stdin)) | ||
251 | { | ||
252 | memset(passphrase, 0, strlen(passphrase)); | ||
253 | RSA_free(private_key); | ||
254 | exit(1); | ||
255 | } | ||
256 | |||
257 | /* Remove terminating newline from comment. */ | ||
258 | if (strchr(new_comment, '\n')) | ||
259 | *strchr(new_comment, '\n') = 0; | ||
260 | } | ||
261 | |||
262 | /* Save the file using the new passphrase. */ | ||
263 | if (!save_private_key(buf, passphrase, private_key, new_comment)) | ||
264 | { | ||
265 | printf("Saving the key failed: %s: %s.\n", | ||
266 | buf, strerror(errno)); | ||
267 | memset(passphrase, 0, strlen(passphrase)); | ||
268 | xfree(passphrase); | ||
269 | RSA_free(private_key); | ||
270 | xfree(comment); | ||
271 | exit(1); | ||
272 | } | ||
273 | |||
274 | /* Destroy the passphrase and the private key in memory. */ | ||
275 | memset(passphrase, 0, strlen(passphrase)); | ||
276 | xfree(passphrase); | ||
277 | RSA_free(private_key); | ||
278 | |||
279 | /* Save the public key in text format in a file with the same name but | ||
280 | .pub appended. */ | ||
281 | strcat(buf, ".pub"); | ||
282 | f = fopen(buf, "w"); | ||
283 | if (!f) | ||
284 | { | ||
285 | printf("Could not save your public key in %s\n", buf); | ||
286 | exit(1); | ||
287 | } | ||
288 | fprintf(f, "%d ", BN_num_bits(public_key->n)); | ||
289 | tmpbuf = BN_bn2dec(public_key->e); | ||
290 | fprintf(f, "%s ", tmpbuf); | ||
291 | free (tmpbuf); | ||
292 | tmpbuf = BN_bn2dec(public_key->n); | ||
293 | fprintf(f, "%s %s\n", tmpbuf, new_comment); | ||
294 | free (tmpbuf); | ||
295 | fclose(f); | ||
296 | |||
297 | xfree(comment); | ||
298 | |||
299 | printf("The comment in your key file has been changed.\n"); | ||
300 | exit(0); | ||
301 | } | ||
302 | |||
303 | /* Main program for key management. */ | ||
304 | |||
305 | int | ||
306 | main(int ac, char **av) | ||
307 | { | ||
308 | char buf[16384], buf2[1024], *passphrase1, *passphrase2; | ||
309 | struct passwd *pw; | ||
310 | char *tmpbuf; | ||
311 | int opt; | ||
312 | struct stat st; | ||
313 | FILE *f; | ||
314 | char hostname[MAXHOSTNAMELEN]; | ||
315 | extern int optind; | ||
316 | extern char *optarg; | ||
317 | |||
318 | /* check if RSA support exists */ | ||
319 | if (rsa_alive() == 0) { | ||
320 | extern char *__progname; | ||
321 | |||
322 | fprintf(stderr, | ||
323 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | ||
324 | __progname); | ||
325 | exit(1); | ||
326 | } | ||
327 | |||
328 | /* Get user\'s passwd structure. We need this for the home directory. */ | ||
329 | pw = getpwuid(getuid()); | ||
330 | if (!pw) | ||
331 | { | ||
332 | printf("You don't exist, go away!\n"); | ||
333 | exit(1); | ||
334 | } | ||
335 | |||
336 | /* Create ~/.ssh directory if it doesn\'t already exist. */ | ||
337 | snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_USER_DIR); | ||
338 | if (stat(buf, &st) < 0) | ||
339 | if (mkdir(buf, 0755) < 0) | ||
340 | error("Could not create directory '%s'.", buf); | ||
341 | |||
342 | /* Parse command line arguments. */ | ||
343 | while ((opt = getopt(ac, av, "qpcb:f:P:N:C:")) != EOF) | ||
344 | { | ||
345 | switch (opt) | ||
346 | { | ||
347 | case 'b': | ||
348 | bits = atoi(optarg); | ||
349 | if (bits < 512 || bits > 32768) | ||
350 | { | ||
351 | printf("Bits has bad value.\n"); | ||
352 | exit(1); | ||
353 | } | ||
354 | break; | ||
355 | |||
356 | case 'p': | ||
357 | change_passphrase = 1; | ||
358 | break; | ||
359 | |||
360 | case 'c': | ||
361 | change_comment = 1; | ||
362 | break; | ||
363 | |||
364 | case 'f': | ||
365 | identity_file = optarg; | ||
366 | break; | ||
367 | |||
368 | case 'P': | ||
369 | identity_passphrase = optarg; | ||
370 | break; | ||
371 | |||
372 | case 'N': | ||
373 | identity_new_passphrase = optarg; | ||
374 | break; | ||
375 | |||
376 | case 'C': | ||
377 | identity_comment = optarg; | ||
378 | break; | ||
379 | |||
380 | case 'q': | ||
381 | quiet = 1; | ||
382 | break; | ||
383 | |||
384 | case '?': | ||
385 | default: | ||
386 | printf("ssh-keygen version %s\n", SSH_VERSION); | ||
387 | printf("Usage: %s [-b bits] [-p] [-c] [-f file] [-P pass] [-N new-pass] [-C comment]\n", av[0]); | ||
388 | exit(1); | ||
389 | } | ||
390 | } | ||
391 | if (optind < ac) | ||
392 | { | ||
393 | printf("Too many arguments.\n"); | ||
394 | exit(1); | ||
395 | } | ||
396 | if (change_passphrase && change_comment) | ||
397 | { | ||
398 | printf("Can only have one of -p and -c.\n"); | ||
399 | exit(1); | ||
400 | } | ||
401 | |||
402 | /* If the user requested to change the passphrase, do it now. This | ||
403 | function never returns. */ | ||
404 | if (change_passphrase) | ||
405 | do_change_passphrase(pw); | ||
406 | |||
407 | /* If the user requested to change the comment, do it now. This function | ||
408 | never returns. */ | ||
409 | if (change_comment) | ||
410 | do_change_comment(pw); | ||
411 | |||
412 | arc4random_stir(); | ||
413 | |||
414 | if (quiet) | ||
415 | rsa_set_verbose(0); | ||
416 | |||
417 | /* Generate the rsa key pair. */ | ||
418 | private_key = RSA_new(); | ||
419 | public_key = RSA_new(); | ||
420 | rsa_generate_key(private_key, public_key, bits); | ||
421 | |||
422 | ask_file_again: | ||
423 | |||
424 | /* Ask for a file to save the key in. */ | ||
425 | if (identity_file) | ||
426 | { | ||
427 | strncpy(buf, identity_file, sizeof(buf)); | ||
428 | buf[sizeof(buf) - 1] = '\0'; | ||
429 | } | ||
430 | else | ||
431 | { | ||
432 | printf("Enter file in which to save the key ($HOME/%s): ", | ||
433 | SSH_CLIENT_IDENTITY); | ||
434 | fflush(stdout); | ||
435 | if (fgets(buf, sizeof(buf), stdin) == NULL) | ||
436 | exit(1); | ||
437 | if (strchr(buf, '\n')) | ||
438 | *strchr(buf, '\n') = 0; | ||
439 | if (strcmp(buf, "") == 0) | ||
440 | snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); | ||
441 | } | ||
442 | |||
443 | /* If the file aready exists, ask the user to confirm. */ | ||
444 | if (stat(buf, &st) >= 0) | ||
445 | { | ||
446 | printf("%s already exists.\n", buf); | ||
447 | printf("Overwrite (y/n)? "); | ||
448 | fflush(stdout); | ||
449 | if (fgets(buf2, sizeof(buf2), stdin) == NULL) | ||
450 | exit(1); | ||
451 | if (buf2[0] != 'y' && buf2[0] != 'Y') | ||
452 | exit(1); | ||
453 | } | ||
454 | |||
455 | /* Ask for a passphrase (twice). */ | ||
456 | if (identity_passphrase) | ||
457 | passphrase1 = xstrdup(identity_passphrase); | ||
458 | else | ||
459 | if (identity_new_passphrase) | ||
460 | passphrase1 = xstrdup(identity_new_passphrase); | ||
461 | else | ||
462 | { | ||
463 | passphrase_again: | ||
464 | passphrase1 = | ||
465 | read_passphrase("Enter passphrase (empty for no passphrase): ", 1); | ||
466 | passphrase2 = read_passphrase("Enter same passphrase again: ", 1); | ||
467 | if (strcmp(passphrase1, passphrase2) != 0) | ||
468 | { | ||
469 | /* The passphrases do not match. Clear them and retry. */ | ||
470 | memset(passphrase1, 0, strlen(passphrase1)); | ||
471 | memset(passphrase2, 0, strlen(passphrase2)); | ||
472 | xfree(passphrase1); | ||
473 | xfree(passphrase2); | ||
474 | printf("Passphrases do not match. Try again.\n"); | ||
475 | goto passphrase_again; | ||
476 | } | ||
477 | /* Clear the other copy of the passphrase. */ | ||
478 | memset(passphrase2, 0, strlen(passphrase2)); | ||
479 | xfree(passphrase2); | ||
480 | } | ||
481 | |||
482 | /* Create default commend field for the passphrase. The user can later | ||
483 | edit this field. */ | ||
484 | if (identity_comment) | ||
485 | { | ||
486 | strlcpy(buf2, identity_comment, sizeof(buf2)); | ||
487 | } | ||
488 | else | ||
489 | { | ||
490 | if (gethostname(hostname, sizeof(hostname)) < 0) | ||
491 | { | ||
492 | perror("gethostname"); | ||
493 | exit(1); | ||
494 | } | ||
495 | snprintf(buf2, sizeof buf2, "%s@%s", pw->pw_name, hostname); | ||
496 | } | ||
497 | |||
498 | /* Save the key with the given passphrase and comment. */ | ||
499 | if (!save_private_key(buf, passphrase1, private_key, buf2)) | ||
500 | { | ||
501 | printf("Saving the key failed: %s: %s.\n", | ||
502 | buf, strerror(errno)); | ||
503 | memset(passphrase1, 0, strlen(passphrase1)); | ||
504 | xfree(passphrase1); | ||
505 | goto ask_file_again; | ||
506 | } | ||
507 | /* Clear the passphrase. */ | ||
508 | memset(passphrase1, 0, strlen(passphrase1)); | ||
509 | xfree(passphrase1); | ||
510 | |||
511 | /* Clear the private key and the random number generator. */ | ||
512 | RSA_free(private_key); | ||
513 | arc4random_stir(); | ||
514 | |||
515 | if (!quiet) | ||
516 | printf("Your identification has been saved in %s.\n", buf); | ||
517 | |||
518 | /* Display the public key on the screen. */ | ||
519 | if (!quiet) { | ||
520 | printf("Your public key is:\n"); | ||
521 | printf("%d ", BN_num_bits(public_key->n)); | ||
522 | tmpbuf = BN_bn2dec(public_key->e); | ||
523 | printf("%s ", tmpbuf); | ||
524 | free(tmpbuf); | ||
525 | tmpbuf = BN_bn2dec(public_key->n); | ||
526 | printf("%s %s\n", tmpbuf, buf2); | ||
527 | free(tmpbuf); | ||
528 | } | ||
529 | |||
530 | /* Save the public key in text format in a file with the same name but | ||
531 | .pub appended. */ | ||
532 | strcat(buf, ".pub"); | ||
533 | f = fopen(buf, "w"); | ||
534 | if (!f) | ||
535 | { | ||
536 | printf("Could not save your public key in %s\n", buf); | ||
537 | exit(1); | ||
538 | } | ||
539 | fprintf(f, "%d ", BN_num_bits(public_key->n)); | ||
540 | tmpbuf = BN_bn2dec(public_key->e); | ||
541 | fprintf(f, "%s ", tmpbuf); | ||
542 | free(tmpbuf); | ||
543 | tmpbuf = BN_bn2dec(public_key->n); | ||
544 | fprintf(f, "%s %s\n", tmpbuf, buf2); | ||
545 | free(tmpbuf); | ||
546 | fclose(f); | ||
547 | |||
548 | if (!quiet) | ||
549 | printf("Your public key has been saved in %s\n", buf); | ||
550 | |||
551 | exit(0); | ||
552 | } | ||
@@ -0,0 +1,966 @@ | |||
1 | .\" -*- nroff -*- | ||
2 | .\" | ||
3 | .\" ssh.1.in | ||
4 | .\" | ||
5 | .\" Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | .\" | ||
7 | .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | .\" All rights reserved | ||
9 | .\" | ||
10 | .\" Created: Sat Apr 22 21:55:14 1995 ylo | ||
11 | .\" | ||
12 | .\" $Id: ssh.1,v 1.1 1999/10/27 03:42:45 damien Exp $ | ||
13 | .\" | ||
14 | .Dd September 25, 1999 | ||
15 | .Dt SSH 1 | ||
16 | .Os | ||
17 | .Sh NAME | ||
18 | .Nm ssh | ||
19 | .Nd OpenSSH secure shell client (remote login program) | ||
20 | .Sh SYNOPSIS | ||
21 | .Nm ssh | ||
22 | .Op Fl l Ar login_name | ||
23 | .Op Ar hostname | user@hostname | ||
24 | .Op Ar command | ||
25 | .Pp | ||
26 | .Nm ssh | ||
27 | .Op Fl afgknqtvxCPX | ||
28 | .Op Fl c Ar blowfish | 3des | ||
29 | .Op Fl e Ar escape_char | ||
30 | .Op Fl i Ar identity_file | ||
31 | .Op Fl l Ar login_name | ||
32 | .Op Fl o Ar option | ||
33 | .Op Fl p Ar port | ||
34 | .Oo Fl L Xo | ||
35 | .Sm off | ||
36 | .Ar host : | ||
37 | .Ar port : | ||
38 | .Ar hostport | ||
39 | .Sm on | ||
40 | .Xc | ||
41 | .Oc | ||
42 | .Oo Fl R Xo | ||
43 | .Sm off | ||
44 | .Ar host : | ||
45 | .Ar port : | ||
46 | .Ar hostport | ||
47 | .Sm on | ||
48 | .Xc | ||
49 | .Oc | ||
50 | .Op Ar hostname | user@hostname | ||
51 | .Op Ar command | ||
52 | .Sh DESCRIPTION | ||
53 | .Nm | ||
54 | (Secure Shell) is a program for logging into a remote machine and for | ||
55 | executing commands on a remote machine. It is intended to replace | ||
56 | rlogin and rsh, and provide secure encrypted communications between | ||
57 | two untrusted hosts over an insecure network. X11 connections and | ||
58 | arbitrary TCP/IP ports can also be forwarded over the secure channel. | ||
59 | .Pp | ||
60 | .Nm | ||
61 | connects and logs into the specified | ||
62 | .Ar hostname . | ||
63 | The user must prove | ||
64 | his/her identity to the remote machine using one of several methods. | ||
65 | .Pp | ||
66 | First, if the machine the user logs in from is listed in | ||
67 | .Pa /etc/hosts.equiv | ||
68 | or | ||
69 | .Pa /etc/shosts.equiv | ||
70 | on the remote machine, and the user names are | ||
71 | the same on both sides, the user is immediately permitted to log in. | ||
72 | Second, if | ||
73 | .Pa \&.rhosts | ||
74 | or | ||
75 | .Pa \&.shosts | ||
76 | exists in the user's home directory on the | ||
77 | remote machine and contains a line containing the name of the client | ||
78 | machine and the name of the user on that machine, the user is | ||
79 | permitted to log in. This form of authentication alone is normally not | ||
80 | allowed by the server because it is not secure. | ||
81 | .Pp | ||
82 | The second (and primary) authentication method is the | ||
83 | .Pa rhosts | ||
84 | or | ||
85 | .Pa hosts.equiv | ||
86 | method combined with RSA-based host authentication. It | ||
87 | means that if the login would be permitted by | ||
88 | .Pa \&.rhosts , | ||
89 | .Pa \&.shosts , | ||
90 | .Pa /etc/hosts.equiv , | ||
91 | or | ||
92 | .Pa /etc/shosts.equiv , | ||
93 | and if additionally the server can verify the client's | ||
94 | host key (see | ||
95 | .Pa /etc/ssh_known_hosts | ||
96 | in the | ||
97 | .Sx FILES | ||
98 | section), only then login is | ||
99 | permitted. This authentication method closes security holes due to IP | ||
100 | spoofing, DNS spoofing and routing spoofing. [Note to the | ||
101 | administrator: | ||
102 | .Pa /etc/hosts.equiv , | ||
103 | .Pa \&.rhosts , | ||
104 | and the rlogin/rsh protocol in general, are inherently insecure and should be | ||
105 | disabled if security is desired.] | ||
106 | .Pp | ||
107 | As a third authentication method, | ||
108 | .Nm | ||
109 | supports RSA based authentication. | ||
110 | The scheme is based on public-key cryptography: there are cryptosystems | ||
111 | where encryption and decryption are done using separate keys, and it | ||
112 | is not possible to derive the decryption key from the encryption key. | ||
113 | RSA is one such system. The idea is that each user creates a public/private | ||
114 | key pair for authentication purposes. The | ||
115 | server knows the public key, and only the user knows the private key. | ||
116 | The file | ||
117 | .Pa $HOME/.ssh/authorized_keys | ||
118 | lists the public keys that are permitted for logging | ||
119 | in. When the user logs in, the | ||
120 | .Nm | ||
121 | program tells the server which key pair it would like to use for | ||
122 | authentication. The server checks if this key is permitted, and if | ||
123 | so, sends the user (actually the | ||
124 | .Nm | ||
125 | program running on behalf of the user) a challenge, a random number, | ||
126 | encrypted by the user's public key. The challenge can only be | ||
127 | decrypted using the proper private key. The user's client then decrypts the | ||
128 | challenge using the private key, proving that he/she knows the private | ||
129 | key but without disclosing it to the server. | ||
130 | .Pp | ||
131 | .Nm | ||
132 | implements the RSA authentication protocol automatically. The user | ||
133 | creates his/her RSA key pair by running | ||
134 | .Xr ssh-keygen 1 . | ||
135 | This stores the private key in | ||
136 | .Pa \&.ssh/identity | ||
137 | and the public key in | ||
138 | .Pa \&.ssh/identity.pub | ||
139 | in the user's home directory. The user should then | ||
140 | copy the | ||
141 | .Pa identity.pub | ||
142 | to | ||
143 | .Pa \&.ssh/authorized_keys | ||
144 | in his/her home directory on the remote machine (the | ||
145 | .Pa authorized_keys | ||
146 | file corresponds to the conventional | ||
147 | .Pa \&.rhosts | ||
148 | file, and has one key | ||
149 | per line, though the lines can be very long). After this, the user | ||
150 | can log in without giving the password. RSA authentication is much | ||
151 | more secure than rhosts authentication. | ||
152 | .Pp | ||
153 | The most convenient way to use RSA authentication may be with an | ||
154 | authentication agent. See | ||
155 | .Xr ssh-agent 1 | ||
156 | for more information. | ||
157 | .Pp | ||
158 | If other authentication methods fail, | ||
159 | .Nm | ||
160 | prompts the user for a password. The password is sent to the remote | ||
161 | host for checking; however, since all communications are encrypted, | ||
162 | the password cannot be seen by someone listening on the network. | ||
163 | .Pp | ||
164 | When the user's identity has been accepted by the server, the server | ||
165 | either executes the given command, or logs into the machine and gives | ||
166 | the user a normal shell on the remote machine. All communication with | ||
167 | the remote command or shell will be automatically encrypted. | ||
168 | .Pp | ||
169 | If a pseudo-terminal has been allocated (normal login session), the | ||
170 | user can disconnect with | ||
171 | .Ic ~. , | ||
172 | and suspend | ||
173 | .Nm | ||
174 | with | ||
175 | .Ic ~^Z . | ||
176 | All forwarded connections can be listed with | ||
177 | .Ic ~# | ||
178 | and if | ||
179 | the session blocks waiting for forwarded X11 or TCP/IP | ||
180 | connections to terminate, it can be backgrounded with | ||
181 | .Ic ~& | ||
182 | (this should not be used while the user shell is active, as it can cause the | ||
183 | shell to hang). All available escapes can be listed with | ||
184 | .Ic ~? . | ||
185 | .Pp | ||
186 | A single tilde character can be sent as | ||
187 | .Ic ~~ | ||
188 | (or by following the tilde by a character other than those described above). | ||
189 | The escape character must always follow a newline to be interpreted as | ||
190 | special. The escape character can be changed in configuration files | ||
191 | or on the command line. | ||
192 | .Pp | ||
193 | If no pseudo tty has been allocated, the | ||
194 | session is transparent and can be used to reliably transfer binary | ||
195 | data. On most systems, setting the escape character to | ||
196 | .Dq none | ||
197 | will also make the session transparent even if a tty is used. | ||
198 | .Pp | ||
199 | The session terminates when the command or shell in on the remote | ||
200 | machine exists and all X11 and TCP/IP connections have been closed. | ||
201 | The exit status of the remote program is returned as the exit status | ||
202 | of | ||
203 | .Nm ssh . | ||
204 | .Pp | ||
205 | If the user is using X11 (the | ||
206 | .Ev DISPLAY | ||
207 | environment variable is set), the connection to the X11 display is | ||
208 | automatically forwarded to the remote side in such a way that any X11 | ||
209 | programs started from the shell (or command) will go through the | ||
210 | encrypted channel, and the connection to the real X server will be made | ||
211 | from the local machine. The user should not manually set | ||
212 | .Ev DISPLAY . | ||
213 | Forwarding of X11 connections can be | ||
214 | configured on the command line or in configuration files. | ||
215 | .Pp | ||
216 | The | ||
217 | .Ev DISPLAY | ||
218 | value set by | ||
219 | .Nm | ||
220 | will point to the server machine, but with a display number greater | ||
221 | than zero. This is normal, and happens because | ||
222 | .Nm | ||
223 | creates a | ||
224 | .Dq proxy | ||
225 | X server on the server machine for forwarding the | ||
226 | connections over the encrypted channel. | ||
227 | .Pp | ||
228 | .Nm | ||
229 | will also automatically set up Xauthority data on the server machine. | ||
230 | For this purpose, it will generate a random authorization cookie, | ||
231 | store it in Xauthority on the server, and verify that any forwarded | ||
232 | connections carry this cookie and replace it by the real cookie when | ||
233 | the connection is opened. The real authentication cookie is never | ||
234 | sent to the server machine (and no cookies are sent in the plain). | ||
235 | .Pp | ||
236 | If the user is using an authentication agent, the connection to the agent | ||
237 | is automatically forwarded to the remote side unless disabled on | ||
238 | command line or in a configuration file. | ||
239 | .Pp | ||
240 | Forwarding of arbitrary TCP/IP connections over the secure channel can | ||
241 | be specified either on command line or in a configuration file. One | ||
242 | possible application of TCP/IP forwarding is a secure connection to an | ||
243 | electronic purse; another is going trough firewalls. | ||
244 | .Pp | ||
245 | .Nm | ||
246 | automatically maintains and checks a database containing RSA-based | ||
247 | identifications for all hosts it has ever been used with. The | ||
248 | database is stored in | ||
249 | .Pa \&.ssh/known_hosts | ||
250 | in the user's home directory. Additionally, the file | ||
251 | .Pa /etc/ssh_known_hosts | ||
252 | is automatically checked for known hosts. Any new hosts are | ||
253 | automatically added to the user's file. If a host's identification | ||
254 | ever changes, | ||
255 | .Nm | ||
256 | warns about this and disables password authentication to prevent a | ||
257 | trojan horse from getting the user's password. Another purpose of | ||
258 | this mechanism is to prevent man-in-the-middle attacks which could | ||
259 | otherwise be used to circumvent the encryption. The | ||
260 | .Cm StrictHostKeyChecking | ||
261 | option (see below) can be used to prevent logins to machines whose | ||
262 | host key is not known or has changed. | ||
263 | .Sh OPTIONS | ||
264 | .Bl -tag -width Ds | ||
265 | .It Fl a | ||
266 | Disables forwarding of the authentication agent connection. This may | ||
267 | also be specified on a per-host basis in the configuration file. | ||
268 | .It Fl c Ar blowfish|3des | ||
269 | Selects the cipher to use for encrypting the session. | ||
270 | .Ar 3des | ||
271 | is used by default. It is believed to be secure. | ||
272 | .Ar 3des | ||
273 | (triple-des) is an encrypt-decrypt-encrypt triple with three different keys. | ||
274 | It is presumably more secure than the | ||
275 | .Ar des | ||
276 | cipher which is no longer supported in ssh. | ||
277 | .Ar blowfish | ||
278 | is a fast block cipher, it appears very secure and is much faster than | ||
279 | .Ar 3des . | ||
280 | .It Fl e Ar ch|^ch|none | ||
281 | Sets the escape character for sessions with a pty (default: | ||
282 | .Ql ~ ) . | ||
283 | The escape character is only recognized at the beginning of a line. The | ||
284 | escape character followed by a dot | ||
285 | .Pq Ql \&. | ||
286 | closes the connection, followed | ||
287 | by control-Z suspends the connection, and followed by itself sends the | ||
288 | escape character once. Setting the character to | ||
289 | .Dq none | ||
290 | disables any escapes and makes the session fully transparent. | ||
291 | .It Fl f | ||
292 | Requests | ||
293 | .Nm | ||
294 | to go to background after authentication. This is useful | ||
295 | if | ||
296 | .Nm | ||
297 | is going to ask for passwords or passphrases, but the user | ||
298 | wants it in the background. This implies | ||
299 | .Fl n . | ||
300 | The recommended way to start X11 programs at a remote site is with | ||
301 | something like | ||
302 | .Ic ssh -f host xterm . | ||
303 | .It Fl i Ar identity_file | ||
304 | Selects the file from which the identity (private key) for | ||
305 | RSA authentication is read. Default is | ||
306 | .Pa \&.ssh/identity | ||
307 | in the user's home directory. Identity files may also be specified on | ||
308 | a per-host basis in the configuration file. It is possible to have | ||
309 | multiple | ||
310 | .Fl i | ||
311 | options (and multiple identities specified in | ||
312 | configuration files). | ||
313 | .It Fl g | ||
314 | Allows remote hosts to connect to local forwarded ports. | ||
315 | .It Fl k | ||
316 | Disables forwarding of Kerberos tickets and AFS tokens. This may | ||
317 | also be specified on a per-host basis in the configuration file. | ||
318 | .It Fl l Ar login_name | ||
319 | Specifies the user to log in as on the remote machine. This may also | ||
320 | be specified on a per-host basis in the configuration file. | ||
321 | .It Fl n | ||
322 | Redirects stdin from | ||
323 | .Pa /dev/null | ||
324 | (actually, prevents reading from stdin). | ||
325 | This must be used when | ||
326 | .Nm | ||
327 | is run in the background. A common trick is to use this to run X11 | ||
328 | programs in a remote machine. For example, | ||
329 | .Ic ssh -n shadows.cs.hut.fi emacs & | ||
330 | will start an emacs on shadows.cs.hut.fi, and the X11 | ||
331 | connection will be automatically forwarded over an encrypted channel. | ||
332 | The | ||
333 | .Nm | ||
334 | program will be put in the background. | ||
335 | (This does not work if | ||
336 | .Nm | ||
337 | needs to ask for a password or passphrase; see also the | ||
338 | .Fl f | ||
339 | option.) | ||
340 | .It Fl o Ar option | ||
341 | Can be used to give options in the format used in the config file. | ||
342 | This is useful for specifying options for which there is no separate | ||
343 | command-line flag. The option has the same format as a line in the | ||
344 | configuration file. | ||
345 | .It Fl p Ar port | ||
346 | Port to connect to on the remote host. This can be specified on a | ||
347 | per-host basis in the configuration file. | ||
348 | .It Fl P | ||
349 | Use a non-privileged port for outgoing connections. | ||
350 | This can be used if your firewall does | ||
351 | not permit connections from privileged ports. | ||
352 | Note that this option turns of | ||
353 | .Cm RhostsAuthentication | ||
354 | and | ||
355 | .Cm RhostsRSAAuthentication . | ||
356 | .It Fl q | ||
357 | Quiet mode. Causes all warning and diagnostic messages to be | ||
358 | suppressed. Only fatal errors are displayed. | ||
359 | .It Fl t | ||
360 | Force pseudo-tty allocation. This can be used to execute arbitary | ||
361 | screen-based programs on a remote machine, which can be very useful | ||
362 | e.g. when implementing menu services. | ||
363 | .It Fl v | ||
364 | Verbose mode. Causes | ||
365 | .Nm | ||
366 | to print debugging messages about its progress. This is helpful in | ||
367 | debugging connection, authentication, and configuration problems. | ||
368 | The verbose mode is also used to display | ||
369 | .Xr skey 1 | ||
370 | challenges, if the user entered "s/key" as password. | ||
371 | .It Fl x | ||
372 | Disables X11 forwarding. This can also be specified on a per-host | ||
373 | basis in a configuration file. | ||
374 | .It Fl X | ||
375 | Enables X11 forwarding. | ||
376 | .It Fl C | ||
377 | Requests compression of all data (including stdin, stdout, stderr, and | ||
378 | data for forwarded X11 and TCP/IP connections). The compression | ||
379 | algorithm is the same used by gzip, and the | ||
380 | .Dq level | ||
381 | can be controlled by the | ||
382 | .Cm CompressionLevel | ||
383 | option (see below). Compression is desirable on modem lines and other | ||
384 | slow connections, but will only slow down things on fast networks. | ||
385 | The default value can be set on a host-by-host basis in the | ||
386 | configuration files; see the | ||
387 | .Cm Compress | ||
388 | option below. | ||
389 | .It Fl L Ar port:host:hostport | ||
390 | Specifies that the given port on the local (client) host is to be | ||
391 | forwarded to the given host and port on the remote side. This works | ||
392 | by allocating a socket to listen to | ||
393 | .Ar port | ||
394 | on the local side, and whenever a connection is made to this port, the | ||
395 | connection is forwarded over the secure channel, and a connection is | ||
396 | made to | ||
397 | .Ar host:hostport | ||
398 | from the remote machine. Port forwardings can also be specified in the | ||
399 | configuration file. Only root can forward privileged ports. | ||
400 | .It Fl R Ar port:host:hostport | ||
401 | Specifies that the given port on the remote (server) host is to be | ||
402 | forwarded to the given host and port on the local side. This works | ||
403 | by allocating a socket to listen to | ||
404 | .Ar port | ||
405 | on the remote side, and whenever a connection is made to this port, the | ||
406 | connection is forwarded over the secure channel, and a connection is | ||
407 | made to | ||
408 | .Ar host:hostport | ||
409 | from the local machine. Port forwardings can also be specified in the | ||
410 | configuration file. Privileged ports can be forwarded only when | ||
411 | logging in as root on the remote machine. | ||
412 | .El | ||
413 | .Sh CONFIGURATION FILES | ||
414 | .Nm | ||
415 | obtains configuration data from the following sources (in this order): | ||
416 | command line options, user's configuration file | ||
417 | .Pq Pa $HOME/.ssh/config , | ||
418 | and system-wide configuration file | ||
419 | .Pq Pa /etc/ssh_config . | ||
420 | For each parameter, the first obtained value | ||
421 | will be used. The configuration files contain sections bracketed by | ||
422 | "Host" specifications, and that section is only applied for hosts that | ||
423 | match one of the patterns given in the specification. The matched | ||
424 | host name is the one given on the command line. | ||
425 | .Pp | ||
426 | Since the first obtained value for each parameter is used, more | ||
427 | host-specific declarations should be given near the beginning of the | ||
428 | file, and general defaults at the end. | ||
429 | .Pp | ||
430 | The configuration file has the following format: | ||
431 | .Pp | ||
432 | Empty lines and lines starting with | ||
433 | .Ql # | ||
434 | are comments. | ||
435 | .Pp | ||
436 | Otherwise a line is of the format | ||
437 | .Dq keyword arguments . | ||
438 | The possible | ||
439 | keywords and their meanings are as follows (note that the | ||
440 | configuration files are case-sensitive): | ||
441 | .Bl -tag -width Ds | ||
442 | .It Cm Host | ||
443 | Restricts the following declarations (up to the next | ||
444 | .Cm Host | ||
445 | keyword) to be only for those hosts that match one of the patterns | ||
446 | given after the keyword. | ||
447 | .Ql \&* | ||
448 | and | ||
449 | .Ql ? | ||
450 | can be used as wildcards in the | ||
451 | patterns. A single | ||
452 | .Ql \&* | ||
453 | as a pattern can be used to provide global | ||
454 | defaults for all hosts. The host is the | ||
455 | .Ar hostname | ||
456 | argument given on the command line (i.e., the name is not converted to | ||
457 | a canonicalized host name before matching). | ||
458 | .It Cm AFSTokenPassing | ||
459 | Specifies whether to pass AFS tokens to remote host. The argument to | ||
460 | this keyword must be | ||
461 | .Dq yes | ||
462 | or | ||
463 | .Dq no . | ||
464 | .It Cm BatchMode | ||
465 | If set to | ||
466 | .Dq yes , | ||
467 | passphrase/password querying will be disabled. This | ||
468 | option is useful in scripts and other batch jobs where you have no | ||
469 | user to supply the password. The argument must be | ||
470 | .Dq yes | ||
471 | or | ||
472 | .Dq no . | ||
473 | .It Cm Cipher | ||
474 | Specifies the cipher to use for encrypting the session. Currently, | ||
475 | .Dq blowfish , | ||
476 | and | ||
477 | .Dq 3des | ||
478 | are supported. The default is | ||
479 | .Dq 3des . | ||
480 | .It Cm Compression | ||
481 | Specifies whether to use compression. The argument must be | ||
482 | .Dq yes | ||
483 | or | ||
484 | .Dq no . | ||
485 | .It Cm CompressionLevel | ||
486 | Specifies the compression level to use if compression is enable. The | ||
487 | argument must be an integer from 1 (fast) to 9 (slow, best). The | ||
488 | default level is 6, which is good for most applications. The meaning | ||
489 | of the values is the same as in GNU GZIP. | ||
490 | .It Cm ConnectionAttempts | ||
491 | Specifies the number of tries (one per second) to make before falling | ||
492 | back to rsh or exiting. The argument must be an integer. This may be | ||
493 | useful in scripts if the connection sometimes fails. | ||
494 | .It Cm EscapeChar | ||
495 | Sets the escape character (default: | ||
496 | .Ql ~ ) . | ||
497 | The escape character can also | ||
498 | be set on the command line. The argument should be a single | ||
499 | character, | ||
500 | .Ql ^ | ||
501 | followed by a letter, or | ||
502 | .Dq none | ||
503 | to disable the escape | ||
504 | character entirely (making the connection transparent for binary | ||
505 | data). | ||
506 | .It Cm FallBackToRsh | ||
507 | Specifies that if connecting via | ||
508 | .Nm | ||
509 | fails due to a connection refused error (there is no | ||
510 | .Xr sshd 8 | ||
511 | listening on the remote host), | ||
512 | .Xr rsh 1 | ||
513 | should automatically be used instead (after a suitable warning about | ||
514 | the session being unencrypted). The argument must be | ||
515 | .Dq yes | ||
516 | or | ||
517 | .Dq no . | ||
518 | .It Cm ForwardAgent | ||
519 | Specifies whether the connection to the authentication agent (if any) | ||
520 | will be forwarded to the remote machine. The argument must be | ||
521 | .Dq yes | ||
522 | or | ||
523 | .Dq no . | ||
524 | .It Cm ForwardX11 | ||
525 | Specifies whether X11 connections will be automatically redirected | ||
526 | over the secure channel and | ||
527 | .Ev DISPLAY | ||
528 | set. The argument must be | ||
529 | .Dq yes | ||
530 | or | ||
531 | .Dq no . | ||
532 | .It Cm GatewayPorts | ||
533 | Specifies whether remote hosts are allowed to connect to local | ||
534 | forwarded ports. | ||
535 | The argument must be | ||
536 | .Dq yes | ||
537 | or | ||
538 | .Dq no . | ||
539 | The default is | ||
540 | .Dq no . | ||
541 | .It Cm GlobalKnownHostsFile | ||
542 | Specifies a file to use instead of | ||
543 | .Pa /etc/ssh_known_hosts . | ||
544 | .It Cm HostName | ||
545 | Specifies the real host name to log into. This can be used to specify | ||
546 | nicnames or abbreviations for hosts. Default is the name given on the | ||
547 | command line. Numeric IP addresses are also permitted (both on the | ||
548 | command line and in | ||
549 | .Cm HostName | ||
550 | specifications). | ||
551 | .It Cm IdentityFile | ||
552 | Specifies the file from which the user's RSA authentication identity | ||
553 | is read (default | ||
554 | .Pa .ssh/identity | ||
555 | in the user's home directory). | ||
556 | Additionally, any identities represented by the authentication agent | ||
557 | will be used for authentication. The file name may use the tilde | ||
558 | syntax to refer to a user's home directory. It is possible to have | ||
559 | multiple identity files specified in configuration files; all these | ||
560 | identities will be tried in sequence. | ||
561 | .It Cm KeepAlive | ||
562 | Specifies whether the system should send keepalive messages to the | ||
563 | other side. If they are sent, death of the connection or crash of one | ||
564 | of the machines will be properly noticed. However, this means that | ||
565 | connections will die if the route is down temporarily, and some people | ||
566 | find it annoying. | ||
567 | .Pp | ||
568 | The default is | ||
569 | .Dq yes | ||
570 | (to send keepalives), and the client will notice | ||
571 | if the network goes down or the remote host dies. This is important | ||
572 | in scripts, and many users want it too. | ||
573 | .Pp | ||
574 | To disable keepalives, the value should be set to | ||
575 | .Dq no | ||
576 | in both the server and the client configuration files. | ||
577 | .It Cm KerberosAuthentication | ||
578 | Specifies whether Kerberos authentication will be used. The argument to | ||
579 | this keyword must be | ||
580 | .Dq yes | ||
581 | or | ||
582 | .Dq no . | ||
583 | .It Cm KerberosTgtPassing | ||
584 | Specifies whether a Kerberos TGT will be forwarded to the server. This | ||
585 | will only work if the Kerberos server is actually an AFS kaserver. The | ||
586 | argument to this keyword must be | ||
587 | .Dq yes | ||
588 | or | ||
589 | .Dq no . | ||
590 | .It Cm LocalForward | ||
591 | Specifies that a TCP/IP port on the local machine be forwarded over | ||
592 | the secure channel to given host:port from the remote machine. The | ||
593 | first argument must be a port number, and the second must be | ||
594 | host:port. Multiple forwardings may be specified, and additional | ||
595 | forwardings can be given on the command line. Only the root can | ||
596 | forward privileged ports. | ||
597 | .It Cm PasswordAuthentication | ||
598 | Specifies whether to use password authentication. The argument to | ||
599 | this keyword must be | ||
600 | .Dq yes | ||
601 | or | ||
602 | .Dq no . | ||
603 | .It Cm NumberOfPasswordPrompts | ||
604 | Specifies the number of password prompts before giving up. The | ||
605 | argument to this keyword must be an integer. Default is 3. | ||
606 | .It Cm Port | ||
607 | Specifies the port number to connect on the remote host. Default is | ||
608 | 22. | ||
609 | .It Cm ProxyCommand | ||
610 | Specifies the command to use to connect to the server. The command | ||
611 | string extends to the end of the line, and is executed with /bin/sh. | ||
612 | In the command string, %h will be substituted by the host name to | ||
613 | connect and %p by the port. The command can be basically anything, | ||
614 | and should read from its stdin and write to its stdout. It should | ||
615 | eventually connect an | ||
616 | .Xr sshd 8 | ||
617 | server running on some machine, or execute | ||
618 | .Ic sshd -i | ||
619 | somewhere. Host key management will be done using the | ||
620 | HostName of the host being connected (defaulting to the name typed by | ||
621 | the user). | ||
622 | .Pp | ||
623 | .It Cm RemoteForward | ||
624 | Specifies that a TCP/IP port on the remote machine be forwarded over | ||
625 | the secure channel to given host:port from the local machine. The | ||
626 | first argument must be a port number, and the second must be | ||
627 | host:port. Multiple forwardings may be specified, and additional | ||
628 | forwardings can be given on the command line. Only the root can | ||
629 | forward privileged ports. | ||
630 | .It Cm RhostsAuthentication | ||
631 | Specifies whether to try rhosts based authentication. Note that this | ||
632 | declaration only affects the client side and has no effect whatsoever | ||
633 | on security. Disabling rhosts authentication may reduce | ||
634 | authentication time on slow connections when rhosts authentication is | ||
635 | not used. Most servers do not permit RhostsAuthentication because it | ||
636 | is not secure (see RhostsRSAAuthentication). The argument to this | ||
637 | keyword must be | ||
638 | .Dq yes | ||
639 | or | ||
640 | .Dq no . | ||
641 | .It Cm RhostsRSAAuthentication | ||
642 | Specifies whether to try rhosts based authentication with RSA host | ||
643 | authentication. This is the primary authentication method for most | ||
644 | sites. The argument must be | ||
645 | .Dq yes | ||
646 | or | ||
647 | .Dq no . | ||
648 | .It Cm RSAAuthentication | ||
649 | Specifies whether to try RSA authentication. The argument to this | ||
650 | keyword must be | ||
651 | .Dq yes | ||
652 | or | ||
653 | .Dq no . | ||
654 | RSA authentication will only be | ||
655 | attempted if the identity file exists, or an authentication agent is | ||
656 | running. | ||
657 | .It Cm CheckHostIP | ||
658 | If this flag is set to | ||
659 | .Dq yes , | ||
660 | ssh will additionally check the host ip address in the | ||
661 | .Pa known_hosts | ||
662 | file. This allows ssh to detect if a host key changed due to DNS spoofing. | ||
663 | If the option is set to | ||
664 | .Dq no , | ||
665 | the check will not be executed. | ||
666 | .It Cm StrictHostKeyChecking | ||
667 | If this flag is set to | ||
668 | .Dq yes , | ||
669 | .Nm | ||
670 | ssh will never automatically add host keys to the | ||
671 | .Pa $HOME/.ssh/known_hosts | ||
672 | file, and refuses to connect hosts whose host key has changed. This | ||
673 | provides maximum protection against trojan horse attacks. However, it | ||
674 | can be somewhat annoying if you don't have good | ||
675 | .Pa /etc/ssh_known_hosts | ||
676 | files installed and frequently | ||
677 | connect new hosts. Basically this option forces the user to manually | ||
678 | add any new hosts. Normally this option is disabled, and new hosts | ||
679 | will automatically be added to the known host files. The host keys of | ||
680 | known hosts will be verified automatically in either case. The | ||
681 | argument must be | ||
682 | .Dq yes | ||
683 | or | ||
684 | .Dq no . | ||
685 | .It Cm User | ||
686 | Specifies the user to log in as. This can be useful if you have a | ||
687 | different user name in different machines. This saves the trouble of | ||
688 | having to remember to give the user name on the command line. | ||
689 | .It Cm UserKnownHostsFile | ||
690 | Specifies a file to use instead of | ||
691 | .Pa $HOME/.ssh/known_hosts . | ||
692 | .It Cm UsePrivilegedPort | ||
693 | Specifies whether to use a privileged port for outgoing connections. | ||
694 | The argument must be | ||
695 | .Dq yes | ||
696 | or | ||
697 | .Dq no . | ||
698 | The default is | ||
699 | .Dq yes . | ||
700 | Note that setting this option to | ||
701 | .Dq no | ||
702 | turns of | ||
703 | .Cm RhostsAuthentication | ||
704 | and | ||
705 | .Cm RhostsRSAAuthentication . | ||
706 | .It Cm UseRsh | ||
707 | Specifies that rlogin/rsh should be used for this host. It is | ||
708 | possible that the host does not at all support the | ||
709 | .Nm | ||
710 | protocol. This causes | ||
711 | .Nm | ||
712 | to immediately exec | ||
713 | .Xr rsh 1 . | ||
714 | All other options (except | ||
715 | .Cm HostName ) | ||
716 | are ignored if this has been specified. The argument must be | ||
717 | .Dq yes | ||
718 | or | ||
719 | .Dq no . | ||
720 | .Sh ENVIRONMENT | ||
721 | .Nm | ||
722 | will normally set the following environment variables: | ||
723 | .Bl -tag -width Ds | ||
724 | .It Ev DISPLAY | ||
725 | The | ||
726 | .Ev DISPLAY | ||
727 | variable indicates the location of the X11 server. It is | ||
728 | automatically set by | ||
729 | .Nm | ||
730 | to point to a value of the form | ||
731 | .Dq hostname:n | ||
732 | where hostname indicates | ||
733 | the host where the shell runs, and n is an integer >= 1. Ssh uses | ||
734 | this special value to forward X11 connections over the secure | ||
735 | channel. The user should normally not set DISPLAY explicitly, as that | ||
736 | will render the X11 connection insecure (and will require the user to | ||
737 | manually copy any required authorization cookies). | ||
738 | .It Ev HOME | ||
739 | Set to the path of the user's home directory. | ||
740 | .It Ev LOGNAME | ||
741 | Synonym for | ||
742 | .Ev USER ; | ||
743 | set for compatibility with systems that use this variable. | ||
744 | .It Ev MAIL | ||
745 | Set to point the user's mailbox. | ||
746 | .It Ev PATH | ||
747 | Set to the default | ||
748 | .Ev PATH , | ||
749 | as specified when compiling | ||
750 | .Nm ssh . | ||
751 | .It Ev SSH_AUTH_SOCK | ||
752 | indicates the path of a unix-domain socket used to communicate with the | ||
753 | agent. | ||
754 | .It Ev SSH_CLIENT | ||
755 | Identifies the client end of the connection. The variable contains | ||
756 | three space-separated values: client ip-address, client port number, | ||
757 | and server port number. | ||
758 | .It Ev SSH_TTY | ||
759 | This is set to the name of the tty (path to the device) associated | ||
760 | with the current shell or command. If the current session has no tty, | ||
761 | this variable is not set. | ||
762 | .It Ev TZ | ||
763 | The timezone variable is set to indicate the present timezone if it | ||
764 | was set when the daemon was started (e.i., the daemon passes the value | ||
765 | on to new connections). | ||
766 | .It Ev USER | ||
767 | Set to the name of the user logging in. | ||
768 | .El | ||
769 | .Pp | ||
770 | Additionally, | ||
771 | .Nm | ||
772 | reads | ||
773 | .Pa $HOME/.ssh/environment , | ||
774 | and adds lines of the format | ||
775 | .Dq VARNAME=value | ||
776 | to the environment. | ||
777 | .Sh FILES | ||
778 | .Bl -tag -width $HOME/.ssh/known_hosts | ||
779 | .It Pa $HOME/.ssh/known_hosts | ||
780 | Records host keys for all hosts the user has logged into (that are not | ||
781 | in | ||
782 | .Pa /etc/ssh_known_hosts ) . | ||
783 | See | ||
784 | .Xr sshd 8 . | ||
785 | .It Pa $HOME/.ssh/random_seed | ||
786 | Used for seeding the random number generator. This file contains | ||
787 | sensitive data and should read/write for the user and not accessible | ||
788 | for others. This file is created the first time the program is run | ||
789 | and updated automatically. The user should never need to read or | ||
790 | modify this file. | ||
791 | .It Pa $HOME/.ssh/identity | ||
792 | Contains the RSA authentication identity of the user. This file | ||
793 | contains sensitive data and should be readable by the user but not | ||
794 | accessible by others (read/write/execute). | ||
795 | Note that | ||
796 | .Nm | ||
797 | ignores this file if it is accessible by others. | ||
798 | It is possible to specify a passphrase when | ||
799 | generating the key; the passphrase will be used to encrypt the | ||
800 | sensitive part of this file using 3DES. | ||
801 | .It Pa $HOME/.ssh/identity.pub | ||
802 | Contains the public key for authentication (public part of the | ||
803 | identity file in human-readable form). The contents of this file | ||
804 | should be added to | ||
805 | .Pa $HOME/.ssh/authorized_keys | ||
806 | on all machines | ||
807 | where you wish to log in using RSA authentication. This file is not | ||
808 | sensitive and can (but need not) be readable by anyone. This file is | ||
809 | never used automatically and is not necessary; it is only provided for | ||
810 | the convenience of the user. | ||
811 | .It Pa $HOME/.ssh/config | ||
812 | This is the per-user configuration file. The format of this file is | ||
813 | described above. This file is used by the | ||
814 | .Nm | ||
815 | client. This file does not usually contain any sensitive information, | ||
816 | but the recommended permissions are read/write for the user, and not | ||
817 | accessible by others. | ||
818 | .It Pa $HOME/.ssh/authorized_keys | ||
819 | Lists the RSA keys that can be used for logging in as this user. The | ||
820 | format of this file is described in the | ||
821 | .Xr sshd 8 | ||
822 | manual page. In the simplest form the format is the same as the .pub | ||
823 | identity files (that is, each line contains the number of bits in | ||
824 | modulus, public exponent, modulus, and comment fields, separated by | ||
825 | spaces). This file is not highly sensitive, but the recommended | ||
826 | permissions are read/write for the user, and not accessible by others. | ||
827 | .It Pa /etc/ssh_known_hosts | ||
828 | Systemwide list of known host keys. This file should be prepared by the | ||
829 | system administrator to contain the public host keys of all machines in the | ||
830 | organization. This file should be world-readable. This file contains | ||
831 | public keys, one per line, in the following format (fields separated | ||
832 | by spaces): system name, number of bits in modulus, public exponent, | ||
833 | modulus, and optional comment field. When different names are used | ||
834 | for the same machine, all such names should be listed, separated by | ||
835 | commas. The format is described on the | ||
836 | .Xr sshd 8 | ||
837 | manual page. | ||
838 | .Pp | ||
839 | The canonical system name (as returned by name servers) is used by | ||
840 | .Xr sshd 8 | ||
841 | to verify the client host when logging in; other names are needed because | ||
842 | .Nm | ||
843 | does not convert the user-supplied name to a canonical name before | ||
844 | checking the key, because someone with access to the name servers | ||
845 | would then be able to fool host authentication. | ||
846 | .It Pa /etc/ssh_config | ||
847 | Systemwide configuration file. This file provides defaults for those | ||
848 | values that are not specified in the user's configuration file, and | ||
849 | for those users who do not have a configuration file. This file must | ||
850 | be world-readable. | ||
851 | .It Pa $HOME/.rhosts | ||
852 | This file is used in | ||
853 | .Pa \&.rhosts | ||
854 | authentication to list the | ||
855 | host/user pairs that are permitted to log in. (Note that this file is | ||
856 | also used by rlogin and rsh, which makes using this file insecure.) | ||
857 | Each line of the file contains a host name (in the canonical form | ||
858 | returned by name servers), and then a user name on that host, | ||
859 | separated by a space. One some machines this file may need to be | ||
860 | world-readable if the user's home directory is on a NFS partition, | ||
861 | because | ||
862 | .Xr sshd 8 | ||
863 | reads it as root. Additionally, this file must be owned by the user, | ||
864 | and must not have write permissions for anyone else. The recommended | ||
865 | permission for most machines is read/write for the user, and not | ||
866 | accessible by others. | ||
867 | .Pp | ||
868 | Note that by default | ||
869 | .Xr sshd 8 | ||
870 | will be installed so that it requires successful RSA host | ||
871 | authentication before permitting \s+2.\s0rhosts authentication. If your | ||
872 | server machine does not have the client's host key in | ||
873 | .Pa /etc/ssh_known_hosts , | ||
874 | you can store it in | ||
875 | .Pa $HOME/.ssh/known_hosts . | ||
876 | The easiest way to do this is to | ||
877 | connect back to the client from the server machine using ssh; this | ||
878 | will automatically add the host key inxi | ||
879 | .Pa $HOME/.ssh/known_hosts . | ||
880 | .It Pa $HOME/.shosts | ||
881 | This file is used exactly the same way as | ||
882 | .Pa \&.rhosts . | ||
883 | The purpose for | ||
884 | having this file is to be able to use rhosts authentication with | ||
885 | .Nm | ||
886 | without permitting login with | ||
887 | .Xr rlogin 1 | ||
888 | or | ||
889 | .Xr rsh 1 . | ||
890 | .It Pa /etc/hosts.equiv | ||
891 | This file is used during | ||
892 | .Pa \&.rhosts authentication. It contains | ||
893 | canonical hosts names, one per line (the full format is described on | ||
894 | the | ||
895 | .Xr sshd 8 | ||
896 | manual page). If the client host is found in this file, login is | ||
897 | automatically permitted provided client and server user names are the | ||
898 | same. Additionally, successful RSA host authentication is normally | ||
899 | required. This file should only be writable by root. | ||
900 | .It Pa /etc/shosts.equiv | ||
901 | This file is processed exactly as | ||
902 | .Pa /etc/hosts.equiv . | ||
903 | This file may be useful to permit logins using | ||
904 | .Nm | ||
905 | but not using rsh/rlogin. | ||
906 | .It Pa /etc/sshrc | ||
907 | Commands in this file are executed by | ||
908 | .Nm | ||
909 | when the user logs in just before the user's shell (or command) is started. | ||
910 | See the | ||
911 | .Xr sshd 8 | ||
912 | manual page for more information. | ||
913 | .It Pa $HOME/.ssh/rc | ||
914 | Commands in this file are executed by | ||
915 | .Nm | ||
916 | when the user logs in just before the user's shell (or command) is | ||
917 | started. | ||
918 | See the | ||
919 | .Xr sshd 8 | ||
920 | manual page for more information. | ||
921 | .It Pa libcrypto.so.X.1 | ||
922 | A version of this library which includes support for the RSA algorithm | ||
923 | is required for proper operation. | ||
924 | .Sh AUTHOR | ||
925 | Tatu Ylonen <ylo@cs.hut.fi> | ||
926 | .Pp | ||
927 | Issues can be found from the SSH WWW home page: | ||
928 | .Pp | ||
929 | .Dl http://www.cs.hut.fi/ssh | ||
930 | .Pp | ||
931 | OpenSSH | ||
932 | is a derivative of the original (free) ssh 1.2.12 release, but with bugs | ||
933 | removed and newer features re-added. Rapidly after the 1.2.12 release, | ||
934 | newer versions bore successively more restrictive licenses. This version | ||
935 | of OpenSSH | ||
936 | .Bl -bullet | ||
937 | .It | ||
938 | has all components of a restrictive nature (ie. patents, see | ||
939 | .Xr ssl 8 ) | ||
940 | directly removed from the source code; any licensed or patented components | ||
941 | are chosen from | ||
942 | external libraries. | ||
943 | .It | ||
944 | has been updated to support ssh protocol 1.5. | ||
945 | .It | ||
946 | contains added support for | ||
947 | .Xr kerberos 8 | ||
948 | authentication and ticket passing. | ||
949 | .It | ||
950 | supports one-time password authentication with | ||
951 | .Xr skey 1 . | ||
952 | .El | ||
953 | .Pp | ||
954 | The libraries described in | ||
955 | .Xr ssl 8 | ||
956 | are required for proper operation. | ||
957 | .Sh SEE ALSO | ||
958 | .Xr rlogin 1 , | ||
959 | .Xr rsh 1 , | ||
960 | .Xr scp 1 , | ||
961 | .Xr ssh-add 1 , | ||
962 | .Xr ssh-agent 1 , | ||
963 | .Xr ssh-keygen 1 , | ||
964 | .Xr telnet 1 , | ||
965 | .Xr sshd 8 , | ||
966 | .Xr ssl 8 | ||
@@ -0,0 +1,809 @@ | |||
1 | /* | ||
2 | |||
3 | ssh.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Mar 18 16:36:11 1995 ylo | ||
11 | |||
12 | Ssh client program. This program can be used to log into a remote machine. | ||
13 | The software supports strong authentication, encryption, and forwarding | ||
14 | of X11, TCP/IP, and authentication connections. | ||
15 | |||
16 | Modified to work with SSL by Niels Provos <provos@citi.umich.edu> in Canada. | ||
17 | |||
18 | */ | ||
19 | |||
20 | #include "includes.h" | ||
21 | RCSID("$Id: ssh.c,v 1.1 1999/10/27 03:42:45 damien Exp $"); | ||
22 | |||
23 | #include "xmalloc.h" | ||
24 | #include "ssh.h" | ||
25 | #include "packet.h" | ||
26 | #include "buffer.h" | ||
27 | #include "authfd.h" | ||
28 | #include "readconf.h" | ||
29 | #include "uidswap.h" | ||
30 | |||
31 | /* Flag indicating whether debug mode is on. This can be set on the | ||
32 | command line. */ | ||
33 | int debug_flag = 0; | ||
34 | |||
35 | /* Flag indicating whether quiet mode is on. */ | ||
36 | int quiet_flag = 0; | ||
37 | |||
38 | /* Flag indicating whether to allocate a pseudo tty. This can be set on the | ||
39 | command line, and is automatically set if no command is given on the command | ||
40 | line. */ | ||
41 | int tty_flag = 0; | ||
42 | |||
43 | /* Flag indicating that nothing should be read from stdin. This can be set | ||
44 | on the command line. */ | ||
45 | int stdin_null_flag = 0; | ||
46 | |||
47 | /* Flag indicating that ssh should fork after authentication. This is useful | ||
48 | so that the pasphrase can be entered manually, and then ssh goes to the | ||
49 | background. */ | ||
50 | int fork_after_authentication_flag = 0; | ||
51 | |||
52 | /* General data structure for command line options and options configurable | ||
53 | in configuration files. See readconf.h. */ | ||
54 | Options options; | ||
55 | |||
56 | /* Name of the host we are connecting to. This is the name given on the | ||
57 | command line, or the HostName specified for the user-supplied name | ||
58 | in a configuration file. */ | ||
59 | char *host; | ||
60 | |||
61 | /* socket address the host resolves to */ | ||
62 | struct sockaddr_in hostaddr; | ||
63 | |||
64 | /* Flag to indicate that we have received a window change signal which has | ||
65 | not yet been processed. This will cause a message indicating the new | ||
66 | window size to be sent to the server a little later. This is volatile | ||
67 | because this is updated in a signal handler. */ | ||
68 | volatile int received_window_change_signal = 0; | ||
69 | |||
70 | /* Value of argv[0] (set in the main program). */ | ||
71 | char *av0; | ||
72 | |||
73 | /* Flag indicating whether we have a valid host private key loaded. */ | ||
74 | int host_private_key_loaded = 0; | ||
75 | |||
76 | /* Host private key. */ | ||
77 | RSA *host_private_key = NULL; | ||
78 | |||
79 | /* Original real UID. */ | ||
80 | uid_t original_real_uid; | ||
81 | |||
82 | /* Prints a help message to the user. This function never returns. */ | ||
83 | |||
84 | void | ||
85 | usage() | ||
86 | { | ||
87 | fprintf(stderr, "Usage: %s [options] host [command]\n", av0); | ||
88 | fprintf(stderr, "Options:\n"); | ||
89 | fprintf(stderr, " -l user Log in using this user name.\n"); | ||
90 | fprintf(stderr, " -n Redirect input from /dev/null.\n"); | ||
91 | fprintf(stderr, " -a Disable authentication agent forwarding.\n"); | ||
92 | #ifdef AFS | ||
93 | fprintf(stderr, " -k Disable Kerberos ticket and AFS token forwarding.\n"); | ||
94 | #endif /* AFS */ | ||
95 | fprintf(stderr, " -x Disable X11 connection forwarding.\n"); | ||
96 | fprintf(stderr, " -i file Identity for RSA authentication (default: ~/.ssh/identity).\n"); | ||
97 | fprintf(stderr, " -t Tty; allocate a tty even if command is given.\n"); | ||
98 | fprintf(stderr, " -v Verbose; display verbose debugging messages.\n"); | ||
99 | fprintf(stderr, " -V Display version number only.\n"); | ||
100 | fprintf(stderr, " -P Don't allocate a privileged port.\n"); | ||
101 | fprintf(stderr, " -q Quiet; don't display any warning messages.\n"); | ||
102 | fprintf(stderr, " -f Fork into background after authentication.\n"); | ||
103 | fprintf(stderr, " -e char Set escape character; ``none'' = disable (default: ~).\n"); | ||
104 | |||
105 | fprintf(stderr, " -c cipher Select encryption algorithm: " | ||
106 | "``3des'', " | ||
107 | "``blowfish''\n"); | ||
108 | fprintf(stderr, " -p port Connect to this port. Server must be on the same port.\n"); | ||
109 | fprintf(stderr, " -L listen-port:host:port Forward local port to remote address\n"); | ||
110 | fprintf(stderr, " -R listen-port:host:port Forward remote port to local address\n"); | ||
111 | fprintf(stderr, " These cause %s to listen for connections on a port, and\n", av0); | ||
112 | fprintf(stderr, " forward them to the other side by connecting to host:port.\n"); | ||
113 | fprintf(stderr, " -C Enable compression.\n"); | ||
114 | fprintf(stderr, " -g Allow remote hosts to connect to forwarded ports.\n"); | ||
115 | fprintf(stderr, " -o 'option' Process the option as if it was read from a configuration file.\n"); | ||
116 | exit(1); | ||
117 | } | ||
118 | |||
119 | /* Connects to the given host using rsh (or prints an error message and exits | ||
120 | if rsh is not available). This function never returns. */ | ||
121 | |||
122 | void | ||
123 | rsh_connect(char *host, char *user, Buffer *command) | ||
124 | { | ||
125 | char *args[10]; | ||
126 | int i; | ||
127 | |||
128 | log("Using rsh. WARNING: Connection will not be encrypted."); | ||
129 | /* Build argument list for rsh. */ | ||
130 | i = 0; | ||
131 | args[i++] = _PATH_RSH; | ||
132 | args[i++] = host; /* may have to come after user on some systems */ | ||
133 | if (user) | ||
134 | { | ||
135 | args[i++] = "-l"; | ||
136 | args[i++] = user; | ||
137 | } | ||
138 | if (buffer_len(command) > 0) | ||
139 | { | ||
140 | buffer_append(command, "\0", 1); | ||
141 | args[i++] = buffer_ptr(command); | ||
142 | } | ||
143 | args[i++] = NULL; | ||
144 | if (debug_flag) | ||
145 | { | ||
146 | for (i = 0; args[i]; i++) | ||
147 | { | ||
148 | if (i != 0) | ||
149 | fprintf(stderr, " "); | ||
150 | fprintf(stderr, "%s", args[i]); | ||
151 | } | ||
152 | fprintf(stderr, "\n"); | ||
153 | } | ||
154 | execv(_PATH_RSH, args); | ||
155 | perror(_PATH_RSH); | ||
156 | exit(1); | ||
157 | } | ||
158 | |||
159 | /* Main program for the ssh client. */ | ||
160 | |||
161 | uid_t original_real_uid; | ||
162 | |||
163 | int | ||
164 | main(int ac, char **av) | ||
165 | { | ||
166 | int i, opt, optind, type, exit_status, ok, fwd_port, fwd_host_port, authfd; | ||
167 | char *optarg, *cp, buf[256]; | ||
168 | Buffer command; | ||
169 | struct winsize ws; | ||
170 | struct stat st; | ||
171 | struct passwd *pw, pwcopy; | ||
172 | int interactive = 0, dummy; | ||
173 | uid_t original_effective_uid; | ||
174 | int plen; | ||
175 | |||
176 | /* Save the original real uid. It will be needed later (uid-swapping may | ||
177 | clobber the real uid). */ | ||
178 | original_real_uid = getuid(); | ||
179 | original_effective_uid = geteuid(); | ||
180 | |||
181 | /* If we are installed setuid root be careful to not drop core. */ | ||
182 | if (original_real_uid != original_effective_uid) | ||
183 | { | ||
184 | struct rlimit rlim; | ||
185 | rlim.rlim_cur = rlim.rlim_max = 0; | ||
186 | if (setrlimit(RLIMIT_CORE, &rlim) < 0) | ||
187 | fatal("setrlimit failed: %.100s", strerror(errno)); | ||
188 | } | ||
189 | |||
190 | /* Use uid-swapping to give up root privileges for the duration of option | ||
191 | processing. We will re-instantiate the rights when we are ready to | ||
192 | create the privileged port, and will permanently drop them when the | ||
193 | port has been created (actually, when the connection has been made, as | ||
194 | we may need to create the port several times). */ | ||
195 | temporarily_use_uid(original_real_uid); | ||
196 | |||
197 | /* Set our umask to something reasonable, as some files are created with | ||
198 | the default umask. This will make them world-readable but writable | ||
199 | only by the owner, which is ok for all files for which we don't set | ||
200 | the modes explicitly. */ | ||
201 | umask(022); | ||
202 | |||
203 | /* Save our own name. */ | ||
204 | av0 = av[0]; | ||
205 | |||
206 | /* Initialize option structure to indicate that no values have been set. */ | ||
207 | initialize_options(&options); | ||
208 | |||
209 | /* Parse command-line arguments. */ | ||
210 | host = NULL; | ||
211 | |||
212 | /* If program name is not one of the standard names, use it as host name. */ | ||
213 | if (strchr(av0, '/')) | ||
214 | cp = strrchr(av0, '/') + 1; | ||
215 | else | ||
216 | cp = av0; | ||
217 | if (strcmp(cp, "rsh") != 0 && strcmp(cp, "ssh") != 0 && | ||
218 | strcmp(cp, "rlogin") != 0 && strcmp(cp, "slogin") != 0) | ||
219 | host = cp; | ||
220 | |||
221 | for (optind = 1; optind < ac; optind++) | ||
222 | { | ||
223 | if (av[optind][0] != '-') | ||
224 | { | ||
225 | if (host) | ||
226 | break; | ||
227 | if ((cp = strchr(av[optind], '@'))) { | ||
228 | options.user = av[optind]; | ||
229 | *cp = '\0'; | ||
230 | host = ++cp; | ||
231 | } | ||
232 | else | ||
233 | host = av[optind]; | ||
234 | continue; | ||
235 | } | ||
236 | opt = av[optind][1]; | ||
237 | if (!opt) | ||
238 | usage(); | ||
239 | if (strchr("eilcpLRo", opt)) /* options with arguments */ | ||
240 | { | ||
241 | optarg = av[optind] + 2; | ||
242 | if (strcmp(optarg, "") == 0) | ||
243 | { | ||
244 | if (optind >= ac - 1) | ||
245 | usage(); | ||
246 | optarg = av[++optind]; | ||
247 | } | ||
248 | } | ||
249 | else | ||
250 | { | ||
251 | if (av[optind][2]) | ||
252 | usage(); | ||
253 | optarg = NULL; | ||
254 | } | ||
255 | switch (opt) | ||
256 | { | ||
257 | case 'n': | ||
258 | stdin_null_flag = 1; | ||
259 | break; | ||
260 | |||
261 | case 'f': | ||
262 | fork_after_authentication_flag = 1; | ||
263 | stdin_null_flag = 1; | ||
264 | break; | ||
265 | |||
266 | case 'x': | ||
267 | options.forward_x11 = 0; | ||
268 | break; | ||
269 | |||
270 | case 'X': | ||
271 | options.forward_x11 = 1; | ||
272 | break; | ||
273 | |||
274 | case 'g': | ||
275 | options.gateway_ports = 1; | ||
276 | break; | ||
277 | |||
278 | case 'P': | ||
279 | options.use_privileged_port = 0; | ||
280 | break; | ||
281 | |||
282 | case 'a': | ||
283 | options.forward_agent = 0; | ||
284 | break; | ||
285 | #ifdef AFS | ||
286 | case 'k': | ||
287 | options.kerberos_tgt_passing = 0; | ||
288 | options.afs_token_passing = 0; | ||
289 | break; | ||
290 | #endif | ||
291 | case 'i': | ||
292 | if (stat(optarg, &st) < 0) | ||
293 | { | ||
294 | fprintf(stderr, "Warning: Identity file %s does not exist.\n", | ||
295 | optarg); | ||
296 | break; | ||
297 | } | ||
298 | if (options.num_identity_files >= SSH_MAX_IDENTITY_FILES) | ||
299 | fatal("Too many identity files specified (max %d)", | ||
300 | SSH_MAX_IDENTITY_FILES); | ||
301 | options.identity_files[options.num_identity_files++] = | ||
302 | xstrdup(optarg); | ||
303 | break; | ||
304 | |||
305 | case 't': | ||
306 | tty_flag = 1; | ||
307 | break; | ||
308 | |||
309 | case 'v': | ||
310 | case 'V': | ||
311 | debug_flag = 1; | ||
312 | fprintf(stderr, "SSH Version %s, protocol version %d.%d.\n", | ||
313 | SSH_VERSION, PROTOCOL_MAJOR, PROTOCOL_MINOR); | ||
314 | fprintf(stderr, "Compiled with SSL.\n"); | ||
315 | if (opt == 'V') | ||
316 | exit(0); | ||
317 | break; | ||
318 | |||
319 | case 'q': | ||
320 | quiet_flag = 1; | ||
321 | break; | ||
322 | |||
323 | case 'e': | ||
324 | if (optarg[0] == '^' && optarg[2] == 0 && | ||
325 | (unsigned char)optarg[1] >= 64 && (unsigned char)optarg[1] < 128) | ||
326 | options.escape_char = (unsigned char)optarg[1] & 31; | ||
327 | else | ||
328 | if (strlen(optarg) == 1) | ||
329 | options.escape_char = (unsigned char)optarg[0]; | ||
330 | else | ||
331 | if (strcmp(optarg, "none") == 0) | ||
332 | options.escape_char = -2; | ||
333 | else | ||
334 | { | ||
335 | fprintf(stderr, "Bad escape character '%s'.\n", optarg); | ||
336 | exit(1); | ||
337 | } | ||
338 | break; | ||
339 | |||
340 | case 'c': | ||
341 | options.cipher = cipher_number(optarg); | ||
342 | if (options.cipher == -1) | ||
343 | { | ||
344 | fprintf(stderr, "Unknown cipher type '%s'\n", optarg); | ||
345 | exit(1); | ||
346 | } | ||
347 | break; | ||
348 | |||
349 | case 'p': | ||
350 | options.port = atoi(optarg); | ||
351 | if (options.port < 1 || options.port > 65535) | ||
352 | { | ||
353 | fprintf(stderr, "Bad port %s.\n", optarg); | ||
354 | exit(1); | ||
355 | } | ||
356 | break; | ||
357 | |||
358 | case 'l': | ||
359 | options.user = optarg; | ||
360 | break; | ||
361 | |||
362 | case 'R': | ||
363 | if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, | ||
364 | &fwd_host_port) != 3) | ||
365 | { | ||
366 | fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); | ||
367 | usage(); | ||
368 | /*NOTREACHED*/ | ||
369 | } | ||
370 | add_remote_forward(&options, fwd_port, buf, fwd_host_port); | ||
371 | break; | ||
372 | |||
373 | case 'L': | ||
374 | if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, | ||
375 | &fwd_host_port) != 3) | ||
376 | { | ||
377 | fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); | ||
378 | usage(); | ||
379 | /*NOTREACHED*/ | ||
380 | } | ||
381 | add_local_forward(&options, fwd_port, buf, fwd_host_port); | ||
382 | break; | ||
383 | |||
384 | case 'C': | ||
385 | options.compression = 1; | ||
386 | break; | ||
387 | |||
388 | case 'o': | ||
389 | dummy = 1; | ||
390 | process_config_line(&options, host ? host : "", optarg, | ||
391 | "command-line", 0, &dummy); | ||
392 | break; | ||
393 | |||
394 | default: | ||
395 | usage(); | ||
396 | } | ||
397 | } | ||
398 | |||
399 | /* Check that we got a host name. */ | ||
400 | if (!host) | ||
401 | usage(); | ||
402 | |||
403 | /* check if RSA support exists */ | ||
404 | if (rsa_alive() == 0) { | ||
405 | extern char *__progname; | ||
406 | |||
407 | fprintf(stderr, | ||
408 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | ||
409 | __progname); | ||
410 | exit(1); | ||
411 | } | ||
412 | |||
413 | /* Initialize the command to execute on remote host. */ | ||
414 | buffer_init(&command); | ||
415 | |||
416 | /* Save the command to execute on the remote host in a buffer. There is | ||
417 | no limit on the length of the command, except by the maximum packet | ||
418 | size. Also sets the tty flag if there is no command. */ | ||
419 | if (optind == ac) | ||
420 | { | ||
421 | /* No command specified - execute shell on a tty. */ | ||
422 | tty_flag = 1; | ||
423 | } | ||
424 | else | ||
425 | { | ||
426 | /* A command has been specified. Store it into the buffer. */ | ||
427 | for (i = optind; i < ac; i++) | ||
428 | { | ||
429 | if (i > optind) | ||
430 | buffer_append(&command, " ", 1); | ||
431 | buffer_append(&command, av[i], strlen(av[i])); | ||
432 | } | ||
433 | } | ||
434 | |||
435 | /* Cannot fork to background if no command. */ | ||
436 | if (fork_after_authentication_flag && buffer_len(&command) == 0) | ||
437 | fatal("Cannot fork into background without a command to execute."); | ||
438 | |||
439 | /* Allocate a tty by default if no command specified. */ | ||
440 | if (buffer_len(&command) == 0) | ||
441 | tty_flag = 1; | ||
442 | |||
443 | /* Do not allocate a tty if stdin is not a tty. */ | ||
444 | if (!isatty(fileno(stdin))) | ||
445 | { | ||
446 | if (tty_flag) | ||
447 | fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n"); | ||
448 | tty_flag = 0; | ||
449 | } | ||
450 | |||
451 | /* Get user data. */ | ||
452 | pw = getpwuid(original_real_uid); | ||
453 | if (!pw) | ||
454 | { | ||
455 | fprintf(stderr, "You don't exist, go away!\n"); | ||
456 | exit(1); | ||
457 | } | ||
458 | |||
459 | /* Take a copy of the returned structure. */ | ||
460 | memset(&pwcopy, 0, sizeof(pwcopy)); | ||
461 | pwcopy.pw_name = xstrdup(pw->pw_name); | ||
462 | pwcopy.pw_passwd = xstrdup(pw->pw_passwd); | ||
463 | pwcopy.pw_uid = pw->pw_uid; | ||
464 | pwcopy.pw_gid = pw->pw_gid; | ||
465 | pwcopy.pw_dir = xstrdup(pw->pw_dir); | ||
466 | pwcopy.pw_shell = xstrdup(pw->pw_shell); | ||
467 | pw = &pwcopy; | ||
468 | |||
469 | /* Initialize "log" output. Since we are the client all output actually | ||
470 | goes to the terminal. */ | ||
471 | log_init(av[0], 1, debug_flag, quiet_flag, SYSLOG_FACILITY_USER); | ||
472 | |||
473 | /* Read per-user configuration file. */ | ||
474 | snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_CONFFILE); | ||
475 | read_config_file(buf, host, &options); | ||
476 | |||
477 | /* Read systemwide configuration file. */ | ||
478 | read_config_file(HOST_CONFIG_FILE, host, &options); | ||
479 | |||
480 | /* Fill configuration defaults. */ | ||
481 | fill_default_options(&options); | ||
482 | if (options.user == NULL) | ||
483 | options.user = xstrdup(pw->pw_name); | ||
484 | |||
485 | if (options.hostname != NULL) | ||
486 | host = options.hostname; | ||
487 | |||
488 | /* Find canonic host name. */ | ||
489 | if (strchr(host, '.') == 0) | ||
490 | { | ||
491 | struct hostent *hp = gethostbyname(host); | ||
492 | if (hp != 0) | ||
493 | { | ||
494 | if (strchr(hp->h_name, '.') != 0) | ||
495 | host = xstrdup(hp->h_name); | ||
496 | else if (hp->h_aliases != 0 | ||
497 | && hp->h_aliases[0] != 0 | ||
498 | && strchr(hp->h_aliases[0], '.') != 0) | ||
499 | host = xstrdup(hp->h_aliases[0]); | ||
500 | } | ||
501 | } | ||
502 | |||
503 | /* Disable rhosts authentication if not running as root. */ | ||
504 | if (original_effective_uid != 0) | ||
505 | { | ||
506 | options.rhosts_authentication = 0; | ||
507 | options.rhosts_rsa_authentication = 0; | ||
508 | } | ||
509 | |||
510 | /* If using rsh has been selected, exec it now (without trying anything | ||
511 | else). Note that we must release privileges first. */ | ||
512 | if (options.use_rsh) | ||
513 | { | ||
514 | /* Restore our superuser privileges. This must be done before | ||
515 | permanently setting the uid. */ | ||
516 | restore_uid(); | ||
517 | |||
518 | /* Switch to the original uid permanently. */ | ||
519 | permanently_set_uid(original_real_uid); | ||
520 | |||
521 | /* Execute rsh. */ | ||
522 | rsh_connect(host, options.user, &command); | ||
523 | fatal("rsh_connect returned"); | ||
524 | } | ||
525 | |||
526 | /* Restore our superuser privileges. */ | ||
527 | restore_uid(); | ||
528 | |||
529 | /* Open a connection to the remote host. This needs root privileges if | ||
530 | rhosts_{rsa_}authentication is true. */ | ||
531 | |||
532 | if (!options.use_privileged_port) | ||
533 | { | ||
534 | options.rhosts_authentication = 0; | ||
535 | options.rhosts_rsa_authentication = 0; | ||
536 | } | ||
537 | |||
538 | ok = ssh_connect(host, &hostaddr, options.port, options.connection_attempts, | ||
539 | !options.rhosts_authentication && | ||
540 | !options.rhosts_rsa_authentication, | ||
541 | original_real_uid, options.proxy_command); | ||
542 | |||
543 | /* If we successfully made the connection, load the host private key in | ||
544 | case we will need it later for combined rsa-rhosts authentication. | ||
545 | This must be done before releasing extra privileges, because the file | ||
546 | is only readable by root. */ | ||
547 | if (ok) | ||
548 | { | ||
549 | host_private_key = RSA_new(); | ||
550 | if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL)) | ||
551 | host_private_key_loaded = 1; | ||
552 | } | ||
553 | |||
554 | /* Get rid of any extra privileges that we may have. We will no longer need | ||
555 | them. Also, extra privileges could make it very hard to read identity | ||
556 | files and other non-world-readable files from the user's home directory | ||
557 | if it happens to be on a NFS volume where root is mapped to nobody. */ | ||
558 | permanently_set_uid(original_real_uid); | ||
559 | |||
560 | /* Now that we are back to our own permissions, create ~/.ssh directory | ||
561 | if it doesn\'t already exist. */ | ||
562 | snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_DIR); | ||
563 | if (stat(buf, &st) < 0) | ||
564 | if (mkdir(buf, 0755) < 0) | ||
565 | error("Could not create directory '%.200s'.", buf); | ||
566 | |||
567 | /* Check if the connection failed, and try "rsh" if appropriate. */ | ||
568 | if (!ok) | ||
569 | { | ||
570 | if (options.port != 0) | ||
571 | log("Secure connection to %.100s on port %d refused%.100s.", | ||
572 | host, options.port, | ||
573 | options.fallback_to_rsh ? "; reverting to insecure method" : ""); | ||
574 | else | ||
575 | log("Secure connection to %.100s refused%.100s.", host, | ||
576 | options.fallback_to_rsh ? "; reverting to insecure method" : ""); | ||
577 | |||
578 | if (options.fallback_to_rsh) | ||
579 | { | ||
580 | rsh_connect(host, options.user, &command); | ||
581 | fatal("rsh_connect returned"); | ||
582 | } | ||
583 | exit(1); | ||
584 | } | ||
585 | |||
586 | /* Expand ~ in options.identity_files. */ | ||
587 | for (i = 0; i < options.num_identity_files; i++) | ||
588 | options.identity_files[i] = | ||
589 | tilde_expand_filename(options.identity_files[i], original_real_uid); | ||
590 | |||
591 | /* Expand ~ in known host file names. */ | ||
592 | options.system_hostfile = tilde_expand_filename(options.system_hostfile, | ||
593 | original_real_uid); | ||
594 | options.user_hostfile = tilde_expand_filename(options.user_hostfile, | ||
595 | original_real_uid); | ||
596 | |||
597 | /* Log into the remote system. This never returns if the login fails. */ | ||
598 | ssh_login(host_private_key_loaded, host_private_key, | ||
599 | host, &hostaddr, &options, original_real_uid); | ||
600 | |||
601 | /* We no longer need the host private key. Clear it now. */ | ||
602 | if (host_private_key_loaded) | ||
603 | RSA_free(host_private_key); /* Destroys contents safely */ | ||
604 | |||
605 | /* Close connection cleanly after attack. */ | ||
606 | cipher_attack_detected = packet_disconnect; | ||
607 | |||
608 | /* If requested, fork and let ssh continue in the background. */ | ||
609 | if (fork_after_authentication_flag) | ||
610 | { | ||
611 | int ret = fork(); | ||
612 | if (ret == -1) | ||
613 | fatal("fork failed: %.100s", strerror(errno)); | ||
614 | if (ret != 0) | ||
615 | exit(0); | ||
616 | setsid(); | ||
617 | } | ||
618 | |||
619 | /* Enable compression if requested. */ | ||
620 | if (options.compression) | ||
621 | { | ||
622 | debug("Requesting compression at level %d.", options.compression_level); | ||
623 | |||
624 | if (options.compression_level < 1 || options.compression_level > 9) | ||
625 | fatal("Compression level must be from 1 (fast) to 9 (slow, best)."); | ||
626 | |||
627 | /* Send the request. */ | ||
628 | packet_start(SSH_CMSG_REQUEST_COMPRESSION); | ||
629 | packet_put_int(options.compression_level); | ||
630 | packet_send(); | ||
631 | packet_write_wait(); | ||
632 | type = packet_read(&plen); | ||
633 | if (type == SSH_SMSG_SUCCESS) | ||
634 | packet_start_compression(options.compression_level); | ||
635 | else if (type == SSH_SMSG_FAILURE) | ||
636 | log("Warning: Remote host refused compression."); | ||
637 | else | ||
638 | packet_disconnect("Protocol error waiting for compression response."); | ||
639 | } | ||
640 | |||
641 | /* Allocate a pseudo tty if appropriate. */ | ||
642 | if (tty_flag) | ||
643 | { | ||
644 | debug("Requesting pty."); | ||
645 | |||
646 | /* Start the packet. */ | ||
647 | packet_start(SSH_CMSG_REQUEST_PTY); | ||
648 | |||
649 | /* Store TERM in the packet. There is no limit on the length of the | ||
650 | string. */ | ||
651 | cp = getenv("TERM"); | ||
652 | if (!cp) | ||
653 | cp = ""; | ||
654 | packet_put_string(cp, strlen(cp)); | ||
655 | |||
656 | /* Store window size in the packet. */ | ||
657 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) | ||
658 | memset(&ws, 0, sizeof(ws)); | ||
659 | packet_put_int(ws.ws_row); | ||
660 | packet_put_int(ws.ws_col); | ||
661 | packet_put_int(ws.ws_xpixel); | ||
662 | packet_put_int(ws.ws_ypixel); | ||
663 | |||
664 | /* Store tty modes in the packet. */ | ||
665 | tty_make_modes(fileno(stdin)); | ||
666 | |||
667 | /* Send the packet, and wait for it to leave. */ | ||
668 | packet_send(); | ||
669 | packet_write_wait(); | ||
670 | |||
671 | /* Read response from the server. */ | ||
672 | type = packet_read(&plen); | ||
673 | if (type == SSH_SMSG_SUCCESS) | ||
674 | interactive = 1; | ||
675 | else if (type == SSH_SMSG_FAILURE) | ||
676 | log("Warning: Remote host failed or refused to allocate a pseudo tty."); | ||
677 | else | ||
678 | packet_disconnect("Protocol error waiting for pty request response."); | ||
679 | } | ||
680 | |||
681 | /* Request X11 forwarding if enabled and DISPLAY is set. */ | ||
682 | if (options.forward_x11 && getenv("DISPLAY") != NULL) | ||
683 | { | ||
684 | char line[512], proto[512], data[512]; | ||
685 | FILE *f; | ||
686 | int forwarded = 0, got_data = 0, i; | ||
687 | |||
688 | #ifdef XAUTH_PATH | ||
689 | /* Try to get Xauthority information for the display. */ | ||
690 | snprintf(line, sizeof line, "%.100s list %.200s 2>/dev/null", | ||
691 | XAUTH_PATH, getenv("DISPLAY")); | ||
692 | f = popen(line, "r"); | ||
693 | if (f && fgets(line, sizeof(line), f) && | ||
694 | sscanf(line, "%*s %s %s", proto, data) == 2) | ||
695 | got_data = 1; | ||
696 | if (f) | ||
697 | pclose(f); | ||
698 | #endif /* XAUTH_PATH */ | ||
699 | /* If we didn't get authentication data, just make up some data. The | ||
700 | forwarding code will check the validity of the response anyway, and | ||
701 | substitute this data. The X11 server, however, will ignore this | ||
702 | fake data and use whatever authentication mechanisms it was using | ||
703 | otherwise for the local connection. */ | ||
704 | if (!got_data) | ||
705 | { | ||
706 | u_int32_t rand = 0; | ||
707 | |||
708 | strlcpy(proto, "MIT-MAGIC-COOKIE-1", sizeof proto); | ||
709 | for (i = 0; i < 16; i++) { | ||
710 | if (i % 4 == 0) | ||
711 | rand = arc4random(); | ||
712 | snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rand & 0xff); | ||
713 | rand >>= 8; | ||
714 | } | ||
715 | } | ||
716 | |||
717 | /* Got local authentication reasonable information. Request forwarding | ||
718 | with authentication spoofing. */ | ||
719 | debug("Requesting X11 forwarding with authentication spoofing."); | ||
720 | x11_request_forwarding_with_spoofing(proto, data); | ||
721 | |||
722 | /* Read response from the server. */ | ||
723 | type = packet_read(&plen); | ||
724 | if (type == SSH_SMSG_SUCCESS) | ||
725 | { | ||
726 | forwarded = 1; | ||
727 | interactive = 1; | ||
728 | } | ||
729 | else if (type == SSH_SMSG_FAILURE) | ||
730 | log("Warning: Remote host denied X11 forwarding."); | ||
731 | else | ||
732 | packet_disconnect("Protocol error waiting for X11 forwarding"); | ||
733 | } | ||
734 | |||
735 | /* Tell the packet module whether this is an interactive session. */ | ||
736 | packet_set_interactive(interactive, options.keepalives); | ||
737 | |||
738 | /* Clear agent forwarding if we don\'t have an agent. */ | ||
739 | authfd = ssh_get_authentication_socket(); | ||
740 | if (authfd < 0) | ||
741 | options.forward_agent = 0; | ||
742 | else | ||
743 | ssh_close_authentication_socket(authfd); | ||
744 | |||
745 | /* Request authentication agent forwarding if appropriate. */ | ||
746 | if (options.forward_agent) | ||
747 | { | ||
748 | debug("Requesting authentication agent forwarding."); | ||
749 | auth_request_forwarding(); | ||
750 | |||
751 | /* Read response from the server. */ | ||
752 | type = packet_read(&plen); | ||
753 | packet_integrity_check(plen, 0, type); | ||
754 | if (type != SSH_SMSG_SUCCESS) | ||
755 | log("Warning: Remote host denied authentication agent forwarding."); | ||
756 | } | ||
757 | |||
758 | /* Initiate local TCP/IP port forwardings. */ | ||
759 | for (i = 0; i < options.num_local_forwards; i++) | ||
760 | { | ||
761 | debug("Connections to local port %d forwarded to remote address %.200s:%d", | ||
762 | options.local_forwards[i].port, options.local_forwards[i].host, | ||
763 | options.local_forwards[i].host_port); | ||
764 | channel_request_local_forwarding(options.local_forwards[i].port, | ||
765 | options.local_forwards[i].host, | ||
766 | options.local_forwards[i].host_port); | ||
767 | } | ||
768 | |||
769 | /* Initiate remote TCP/IP port forwardings. */ | ||
770 | for (i = 0; i < options.num_remote_forwards; i++) | ||
771 | { | ||
772 | debug("Connections to remote port %d forwarded to local address %.200s:%d", | ||
773 | options.remote_forwards[i].port, options.remote_forwards[i].host, | ||
774 | options.remote_forwards[i].host_port); | ||
775 | channel_request_remote_forwarding(options.remote_forwards[i].port, | ||
776 | options.remote_forwards[i].host, | ||
777 | options.remote_forwards[i].host_port); | ||
778 | } | ||
779 | |||
780 | /* If a command was specified on the command line, execute the command now. | ||
781 | Otherwise request the server to start a shell. */ | ||
782 | if (buffer_len(&command) > 0) | ||
783 | { | ||
784 | int len = buffer_len(&command); | ||
785 | if (len > 900) | ||
786 | len = 900; | ||
787 | debug("Sending command: %.*s", len, buffer_ptr(&command)); | ||
788 | packet_start(SSH_CMSG_EXEC_CMD); | ||
789 | packet_put_string(buffer_ptr(&command), buffer_len(&command)); | ||
790 | packet_send(); | ||
791 | packet_write_wait(); | ||
792 | } | ||
793 | else | ||
794 | { | ||
795 | debug("Requesting shell."); | ||
796 | packet_start(SSH_CMSG_EXEC_SHELL); | ||
797 | packet_send(); | ||
798 | packet_write_wait(); | ||
799 | } | ||
800 | |||
801 | /* Enter the interactive session. */ | ||
802 | exit_status = client_loop(tty_flag, tty_flag ? options.escape_char : -1); | ||
803 | |||
804 | /* Close the connection to the remote host. */ | ||
805 | packet_close(); | ||
806 | |||
807 | /* Exit with the status returned by the program on the remote side. */ | ||
808 | exit(exit_status); | ||
809 | } | ||
@@ -0,0 +1,589 @@ | |||
1 | /* | ||
2 | |||
3 | ssh.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Fri Mar 17 17:09:37 1995 ylo | ||
11 | |||
12 | Generic header file for ssh. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: ssh.h,v 1.1 1999/10/27 03:42:45 damien Exp $"); */ | ||
17 | |||
18 | #ifndef SSH_H | ||
19 | #define SSH_H | ||
20 | |||
21 | #include "rsa.h" | ||
22 | #include "cipher.h" | ||
23 | |||
24 | /* The default cipher used if IDEA is not supported by the remote host. | ||
25 | It is recommended that this be one of the mandatory ciphers (DES, 3DES), | ||
26 | though that is not required. */ | ||
27 | #define SSH_FALLBACK_CIPHER SSH_CIPHER_3DES | ||
28 | |||
29 | /* Cipher used for encrypting authentication files. */ | ||
30 | #define SSH_AUTHFILE_CIPHER SSH_CIPHER_3DES | ||
31 | |||
32 | /* Default port number. */ | ||
33 | #define SSH_DEFAULT_PORT 22 | ||
34 | |||
35 | /* Maximum number of TCP/IP ports forwarded per direction. */ | ||
36 | #define SSH_MAX_FORWARDS_PER_DIRECTION 100 | ||
37 | |||
38 | /* Maximum number of RSA authentication identity files that can be specified | ||
39 | in configuration files or on the command line. */ | ||
40 | #define SSH_MAX_IDENTITY_FILES 100 | ||
41 | |||
42 | /* Major protocol version. Different version indicates major incompatiblity | ||
43 | that prevents communication. */ | ||
44 | #define PROTOCOL_MAJOR 1 | ||
45 | |||
46 | /* Minor protocol version. Different version indicates minor incompatibility | ||
47 | that does not prevent interoperation. */ | ||
48 | #define PROTOCOL_MINOR 5 | ||
49 | |||
50 | /* Name for the service. The port named by this service overrides the default | ||
51 | port if present. */ | ||
52 | #define SSH_SERVICE_NAME "ssh" | ||
53 | |||
54 | #ifndef ETCDIR | ||
55 | #define ETCDIR "/etc" | ||
56 | #endif /* ETCDIR */ | ||
57 | |||
58 | #define PIDDIR "/var/run" | ||
59 | |||
60 | /* System-wide file containing host keys of known hosts. This file should be | ||
61 | world-readable. */ | ||
62 | #define SSH_SYSTEM_HOSTFILE ETCDIR "/ssh_known_hosts" | ||
63 | |||
64 | /* HOST_KEY_FILE /etc/ssh_host_key, | ||
65 | SERVER_CONFIG_FILE /etc/sshd_config, | ||
66 | and HOST_CONFIG_FILE /etc/ssh_config | ||
67 | are all defined in Makefile.in. Of these, ssh_host_key should be readable | ||
68 | only by root, whereas ssh_config should be world-readable. */ | ||
69 | |||
70 | #define HOST_KEY_FILE ETCDIR "/ssh_host_key" | ||
71 | #define SERVER_CONFIG_FILE ETCDIR "/sshd_config" | ||
72 | #define HOST_CONFIG_FILE ETCDIR "/ssh_config" | ||
73 | |||
74 | #define SSH_PROGRAM "/usr/bin/ssh" | ||
75 | |||
76 | /* The process id of the daemon listening for connections is saved | ||
77 | here to make it easier to kill the correct daemon when necessary. */ | ||
78 | #define SSH_DAEMON_PID_FILE PIDDIR "/sshd.pid" | ||
79 | |||
80 | /* The directory in user\'s home directory in which the files reside. | ||
81 | The directory should be world-readable (though not all files are). */ | ||
82 | #define SSH_USER_DIR ".ssh" | ||
83 | |||
84 | /* Per-user file containing host keys of known hosts. This file need | ||
85 | not be readable by anyone except the user him/herself, though this does | ||
86 | not contain anything particularly secret. */ | ||
87 | #define SSH_USER_HOSTFILE "~/.ssh/known_hosts" | ||
88 | |||
89 | /* Name of the default file containing client-side authentication key. | ||
90 | This file should only be readable by the user him/herself. */ | ||
91 | #define SSH_CLIENT_IDENTITY ".ssh/identity" | ||
92 | |||
93 | /* Configuration file in user\'s home directory. This file need not be | ||
94 | readable by anyone but the user him/herself, but does not contain | ||
95 | anything particularly secret. If the user\'s home directory resides | ||
96 | on an NFS volume where root is mapped to nobody, this may need to be | ||
97 | world-readable. */ | ||
98 | #define SSH_USER_CONFFILE ".ssh/config" | ||
99 | |||
100 | /* File containing a list of those rsa keys that permit logging in as | ||
101 | this user. This file need not be | ||
102 | readable by anyone but the user him/herself, but does not contain | ||
103 | anything particularly secret. If the user\'s home directory resides | ||
104 | on an NFS volume where root is mapped to nobody, this may need to be | ||
105 | world-readable. (This file is read by the daemon which is running as | ||
106 | root.) */ | ||
107 | #define SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" | ||
108 | |||
109 | /* Per-user and system-wide ssh "rc" files. These files are executed with | ||
110 | /bin/sh before starting the shell or command if they exist. They | ||
111 | will be passed "proto cookie" as arguments if X11 forwarding with | ||
112 | spoofing is in use. xauth will be run if neither of these exists. */ | ||
113 | #define SSH_USER_RC ".ssh/rc" | ||
114 | #define SSH_SYSTEM_RC ETCDIR "/sshrc" | ||
115 | |||
116 | /* Ssh-only version of /etc/hosts.equiv. */ | ||
117 | #define SSH_HOSTS_EQUIV ETCDIR "/shosts.equiv" | ||
118 | |||
119 | /* Additionally, the daemon may use ~/.rhosts and /etc/hosts.equiv if | ||
120 | rhosts authentication is enabled. */ | ||
121 | |||
122 | /* Name of the environment variable containing the pathname of the | ||
123 | authentication socket. */ | ||
124 | #define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK" | ||
125 | |||
126 | /* Force host key length and server key length to differ by at least this | ||
127 | many bits. This is to make double encryption with rsaref work. */ | ||
128 | #define SSH_KEY_BITS_RESERVED 128 | ||
129 | |||
130 | /* Length of the session key in bytes. (Specified as 256 bits in the | ||
131 | protocol.) */ | ||
132 | #define SSH_SESSION_KEY_LENGTH 32 | ||
133 | |||
134 | /* Name of Kerberos service for SSH to use. */ | ||
135 | #define KRB4_SERVICE_NAME "rcmd" | ||
136 | |||
137 | /* Authentication methods. New types can be added, but old types should not | ||
138 | be removed for compatibility. The maximum allowed value is 31. */ | ||
139 | #define SSH_AUTH_RHOSTS 1 | ||
140 | #define SSH_AUTH_RSA 2 | ||
141 | #define SSH_AUTH_PASSWORD 3 | ||
142 | #define SSH_AUTH_RHOSTS_RSA 4 | ||
143 | /* 5 is TIS */ | ||
144 | #define SSH_AUTH_KERBEROS 6 | ||
145 | #define SSH_PASS_KERBEROS_TGT 7 | ||
146 | /* 8 to 15 are reserved */ | ||
147 | #define SSH_PASS_AFS_TOKEN 21 | ||
148 | |||
149 | /* Protocol flags. These are bit masks. */ | ||
150 | #define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes screen */ | ||
151 | #define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain host */ | ||
152 | |||
153 | /* Definition of message types. New values can be added, but old values | ||
154 | should not be removed or without careful consideration of the consequences | ||
155 | for compatibility. The maximum value is 254; value 255 is reserved | ||
156 | for future extension. */ | ||
157 | /* Message name */ /* msg code */ /* arguments */ | ||
158 | #define SSH_MSG_NONE 0 /* no message */ | ||
159 | #define SSH_MSG_DISCONNECT 1 /* cause (string) */ | ||
160 | #define SSH_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */ | ||
161 | #define SSH_CMSG_SESSION_KEY 3 /* key (BIGNUM) */ | ||
162 | #define SSH_CMSG_USER 4 /* user (string) */ | ||
163 | #define SSH_CMSG_AUTH_RHOSTS 5 /* user (string) */ | ||
164 | #define SSH_CMSG_AUTH_RSA 6 /* modulus (BIGNUM) */ | ||
165 | #define SSH_SMSG_AUTH_RSA_CHALLENGE 7 /* int (BIGNUM) */ | ||
166 | #define SSH_CMSG_AUTH_RSA_RESPONSE 8 /* int (BIGNUM) */ | ||
167 | #define SSH_CMSG_AUTH_PASSWORD 9 /* pass (string) */ | ||
168 | #define SSH_CMSG_REQUEST_PTY 10 /* TERM, tty modes */ | ||
169 | #define SSH_CMSG_WINDOW_SIZE 11 /* row,col,xpix,ypix */ | ||
170 | #define SSH_CMSG_EXEC_SHELL 12 /* */ | ||
171 | #define SSH_CMSG_EXEC_CMD 13 /* cmd (string) */ | ||
172 | #define SSH_SMSG_SUCCESS 14 /* */ | ||
173 | #define SSH_SMSG_FAILURE 15 /* */ | ||
174 | #define SSH_CMSG_STDIN_DATA 16 /* data (string) */ | ||
175 | #define SSH_SMSG_STDOUT_DATA 17 /* data (string) */ | ||
176 | #define SSH_SMSG_STDERR_DATA 18 /* data (string) */ | ||
177 | #define SSH_CMSG_EOF 19 /* */ | ||
178 | #define SSH_SMSG_EXITSTATUS 20 /* status (int) */ | ||
179 | #define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 21 /* channel (int) */ | ||
180 | #define SSH_MSG_CHANNEL_OPEN_FAILURE 22 /* channel (int) */ | ||
181 | #define SSH_MSG_CHANNEL_DATA 23 /* ch,data (int,str) */ | ||
182 | #define SSH_MSG_CHANNEL_CLOSE 24 /* channel (int) */ | ||
183 | #define SSH_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* channel (int) */ | ||
184 | /* SSH_CMSG_X11_REQUEST_FORWARDING 26 OBSOLETE */ | ||
185 | #define SSH_SMSG_X11_OPEN 27 /* channel (int) */ | ||
186 | #define SSH_CMSG_PORT_FORWARD_REQUEST 28 /* p,host,hp (i,s,i) */ | ||
187 | #define SSH_MSG_PORT_OPEN 29 /* ch,h,p (i,s,i) */ | ||
188 | #define SSH_CMSG_AGENT_REQUEST_FORWARDING 30 /* */ | ||
189 | #define SSH_SMSG_AGENT_OPEN 31 /* port (int) */ | ||
190 | #define SSH_MSG_IGNORE 32 /* string */ | ||
191 | #define SSH_CMSG_EXIT_CONFIRMATION 33 /* */ | ||
192 | #define SSH_CMSG_X11_REQUEST_FORWARDING 34 /* proto,data (s,s) */ | ||
193 | #define SSH_CMSG_AUTH_RHOSTS_RSA 35 /* user,mod (s,mpi) */ | ||
194 | #define SSH_MSG_DEBUG 36 /* string */ | ||
195 | #define SSH_CMSG_REQUEST_COMPRESSION 37 /* level 1-9 (int) */ | ||
196 | #define SSH_CMSG_MAX_PACKET_SIZE 38 /* size 4k-1024k (int) */ | ||
197 | #define SSH_CMSG_AUTH_TIS 39 /* this is proto-1.5, but we ignore TIS */ | ||
198 | #define SSH_SMSG_AUTH_TIS_CHALLENGE 40 | ||
199 | #define SSH_CMSG_AUTH_TIS_RESPONSE 41 | ||
200 | |||
201 | #define SSH_CMSG_AUTH_KERBEROS 42 /* (KTEXT) */ | ||
202 | #define SSH_SMSG_AUTH_KERBEROS_RESPONSE 43 /* (KTEXT) */ | ||
203 | #define SSH_CMSG_HAVE_KERBEROS_TGT 44 /* credentials (s) */ | ||
204 | #define SSH_CMSG_HAVE_AFS_TOKEN 65 /* token (s) */ | ||
205 | |||
206 | |||
207 | /* Includes that need definitions above. */ | ||
208 | |||
209 | #include "readconf.h" | ||
210 | |||
211 | /*------------ definitions for login.c -------------*/ | ||
212 | |||
213 | /* Returns the time when the user last logged in. Returns 0 if the | ||
214 | information is not available. This must be called before record_login. | ||
215 | The host from which the user logged in is stored in buf. */ | ||
216 | unsigned long get_last_login_time(uid_t uid, const char *logname, | ||
217 | char *buf, unsigned int bufsize); | ||
218 | |||
219 | /* Records that the user has logged in. This does many things normally | ||
220 | done by login(1). */ | ||
221 | void record_login(int pid, const char *ttyname, const char *user, uid_t uid, | ||
222 | const char *host, struct sockaddr_in *addr); | ||
223 | |||
224 | /* Records that the user has logged out. This does many thigs normally | ||
225 | done by login(1) or init. */ | ||
226 | void record_logout(int pid, const char *ttyname); | ||
227 | |||
228 | /*------------ definitions for sshconnect.c ----------*/ | ||
229 | |||
230 | /* Opens a TCP/IP connection to the remote server on the given host. If | ||
231 | port is 0, the default port will be used. If anonymous is zero, | ||
232 | a privileged port will be allocated to make the connection. | ||
233 | This requires super-user privileges if anonymous is false. | ||
234 | Connection_attempts specifies the maximum number of tries, one per | ||
235 | second. This returns true on success, and zero on failure. If the | ||
236 | connection is successful, this calls packet_set_connection for the | ||
237 | connection. */ | ||
238 | int ssh_connect(const char *host, struct sockaddr_in *hostaddr, | ||
239 | int port, int connection_attempts, | ||
240 | int anonymous, uid_t original_real_uid, | ||
241 | const char *proxy_command); | ||
242 | |||
243 | /* Starts a dialog with the server, and authenticates the current user on the | ||
244 | server. This does not need any extra privileges. The basic connection | ||
245 | to the server must already have been established before this is called. | ||
246 | If login fails, this function prints an error and never returns. | ||
247 | This initializes the random state, and leaves it initialized (it will also | ||
248 | have references from the packet module). */ | ||
249 | void ssh_login(int host_key_valid, RSA *host_key, const char *host, | ||
250 | struct sockaddr_in *hostaddr, Options *options, | ||
251 | uid_t original_real_uid); | ||
252 | |||
253 | /*------------ Definitions for various authentication methods. -------*/ | ||
254 | |||
255 | /* Tries to authenticate the user using the .rhosts file. Returns true if | ||
256 | authentication succeeds. If ignore_rhosts is non-zero, this will not | ||
257 | consider .rhosts and .shosts (/etc/hosts.equiv will still be used). | ||
258 | If strict_modes is true, checks ownership and modes of .rhosts/.shosts. */ | ||
259 | int auth_rhosts(struct passwd *pw, const char *client_user, | ||
260 | int ignore_rhosts, int strict_modes); | ||
261 | |||
262 | /* Tries to authenticate the user using the .rhosts file and the host using | ||
263 | its host key. Returns true if authentication succeeds. */ | ||
264 | int auth_rhosts_rsa(struct passwd *pw, const char *client_user, | ||
265 | unsigned int bits, BIGNUM *client_host_key_e, | ||
266 | BIGNUM *client_host_key_n, int ignore_rhosts, | ||
267 | int strict_modes); | ||
268 | |||
269 | /* Tries to authenticate the user using password. Returns true if | ||
270 | authentication succeeds. */ | ||
271 | int auth_password(struct passwd *pw, const char *password); | ||
272 | |||
273 | /* Performs the RSA authentication dialog with the client. This returns | ||
274 | 0 if the client could not be authenticated, and 1 if authentication was | ||
275 | successful. This may exit if there is a serious protocol violation. */ | ||
276 | int auth_rsa(struct passwd *pw, BIGNUM *client_n, int strict_modes); | ||
277 | |||
278 | /* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer | ||
279 | over the key. Skips any whitespace at the beginning and at end. */ | ||
280 | int auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM *e, BIGNUM *n); | ||
281 | |||
282 | /* Returns the name of the machine at the other end of the socket. The | ||
283 | returned string should be freed by the caller. */ | ||
284 | char *get_remote_hostname(int socket); | ||
285 | |||
286 | /* Return the canonical name of the host in the other side of the current | ||
287 | connection (as returned by packet_get_connection). The host name is | ||
288 | cached, so it is efficient to call this several times. */ | ||
289 | const char *get_canonical_hostname(void); | ||
290 | |||
291 | /* Returns the remote IP address as an ascii string. The value need not be | ||
292 | freed by the caller. */ | ||
293 | const char *get_remote_ipaddr(void); | ||
294 | |||
295 | /* Returns the port number of the peer of the socket. */ | ||
296 | int get_peer_port(int sock); | ||
297 | |||
298 | /* Returns the port number of the remote host. */ | ||
299 | int get_remote_port(void); | ||
300 | |||
301 | /* Tries to match the host name (which must be in all lowercase) against the | ||
302 | comma-separated sequence of subpatterns (each possibly preceded by ! to | ||
303 | indicate negation). Returns true if there is a positive match; zero | ||
304 | otherwise. */ | ||
305 | int match_hostname(const char *host, const char *pattern, unsigned int len); | ||
306 | |||
307 | /* Checks whether the given host is already in the list of our known hosts. | ||
308 | Returns HOST_OK if the host is known and has the specified key, | ||
309 | HOST_NEW if the host is not known, and HOST_CHANGED if the host is known | ||
310 | but used to have a different host key. The host must be in all lowercase. */ | ||
311 | typedef enum { HOST_OK, HOST_NEW, HOST_CHANGED } HostStatus; | ||
312 | HostStatus check_host_in_hostfile(const char *filename, | ||
313 | const char *host, unsigned int bits, | ||
314 | BIGNUM *e, BIGNUM *n, | ||
315 | BIGNUM *ke, BIGNUM *kn); | ||
316 | |||
317 | /* Appends an entry to the host file. Returns false if the entry | ||
318 | could not be appended. */ | ||
319 | int add_host_to_hostfile(const char *filename, const char *host, | ||
320 | unsigned int bits, BIGNUM *e, BIGNUM *n); | ||
321 | |||
322 | /* Performs the RSA authentication challenge-response dialog with the client, | ||
323 | and returns true (non-zero) if the client gave the correct answer to | ||
324 | our challenge; returns zero if the client gives a wrong answer. */ | ||
325 | int auth_rsa_challenge_dialog(unsigned int bits, BIGNUM *e, BIGNUM *n); | ||
326 | |||
327 | /* Reads a passphrase from /dev/tty with echo turned off. Returns the | ||
328 | passphrase (allocated with xmalloc). Exits if EOF is encountered. | ||
329 | If from_stdin is true, the passphrase will be read from stdin instead. */ | ||
330 | char *read_passphrase(const char *prompt, int from_stdin); | ||
331 | |||
332 | /* Saves the authentication (private) key in a file, encrypting it with | ||
333 | passphrase. The identification of the file (lowest 64 bits of n) | ||
334 | will precede the key to provide identification of the key without | ||
335 | needing a passphrase. */ | ||
336 | int save_private_key(const char *filename, const char *passphrase, | ||
337 | RSA *private_key, const char *comment); | ||
338 | |||
339 | /* Loads the public part of the key file (public key and comment). | ||
340 | Returns 0 if an error occurred; zero if the public key was successfully | ||
341 | read. The comment of the key is returned in comment_return if it is | ||
342 | non-NULL; the caller must free the value with xfree. */ | ||
343 | int load_public_key(const char *filename, RSA *pub, | ||
344 | char **comment_return); | ||
345 | |||
346 | /* Loads the private key from the file. Returns 0 if an error is encountered | ||
347 | (file does not exist or is not readable, or passphrase is bad). | ||
348 | This initializes the private key. The comment of the key is returned | ||
349 | in comment_return if it is non-NULL; the caller must free the value | ||
350 | with xfree. */ | ||
351 | int load_private_key(const char *filename, const char *passphrase, | ||
352 | RSA *private_key, char **comment_return); | ||
353 | |||
354 | /*------------ Definitions for logging. -----------------------*/ | ||
355 | |||
356 | /* Supported syslog facilities. */ | ||
357 | typedef enum | ||
358 | { | ||
359 | SYSLOG_FACILITY_DAEMON, | ||
360 | SYSLOG_FACILITY_USER, | ||
361 | SYSLOG_FACILITY_AUTH, | ||
362 | SYSLOG_FACILITY_LOCAL0, | ||
363 | SYSLOG_FACILITY_LOCAL1, | ||
364 | SYSLOG_FACILITY_LOCAL2, | ||
365 | SYSLOG_FACILITY_LOCAL3, | ||
366 | SYSLOG_FACILITY_LOCAL4, | ||
367 | SYSLOG_FACILITY_LOCAL5, | ||
368 | SYSLOG_FACILITY_LOCAL6, | ||
369 | SYSLOG_FACILITY_LOCAL7 | ||
370 | } SyslogFacility; | ||
371 | |||
372 | /* Initializes logging. If debug is non-zero, debug() will output something. | ||
373 | If quiet is non-zero, none of these will log send anything to syslog | ||
374 | (but maybe to stderr). */ | ||
375 | void log_init(char *av0, int on_stderr, int debug, int quiet, | ||
376 | SyslogFacility facility); | ||
377 | |||
378 | /* Outputs a message to syslog or stderr, depending on the implementation. | ||
379 | The format must guarantee that the final message does not exceed 1024 | ||
380 | characters. The message should not contain newline. */ | ||
381 | void log(const char *fmt, ...); | ||
382 | |||
383 | /* Outputs a message to syslog or stderr, depending on the implementation. | ||
384 | The format must guarantee that the final message does not exceed 1024 | ||
385 | characters. The message should not contain newline. */ | ||
386 | void debug(const char *fmt, ...); | ||
387 | |||
388 | /* Outputs a message to syslog or stderr, depending on the implementation. | ||
389 | The format must guarantee that the final message does not exceed 1024 | ||
390 | characters. The message should not contain newline. */ | ||
391 | void error(const char *fmt, ...); | ||
392 | |||
393 | /* Outputs a message to syslog or stderr, depending on the implementation. | ||
394 | The format must guarantee that the final message does not exceed 1024 | ||
395 | characters. The message should not contain newline. | ||
396 | This call never returns. */ | ||
397 | void fatal(const char *fmt, ...); | ||
398 | |||
399 | /* Registers a cleanup function to be called by fatal() before exiting. | ||
400 | It is permissible to call fatal_remove_cleanup for the function itself | ||
401 | from the function. */ | ||
402 | void fatal_add_cleanup(void (*proc)(void *context), void *context); | ||
403 | |||
404 | /* Removes a cleanup frunction to be called at fatal(). */ | ||
405 | void fatal_remove_cleanup(void (*proc)(void *context), void *context); | ||
406 | |||
407 | /*---------------- definitions for channels ------------------*/ | ||
408 | |||
409 | /* Sets specific protocol options. */ | ||
410 | void channel_set_options(int hostname_in_open); | ||
411 | |||
412 | /* Allocate a new channel object and set its type and socket. Remote_name | ||
413 | must have been allocated with xmalloc; this will free it when the channel | ||
414 | is freed. */ | ||
415 | int channel_allocate(int type, int sock, char *remote_name); | ||
416 | |||
417 | /* Free the channel and close its socket. */ | ||
418 | void channel_free(int channel); | ||
419 | |||
420 | /* Add any bits relevant to channels in select bitmasks. */ | ||
421 | void channel_prepare_select(fd_set *readset, fd_set *writeset); | ||
422 | |||
423 | /* After select, perform any appropriate operations for channels which | ||
424 | have events pending. */ | ||
425 | void channel_after_select(fd_set *readset, fd_set *writeset); | ||
426 | |||
427 | /* If there is data to send to the connection, send some of it now. */ | ||
428 | void channel_output_poll(void); | ||
429 | |||
430 | /* This is called when a packet of type CHANNEL_DATA has just been received. | ||
431 | The message type has already been consumed, but channel number and data | ||
432 | is still there. */ | ||
433 | void channel_input_data(int payload_len); | ||
434 | |||
435 | /* Returns true if no channel has too much buffered data. */ | ||
436 | int channel_not_very_much_buffered_data(void); | ||
437 | |||
438 | /* This is called after receiving CHANNEL_CLOSE. */ | ||
439 | void channel_input_close(void); | ||
440 | |||
441 | /* This is called after receiving CHANNEL_CLOSE_CONFIRMATION. */ | ||
442 | void channel_input_close_confirmation(void); | ||
443 | |||
444 | /* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ | ||
445 | void channel_input_open_confirmation(void); | ||
446 | |||
447 | /* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ | ||
448 | void channel_input_open_failure(void); | ||
449 | |||
450 | /* This closes any sockets that are listening for connections; this removes | ||
451 | any unix domain sockets. */ | ||
452 | void channel_stop_listening(void); | ||
453 | |||
454 | /* Closes the sockets of all channels. This is used to close extra file | ||
455 | descriptors after a fork. */ | ||
456 | void channel_close_all(void); | ||
457 | |||
458 | /* Returns the maximum file descriptor number used by the channels. */ | ||
459 | int channel_max_fd(void); | ||
460 | |||
461 | /* Returns true if there is still an open channel over the connection. */ | ||
462 | int channel_still_open(void); | ||
463 | |||
464 | /* Returns a string containing a list of all open channels. The list is | ||
465 | suitable for displaying to the user. It uses crlf instead of newlines. | ||
466 | The caller should free the string with xfree. */ | ||
467 | char *channel_open_message(void); | ||
468 | |||
469 | /* Initiate forwarding of connections to local port "port" through the secure | ||
470 | channel to host:port from remote side. This never returns if there | ||
471 | was an error. */ | ||
472 | void channel_request_local_forwarding(int port, const char *host, | ||
473 | int remote_port); | ||
474 | |||
475 | /* Initiate forwarding of connections to port "port" on remote host through | ||
476 | the secure channel to host:port from local side. This never returns | ||
477 | if there was an error. This registers that open requests for that | ||
478 | port are permitted. */ | ||
479 | void channel_request_remote_forwarding(int port, const char *host, | ||
480 | int remote_port); | ||
481 | |||
482 | /* Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually | ||
483 | called by the server, because the user could connect to any port anyway, | ||
484 | and the server has no way to know but to trust the client anyway. */ | ||
485 | void channel_permit_all_opens(void); | ||
486 | |||
487 | /* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates | ||
488 | listening for the port, and sends back a success reply (or disconnect | ||
489 | message if there was an error). This never returns if there was an | ||
490 | error. */ | ||
491 | void channel_input_port_forward_request(int is_root); | ||
492 | |||
493 | /* This is called after receiving PORT_OPEN message. This attempts to connect | ||
494 | to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or | ||
495 | CHANNEL_OPEN_FAILURE. */ | ||
496 | void channel_input_port_open(int payload_len); | ||
497 | |||
498 | /* Creates a port for X11 connections, and starts listening for it. | ||
499 | Returns the display name, or NULL if an error was encountered. */ | ||
500 | char *x11_create_display(int screen); | ||
501 | |||
502 | /* Creates an internet domain socket for listening for X11 connections. | ||
503 | Returns a suitable value for the DISPLAY variable, or NULL if an error | ||
504 | occurs. */ | ||
505 | char *x11_create_display_inet(int screen); | ||
506 | |||
507 | /* This is called when SSH_SMSG_X11_OPEN is received. The packet contains | ||
508 | the remote channel number. We should do whatever we want, and respond | ||
509 | with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ | ||
510 | void x11_input_open(int payload_len); | ||
511 | |||
512 | /* Requests forwarding of X11 connections. This should be called on the | ||
513 | client only. */ | ||
514 | void x11_request_forwarding(void); | ||
515 | |||
516 | /* Requests forwarding for X11 connections, with authentication spoofing. | ||
517 | This should be called in the client only. */ | ||
518 | void x11_request_forwarding_with_spoofing(const char *proto, const char *data); | ||
519 | |||
520 | /* Local Xauthority file (server only). */ | ||
521 | extern char *xauthfile; | ||
522 | |||
523 | /* Sends a message to the server to request authentication fd forwarding. */ | ||
524 | void auth_request_forwarding(void); | ||
525 | |||
526 | /* Returns the name of the forwarded authentication socket. Returns NULL | ||
527 | if there is no forwarded authentication socket. The returned value points | ||
528 | to a static buffer. */ | ||
529 | char *auth_get_socket_name(void); | ||
530 | |||
531 | /* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. | ||
532 | This starts forwarding authentication requests. */ | ||
533 | void auth_input_request_forwarding(struct passwd *pw); | ||
534 | |||
535 | /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ | ||
536 | void auth_input_open_request(void); | ||
537 | |||
538 | /* Returns true if the given string matches the pattern (which may contain | ||
539 | ? and * as wildcards), and zero if it does not match. */ | ||
540 | int match_pattern(const char *s, const char *pattern); | ||
541 | |||
542 | /* Expands tildes in the file name. Returns data allocated by xmalloc. | ||
543 | Warning: this calls getpw*. */ | ||
544 | char *tilde_expand_filename(const char *filename, uid_t my_uid); | ||
545 | |||
546 | /* Performs the interactive session. This handles data transmission between | ||
547 | the client and the program. Note that the notion of stdin, stdout, and | ||
548 | stderr in this function is sort of reversed: this function writes to | ||
549 | stdin (of the child program), and reads from stdout and stderr (of the | ||
550 | child program). */ | ||
551 | void server_loop(int pid, int fdin, int fdout, int fderr); | ||
552 | |||
553 | /* Client side main loop for the interactive session. */ | ||
554 | int client_loop(int have_pty, int escape_char); | ||
555 | |||
556 | /* Linked list of custom environment strings (see auth-rsa.c). */ | ||
557 | struct envstring { | ||
558 | struct envstring *next; | ||
559 | char *s; | ||
560 | }; | ||
561 | |||
562 | #ifdef KRB4 | ||
563 | #include <krb.h> | ||
564 | |||
565 | /* Performs Kerberos v4 mutual authentication with the client. This returns | ||
566 | 0 if the client could not be authenticated, and 1 if authentication was | ||
567 | successful. This may exit if there is a serious protocol violation. */ | ||
568 | int auth_krb4(const char *server_user, KTEXT auth, char **client); | ||
569 | int ssh_tf_init(uid_t uid); | ||
570 | |||
571 | #ifdef AFS | ||
572 | #include <kafs.h> | ||
573 | |||
574 | /* Accept passed Kerberos v4 ticket-granting ticket and AFS tokens. */ | ||
575 | int auth_kerberos_tgt(struct passwd *pw, const char *string); | ||
576 | int auth_afs_token(char *server_user, uid_t uid, const char *string); | ||
577 | |||
578 | int creds_to_radix(CREDENTIALS *creds, unsigned char *buf); | ||
579 | int radix_to_creds(const char *buf, CREDENTIALS *creds); | ||
580 | #endif /* AFS */ | ||
581 | |||
582 | #endif /* KRB4 */ | ||
583 | |||
584 | #ifdef SKEY | ||
585 | #include <skey.h> | ||
586 | char *skey_fake_keyinfo(char *username); | ||
587 | #endif /* SKEY */ | ||
588 | |||
589 | #endif /* SSH_H */ | ||
diff --git a/ssh.pam b/ssh.pam new file mode 100644 index 000000000..2a7d1fbd7 --- /dev/null +++ b/ssh.pam | |||
@@ -0,0 +1,7 @@ | |||
1 | #%PAM-1.0 | ||
2 | auth required /lib/security/pam_pwdb.so shadow | ||
3 | auth required /lib/security/pam_nologin.so | ||
4 | account required /lib/security/pam_pwdb.so | ||
5 | password required /lib/security/pam_cracklib.so | ||
6 | password required /lib/security/pam_pwdb.so shadow nullok use_authtok | ||
7 | session required /lib/security/pam_pwdb.so | ||
diff --git a/ssh_config b/ssh_config new file mode 100644 index 000000000..9fb064deb --- /dev/null +++ b/ssh_config | |||
@@ -0,0 +1,30 @@ | |||
1 | # This is ssh client systemwide configuration file. This file provides | ||
2 | # defaults for users, and the values can be changed in per-user configuration | ||
3 | # files or on the command line. | ||
4 | |||
5 | # Configuration data is parsed as follows: | ||
6 | # 1. command line options | ||
7 | # 2. user-specific file | ||
8 | # 3. system-wide file | ||
9 | # Any configuration value is only changed the first time it is set. | ||
10 | # Thus, host-specific definitions should be at the beginning of the | ||
11 | # configuration file, and defaults at the end. | ||
12 | |||
13 | # Site-wide defaults for various options | ||
14 | |||
15 | # Host * | ||
16 | # ForwardAgent yes | ||
17 | # ForwardX11 yes | ||
18 | # RhostsAuthentication yes | ||
19 | # RhostsRSAAuthentication yes | ||
20 | # RSAAuthentication yes | ||
21 | # PasswordAuthentication yes | ||
22 | # FallBackToRsh yes | ||
23 | # UseRsh no | ||
24 | # BatchMode no | ||
25 | # CheckHostIP yes | ||
26 | # StrictHostKeyChecking no | ||
27 | # IdentityFile ~/.ssh/identity | ||
28 | # Port 22 | ||
29 | # Cipher blowfish | ||
30 | # EscapeChar ~ | ||
diff --git a/sshconnect.c b/sshconnect.c new file mode 100644 index 000000000..3437b04ca --- /dev/null +++ b/sshconnect.c | |||
@@ -0,0 +1,1495 @@ | |||
1 | /* | ||
2 | |||
3 | sshconnect.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Mar 18 22:15:47 1995 ylo | ||
11 | |||
12 | Code to connect to a remote host, and to perform the client side of the | ||
13 | login (authentication) dialog. | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: sshconnect.c,v 1.1 1999/10/27 03:42:45 damien Exp $"); | ||
19 | |||
20 | #include <openssl/bn.h> | ||
21 | #include "xmalloc.h" | ||
22 | #include "rsa.h" | ||
23 | #include "ssh.h" | ||
24 | #include "packet.h" | ||
25 | #include "authfd.h" | ||
26 | #include "cipher.h" | ||
27 | #include "mpaux.h" | ||
28 | #include "uidswap.h" | ||
29 | #include "compat.h" | ||
30 | |||
31 | #include <openssl/md5.h> | ||
32 | |||
33 | /* Session id for the current session. */ | ||
34 | unsigned char session_id[16]; | ||
35 | |||
36 | /* Connect to the given ssh server using a proxy command. */ | ||
37 | |||
38 | int | ||
39 | ssh_proxy_connect(const char *host, int port, uid_t original_real_uid, | ||
40 | const char *proxy_command) | ||
41 | { | ||
42 | Buffer command; | ||
43 | const char *cp; | ||
44 | char *command_string; | ||
45 | int pin[2], pout[2]; | ||
46 | int pid; | ||
47 | char portstring[100]; | ||
48 | |||
49 | /* Convert the port number into a string. */ | ||
50 | snprintf(portstring, sizeof portstring, "%d", port); | ||
51 | |||
52 | /* Build the final command string in the buffer by making the appropriate | ||
53 | substitutions to the given proxy command. */ | ||
54 | buffer_init(&command); | ||
55 | for (cp = proxy_command; *cp; cp++) | ||
56 | { | ||
57 | if (cp[0] == '%' && cp[1] == '%') | ||
58 | { | ||
59 | buffer_append(&command, "%", 1); | ||
60 | cp++; | ||
61 | continue; | ||
62 | } | ||
63 | if (cp[0] == '%' && cp[1] == 'h') | ||
64 | { | ||
65 | buffer_append(&command, host, strlen(host)); | ||
66 | cp++; | ||
67 | continue; | ||
68 | } | ||
69 | if (cp[0] == '%' && cp[1] == 'p') | ||
70 | { | ||
71 | buffer_append(&command, portstring, strlen(portstring)); | ||
72 | cp++; | ||
73 | continue; | ||
74 | } | ||
75 | buffer_append(&command, cp, 1); | ||
76 | } | ||
77 | buffer_append(&command, "\0", 1); | ||
78 | |||
79 | /* Get the final command string. */ | ||
80 | command_string = buffer_ptr(&command); | ||
81 | |||
82 | /* Create pipes for communicating with the proxy. */ | ||
83 | if (pipe(pin) < 0 || pipe(pout) < 0) | ||
84 | fatal("Could not create pipes to communicate with the proxy: %.100s", | ||
85 | strerror(errno)); | ||
86 | |||
87 | debug("Executing proxy command: %.500s", command_string); | ||
88 | |||
89 | /* Fork and execute the proxy command. */ | ||
90 | if ((pid = fork()) == 0) | ||
91 | { | ||
92 | char *argv[10]; | ||
93 | |||
94 | /* Child. Permanently give up superuser privileges. */ | ||
95 | permanently_set_uid(original_real_uid); | ||
96 | |||
97 | /* Redirect stdin and stdout. */ | ||
98 | close(pin[1]); | ||
99 | if (pin[0] != 0) | ||
100 | { | ||
101 | if (dup2(pin[0], 0) < 0) | ||
102 | perror("dup2 stdin"); | ||
103 | close(pin[0]); | ||
104 | } | ||
105 | close(pout[0]); | ||
106 | if (dup2(pout[1], 1) < 0) | ||
107 | perror("dup2 stdout"); | ||
108 | close(pout[1]); /* Cannot be 1 because pin allocated two descriptors. */ | ||
109 | |||
110 | /* Stderr is left as it is so that error messages get printed on | ||
111 | the user's terminal. */ | ||
112 | argv[0] = "/bin/sh"; | ||
113 | argv[1] = "-c"; | ||
114 | argv[2] = command_string; | ||
115 | argv[3] = NULL; | ||
116 | |||
117 | /* Execute the proxy command. Note that we gave up any extra | ||
118 | privileges above. */ | ||
119 | execv("/bin/sh", argv); | ||
120 | perror("/bin/sh"); | ||
121 | exit(1); | ||
122 | } | ||
123 | /* Parent. */ | ||
124 | if (pid < 0) | ||
125 | fatal("fork failed: %.100s", strerror(errno)); | ||
126 | |||
127 | /* Close child side of the descriptors. */ | ||
128 | close(pin[0]); | ||
129 | close(pout[1]); | ||
130 | |||
131 | /* Free the command name. */ | ||
132 | buffer_free(&command); | ||
133 | |||
134 | /* Set the connection file descriptors. */ | ||
135 | packet_set_connection(pout[0], pin[1]); | ||
136 | |||
137 | return 1; | ||
138 | } | ||
139 | |||
140 | /* Creates a (possibly privileged) socket for use as the ssh connection. */ | ||
141 | |||
142 | int ssh_create_socket(uid_t original_real_uid, int privileged) | ||
143 | { | ||
144 | int sock; | ||
145 | |||
146 | /* If we are running as root and want to connect to a privileged port, | ||
147 | bind our own socket to a privileged port. */ | ||
148 | if (privileged) | ||
149 | { | ||
150 | int p = IPPORT_RESERVED - 1; | ||
151 | |||
152 | sock = rresvport(&p); | ||
153 | if (sock < 0) | ||
154 | fatal("rresvport: %.100s", strerror(errno)); | ||
155 | debug("Allocated local port %d.", p); | ||
156 | } | ||
157 | else | ||
158 | { | ||
159 | /* Just create an ordinary socket on arbitrary port. We use the | ||
160 | user's uid to create the socket. */ | ||
161 | temporarily_use_uid(original_real_uid); | ||
162 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
163 | if (sock < 0) | ||
164 | fatal("socket: %.100s", strerror(errno)); | ||
165 | restore_uid(); | ||
166 | } | ||
167 | return sock; | ||
168 | } | ||
169 | |||
170 | /* Opens a TCP/IP connection to the remote server on the given host. If | ||
171 | port is 0, the default port will be used. If anonymous is zero, | ||
172 | a privileged port will be allocated to make the connection. | ||
173 | This requires super-user privileges if anonymous is false. | ||
174 | Connection_attempts specifies the maximum number of tries (one per | ||
175 | second). If proxy_command is non-NULL, it specifies the command (with %h | ||
176 | and %p substituted for host and port, respectively) to use to contact | ||
177 | the daemon. */ | ||
178 | |||
179 | int ssh_connect(const char *host, struct sockaddr_in *hostaddr, | ||
180 | int port, int connection_attempts, | ||
181 | int anonymous, uid_t original_real_uid, | ||
182 | const char *proxy_command) | ||
183 | { | ||
184 | int sock = -1, attempt, i; | ||
185 | int on = 1; | ||
186 | struct servent *sp; | ||
187 | struct hostent *hp; | ||
188 | struct linger linger; | ||
189 | |||
190 | debug("ssh_connect: getuid %d geteuid %d anon %d", | ||
191 | (int)getuid(), (int)geteuid(), anonymous); | ||
192 | |||
193 | /* Get default port if port has not been set. */ | ||
194 | if (port == 0) | ||
195 | { | ||
196 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | ||
197 | if (sp) | ||
198 | port = ntohs(sp->s_port); | ||
199 | else | ||
200 | port = SSH_DEFAULT_PORT; | ||
201 | } | ||
202 | |||
203 | /* If a proxy command is given, connect using it. */ | ||
204 | if (proxy_command != NULL) | ||
205 | return ssh_proxy_connect(host, port, original_real_uid, proxy_command); | ||
206 | |||
207 | /* No proxy command. */ | ||
208 | |||
209 | /* No host lookup made yet. */ | ||
210 | hp = NULL; | ||
211 | |||
212 | /* Try to connect several times. On some machines, the first time will | ||
213 | sometimes fail. In general socket code appears to behave quite | ||
214 | magically on many machines. */ | ||
215 | for (attempt = 0; attempt < connection_attempts; attempt++) | ||
216 | { | ||
217 | if (attempt > 0) | ||
218 | debug("Trying again..."); | ||
219 | |||
220 | /* Try to parse the host name as a numeric inet address. */ | ||
221 | memset(hostaddr, 0, sizeof(hostaddr)); | ||
222 | hostaddr->sin_family = AF_INET; | ||
223 | hostaddr->sin_port = htons(port); | ||
224 | hostaddr->sin_addr.s_addr = inet_addr(host); | ||
225 | if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) | ||
226 | { | ||
227 | /* Valid numeric IP address */ | ||
228 | debug("Connecting to %.100s port %d.", | ||
229 | inet_ntoa(hostaddr->sin_addr), port); | ||
230 | |||
231 | /* Create a socket. */ | ||
232 | sock = ssh_create_socket(original_real_uid, | ||
233 | !anonymous && geteuid() == 0 && | ||
234 | port < IPPORT_RESERVED); | ||
235 | |||
236 | /* Connect to the host. We use the user's uid in the hope that | ||
237 | it will help with the problems of tcp_wrappers showing the | ||
238 | remote uid as root. */ | ||
239 | temporarily_use_uid(original_real_uid); | ||
240 | if (connect(sock, (struct sockaddr *)hostaddr, sizeof(*hostaddr)) | ||
241 | >= 0) | ||
242 | { | ||
243 | /* Successful connect. */ | ||
244 | restore_uid(); | ||
245 | break; | ||
246 | } | ||
247 | debug("connect: %.100s", strerror(errno)); | ||
248 | restore_uid(); | ||
249 | |||
250 | /* Destroy the failed socket. */ | ||
251 | shutdown(sock, SHUT_RDWR); | ||
252 | close(sock); | ||
253 | } | ||
254 | else | ||
255 | { | ||
256 | /* Not a valid numeric inet address. */ | ||
257 | /* Map host name to an address. */ | ||
258 | if (!hp) | ||
259 | hp = gethostbyname(host); | ||
260 | if (!hp) | ||
261 | fatal("Bad host name: %.100s", host); | ||
262 | if (!hp->h_addr_list[0]) | ||
263 | fatal("Host does not have an IP address: %.100s", host); | ||
264 | |||
265 | /* Loop through addresses for this host, and try each one in | ||
266 | sequence until the connection succeeds. */ | ||
267 | for (i = 0; hp->h_addr_list[i]; i++) | ||
268 | { | ||
269 | /* Set the address to connect to. */ | ||
270 | hostaddr->sin_family = hp->h_addrtype; | ||
271 | memcpy(&hostaddr->sin_addr, hp->h_addr_list[i], | ||
272 | sizeof(hostaddr->sin_addr)); | ||
273 | |||
274 | debug("Connecting to %.200s [%.100s] port %d.", | ||
275 | host, inet_ntoa(hostaddr->sin_addr), port); | ||
276 | |||
277 | /* Create a socket for connecting. */ | ||
278 | sock = ssh_create_socket(original_real_uid, | ||
279 | !anonymous && geteuid() == 0 && | ||
280 | port < IPPORT_RESERVED); | ||
281 | |||
282 | /* Connect to the host. We use the user's uid in the hope that | ||
283 | it will help with tcp_wrappers showing the remote uid as | ||
284 | root. */ | ||
285 | temporarily_use_uid(original_real_uid); | ||
286 | if (connect(sock, (struct sockaddr *)hostaddr, | ||
287 | sizeof(*hostaddr)) >= 0) | ||
288 | { | ||
289 | /* Successful connection. */ | ||
290 | restore_uid(); | ||
291 | break; | ||
292 | } | ||
293 | debug("connect: %.100s", strerror(errno)); | ||
294 | restore_uid(); | ||
295 | |||
296 | /* Close the failed socket; there appear to be some problems | ||
297 | when reusing a socket for which connect() has already | ||
298 | returned an error. */ | ||
299 | shutdown(sock, SHUT_RDWR); | ||
300 | close(sock); | ||
301 | } | ||
302 | if (hp->h_addr_list[i]) | ||
303 | break; /* Successful connection. */ | ||
304 | } | ||
305 | |||
306 | /* Sleep a moment before retrying. */ | ||
307 | sleep(1); | ||
308 | } | ||
309 | /* Return failure if we didn't get a successful connection. */ | ||
310 | if (attempt >= connection_attempts) | ||
311 | return 0; | ||
312 | |||
313 | debug("Connection established."); | ||
314 | |||
315 | /* Set socket options. We would like the socket to disappear as soon as | ||
316 | it has been closed for whatever reason. */ | ||
317 | /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ | ||
318 | setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)); | ||
319 | linger.l_onoff = 1; | ||
320 | linger.l_linger = 5; | ||
321 | setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); | ||
322 | |||
323 | /* Set the connection. */ | ||
324 | packet_set_connection(sock, sock); | ||
325 | |||
326 | return 1; | ||
327 | } | ||
328 | |||
329 | /* Checks if the user has an authentication agent, and if so, tries to | ||
330 | authenticate using the agent. */ | ||
331 | |||
332 | int | ||
333 | try_agent_authentication() | ||
334 | { | ||
335 | int status, type, bits; | ||
336 | char *comment; | ||
337 | AuthenticationConnection *auth; | ||
338 | unsigned char response[16]; | ||
339 | unsigned int i; | ||
340 | BIGNUM *e, *n, *challenge; | ||
341 | |||
342 | /* Get connection to the agent. */ | ||
343 | auth = ssh_get_authentication_connection(); | ||
344 | if (!auth) | ||
345 | return 0; | ||
346 | |||
347 | e = BN_new(); | ||
348 | n = BN_new(); | ||
349 | challenge = BN_new(); | ||
350 | |||
351 | /* Loop through identities served by the agent. */ | ||
352 | for (status = ssh_get_first_identity(auth, &bits, e, n, &comment); | ||
353 | status; | ||
354 | status = ssh_get_next_identity(auth, &bits, e, n, &comment)) | ||
355 | { | ||
356 | int plen, clen; | ||
357 | |||
358 | /* Try this identity. */ | ||
359 | debug("Trying RSA authentication via agent with '%.100s'", comment); | ||
360 | xfree(comment); | ||
361 | |||
362 | /* Tell the server that we are willing to authenticate using this key. */ | ||
363 | packet_start(SSH_CMSG_AUTH_RSA); | ||
364 | packet_put_bignum(n); | ||
365 | packet_send(); | ||
366 | packet_write_wait(); | ||
367 | |||
368 | /* Wait for server's response. */ | ||
369 | type = packet_read(&plen); | ||
370 | |||
371 | /* The server sends failure if it doesn\'t like our key or does not | ||
372 | support RSA authentication. */ | ||
373 | if (type == SSH_SMSG_FAILURE) | ||
374 | { | ||
375 | debug("Server refused our key."); | ||
376 | continue; | ||
377 | } | ||
378 | |||
379 | /* Otherwise it should have sent a challenge. */ | ||
380 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
381 | packet_disconnect("Protocol error during RSA authentication: %d", | ||
382 | type); | ||
383 | |||
384 | packet_get_bignum(challenge, &clen); | ||
385 | |||
386 | packet_integrity_check(plen, clen, type); | ||
387 | |||
388 | debug("Received RSA challenge from server."); | ||
389 | |||
390 | /* Ask the agent to decrypt the challenge. */ | ||
391 | if (!ssh_decrypt_challenge(auth, bits, e, n, challenge, | ||
392 | session_id, 1, response)) | ||
393 | { | ||
394 | /* The agent failed to authenticate this identifier although it | ||
395 | advertised it supports this. Just return a wrong value. */ | ||
396 | log("Authentication agent failed to decrypt challenge."); | ||
397 | memset(response, 0, sizeof(response)); | ||
398 | } | ||
399 | |||
400 | debug("Sending response to RSA challenge."); | ||
401 | |||
402 | /* Send the decrypted challenge back to the server. */ | ||
403 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
404 | for (i = 0; i < 16; i++) | ||
405 | packet_put_char(response[i]); | ||
406 | packet_send(); | ||
407 | packet_write_wait(); | ||
408 | |||
409 | /* Wait for response from the server. */ | ||
410 | type = packet_read(&plen); | ||
411 | |||
412 | /* The server returns success if it accepted the authentication. */ | ||
413 | if (type == SSH_SMSG_SUCCESS) | ||
414 | { | ||
415 | debug("RSA authentication accepted by server."); | ||
416 | BN_clear_free(e); | ||
417 | BN_clear_free(n); | ||
418 | BN_clear_free(challenge); | ||
419 | return 1; | ||
420 | } | ||
421 | |||
422 | /* Otherwise it should return failure. */ | ||
423 | if (type != SSH_SMSG_FAILURE) | ||
424 | packet_disconnect("Protocol error waiting RSA auth response: %d", | ||
425 | type); | ||
426 | } | ||
427 | |||
428 | BN_clear_free(e); | ||
429 | BN_clear_free(n); | ||
430 | BN_clear_free(challenge); | ||
431 | |||
432 | debug("RSA authentication using agent refused."); | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /* Computes the proper response to a RSA challenge, and sends the response to | ||
437 | the server. */ | ||
438 | |||
439 | void | ||
440 | respond_to_rsa_challenge(BIGNUM *challenge, RSA *prv) | ||
441 | { | ||
442 | unsigned char buf[32], response[16]; | ||
443 | MD5_CTX md; | ||
444 | int i, len; | ||
445 | |||
446 | /* Decrypt the challenge using the private key. */ | ||
447 | rsa_private_decrypt(challenge, challenge, prv); | ||
448 | |||
449 | /* Compute the response. */ | ||
450 | /* The response is MD5 of decrypted challenge plus session id. */ | ||
451 | len = BN_num_bytes(challenge); | ||
452 | assert(len <= sizeof(buf) && len); | ||
453 | memset(buf, 0, sizeof(buf)); | ||
454 | BN_bn2bin(challenge, buf + sizeof(buf) - len); | ||
455 | MD5_Init(&md); | ||
456 | MD5_Update(&md, buf, 32); | ||
457 | MD5_Update(&md, session_id, 16); | ||
458 | MD5_Final(response, &md); | ||
459 | |||
460 | debug("Sending response to host key RSA challenge."); | ||
461 | |||
462 | /* Send the response back to the server. */ | ||
463 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
464 | for (i = 0; i < 16; i++) | ||
465 | packet_put_char(response[i]); | ||
466 | packet_send(); | ||
467 | packet_write_wait(); | ||
468 | |||
469 | memset(buf, 0, sizeof(buf)); | ||
470 | memset(response, 0, sizeof(response)); | ||
471 | memset(&md, 0, sizeof(md)); | ||
472 | } | ||
473 | |||
474 | /* Checks if the user has authentication file, and if so, tries to authenticate | ||
475 | the user using it. */ | ||
476 | |||
477 | int | ||
478 | try_rsa_authentication(struct passwd *pw, const char *authfile, | ||
479 | int may_ask_passphrase) | ||
480 | { | ||
481 | BIGNUM *challenge; | ||
482 | RSA *private_key; | ||
483 | RSA *public_key; | ||
484 | char *passphrase, *comment; | ||
485 | int type, i; | ||
486 | int plen, clen; | ||
487 | |||
488 | /* Try to load identification for the authentication key. */ | ||
489 | public_key = RSA_new(); | ||
490 | if (!load_public_key(authfile, public_key, &comment)) { | ||
491 | RSA_free(public_key); | ||
492 | return 0; /* Could not load it. Fail. */ | ||
493 | } | ||
494 | |||
495 | debug("Trying RSA authentication with key '%.100s'", comment); | ||
496 | |||
497 | /* Tell the server that we are willing to authenticate using this key. */ | ||
498 | packet_start(SSH_CMSG_AUTH_RSA); | ||
499 | packet_put_bignum(public_key->n); | ||
500 | packet_send(); | ||
501 | packet_write_wait(); | ||
502 | |||
503 | /* We no longer need the public key. */ | ||
504 | RSA_free(public_key); | ||
505 | |||
506 | /* Wait for server's response. */ | ||
507 | type = packet_read(&plen); | ||
508 | |||
509 | /* The server responds with failure if it doesn\'t like our key or doesn\'t | ||
510 | support RSA authentication. */ | ||
511 | if (type == SSH_SMSG_FAILURE) | ||
512 | { | ||
513 | debug("Server refused our key."); | ||
514 | xfree(comment); | ||
515 | return 0; /* Server refuses to authenticate with this key. */ | ||
516 | } | ||
517 | |||
518 | /* Otherwise, the server should respond with a challenge. */ | ||
519 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
520 | packet_disconnect("Protocol error during RSA authentication: %d", type); | ||
521 | |||
522 | /* Get the challenge from the packet. */ | ||
523 | challenge = BN_new(); | ||
524 | packet_get_bignum(challenge, &clen); | ||
525 | |||
526 | packet_integrity_check(plen, clen, type); | ||
527 | |||
528 | debug("Received RSA challenge from server."); | ||
529 | |||
530 | private_key = RSA_new(); | ||
531 | /* Load the private key. Try first with empty passphrase; if it fails, | ||
532 | ask for a passphrase. */ | ||
533 | if (!load_private_key(authfile, "", private_key, NULL)) | ||
534 | { | ||
535 | char buf[300]; | ||
536 | /* Request passphrase from the user. We read from /dev/tty to make | ||
537 | this work even if stdin has been redirected. If running in | ||
538 | batch mode, we just use the empty passphrase, which will fail and | ||
539 | return. */ | ||
540 | snprintf(buf, sizeof buf, | ||
541 | "Enter passphrase for RSA key '%.100s': ", comment); | ||
542 | if (may_ask_passphrase) | ||
543 | passphrase = read_passphrase(buf, 0); | ||
544 | else | ||
545 | { | ||
546 | debug("Will not query passphrase for %.100s in batch mode.", | ||
547 | comment); | ||
548 | passphrase = xstrdup(""); | ||
549 | } | ||
550 | |||
551 | /* Load the authentication file using the pasphrase. */ | ||
552 | if (!load_private_key(authfile, passphrase, private_key, NULL)) | ||
553 | { | ||
554 | memset(passphrase, 0, strlen(passphrase)); | ||
555 | xfree(passphrase); | ||
556 | error("Bad passphrase."); | ||
557 | |||
558 | /* Send a dummy response packet to avoid protocol error. */ | ||
559 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
560 | for (i = 0; i < 16; i++) | ||
561 | packet_put_char(0); | ||
562 | packet_send(); | ||
563 | packet_write_wait(); | ||
564 | |||
565 | /* Expect the server to reject it... */ | ||
566 | packet_read_expect(&plen, SSH_SMSG_FAILURE); | ||
567 | xfree(comment); | ||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | /* Destroy the passphrase. */ | ||
572 | memset(passphrase, 0, strlen(passphrase)); | ||
573 | xfree(passphrase); | ||
574 | } | ||
575 | |||
576 | /* We no longer need the comment. */ | ||
577 | xfree(comment); | ||
578 | |||
579 | /* Compute and send a response to the challenge. */ | ||
580 | respond_to_rsa_challenge(challenge, private_key); | ||
581 | |||
582 | /* Destroy the private key. */ | ||
583 | RSA_free(private_key); | ||
584 | |||
585 | /* We no longer need the challenge. */ | ||
586 | BN_clear_free(challenge); | ||
587 | |||
588 | /* Wait for response from the server. */ | ||
589 | type = packet_read(&plen); | ||
590 | if (type == SSH_SMSG_SUCCESS) | ||
591 | { | ||
592 | debug("RSA authentication accepted by server."); | ||
593 | return 1; | ||
594 | } | ||
595 | if (type != SSH_SMSG_FAILURE) | ||
596 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | ||
597 | debug("RSA authentication refused."); | ||
598 | return 0; | ||
599 | } | ||
600 | |||
601 | /* Tries to authenticate the user using combined rhosts or /etc/hosts.equiv | ||
602 | authentication and RSA host authentication. */ | ||
603 | |||
604 | int | ||
605 | try_rhosts_rsa_authentication(const char *local_user, RSA *host_key) | ||
606 | { | ||
607 | int type; | ||
608 | BIGNUM *challenge; | ||
609 | int plen, clen; | ||
610 | |||
611 | debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication."); | ||
612 | |||
613 | /* Tell the server that we are willing to authenticate using this key. */ | ||
614 | packet_start(SSH_CMSG_AUTH_RHOSTS_RSA); | ||
615 | packet_put_string(local_user, strlen(local_user)); | ||
616 | packet_put_int(BN_num_bits(host_key->n)); | ||
617 | packet_put_bignum(host_key->e); | ||
618 | packet_put_bignum(host_key->n); | ||
619 | packet_send(); | ||
620 | packet_write_wait(); | ||
621 | |||
622 | /* Wait for server's response. */ | ||
623 | type = packet_read(&plen); | ||
624 | |||
625 | /* The server responds with failure if it doesn't admit our .rhosts | ||
626 | authentication or doesn't know our host key. */ | ||
627 | if (type == SSH_SMSG_FAILURE) | ||
628 | { | ||
629 | debug("Server refused our rhosts authentication or host key."); | ||
630 | return 0; /* Server refuses to authenticate us with this method. */ | ||
631 | } | ||
632 | |||
633 | /* Otherwise, the server should respond with a challenge. */ | ||
634 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
635 | packet_disconnect("Protocol error during RSA authentication: %d", type); | ||
636 | |||
637 | /* Get the challenge from the packet. */ | ||
638 | challenge = BN_new(); | ||
639 | packet_get_bignum(challenge, &clen); | ||
640 | |||
641 | packet_integrity_check(plen, clen, type); | ||
642 | |||
643 | debug("Received RSA challenge for host key from server."); | ||
644 | |||
645 | /* Compute a response to the challenge. */ | ||
646 | respond_to_rsa_challenge(challenge, host_key); | ||
647 | |||
648 | /* We no longer need the challenge. */ | ||
649 | BN_clear_free(challenge); | ||
650 | |||
651 | /* Wait for response from the server. */ | ||
652 | type = packet_read(&plen); | ||
653 | if (type == SSH_SMSG_SUCCESS) | ||
654 | { | ||
655 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server."); | ||
656 | return 1; | ||
657 | } | ||
658 | if (type != SSH_SMSG_FAILURE) | ||
659 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | ||
660 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused."); | ||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | #ifdef KRB4 | ||
665 | int try_kerberos_authentication() | ||
666 | { | ||
667 | KTEXT_ST auth; /* Kerberos data */ | ||
668 | char *reply; | ||
669 | char inst[INST_SZ]; | ||
670 | char *realm; | ||
671 | CREDENTIALS cred; | ||
672 | int r, type, plen; | ||
673 | Key_schedule schedule; | ||
674 | u_long checksum, cksum; | ||
675 | MSG_DAT msg_data; | ||
676 | struct sockaddr_in local, foreign; | ||
677 | struct stat st; | ||
678 | |||
679 | /* Don't do anything if we don't have any tickets. */ | ||
680 | if (stat(tkt_string(), &st) < 0) return 0; | ||
681 | |||
682 | strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); | ||
683 | |||
684 | realm = (char *)krb_realmofhost(get_canonical_hostname()); | ||
685 | if (!realm) { | ||
686 | debug("Kerberos V4: no realm for %s", get_canonical_hostname()); | ||
687 | return 0; | ||
688 | } | ||
689 | /* This can really be anything. */ | ||
690 | checksum = (u_long) getpid(); | ||
691 | |||
692 | r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum); | ||
693 | if (r != KSUCCESS) { | ||
694 | debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]); | ||
695 | return 0; | ||
696 | } | ||
697 | /* Get session key to decrypt the server's reply with. */ | ||
698 | r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred); | ||
699 | if (r != KSUCCESS) { | ||
700 | debug("get_cred failed: %s", krb_err_txt[r]); | ||
701 | return 0; | ||
702 | } | ||
703 | des_key_sched((des_cblock *)cred.session, schedule); | ||
704 | |||
705 | /* Send authentication info to server. */ | ||
706 | packet_start(SSH_CMSG_AUTH_KERBEROS); | ||
707 | packet_put_string((char *)auth.dat, auth.length); | ||
708 | packet_send(); | ||
709 | packet_write_wait(); | ||
710 | |||
711 | /* Zero the buffer. */ | ||
712 | (void) memset(auth.dat, 0, MAX_KTXT_LEN); | ||
713 | |||
714 | r = sizeof(local); | ||
715 | memset(&local, 0, sizeof(local)); | ||
716 | if (getsockname(packet_get_connection_in(), | ||
717 | (struct sockaddr *) &local, &r) < 0) | ||
718 | debug("getsockname failed: %s", strerror(errno)); | ||
719 | |||
720 | r = sizeof(foreign); | ||
721 | memset(&foreign, 0, sizeof(foreign)); | ||
722 | if (getpeername(packet_get_connection_in(), | ||
723 | (struct sockaddr *)&foreign, &r) < 0) | ||
724 | debug("getpeername failed: %s", strerror(errno)); | ||
725 | |||
726 | /* Get server reply. */ | ||
727 | type = packet_read(&plen); | ||
728 | switch(type) { | ||
729 | |||
730 | case SSH_SMSG_FAILURE: /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ | ||
731 | debug("Kerberos V4 authentication failed."); | ||
732 | return 0; | ||
733 | break; | ||
734 | |||
735 | case SSH_SMSG_AUTH_KERBEROS_RESPONSE: /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ | ||
736 | debug("Kerberos V4 authentication accepted."); | ||
737 | |||
738 | /* Get server's response. */ | ||
739 | reply = packet_get_string((unsigned int *)&auth.length); | ||
740 | memcpy(auth.dat, reply, auth.length); | ||
741 | xfree(reply); | ||
742 | |||
743 | packet_integrity_check(plen, 4 + auth.length, type); | ||
744 | |||
745 | /* If his response isn't properly encrypted with the session key, | ||
746 | and the decrypted checksum fails to match, he's bogus. Bail out. */ | ||
747 | r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session, | ||
748 | &foreign, &local, &msg_data); | ||
749 | if (r != KSUCCESS) { | ||
750 | debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]); | ||
751 | packet_disconnect("Kerberos V4 challenge failed!"); | ||
752 | } | ||
753 | /* Fetch the (incremented) checksum that we supplied in the request. */ | ||
754 | (void)memcpy((char *)&cksum, (char *)msg_data.app_data, sizeof(cksum)); | ||
755 | cksum = ntohl(cksum); | ||
756 | |||
757 | /* If it matches, we're golden. */ | ||
758 | if (cksum == checksum + 1) { | ||
759 | debug("Kerberos V4 challenge successful."); | ||
760 | return 1; | ||
761 | } | ||
762 | else | ||
763 | packet_disconnect("Kerberos V4 challenge failed!"); | ||
764 | break; | ||
765 | |||
766 | default: | ||
767 | packet_disconnect("Protocol error on Kerberos V4 response: %d", type); | ||
768 | } | ||
769 | return 0; | ||
770 | } | ||
771 | #endif /* KRB4 */ | ||
772 | |||
773 | #ifdef AFS | ||
774 | int send_kerberos_tgt() | ||
775 | { | ||
776 | CREDENTIALS *creds; | ||
777 | char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; | ||
778 | int r, type, plen; | ||
779 | unsigned char buffer[8192]; | ||
780 | struct stat st; | ||
781 | |||
782 | /* Don't do anything if we don't have any tickets. */ | ||
783 | if (stat(tkt_string(), &st) < 0) return 0; | ||
784 | |||
785 | creds = xmalloc(sizeof(*creds)); | ||
786 | |||
787 | if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) { | ||
788 | debug("Kerberos V4 tf_fullname failed: %s",krb_err_txt[r]); | ||
789 | return 0; | ||
790 | } | ||
791 | if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { | ||
792 | debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); | ||
793 | return 0; | ||
794 | } | ||
795 | if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) { | ||
796 | debug("Kerberos V4 ticket expired: %s", TKT_FILE); | ||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | creds_to_radix(creds, buffer); | ||
801 | xfree(creds); | ||
802 | |||
803 | packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); | ||
804 | packet_put_string((char *)buffer, strlen(buffer)); | ||
805 | packet_send(); | ||
806 | packet_write_wait(); | ||
807 | |||
808 | type = packet_read(&plen); | ||
809 | |||
810 | if (type == SSH_SMSG_FAILURE) | ||
811 | debug("Kerberos TGT for realm %s rejected.", prealm); | ||
812 | else if (type != SSH_SMSG_SUCCESS) | ||
813 | packet_disconnect("Protocol error on Kerberos TGT response: %d", type); | ||
814 | |||
815 | return 1; | ||
816 | } | ||
817 | |||
818 | void send_afs_tokens(void) | ||
819 | { | ||
820 | CREDENTIALS creds; | ||
821 | struct ViceIoctl parms; | ||
822 | struct ClearToken ct; | ||
823 | int i, type, len, plen; | ||
824 | char buf[2048], *p, *server_cell; | ||
825 | unsigned char buffer[8192]; | ||
826 | |||
827 | /* Move over ktc_GetToken, here's something leaner. */ | ||
828 | for (i = 0; i < 100; i++) { /* just in case */ | ||
829 | parms.in = (char *)&i; | ||
830 | parms.in_size = sizeof(i); | ||
831 | parms.out = buf; | ||
832 | parms.out_size = sizeof(buf); | ||
833 | if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) break; | ||
834 | p = buf; | ||
835 | |||
836 | /* Get secret token. */ | ||
837 | memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); | ||
838 | if (creds.ticket_st.length > MAX_KTXT_LEN) break; | ||
839 | p += sizeof(unsigned int); | ||
840 | memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); | ||
841 | p += creds.ticket_st.length; | ||
842 | |||
843 | /* Get clear token. */ | ||
844 | memcpy(&len, p, sizeof(len)); | ||
845 | if (len != sizeof(struct ClearToken)) break; | ||
846 | p += sizeof(len); | ||
847 | memcpy(&ct, p, len); | ||
848 | p += len; | ||
849 | p += sizeof(len); /* primary flag */ | ||
850 | server_cell = p; | ||
851 | |||
852 | /* Flesh out our credentials. */ | ||
853 | strlcpy(creds.service, "afs", sizeof creds.service); | ||
854 | creds.instance[0] = '\0'; | ||
855 | strlcpy(creds.realm, server_cell, REALM_SZ); | ||
856 | memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); | ||
857 | creds.issue_date = ct.BeginTimestamp; | ||
858 | creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); | ||
859 | creds.kvno = ct.AuthHandle; | ||
860 | snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); | ||
861 | creds.pinst[0] = '\0'; | ||
862 | |||
863 | /* Encode token, ship it off. */ | ||
864 | if (!creds_to_radix(&creds, buffer)) break; | ||
865 | packet_start(SSH_CMSG_HAVE_AFS_TOKEN); | ||
866 | packet_put_string((char *)buffer, strlen(buffer)); | ||
867 | packet_send(); | ||
868 | packet_write_wait(); | ||
869 | |||
870 | /* Roger, Roger. Clearance, Clarence. What's your vector, Victor? */ | ||
871 | type = packet_read(&plen); | ||
872 | |||
873 | if (type == SSH_SMSG_FAILURE) | ||
874 | debug("AFS token for cell %s rejected.", server_cell); | ||
875 | else if (type != SSH_SMSG_SUCCESS) | ||
876 | packet_disconnect("Protocol error on AFS token response: %d", type); | ||
877 | } | ||
878 | } | ||
879 | #endif /* AFS */ | ||
880 | |||
881 | /* Waits for the server identification string, and sends our own identification | ||
882 | string. */ | ||
883 | |||
884 | void ssh_exchange_identification() | ||
885 | { | ||
886 | char buf[256], remote_version[256]; /* must be same size! */ | ||
887 | int remote_major, remote_minor, i; | ||
888 | int connection_in = packet_get_connection_in(); | ||
889 | int connection_out = packet_get_connection_out(); | ||
890 | extern Options options; | ||
891 | |||
892 | /* Read other side\'s version identification. */ | ||
893 | for (i = 0; i < sizeof(buf) - 1; i++) | ||
894 | { | ||
895 | if (read(connection_in, &buf[i], 1) != 1) | ||
896 | fatal("read: %.100s", strerror(errno)); | ||
897 | if (buf[i] == '\r') | ||
898 | { | ||
899 | buf[i] = '\n'; | ||
900 | buf[i + 1] = 0; | ||
901 | break; | ||
902 | } | ||
903 | if (buf[i] == '\n') | ||
904 | { | ||
905 | buf[i + 1] = 0; | ||
906 | break; | ||
907 | } | ||
908 | } | ||
909 | buf[sizeof(buf) - 1] = 0; | ||
910 | |||
911 | /* Check that the versions match. In future this might accept several | ||
912 | versions and set appropriate flags to handle them. */ | ||
913 | if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, | ||
914 | remote_version) != 3) | ||
915 | fatal("Bad remote protocol version identification: '%.100s'", buf); | ||
916 | debug("Remote protocol version %d.%d, remote software version %.100s", | ||
917 | remote_major, remote_minor, remote_version); | ||
918 | |||
919 | /* Check if the remote protocol version is too old. */ | ||
920 | if (remote_major == 1 && remote_minor < 3) | ||
921 | fatal("Remote machine has too old SSH software version."); | ||
922 | |||
923 | /* We speak 1.3, too. */ | ||
924 | if (remote_major == 1 && remote_minor == 3) { | ||
925 | enable_compat13(); | ||
926 | if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) { | ||
927 | log("Agent forwarding disabled, remote version '%s' is not compatible.", | ||
928 | remote_version); | ||
929 | options.forward_agent = 0; | ||
930 | } | ||
931 | } | ||
932 | #if 0 | ||
933 | /* Removed for now, to permit compatibility with latter versions. The server | ||
934 | will reject our version and disconnect if it doesn't support it. */ | ||
935 | if (remote_major != PROTOCOL_MAJOR) | ||
936 | fatal("Protocol major versions differ: %d vs. %d", | ||
937 | PROTOCOL_MAJOR, remote_major); | ||
938 | #endif | ||
939 | |||
940 | /* Send our own protocol version identification. */ | ||
941 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", | ||
942 | PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); | ||
943 | if (write(connection_out, buf, strlen(buf)) != strlen(buf)) | ||
944 | fatal("write: %.100s", strerror(errno)); | ||
945 | } | ||
946 | |||
947 | int ssh_cipher_default = SSH_CIPHER_3DES; | ||
948 | |||
949 | int read_yes_or_no(const char *prompt, int defval) | ||
950 | { | ||
951 | char buf[1024]; | ||
952 | FILE *f; | ||
953 | int retval = -1; | ||
954 | |||
955 | if (isatty(0)) | ||
956 | f = stdin; | ||
957 | else | ||
958 | f = fopen("/dev/tty", "rw"); | ||
959 | |||
960 | if (f == NULL) | ||
961 | return 0; | ||
962 | |||
963 | fflush(stdout); | ||
964 | |||
965 | while (1) | ||
966 | { | ||
967 | fprintf(stderr, "%s", prompt); | ||
968 | if (fgets(buf, sizeof(buf), f) == NULL) | ||
969 | { | ||
970 | /* Print a newline (the prompt probably didn\'t have one). */ | ||
971 | fprintf(stderr, "\n"); | ||
972 | strlcpy(buf, "no", sizeof buf); | ||
973 | } | ||
974 | /* Remove newline from response. */ | ||
975 | if (strchr(buf, '\n')) | ||
976 | *strchr(buf, '\n') = 0; | ||
977 | |||
978 | if (buf[0] == 0) | ||
979 | retval = defval; | ||
980 | if (strcmp(buf, "yes") == 0) | ||
981 | retval = 1; | ||
982 | if (strcmp(buf, "no") == 0) | ||
983 | retval = 0; | ||
984 | |||
985 | if (retval != -1) | ||
986 | { | ||
987 | if (f != stdin) | ||
988 | fclose(f); | ||
989 | return retval; | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | |||
994 | /* Starts a dialog with the server, and authenticates the current user on the | ||
995 | server. This does not need any extra privileges. The basic connection | ||
996 | to the server must already have been established before this is called. | ||
997 | User is the remote user; if it is NULL, the current local user name will | ||
998 | be used. Anonymous indicates that no rhosts authentication will be used. | ||
999 | If login fails, this function prints an error and never returns. | ||
1000 | This function does not require super-user privileges. */ | ||
1001 | |||
1002 | void ssh_login(int host_key_valid, | ||
1003 | RSA *own_host_key, | ||
1004 | const char *orighost, | ||
1005 | struct sockaddr_in *hostaddr, | ||
1006 | Options *options, uid_t original_real_uid) | ||
1007 | { | ||
1008 | int i, type; | ||
1009 | char *password; | ||
1010 | struct passwd *pw; | ||
1011 | BIGNUM *key; | ||
1012 | RSA *host_key, *file_key; | ||
1013 | RSA *public_key; | ||
1014 | unsigned char session_key[SSH_SESSION_KEY_LENGTH]; | ||
1015 | const char *server_user, *local_user; | ||
1016 | char *cp, *host, *ip = NULL; | ||
1017 | unsigned char check_bytes[8]; | ||
1018 | unsigned int supported_ciphers, supported_authentications, protocol_flags; | ||
1019 | HostStatus host_status; | ||
1020 | HostStatus ip_status; | ||
1021 | int host_ip_differ = 0; | ||
1022 | int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; | ||
1023 | int payload_len, clen, sum_len = 0; | ||
1024 | u_int32_t rand = 0; | ||
1025 | |||
1026 | if (options->check_host_ip) | ||
1027 | ip = xstrdup(inet_ntoa(hostaddr->sin_addr)); | ||
1028 | |||
1029 | /* Convert the user-supplied hostname into all lowercase. */ | ||
1030 | host = xstrdup(orighost); | ||
1031 | for (cp = host; *cp; cp++) | ||
1032 | if (isupper(*cp)) | ||
1033 | *cp = tolower(*cp); | ||
1034 | |||
1035 | /* Exchange protocol version identification strings with the server. */ | ||
1036 | ssh_exchange_identification(); | ||
1037 | |||
1038 | /* Put the connection into non-blocking mode. */ | ||
1039 | packet_set_nonblocking(); | ||
1040 | |||
1041 | /* Get local user name. Use it as server user if no user name | ||
1042 | was given. */ | ||
1043 | pw = getpwuid(original_real_uid); | ||
1044 | if (!pw) | ||
1045 | fatal("User id %d not found from user database.", original_real_uid); | ||
1046 | local_user = xstrdup(pw->pw_name); | ||
1047 | server_user = options->user ? options->user : local_user; | ||
1048 | |||
1049 | debug("Waiting for server public key."); | ||
1050 | |||
1051 | /* Wait for a public key packet from the server. */ | ||
1052 | packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY); | ||
1053 | |||
1054 | /* Get check bytes from the packet. */ | ||
1055 | for (i = 0; i < 8; i++) | ||
1056 | check_bytes[i] = packet_get_char(); | ||
1057 | |||
1058 | /* Get the public key. */ | ||
1059 | public_key = RSA_new(); | ||
1060 | packet_get_int(); /* bits */ | ||
1061 | public_key->e = BN_new(); | ||
1062 | packet_get_bignum(public_key->e, &clen); | ||
1063 | sum_len += clen; | ||
1064 | public_key->n = BN_new(); | ||
1065 | packet_get_bignum(public_key->n, &clen); | ||
1066 | sum_len += clen; | ||
1067 | |||
1068 | /* Get the host key. */ | ||
1069 | host_key = RSA_new(); | ||
1070 | packet_get_int(); /* bits */ | ||
1071 | host_key->e = BN_new(); | ||
1072 | packet_get_bignum(host_key->e, &clen); | ||
1073 | sum_len += clen; | ||
1074 | host_key->n = BN_new(); | ||
1075 | packet_get_bignum(host_key->n, &clen); | ||
1076 | sum_len += clen; | ||
1077 | |||
1078 | /* Store the host key from the known host file in here | ||
1079 | * so that we can compare it with the key for the IP | ||
1080 | * address. */ | ||
1081 | file_key = RSA_new(); | ||
1082 | file_key->n = BN_new(); | ||
1083 | file_key->e = BN_new(); | ||
1084 | |||
1085 | /* Get protocol flags. */ | ||
1086 | protocol_flags = packet_get_int(); | ||
1087 | packet_set_protocol_flags(protocol_flags); | ||
1088 | |||
1089 | /* Get supported cipher types. */ | ||
1090 | supported_ciphers = packet_get_int(); | ||
1091 | |||
1092 | /* Get supported authentication types. */ | ||
1093 | supported_authentications = packet_get_int(); | ||
1094 | |||
1095 | debug("Received server public key (%d bits) and host key (%d bits).", | ||
1096 | BN_num_bits(public_key->n), BN_num_bits(host_key->n)); | ||
1097 | |||
1098 | packet_integrity_check(payload_len, | ||
1099 | 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, | ||
1100 | SSH_SMSG_PUBLIC_KEY); | ||
1101 | |||
1102 | /* Compute the session id. */ | ||
1103 | compute_session_id(session_id, check_bytes, | ||
1104 | BN_num_bits(host_key->n), host_key->n, | ||
1105 | BN_num_bits(public_key->n), public_key->n); | ||
1106 | |||
1107 | /* Check if the host key is present in the user\'s list of known hosts | ||
1108 | or in the systemwide list. */ | ||
1109 | host_status = check_host_in_hostfile(options->user_hostfile, | ||
1110 | host, BN_num_bits(host_key->n), | ||
1111 | host_key->e, host_key->n, | ||
1112 | file_key->e, file_key->n); | ||
1113 | if (host_status == HOST_NEW) | ||
1114 | host_status = check_host_in_hostfile(options->system_hostfile, host, | ||
1115 | BN_num_bits(host_key->n), | ||
1116 | host_key->e, host_key->n, | ||
1117 | file_key->e, file_key->n); | ||
1118 | /* Force accepting of the host key for localhost and 127.0.0.1. | ||
1119 | The problem is that if the home directory is NFS-mounted to multiple | ||
1120 | machines, localhost will refer to a different machine in each of them, | ||
1121 | and the user will get bogus HOST_CHANGED warnings. This essentially | ||
1122 | disables host authentication for localhost; however, this is probably | ||
1123 | not a real problem. */ | ||
1124 | if (local) { | ||
1125 | debug("Forcing accepting of host key for localhost."); | ||
1126 | host_status = HOST_OK; | ||
1127 | } | ||
1128 | |||
1129 | /* Also perform check for the ip address, skip the check if we are | ||
1130 | localhost or the hostname was an ip address to begin with */ | ||
1131 | if (options->check_host_ip && !local && strcmp(host, ip)) { | ||
1132 | RSA *ip_key = RSA_new(); | ||
1133 | ip_key->n = BN_new(); | ||
1134 | ip_key->e = BN_new(); | ||
1135 | ip_status = check_host_in_hostfile(options->user_hostfile, ip, | ||
1136 | BN_num_bits(host_key->n), | ||
1137 | host_key->e, host_key->n, | ||
1138 | ip_key->e, ip_key->n); | ||
1139 | |||
1140 | if (ip_status == HOST_NEW) | ||
1141 | ip_status = check_host_in_hostfile(options->system_hostfile, ip, | ||
1142 | BN_num_bits(host_key->n), | ||
1143 | host_key->e, host_key->n, | ||
1144 | ip_key->e, ip_key->n); | ||
1145 | if (host_status == HOST_CHANGED && | ||
1146 | (ip_status != HOST_CHANGED || | ||
1147 | (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n)))) | ||
1148 | host_ip_differ = 1; | ||
1149 | |||
1150 | RSA_free(ip_key); | ||
1151 | } else | ||
1152 | ip_status = host_status; | ||
1153 | |||
1154 | RSA_free(file_key); | ||
1155 | |||
1156 | switch (host_status) { | ||
1157 | case HOST_OK: | ||
1158 | /* The host is known and the key matches. */ | ||
1159 | debug("Host '%.200s' is known and matches the host key.", host); | ||
1160 | if (options->check_host_ip) { | ||
1161 | if (ip_status == HOST_NEW) { | ||
1162 | if (!add_host_to_hostfile(options->user_hostfile, ip, | ||
1163 | BN_num_bits(host_key->n), | ||
1164 | host_key->e, host_key->n)) | ||
1165 | log("Failed to add the host ip to the list of known hosts (%.30s).", | ||
1166 | options->user_hostfile); | ||
1167 | else | ||
1168 | log("Warning: Permanently added host ip '%.30s' to the list of known hosts.", ip); | ||
1169 | } else if (ip_status != HOST_OK) | ||
1170 | log("Warning: the host key differ from the key of the ip address '%.30s' differs", ip); | ||
1171 | } | ||
1172 | |||
1173 | break; | ||
1174 | case HOST_NEW: | ||
1175 | { | ||
1176 | char hostline[1000], *hostp = hostline; | ||
1177 | /* The host is new. */ | ||
1178 | if (options->strict_host_key_checking == 1) { | ||
1179 | /* User has requested strict host key checking. We will not | ||
1180 | add the host key automatically. The only alternative left | ||
1181 | is to abort. */ | ||
1182 | fatal("No host key is known for %.200s and you have requested strict checking.", host); | ||
1183 | } else if (options->strict_host_key_checking == 2) { /* The default */ | ||
1184 | char prompt[1024]; | ||
1185 | snprintf(prompt, sizeof(prompt), | ||
1186 | "The authenticity of host '%.200s' can't be established.\n" | ||
1187 | "Are you sure you want to continue connecting (yes/no)? ", | ||
1188 | host); | ||
1189 | if (!read_yes_or_no(prompt, -1)) | ||
1190 | fatal("Aborted by user!\n"); | ||
1191 | } | ||
1192 | |||
1193 | if (options->check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) | ||
1194 | snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); | ||
1195 | else | ||
1196 | hostp = host; | ||
1197 | |||
1198 | /* If not in strict mode, add the key automatically to the local | ||
1199 | known_hosts file. */ | ||
1200 | if (!add_host_to_hostfile(options->user_hostfile, hostp, | ||
1201 | BN_num_bits(host_key->n), | ||
1202 | host_key->e, host_key->n)) | ||
1203 | log("Failed to add the host to the list of known hosts (%.500s).", | ||
1204 | options->user_hostfile); | ||
1205 | else | ||
1206 | log("Warning: Permanently added '%.200s' to the list of known hosts.", | ||
1207 | hostp); | ||
1208 | break; | ||
1209 | } | ||
1210 | case HOST_CHANGED: | ||
1211 | if (options->check_host_ip) { | ||
1212 | if (host_ip_differ) { | ||
1213 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
1214 | error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); | ||
1215 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
1216 | error("The host key for %s has changed,", host); | ||
1217 | error("but the key for the according IP address %s has", ip); | ||
1218 | error("a different status. This could either mean that DNS"); | ||
1219 | error("SPOOFING is happening or the IP address for the host"); | ||
1220 | error("and its host key have changed at the same time"); | ||
1221 | } | ||
1222 | } | ||
1223 | |||
1224 | /* The host key has changed. */ | ||
1225 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
1226 | error("@ WARNING: HOST IDENTIFICATION HAS CHANGED! @"); | ||
1227 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
1228 | error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); | ||
1229 | error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); | ||
1230 | error("It is also possible that the host key has just been changed."); | ||
1231 | error("Please contact your system administrator."); | ||
1232 | error("Add correct host key in %.100s to get rid of this message.", | ||
1233 | options->user_hostfile); | ||
1234 | |||
1235 | /* If strict host key checking is in use, the user will have to edit | ||
1236 | the key manually and we can only abort. */ | ||
1237 | if (options->strict_host_key_checking) | ||
1238 | fatal("Host key for %.200s has changed and you have requested strict checking.", host); | ||
1239 | |||
1240 | /* If strict host key checking has not been requested, allow the | ||
1241 | connection but without password authentication or | ||
1242 | agent forwarding. */ | ||
1243 | if (options->password_authentication) { | ||
1244 | error("Password authentication is disabled to avoid trojan horses."); | ||
1245 | options->password_authentication = 0; | ||
1246 | } | ||
1247 | if (options->forward_agent) { | ||
1248 | error("Agent forwarding is disabled to avoid trojan horses."); | ||
1249 | options->forward_agent = 0; | ||
1250 | } | ||
1251 | /* XXX Should permit the user to change to use the new id. This could | ||
1252 | be done by converting the host key to an identifying sentence, tell | ||
1253 | that the host identifies itself by that sentence, and ask the user | ||
1254 | if he/she whishes to accept the authentication. */ | ||
1255 | break; | ||
1256 | } | ||
1257 | |||
1258 | if (options->check_host_ip) | ||
1259 | xfree(ip); | ||
1260 | |||
1261 | /* Generate a session key. */ | ||
1262 | arc4random_stir(); | ||
1263 | |||
1264 | /* Generate an encryption key for the session. The key is a 256 bit | ||
1265 | random number, interpreted as a 32-byte key, with the least significant | ||
1266 | 8 bits being the first byte of the key. */ | ||
1267 | for (i = 0; i < 32; i++) { | ||
1268 | if (i % 4 == 0) | ||
1269 | rand = arc4random(); | ||
1270 | session_key[i] = rand & 0xff; | ||
1271 | rand >>= 8; | ||
1272 | } | ||
1273 | |||
1274 | /* According to the protocol spec, the first byte of the session key is | ||
1275 | the highest byte of the integer. The session key is xored with the | ||
1276 | first 16 bytes of the session id. */ | ||
1277 | key = BN_new(); | ||
1278 | BN_set_word(key, 0); | ||
1279 | for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) | ||
1280 | { | ||
1281 | BN_lshift(key, key, 8); | ||
1282 | if (i < 16) | ||
1283 | BN_add_word(key, session_key[i] ^ session_id[i]); | ||
1284 | else | ||
1285 | BN_add_word(key, session_key[i]); | ||
1286 | } | ||
1287 | |||
1288 | /* Encrypt the integer using the public key and host key of the server | ||
1289 | (key with smaller modulus first). */ | ||
1290 | if (BN_cmp(public_key->n, host_key->n) < 0) | ||
1291 | { | ||
1292 | /* Public key has smaller modulus. */ | ||
1293 | assert(BN_num_bits(host_key->n) >= | ||
1294 | BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED); | ||
1295 | |||
1296 | rsa_public_encrypt(key, key, public_key); | ||
1297 | rsa_public_encrypt(key, key, host_key); | ||
1298 | } | ||
1299 | else | ||
1300 | { | ||
1301 | /* Host key has smaller modulus (or they are equal). */ | ||
1302 | assert(BN_num_bits(public_key->n) >= | ||
1303 | BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED); | ||
1304 | |||
1305 | rsa_public_encrypt(key, key, host_key); | ||
1306 | rsa_public_encrypt(key, key, public_key); | ||
1307 | } | ||
1308 | |||
1309 | if (options->cipher == SSH_CIPHER_NOT_SET) { | ||
1310 | if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default)) | ||
1311 | options->cipher = ssh_cipher_default; | ||
1312 | else { | ||
1313 | debug("Cipher %d not supported, using %.100s instead.", | ||
1314 | cipher_name(ssh_cipher_default), | ||
1315 | cipher_name(SSH_FALLBACK_CIPHER)); | ||
1316 | options->cipher = SSH_FALLBACK_CIPHER; | ||
1317 | } | ||
1318 | } | ||
1319 | |||
1320 | /* Check that the selected cipher is supported. */ | ||
1321 | if (!(supported_ciphers & (1 << options->cipher))) | ||
1322 | fatal("Selected cipher type %.100s not supported by server.", | ||
1323 | cipher_name(options->cipher)); | ||
1324 | |||
1325 | debug("Encryption type: %.100s", cipher_name(options->cipher)); | ||
1326 | |||
1327 | /* Send the encrypted session key to the server. */ | ||
1328 | packet_start(SSH_CMSG_SESSION_KEY); | ||
1329 | packet_put_char(options->cipher); | ||
1330 | |||
1331 | /* Send the check bytes back to the server. */ | ||
1332 | for (i = 0; i < 8; i++) | ||
1333 | packet_put_char(check_bytes[i]); | ||
1334 | |||
1335 | /* Send the encrypted encryption key. */ | ||
1336 | packet_put_bignum(key); | ||
1337 | |||
1338 | /* Send protocol flags. */ | ||
1339 | packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN); | ||
1340 | |||
1341 | /* Send the packet now. */ | ||
1342 | packet_send(); | ||
1343 | packet_write_wait(); | ||
1344 | |||
1345 | /* Destroy the session key integer and the public keys since we no longer | ||
1346 | need them. */ | ||
1347 | BN_clear_free(key); | ||
1348 | RSA_free(public_key); | ||
1349 | RSA_free(host_key); | ||
1350 | |||
1351 | debug("Sent encrypted session key."); | ||
1352 | |||
1353 | /* Set the encryption key. */ | ||
1354 | packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, | ||
1355 | options->cipher, 1); | ||
1356 | |||
1357 | /* We will no longer need the session key here. Destroy any extra copies. */ | ||
1358 | memset(session_key, 0, sizeof(session_key)); | ||
1359 | |||
1360 | /* Expect a success message from the server. Note that this message will | ||
1361 | be received in encrypted form. */ | ||
1362 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | ||
1363 | |||
1364 | debug("Received encrypted confirmation."); | ||
1365 | |||
1366 | /* Send the name of the user to log in as on the server. */ | ||
1367 | packet_start(SSH_CMSG_USER); | ||
1368 | packet_put_string(server_user, strlen(server_user)); | ||
1369 | packet_send(); | ||
1370 | packet_write_wait(); | ||
1371 | |||
1372 | /* The server should respond with success if no authentication is needed | ||
1373 | (the user has no password). Otherwise the server responds with | ||
1374 | failure. */ | ||
1375 | type = packet_read(&payload_len); | ||
1376 | if (type == SSH_SMSG_SUCCESS) | ||
1377 | return; /* Connection was accepted without authentication. */ | ||
1378 | if (type != SSH_SMSG_FAILURE) | ||
1379 | packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", | ||
1380 | type); | ||
1381 | |||
1382 | #ifdef AFS | ||
1383 | /* Try Kerberos tgt passing if the server supports it. */ | ||
1384 | if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) && | ||
1385 | options->kerberos_tgt_passing) | ||
1386 | { | ||
1387 | if (options->cipher == SSH_CIPHER_NONE) | ||
1388 | log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); | ||
1389 | (void)send_kerberos_tgt(); | ||
1390 | } | ||
1391 | |||
1392 | /* Try AFS token passing if the server supports it. */ | ||
1393 | if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) && | ||
1394 | options->afs_token_passing && k_hasafs()) { | ||
1395 | if (options->cipher == SSH_CIPHER_NONE) | ||
1396 | log("WARNING: Encryption is disabled! Token will be transmitted in the clear!"); | ||
1397 | send_afs_tokens(); | ||
1398 | } | ||
1399 | #endif /* AFS */ | ||
1400 | |||
1401 | #ifdef KRB4 | ||
1402 | if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) && | ||
1403 | options->kerberos_authentication) | ||
1404 | { | ||
1405 | debug("Trying Kerberos authentication."); | ||
1406 | if (try_kerberos_authentication()) { | ||
1407 | /* The server should respond with success or failure. */ | ||
1408 | type = packet_read(&payload_len); | ||
1409 | if (type == SSH_SMSG_SUCCESS) | ||
1410 | return; /* Successful connection. */ | ||
1411 | if (type != SSH_SMSG_FAILURE) | ||
1412 | packet_disconnect("Protocol error: got %d in response to Kerberos auth", type); | ||
1413 | } | ||
1414 | } | ||
1415 | #endif /* KRB4 */ | ||
1416 | |||
1417 | /* Use rhosts authentication if running in privileged socket and we do not | ||
1418 | wish to remain anonymous. */ | ||
1419 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && | ||
1420 | options->rhosts_authentication) | ||
1421 | { | ||
1422 | debug("Trying rhosts authentication."); | ||
1423 | packet_start(SSH_CMSG_AUTH_RHOSTS); | ||
1424 | packet_put_string(local_user, strlen(local_user)); | ||
1425 | packet_send(); | ||
1426 | packet_write_wait(); | ||
1427 | |||
1428 | /* The server should respond with success or failure. */ | ||
1429 | type = packet_read(&payload_len); | ||
1430 | if (type == SSH_SMSG_SUCCESS) | ||
1431 | return; /* Successful connection. */ | ||
1432 | if (type != SSH_SMSG_FAILURE) | ||
1433 | packet_disconnect("Protocol error: got %d in response to rhosts auth", | ||
1434 | type); | ||
1435 | } | ||
1436 | |||
1437 | /* Try .rhosts or /etc/hosts.equiv authentication with RSA host | ||
1438 | authentication. */ | ||
1439 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && | ||
1440 | options->rhosts_rsa_authentication && host_key_valid) | ||
1441 | { | ||
1442 | if (try_rhosts_rsa_authentication(local_user, own_host_key)) | ||
1443 | return; /* Successful authentication. */ | ||
1444 | } | ||
1445 | |||
1446 | /* Try RSA authentication if the server supports it. */ | ||
1447 | if ((supported_authentications & (1 << SSH_AUTH_RSA)) && | ||
1448 | options->rsa_authentication) | ||
1449 | { | ||
1450 | /* Try RSA authentication using the authentication agent. The agent | ||
1451 | is tried first because no passphrase is needed for it, whereas | ||
1452 | identity files may require passphrases. */ | ||
1453 | if (try_agent_authentication()) | ||
1454 | return; /* Successful connection. */ | ||
1455 | |||
1456 | /* Try RSA authentication for each identity. */ | ||
1457 | for (i = 0; i < options->num_identity_files; i++) | ||
1458 | if (try_rsa_authentication(pw, options->identity_files[i], | ||
1459 | !options->batch_mode)) | ||
1460 | return; /* Successful connection. */ | ||
1461 | } | ||
1462 | |||
1463 | /* Try password authentication if the server supports it. */ | ||
1464 | if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) && | ||
1465 | options->password_authentication && !options->batch_mode) | ||
1466 | { | ||
1467 | char prompt[80]; | ||
1468 | snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ", | ||
1469 | server_user, host); | ||
1470 | debug("Doing password authentication."); | ||
1471 | if (options->cipher == SSH_CIPHER_NONE) | ||
1472 | log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); | ||
1473 | for (i = 0; i < options->number_of_password_prompts; i++) { | ||
1474 | if (i != 0) | ||
1475 | error("Permission denied, please try again."); | ||
1476 | password = read_passphrase(prompt, 0); | ||
1477 | packet_start(SSH_CMSG_AUTH_PASSWORD); | ||
1478 | packet_put_string(password, strlen(password)); | ||
1479 | memset(password, 0, strlen(password)); | ||
1480 | xfree(password); | ||
1481 | packet_send(); | ||
1482 | packet_write_wait(); | ||
1483 | |||
1484 | type = packet_read(&payload_len); | ||
1485 | if (type == SSH_SMSG_SUCCESS) | ||
1486 | return; /* Successful connection. */ | ||
1487 | if (type != SSH_SMSG_FAILURE) | ||
1488 | packet_disconnect("Protocol error: got %d in response to passwd auth", type); | ||
1489 | } | ||
1490 | } | ||
1491 | |||
1492 | /* All authentication methods have failed. Exit with an error message. */ | ||
1493 | fatal("Permission denied."); | ||
1494 | /*NOTREACHED*/ | ||
1495 | } | ||
@@ -0,0 +1,781 @@ | |||
1 | .\" -*- nroff -*- | ||
2 | .\" | ||
3 | .\" sshd.8.in | ||
4 | .\" | ||
5 | .\" Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | .\" | ||
7 | .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | .\" All rights reserved | ||
9 | .\" | ||
10 | .\" Created: Sat Apr 22 21:55:14 1995 ylo | ||
11 | .\" | ||
12 | .\" $Id: sshd.8,v 1.1 1999/10/27 03:42:46 damien Exp $ | ||
13 | .\" | ||
14 | .Dd September 25, 1999 | ||
15 | .Dt SSHD 8 | ||
16 | .Os | ||
17 | .Sh NAME | ||
18 | .Nm sshd | ||
19 | .Nd secure shell daemon | ||
20 | .Sh SYNOPSIS | ||
21 | .Nm sshd | ||
22 | .Op Fl diq | ||
23 | .Op Fl b Ar bits | ||
24 | .Op Fl f Ar config_file | ||
25 | .Op Fl g Ar login_grace_time | ||
26 | .Op Fl h Ar host_key_file | ||
27 | .Op Fl k Ar key_gen_time | ||
28 | .Op Fl p Ar port | ||
29 | .Sh DESCRIPTION | ||
30 | .Nm | ||
31 | (Secure Shell Daemon) is the daemon program for | ||
32 | .Xr ssh 1 . | ||
33 | Together these programs replace rlogin and rsh programs, and | ||
34 | provide secure encrypted communications between two untrusted hosts | ||
35 | over an insecure network. The programs are intended to be as easy to | ||
36 | install and use as possible. | ||
37 | .Pp | ||
38 | .Nm | ||
39 | is the daemon that listens for connections from clients. It is | ||
40 | normally started at boot from | ||
41 | .Pa /etc/rc . | ||
42 | It forks a new | ||
43 | daemon for each incoming connection. The forked daemons handle | ||
44 | key exchange, encryption, authentication, command execution, | ||
45 | and data exchange. | ||
46 | .Pp | ||
47 | .Nm | ||
48 | works as follows. Each host has a host-specific RSA key | ||
49 | (normally 1024 bits) used to identify the host. Additionally, when | ||
50 | the daemon starts, it generates a server RSA key (normally 768 bits). | ||
51 | This key is normally regenerated every hour if it has been used, and | ||
52 | is never stored on disk. | ||
53 | .Pp | ||
54 | Whenever a client connects the daemon, the daemon sends its host | ||
55 | and server public keys to the client. The client compares the | ||
56 | host key against its own database to verify that it has not changed. | ||
57 | The client then generates a 256 bit random number. It encrypts this | ||
58 | random number using both the host key and the server key, and sends | ||
59 | the encrypted number to the server. Both sides then start to use this | ||
60 | random number as a session key which is used to encrypt all further | ||
61 | communications in the session. The rest of the session is encrypted | ||
62 | using a conventional cipher, currently Blowfish and 3DES, with 3DES | ||
63 | being is used by default. The client selects the encryption algorithm | ||
64 | to use from those offered by the server. | ||
65 | .Pp | ||
66 | Next, the server and the client enter an authentication dialog. The | ||
67 | client tries to authenticate itself using | ||
68 | .Pa .rhosts | ||
69 | authentication, | ||
70 | .Pa .rhosts | ||
71 | authentication combined with RSA host | ||
72 | authentication, RSA challenge-response authentication, or password | ||
73 | based authentication. | ||
74 | .Pp | ||
75 | Rhosts authentication is normally disabled | ||
76 | because it is fundamentally insecure, but can be enabled in the server | ||
77 | configuration file if desired. System security is not improved unless | ||
78 | .Xr rshd 8 , | ||
79 | .Xr rlogind 8 , | ||
80 | .Xr rexecd 8 , | ||
81 | and | ||
82 | .Xr rexd 8 | ||
83 | are disabled (thus completely disabling | ||
84 | .Xr rlogin 1 | ||
85 | and | ||
86 | .Xr rsh 1 | ||
87 | into that machine). | ||
88 | .Pp | ||
89 | If the client successfully authenticates itself, a dialog for | ||
90 | preparing the session is entered. At this time the client may request | ||
91 | things like allocating a pseudo-tty, forwarding X11 connections, | ||
92 | forwarding TCP/IP connections, or forwarding the authentication agent | ||
93 | connection over the secure channel. | ||
94 | .Pp | ||
95 | Finally, the client either requests a shell or execution of a command. | ||
96 | The sides then enter session mode. In this mode, either side may send | ||
97 | data at any time, and such data is forwarded to/from the shell or | ||
98 | command on the server side, and the user terminal in the client side. | ||
99 | .Pp | ||
100 | When the user program terminates and all forwarded X11 and other | ||
101 | connections have been closed, the server sends command exit status to | ||
102 | the client, and both sides exit. | ||
103 | .Pp | ||
104 | .Nm | ||
105 | can be configured using command-line options or a configuration | ||
106 | file. Command-line options override values specified in the | ||
107 | configuration file. | ||
108 | .Pp | ||
109 | The options are as follows: | ||
110 | .Bl -tag -width Ds | ||
111 | .It Fl b Ar bits | ||
112 | Specifies the number of bits in the server key (default 768). | ||
113 | .Pp | ||
114 | .It Fl d | ||
115 | Debug mode. The server sends verbose debug output to the system | ||
116 | log, and does not put itself in the background. The server also will | ||
117 | not fork and will only process one connection. This option is only | ||
118 | intended for debugging for the server. | ||
119 | .It Fl f Ar configuration_file | ||
120 | Specifies the name of the configuration file. The default is | ||
121 | .Pa /etc/sshd_config . | ||
122 | .Nm | ||
123 | refuses to start if there is no configuration file. | ||
124 | .It Fl g Ar login_grace_time | ||
125 | Gives the grace time for clients to authenticate themselves (default | ||
126 | 300 seconds). If the client fails to authenticate the user within | ||
127 | this many seconds, the server disconnects and exits. A value of zero | ||
128 | indicates no limit. | ||
129 | .It Fl h Ar host_key_file | ||
130 | Specifies the file from which the host key is read (default | ||
131 | .Pa /etc/ssh_host_key ) . | ||
132 | This option must be given if | ||
133 | .Nm | ||
134 | is not run as root (as the normal | ||
135 | host file is normally not readable by anyone but root). | ||
136 | .It Fl i | ||
137 | Specifies that | ||
138 | .Nm | ||
139 | is being run from inetd. | ||
140 | .Nm | ||
141 | is normally not run | ||
142 | from inetd because it needs to generate the server key before it can | ||
143 | respond to the client, and this may take tens of seconds. Clients | ||
144 | would have to wait too long if the key was regenerated every time. | ||
145 | However, with small key sizes (e.g. 512) using | ||
146 | .Nm | ||
147 | from inetd may | ||
148 | be feasible. | ||
149 | .It Fl k Ar key_gen_time | ||
150 | Specifies how often the server key is regenerated (default 3600 | ||
151 | seconds, or one hour). The motivation for regenerating the key fairly | ||
152 | often is that the key is not stored anywhere, and after about an hour, | ||
153 | it becomes impossible to recover the key for decrypting intercepted | ||
154 | communications even if the machine is cracked into or physically | ||
155 | seized. A value of zero indicates that the key will never be regenerated. | ||
156 | .It Fl p Ar port | ||
157 | Specifies the port on which the server listens for connections | ||
158 | (default 22). | ||
159 | .It Fl q | ||
160 | Quiet mode. Nothing is sent to the system log. Normally the beginning, | ||
161 | authentication, and termination of each connection is logged. | ||
162 | .It Fl Q | ||
163 | Do not print an error message if RSA support is missing. | ||
164 | .El | ||
165 | .Sh CONFIGURATION FILE | ||
166 | .Nm | ||
167 | reads configuration data from | ||
168 | .Pa /etc/sshd_config | ||
169 | (or the file specified with | ||
170 | .Fl f | ||
171 | on the command line). The file | ||
172 | contains keyword-value pairs, one per line. Lines starting with | ||
173 | .Ql # | ||
174 | and empty lines are interpreted as comments. | ||
175 | .Pp | ||
176 | The following keywords are possible. | ||
177 | .Bl -tag -width Ds | ||
178 | .It Cm AFSTokenPassing | ||
179 | Specifies whether an AFS token may be forwarded to the server. Default is | ||
180 | .Dq yes . | ||
181 | .It Cm AllowGroups | ||
182 | This keyword can be followed by a number of group names, separated | ||
183 | by spaces. If specified, login is allowed only for users whose primary | ||
184 | group matches one of the patterns. | ||
185 | .Ql \&* | ||
186 | and | ||
187 | .Ql ? | ||
188 | can be used as | ||
189 | wildcards in the patterns. Only group names are valid, a numerical group | ||
190 | id isn't recognized. By default login is allowed regardless of | ||
191 | the primary group. | ||
192 | .Pp | ||
193 | .It Cm AllowUsers | ||
194 | This keyword can be followed by a number of user names, separated | ||
195 | by spaces. If specified, login is allowed only for users names that | ||
196 | match one of the patterns. | ||
197 | .Ql \&* | ||
198 | and | ||
199 | .Ql ? | ||
200 | can be used as | ||
201 | wildcards in the patterns. Only user names are valid, a numerical user | ||
202 | id isn't recognized. By default login is allowed regardless of | ||
203 | the user name. | ||
204 | .Pp | ||
205 | .It Cm CheckMail | ||
206 | Specifies whether | ||
207 | .Nm | ||
208 | should check for new mail for interactive logins. | ||
209 | The default is | ||
210 | .Dq no . | ||
211 | .It Cm DenyGroups | ||
212 | This keyword can be followed by a number of group names, separated | ||
213 | by spaces. Users whose primary group matches one of the patterns | ||
214 | aren't allowed to log in. | ||
215 | .Ql \&* | ||
216 | and | ||
217 | .Ql ? | ||
218 | can be used as | ||
219 | wildcards in the patterns. Only group names are valid, a numerical group | ||
220 | id isn't recognized. By default login is allowed regardless of | ||
221 | the primary group. | ||
222 | .Pp | ||
223 | .It Cm DenyUsers | ||
224 | This keyword can be followed by a number of user names, separated | ||
225 | by spaces. Login is allowed disallowed for user names that match | ||
226 | one of the patterns. | ||
227 | .Ql \&* | ||
228 | and | ||
229 | .Ql ? | ||
230 | can be used as | ||
231 | wildcards in the patterns. Only user names are valid, a numerical user | ||
232 | id isn't recognized. By default login is allowed regardless of | ||
233 | the user name. | ||
234 | .Pp | ||
235 | .It Cm FascistLogging | ||
236 | Specifies whether to use verbose logging. Verbose logging violates | ||
237 | the privacy of users and is not recommended. The argument must be | ||
238 | .Dq yes | ||
239 | or | ||
240 | .Dq no . | ||
241 | The default is | ||
242 | .Dq no . | ||
243 | .It Cm HostKey | ||
244 | Specifies the file containing the private host key (default | ||
245 | .Pa /etc/ssh_host_key ) . | ||
246 | Note that | ||
247 | .Nm | ||
248 | does not start if this file is group/world-accessible. | ||
249 | .It Cm IgnoreRhosts | ||
250 | Specifies that rhosts and shosts files will not be used in | ||
251 | authentication. | ||
252 | .Pa /etc/hosts.equiv | ||
253 | and | ||
254 | .Pa /etc/shosts.equiv | ||
255 | are still used. The default is | ||
256 | .Dq no . | ||
257 | .It Cm KeepAlive | ||
258 | Specifies whether the system should send keepalive messages to the | ||
259 | other side. If they are sent, death of the connection or crash of one | ||
260 | of the machines will be properly noticed. However, this means that | ||
261 | connections will die if the route is down temporarily, and some people | ||
262 | find it annoying. On the other hand, if keepalives are not send, | ||
263 | sessions may hang indefinitely on the server, leaving | ||
264 | .Dq ghost | ||
265 | users and consuming server resources. | ||
266 | .Pp | ||
267 | The default is | ||
268 | .Dq yes | ||
269 | (to send keepalives), and the server will notice | ||
270 | if the network goes down or the client host reboots. This avoids | ||
271 | infinitely hanging sessions. | ||
272 | .Pp | ||
273 | To disable keepalives, the value should be set to | ||
274 | .Dq no | ||
275 | in both the server and the client configuration files. | ||
276 | .It Cm KerberosAuthentication | ||
277 | Specifies whether Kerberos authentication is allowed. This can | ||
278 | be in the form of a Kerberos ticket, or if | ||
279 | .Cm PasswordAuthentication | ||
280 | is yes, the password provided by the user will be validated through | ||
281 | the Kerberos KDC. Default is | ||
282 | .Dq yes . | ||
283 | .It Cm KerberosOrLocalPasswd | ||
284 | If set then if password authentication through Kerberos fails then | ||
285 | the password will be validated via any additional local mechanism | ||
286 | such as | ||
287 | .Pa /etc/passwd | ||
288 | or SecurID. Default is | ||
289 | .Dq yes . | ||
290 | .It Cm KerberosTgtPassing | ||
291 | Specifies whether a Kerberos TGT may be forwarded to the server. | ||
292 | Default is | ||
293 | .Dq no , | ||
294 | as this only works when the Kerberos KDC is actually an AFS kaserver. | ||
295 | .It Cm KerberosTicketCleanup | ||
296 | Specifies whether to automatically destroy the user's ticket cache | ||
297 | file on logout. Default is | ||
298 | .Dq yes . | ||
299 | .It Cm KeyRegenerationInterval | ||
300 | The server key is automatically regenerated after this many seconds | ||
301 | (if it has been used). The purpose of regeneration is to prevent | ||
302 | decrypting captured sessions by later breaking into the machine and | ||
303 | stealing the keys. The key is never stored anywhere. If the value is | ||
304 | 0, the key is never regenerated. The default is 3600 | ||
305 | (seconds). | ||
306 | .It Cm ListenAddress | ||
307 | Specifies what local address | ||
308 | .Nm | ||
309 | should listen on. | ||
310 | The default is to listen to all local addresses. | ||
311 | .It Cm LoginGraceTime | ||
312 | The server disconnects after this time if the user has not | ||
313 | successfully logged in. If the value is 0, there is no time limit. | ||
314 | The default is 600 (seconds). | ||
315 | .It Cm PasswordAuthentication | ||
316 | Specifies whether password authentication is allowed. | ||
317 | The default is | ||
318 | .Dq yes . | ||
319 | .It Cm PermitEmptyPasswords | ||
320 | When password authentication is allowed, it specifies whether the | ||
321 | server allows login to accounts with empty password strings. The default | ||
322 | is | ||
323 | .Dq yes . | ||
324 | .It Cm PermitRootLogin | ||
325 | Specifies whether the root can log in using | ||
326 | .Xr ssh 1 . | ||
327 | The argument must be | ||
328 | .Dq yes , | ||
329 | .Dq without-password | ||
330 | or | ||
331 | .Dq no . | ||
332 | The default is | ||
333 | .Dq yes . | ||
334 | If this options is set to | ||
335 | .Dq without-password | ||
336 | only password authentication is disabled for root. | ||
337 | .Pp | ||
338 | Root login with RSA authentication when the | ||
339 | .Ar command | ||
340 | option has been | ||
341 | specified will be allowed regardless of the value of this setting | ||
342 | (which may be useful for taking remote backups even if root login is | ||
343 | normally not allowed). | ||
344 | .It Cm Port | ||
345 | Specifies the port number that | ||
346 | .Nm | ||
347 | listens on. The default is 22. | ||
348 | .It Cm PrintMotd | ||
349 | Specifies whether | ||
350 | .Nm | ||
351 | should print | ||
352 | .Pa /etc/motd | ||
353 | when a user logs in interactively. (On some systems it is also | ||
354 | printed by the shell, | ||
355 | .Pa /etc/profile , | ||
356 | or equivalent.) The default is | ||
357 | .Dq yes . | ||
358 | .It Cm QuietMode | ||
359 | Specifies whether the system runs in quiet mode. In quiet mode, | ||
360 | nothing is logged in the system log, except fatal errors. The default | ||
361 | is | ||
362 | .Dq no . | ||
363 | .It Cm RandomSeed | ||
364 | Obsolete. Random number generation uses other techniques. | ||
365 | .It Cm RhostsAuthentication | ||
366 | Specifies whether authentication using rhosts or /etc/hosts.equiv | ||
367 | files is sufficient. Normally, this method should not be permitted | ||
368 | because it is insecure. | ||
369 | .Cm RhostsRSAAuthentication | ||
370 | should be used | ||
371 | instead, because it performs RSA-based host authentication in addition | ||
372 | to normal rhosts or /etc/hosts.equiv authentication. | ||
373 | The default is | ||
374 | .Dq no . | ||
375 | .It Cm RhostsRSAAuthentication | ||
376 | Specifies whether rhosts or /etc/hosts.equiv authentication together | ||
377 | with successful RSA host authentication is allowed. The default is | ||
378 | .Dq yes . | ||
379 | .It Cm RSAAuthentication | ||
380 | Specifies whether pure RSA authentication is allowed. The default is | ||
381 | .Dq yes . | ||
382 | .It Cm ServerKeyBits | ||
383 | Defines the number of bits in the server key. The minimum value is | ||
384 | 512, and the default is 768. | ||
385 | .It Cm SkeyAuthentication | ||
386 | Specifies whether | ||
387 | .Xr skey 1 | ||
388 | authentication is allowed. The default is | ||
389 | .Dq yes . | ||
390 | Note that s/key authentication is enabled only if | ||
391 | .Cm PasswordAuthentication | ||
392 | is allowed, too. | ||
393 | .It Cm StrictModes | ||
394 | Specifies whether | ||
395 | .Nm | ||
396 | should check file modes and ownership of the | ||
397 | user's files and home directory before accepting login. This | ||
398 | is normally desirable because novices sometimes accidentally leave their | ||
399 | directory or files world-writable. The default is | ||
400 | .Dq yes . | ||
401 | .It Cm SyslogFacility | ||
402 | Gives the facility code that is used when logging messages from | ||
403 | .Nm sshd . | ||
404 | The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, | ||
405 | LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The default is AUTH. | ||
406 | .It Cm UseLogin | ||
407 | Specifies whether | ||
408 | .Xr login 1 | ||
409 | is used. The default is | ||
410 | .Dq no . | ||
411 | .It Cm X11Forwarding | ||
412 | Specifies whether X11 forwarding is permitted. The default is | ||
413 | .Dq yes . | ||
414 | Note that disabling X11 forwarding does not improve security in any | ||
415 | way, as users can always install their own forwarders. | ||
416 | .It Cm X11DisplayOffset | ||
417 | Specifies the first display number available for | ||
418 | .Nm sshd Ns 's | ||
419 | X11 forwarding. This prevents | ||
420 | .Nm | ||
421 | from interfering with real X11 servers. | ||
422 | .El | ||
423 | .Sh LOGIN PROCESS | ||
424 | When a user successfully logs in, | ||
425 | .Nm | ||
426 | does the following: | ||
427 | .Bl -enum -offset indent | ||
428 | .It | ||
429 | If the login is on a tty, and no command has been specified, | ||
430 | prints last login time and | ||
431 | .Pa /etc/motd | ||
432 | (unless prevented in the configuration file or by | ||
433 | .Pa $HOME/.hushlogin ; | ||
434 | see the | ||
435 | .Sx FILES | ||
436 | section). | ||
437 | .It | ||
438 | If the login is on a tty, records login time. | ||
439 | .It | ||
440 | Checks | ||
441 | .Pa /etc/nologin ; | ||
442 | if it exists, prints contents and quits | ||
443 | (unless root). | ||
444 | .It | ||
445 | Changes to run with normal user privileges. | ||
446 | .It | ||
447 | Sets up basic environment. | ||
448 | .It | ||
449 | Reads | ||
450 | .Pa $HOME/.ssh/environment | ||
451 | if it exists. | ||
452 | .It | ||
453 | Changes to user's home directory. | ||
454 | .It | ||
455 | If | ||
456 | .Pa $HOME/.ssh/rc | ||
457 | exists, runs it; else if | ||
458 | .Pa /etc/sshrc | ||
459 | exists, runs | ||
460 | it; otherwise runs xauth. The | ||
461 | .Dq rc | ||
462 | files are given the X11 | ||
463 | authentication protocol and cookie in standard input. | ||
464 | .It | ||
465 | Runs user's shell or command. | ||
466 | .El | ||
467 | .Sh AUTHORIZED_KEYS FILE FORMAT | ||
468 | The | ||
469 | .Pa $HOME/.ssh/authorized_keys | ||
470 | file lists the RSA keys that are | ||
471 | permitted for RSA authentication. Each line of the file contains one | ||
472 | key (empty lines and lines starting with a | ||
473 | .Ql # | ||
474 | are ignored as | ||
475 | comments). Each line consists of the following fields, separated by | ||
476 | spaces: options, bits, exponent, modulus, comment. The options field | ||
477 | is optional; its presence is determined by whether the line starts | ||
478 | with a number or not (the option field never starts with a number). | ||
479 | The bits, exponent, modulus and comment fields give the RSA key; the | ||
480 | comment field is not used for anything (but may be convenient for the | ||
481 | user to identify the key). | ||
482 | .Pp | ||
483 | Note that lines in this file are usually several hundred bytes long | ||
484 | (because of the size of the RSA key modulus). You don't want to type | ||
485 | them in; instead, copy the | ||
486 | .Pa identity.pub | ||
487 | file and edit it. | ||
488 | .Pp | ||
489 | The options (if present) consists of comma-separated option | ||
490 | specifications. No spaces are permitted, except within double quotes. | ||
491 | The following option specifications are supported: | ||
492 | .Bl -tag -width Ds | ||
493 | .It Cm from="pattern-list" | ||
494 | Specifies that in addition to RSA authentication, the canonical name | ||
495 | of the remote host must be present in the comma-separated list of | ||
496 | patterns ('*' and '?' serve as wildcards). The list may also contain | ||
497 | patterns negated by prefixing them with '!'; if the canonical host | ||
498 | name matches a negated pattern, the key is not accepted. The purpose | ||
499 | of this option is to optionally increase security: RSA authentication | ||
500 | by itself does not trust the network or name servers or anything (but | ||
501 | the key); however, if somebody somehow steals the key, the key | ||
502 | permits an intruder to log in from anywhere in the world. This | ||
503 | additional option makes using a stolen key more difficult (name | ||
504 | servers and/or routers would have to be compromised in addition to | ||
505 | just the key). | ||
506 | .It Cm command="command" | ||
507 | Specifies that the command is executed whenever this key is used for | ||
508 | authentication. The command supplied by the user (if any) is ignored. | ||
509 | The command is run on a pty if the connection requests a pty; | ||
510 | otherwise it is run without a tty. A quote may be included in the | ||
511 | command by quoting it with a backslash. This option might be useful | ||
512 | to restrict certain RSA keys to perform just a specific operation. An | ||
513 | example might be a key that permits remote backups but nothing | ||
514 | else. Notice that the client may specify TCP/IP and/or X11 | ||
515 | forwardings unless they are explicitly prohibited. | ||
516 | .It Cm environment="NAME=value" | ||
517 | Specifies that the string is to be added to the environment when | ||
518 | logging in using this key. Environment variables set this way | ||
519 | override other default environment values. Multiple options of this | ||
520 | type are permitted. | ||
521 | .It Cm no-port-forwarding | ||
522 | Forbids TCP/IP forwarding when this key is used for authentication. | ||
523 | Any port forward requests by the client will return an error. This | ||
524 | might be used, e.g., in connection with the | ||
525 | .Cm command | ||
526 | option. | ||
527 | .It Cm no-X11-forwarding | ||
528 | Forbids X11 forwarding when this key is used for authentication. | ||
529 | Any X11 forward requests by the client will return an error. | ||
530 | .It Cm no-agent-forwarding | ||
531 | Forbids authentication agent forwarding when this key is used for | ||
532 | authentication. | ||
533 | .It Cm no-pty | ||
534 | Prevents tty allocation (a request to allocate a pty will fail). | ||
535 | .El | ||
536 | .Ss Examples | ||
537 | 1024 33 12121.\|.\|.\|312314325 ylo@foo.bar | ||
538 | .Pp | ||
539 | from="*.niksula.hut.fi,!pc.niksula.hut.fi" 1024 35 23.\|.\|.\|2334 ylo@niksula | ||
540 | .Pp | ||
541 | command="dump /home",no-pty,no-port-forwarding 1024 33 23.\|.\|.\|2323 backup.hut.fi | ||
542 | .Sh SSH_KNOWN_HOSTS FILE FORMAT | ||
543 | The | ||
544 | .Pa /etc/ssh_known_hosts | ||
545 | and | ||
546 | .Pa $HOME/.ssh/known_hosts | ||
547 | files contain host public keys for all known hosts. The global file should | ||
548 | be prepared by the admistrator (optional), and the per-user file is | ||
549 | maintained automatically: whenever the user connects an unknown host | ||
550 | its key is added to the per-user file. | ||
551 | .Pp | ||
552 | Each line in these files contains the following fields: hostnames, | ||
553 | bits, exponent, modulus, comment. The fields are separated by spaces. | ||
554 | .Pp | ||
555 | Hostnames is a comma-separated list of patterns ('*' and '?' act as | ||
556 | wildcards); each pattern in turn is matched against the canonical host | ||
557 | name (when authenticating a client) or against the user-supplied | ||
558 | name (when authenticating a server). A pattern may also be preceded | ||
559 | by | ||
560 | .Ql ! | ||
561 | to indicate negation: if the host name matches a negated | ||
562 | pattern, it is not accepted (by that line) even if it matched another | ||
563 | pattern on the line. | ||
564 | .Pp | ||
565 | Bits, exponent, and modulus are taken directly from the host key; they | ||
566 | can be obtained, e.g., from | ||
567 | .Pa /etc/ssh_host_key.pub . | ||
568 | The optional comment field continues to the end of the line, and is not used. | ||
569 | .Pp | ||
570 | Lines starting with | ||
571 | .Ql # | ||
572 | and empty lines are ignored as comments. | ||
573 | .Pp | ||
574 | When performing host authentication, authentication is accepted if any | ||
575 | matching line has the proper key. It is thus permissible (but not | ||
576 | recommended) to have several lines or different host keys for the same | ||
577 | names. This will inevitably happen when short forms of host names | ||
578 | from different domains are put in the file. It is possible | ||
579 | that the files contain conflicting information; authentication is | ||
580 | accepted if valid information can be found from either file. | ||
581 | .Pp | ||
582 | Note that the lines in these files are typically hundreds of characters | ||
583 | long, and you definitely don't want to type in the host keys by hand. | ||
584 | Rather, generate them by a script | ||
585 | or by taking | ||
586 | .Pa /etc/ssh_host_key.pub | ||
587 | and adding the host names at the front. | ||
588 | .Ss Examples | ||
589 | closenet,closenet.hut.fi,.\|.\|.\|,130.233.208.41 1024 37 159.\|.\|.93 closenet.hut.fi | ||
590 | .Sh FILES | ||
591 | .Bl -tag -width Ds | ||
592 | .It Pa /etc/sshd_config | ||
593 | Contains configuration data for | ||
594 | .Nm sshd . | ||
595 | This file should be writable by root only, but it is recommended | ||
596 | (though not necessary) that it be world-readable. | ||
597 | .It Pa /etc/ssh_host_key | ||
598 | Contains the private part of the host key. | ||
599 | This file should only be owned by root, readable only by root, and not | ||
600 | accessible to others. | ||
601 | Note that | ||
602 | .Nm | ||
603 | does not start if this file is group/world-accessible. | ||
604 | .It Pa /etc/ssh_host_key.pub | ||
605 | Contains the public part of the host key. | ||
606 | This file should be world-readable but writable only by | ||
607 | root. Its contents should match the private part. This file is not | ||
608 | really used for anything; it is only provided for the convenience of | ||
609 | the user so its contents can be copied to known hosts files. | ||
610 | These two files are created using | ||
611 | .Xr ssh-keygen 1 . | ||
612 | .It Pa /var/run/sshd.pid | ||
613 | Contains the process ID of the | ||
614 | .Nm | ||
615 | listening for connections (if there are several daemons running | ||
616 | concurrently for different ports, this contains the pid of the one | ||
617 | started last). The contents of this file are not sensitive; it can be | ||
618 | world-readable. | ||
619 | .It Pa $HOME/.ssh/authorized_keys | ||
620 | Lists the RSA keys that can be used to log into the user's account. | ||
621 | This file must be readable by root (which may on some machines imply | ||
622 | it being world-readable if the user's home directory resides on an NFS | ||
623 | volume). It is recommended that it not be accessible by others. The | ||
624 | format of this file is described above. | ||
625 | .It Pa /etc/ssh_known_hosts | ||
626 | This file is consulted when using rhosts with RSA host | ||
627 | authentication to check the public key of the host. The key must be | ||
628 | listed in this file to be accepted. | ||
629 | .It Pa $HOME/.ssh/known_hosts | ||
630 | The client uses this file | ||
631 | and | ||
632 | .Pa /etc/ssh_known_hosts | ||
633 | to verify that the remote host is the one we intended to | ||
634 | connect. These files should be writable only by root/the owner. | ||
635 | .Pa /etc/ssh_known_hosts | ||
636 | should be world-readable, and | ||
637 | .Pa $HOME/.ssh/known_hosts | ||
638 | can but need not be world-readable. | ||
639 | .It Pa /etc/nologin | ||
640 | If this file exists, | ||
641 | .Nm | ||
642 | refuses to let anyone except root log in. The contents of the file | ||
643 | are displayed to anyone trying to log in, and non-root connections are | ||
644 | refused. The file should be world-readable. | ||
645 | .It Pa /etc/hosts.allow, /etc/hosts.deny | ||
646 | If compiled with | ||
647 | .Sy LIBWRAP | ||
648 | support, tcp-wrappers access controls may be defined here as described in | ||
649 | .Xr hosts_access 5 . | ||
650 | .It Pa $HOME/.rhosts | ||
651 | This file contains host-username pairs, separated by a space, one per | ||
652 | line. The given user on the corresponding host is permitted to log in | ||
653 | without password. The same file is used by rlogind and rshd. | ||
654 | The file must | ||
655 | be writable only by the user; it is recommended that it not be | ||
656 | accessible by others. | ||
657 | .Pp | ||
658 | If is also possible to use netgroups in the file. Either host or user | ||
659 | name may be of the form +@groupname to specify all hosts or all users | ||
660 | in the group. | ||
661 | .It Pa $HOME/.shosts | ||
662 | For ssh, | ||
663 | this file is exactly the same as for | ||
664 | .Pa .rhosts . | ||
665 | However, this file is | ||
666 | not used by rlogin and rshd, so using this permits access using SSH only. | ||
667 | .Pa /etc/hosts.equiv | ||
668 | This file is used during | ||
669 | .Pa .rhosts | ||
670 | authentication. In the | ||
671 | simplest form, this file contains host names, one per line. Users on | ||
672 | those hosts are permitted to log in without a password, provided they | ||
673 | have the same user name on both machines. The host name may also be | ||
674 | followed by a user name; such users are permitted to log in as | ||
675 | .Em any | ||
676 | user on this machine (except root). Additionally, the syntax | ||
677 | .Dq +@group | ||
678 | can be used to specify netgroups. Negated entries start with | ||
679 | .Ql \&- . | ||
680 | .Pp | ||
681 | If the client host/user is successfully matched in this file, login is | ||
682 | automatically permitted provided the client and server user names are the | ||
683 | same. Additionally, successful RSA host authentication is normally | ||
684 | required. This file must be writable only by root; it is recommended | ||
685 | that it be world-readable. | ||
686 | .Pp | ||
687 | .Sy "Warning: It is almost never a good idea to use user names in" | ||
688 | .Pa hosts.equiv . | ||
689 | Beware that it really means that the named user(s) can log in as | ||
690 | .Em anybody , | ||
691 | which includes bin, daemon, adm, and other accounts that own critical | ||
692 | binaries and directories. Using a user name practically grants the | ||
693 | user root access. The only valid use for user names that I can think | ||
694 | of is in negative entries. | ||
695 | .Pp | ||
696 | Note that this warning also applies to rsh/rlogin. | ||
697 | .It Pa /etc/shosts.equiv | ||
698 | This is processed exactly as | ||
699 | .Pa /etc/hosts.equiv . | ||
700 | However, this file may be useful in environments that want to run both | ||
701 | rsh/rlogin and ssh. | ||
702 | .It Pa $HOME/.ssh/environment | ||
703 | This file is read into the environment at login (if it exists). It | ||
704 | can only contain empty lines, comment lines (that start with | ||
705 | .Ql # ) , | ||
706 | and assignment lines of the form name=value. The file should be writable | ||
707 | only by the user; it need not be readable by anyone else. | ||
708 | .It Pa $HOME/.ssh/rc | ||
709 | If this file exists, it is run with /bin/sh after reading the | ||
710 | environment files but before starting the user's shell or command. If | ||
711 | X11 spoofing is in use, this will receive the "proto cookie" pair in | ||
712 | standard input (and | ||
713 | .Ev DISPLAY | ||
714 | in environment). This must call | ||
715 | .Xr xauth 1 | ||
716 | in that case. | ||
717 | .Pp | ||
718 | The primary purpose of this file is to run any initialization routines | ||
719 | which may be needed before the user's home directory becomes | ||
720 | accessible; AFS is a particular example of such an environment. | ||
721 | .Pp | ||
722 | This file will probably contain some initialization code followed by | ||
723 | something similar to: "if read proto cookie; then echo add $DISPLAY | ||
724 | $proto $cookie | xauth -q -; fi". | ||
725 | .Pp | ||
726 | If this file does not exist, | ||
727 | .Pa /etc/sshrc | ||
728 | is run, and if that | ||
729 | does not exist either, xauth is used to store the cookie. | ||
730 | .Pp | ||
731 | This file should be writable only by the user, and need not be | ||
732 | readable by anyone else. | ||
733 | .It Pa /etc/sshrc | ||
734 | Like | ||
735 | .Pa $HOME/.ssh/rc . | ||
736 | This can be used to specify | ||
737 | machine-specific login-time initializations globally. This file | ||
738 | should be writable only by root, and should be world-readable. | ||
739 | .Sh AUTHOR | ||
740 | Tatu Ylonen <ylo@cs.hut.fi> | ||
741 | .Pp | ||
742 | Information about new releases, mailing lists, and other related | ||
743 | issues can be found from the SSH WWW home page: | ||
744 | .Pp | ||
745 | .Dl http://www.cs.hut.fi/ssh. | ||
746 | .Pp | ||
747 | OpenSSH | ||
748 | is a derivative of the original (free) ssh 1.2.12 release, but with bugs | ||
749 | removed and newer features re-added. Rapidly after the 1.2.12 release, | ||
750 | newer versions bore successively more restrictive licenses. This version | ||
751 | of OpenSSH | ||
752 | .Bl -bullet | ||
753 | .It | ||
754 | has all components of a restrictive nature (ie. patents, see | ||
755 | .Xr ssl 8 ) | ||
756 | directly removed from the source code; any licensed or patented components | ||
757 | are chosen from | ||
758 | external libraries. | ||
759 | .It | ||
760 | has been updated to support ssh protocol 1.5. | ||
761 | .It | ||
762 | contains added support for | ||
763 | .Xr kerberos 8 | ||
764 | authentication and ticket passing. | ||
765 | .It | ||
766 | supports one-time password authentication with | ||
767 | .Xr skey 1 . | ||
768 | .El | ||
769 | .Pp | ||
770 | The libraries described in | ||
771 | .Xr ssl 8 | ||
772 | are required for proper operation. | ||
773 | .Sh SEE ALSO | ||
774 | .Xr rlogin 1 , | ||
775 | .Xr rsh 1 , | ||
776 | .Xr scp 1 , | ||
777 | .Xr ssh 1 , | ||
778 | .Xr ssh-add 1 , | ||
779 | .Xr ssh-agent 1 , | ||
780 | .Xr ssh-keygen 1 , | ||
781 | .Xr ssl 8 | ||
@@ -0,0 +1,2445 @@ | |||
1 | /* | ||
2 | |||
3 | sshd.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Fri Mar 17 17:09:28 1995 ylo | ||
11 | |||
12 | This program is the ssh daemon. It listens for connections from clients, and | ||
13 | performs authentication, executes use commands or shell, and forwards | ||
14 | information to/from the application to the user client over an encrypted | ||
15 | connection. This can also handle forwarding of X11, TCP/IP, and authentication | ||
16 | agent connections. | ||
17 | |||
18 | */ | ||
19 | |||
20 | #include "includes.h" | ||
21 | RCSID("$Id: sshd.c,v 1.1 1999/10/27 03:42:46 damien Exp $"); | ||
22 | |||
23 | #include "xmalloc.h" | ||
24 | #include "rsa.h" | ||
25 | #include "ssh.h" | ||
26 | #include "pty.h" | ||
27 | #include "packet.h" | ||
28 | #include "buffer.h" | ||
29 | #include "cipher.h" | ||
30 | #include "mpaux.h" | ||
31 | #include "servconf.h" | ||
32 | #include "uidswap.h" | ||
33 | #include "compat.h" | ||
34 | |||
35 | #ifdef LIBWRAP | ||
36 | #include <tcpd.h> | ||
37 | #include <syslog.h> | ||
38 | int allow_severity = LOG_INFO; | ||
39 | int deny_severity = LOG_WARNING; | ||
40 | #endif /* LIBWRAP */ | ||
41 | |||
42 | #ifndef O_NOCTTY | ||
43 | #define O_NOCTTY 0 | ||
44 | #endif | ||
45 | |||
46 | #ifdef KRB4 | ||
47 | char *ticket = NULL; | ||
48 | #endif /* KRB4 */ | ||
49 | |||
50 | #ifdef HAVE_PAM | ||
51 | #include <security/pam_appl.h> | ||
52 | struct pam_handle_t *pamh=NULL; | ||
53 | char *pampasswd=NULL; | ||
54 | int retval; | ||
55 | int origretval; | ||
56 | #endif /* HAVE_PAM */ | ||
57 | |||
58 | /* Local Xauthority file. */ | ||
59 | char *xauthfile = NULL; | ||
60 | |||
61 | /* Server configuration options. */ | ||
62 | ServerOptions options; | ||
63 | |||
64 | /* Name of the server configuration file. */ | ||
65 | char *config_file_name = SERVER_CONFIG_FILE; | ||
66 | |||
67 | /* Debug mode flag. This can be set on the command line. If debug | ||
68 | mode is enabled, extra debugging output will be sent to the system | ||
69 | log, the daemon will not go to background, and will exit after processing | ||
70 | the first connection. */ | ||
71 | int debug_flag = 0; | ||
72 | |||
73 | /* Flag indicating that the daemon is being started from inetd. */ | ||
74 | int inetd_flag = 0; | ||
75 | |||
76 | /* argv[0] without path. */ | ||
77 | char *av0; | ||
78 | |||
79 | /* Saved arguments to main(). */ | ||
80 | char **saved_argv; | ||
81 | |||
82 | /* This is set to the socket that the server is listening; this is used in | ||
83 | the SIGHUP signal handler. */ | ||
84 | int listen_sock; | ||
85 | |||
86 | /* Flags set in auth-rsa from authorized_keys flags. These are set in | ||
87 | auth-rsa.c. */ | ||
88 | int no_port_forwarding_flag = 0; | ||
89 | int no_agent_forwarding_flag = 0; | ||
90 | int no_x11_forwarding_flag = 0; | ||
91 | int no_pty_flag = 0; | ||
92 | char *forced_command = NULL; /* RSA authentication "command=" option. */ | ||
93 | struct envstring *custom_environment = NULL; | ||
94 | /* RSA authentication "environment=" options. */ | ||
95 | |||
96 | /* Session id for the current session. */ | ||
97 | unsigned char session_id[16]; | ||
98 | |||
99 | /* Any really sensitive data in the application is contained in this structure. | ||
100 | The idea is that this structure could be locked into memory so that the | ||
101 | pages do not get written into swap. However, there are some problems. | ||
102 | The private key contains BIGNUMs, and we do not (in principle) have | ||
103 | access to the internals of them, and locking just the structure is not | ||
104 | very useful. Currently, memory locking is not implemented. */ | ||
105 | struct | ||
106 | { | ||
107 | /* Private part of server key. */ | ||
108 | RSA *private_key; | ||
109 | |||
110 | /* Private part of host key. */ | ||
111 | RSA *host_key; | ||
112 | } sensitive_data; | ||
113 | |||
114 | /* Flag indicating whether the current session key has been used. This flag | ||
115 | is set whenever the key is used, and cleared when the key is regenerated. */ | ||
116 | int key_used = 0; | ||
117 | |||
118 | /* This is set to true when SIGHUP is received. */ | ||
119 | int received_sighup = 0; | ||
120 | |||
121 | /* Public side of the server key. This value is regenerated regularly with | ||
122 | the private key. */ | ||
123 | RSA *public_key; | ||
124 | |||
125 | /* Prototypes for various functions defined later in this file. */ | ||
126 | void do_connection(int privileged_port); | ||
127 | void do_authentication(char *user, int privileged_port); | ||
128 | void do_authenticated(struct passwd *pw); | ||
129 | void do_exec_pty(const char *command, int ptyfd, int ttyfd, | ||
130 | const char *ttyname, struct passwd *pw, const char *term, | ||
131 | const char *display, const char *auth_proto, | ||
132 | const char *auth_data); | ||
133 | void do_exec_no_pty(const char *command, struct passwd *pw, | ||
134 | const char *display, const char *auth_proto, | ||
135 | const char *auth_data); | ||
136 | void do_child(const char *command, struct passwd *pw, const char *term, | ||
137 | const char *display, const char *auth_proto, | ||
138 | const char *auth_data, const char *ttyname); | ||
139 | #ifdef HAVE_PAM | ||
140 | static int pamconv(int num_msg, const struct pam_message **msg, | ||
141 | struct pam_response **resp, void *appdata_ptr); | ||
142 | |||
143 | static struct pam_conv conv = { | ||
144 | pamconv, | ||
145 | NULL | ||
146 | }; | ||
147 | |||
148 | static int pamconv(int num_msg, const struct pam_message **msg, | ||
149 | struct pam_response **resp, void *appdata_ptr) | ||
150 | { | ||
151 | int count = 0; | ||
152 | int replies = 0; | ||
153 | struct pam_response *reply = NULL; | ||
154 | int size = sizeof(struct pam_response); | ||
155 | |||
156 | for(count = 0; count < num_msg; count++) | ||
157 | { | ||
158 | switch (msg[count]->msg_style) | ||
159 | { | ||
160 | case PAM_PROMPT_ECHO_ON: | ||
161 | case PAM_PROMPT_ECHO_OFF: | ||
162 | if (reply == NULL) | ||
163 | reply = xmalloc(size); | ||
164 | else | ||
165 | reply = realloc(reply, size); | ||
166 | |||
167 | if (reply == NULL) | ||
168 | return PAM_CONV_ERR; | ||
169 | |||
170 | size += sizeof(struct pam_response); | ||
171 | |||
172 | reply[replies].resp_retcode = PAM_SUCCESS; | ||
173 | |||
174 | reply[replies++].resp = xstrdup(pampasswd); | ||
175 | /* PAM frees resp */ | ||
176 | break; | ||
177 | |||
178 | case PAM_TEXT_INFO: | ||
179 | /* ignore it... */ | ||
180 | break; | ||
181 | |||
182 | case PAM_ERROR_MSG: | ||
183 | default: | ||
184 | /* Must be an error of some sort... */ | ||
185 | if (reply != NULL) | ||
186 | free(reply); | ||
187 | |||
188 | return PAM_CONV_ERR; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | if (reply != NULL) | ||
193 | *resp = reply; | ||
194 | |||
195 | return PAM_SUCCESS; | ||
196 | } | ||
197 | |||
198 | void pam_cleanup_proc(void *context) | ||
199 | { | ||
200 | if (retval == PAM_SUCCESS) | ||
201 | retval = pam_close_session((pam_handle_t *)pamh, 0); | ||
202 | |||
203 | if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS) | ||
204 | log("Cannot release PAM authentication."); | ||
205 | } | ||
206 | #endif /* HAVE_PAM */ | ||
207 | |||
208 | /* Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; | ||
209 | the effect is to reread the configuration file (and to regenerate | ||
210 | the server key). */ | ||
211 | |||
212 | void sighup_handler(int sig) | ||
213 | { | ||
214 | received_sighup = 1; | ||
215 | signal(SIGHUP, sighup_handler); | ||
216 | } | ||
217 | |||
218 | /* Called from the main program after receiving SIGHUP. Restarts the | ||
219 | server. */ | ||
220 | |||
221 | void sighup_restart() | ||
222 | { | ||
223 | log("Received SIGHUP; restarting."); | ||
224 | close(listen_sock); | ||
225 | execv(saved_argv[0], saved_argv); | ||
226 | log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); | ||
227 | exit(1); | ||
228 | } | ||
229 | |||
230 | /* Generic signal handler for terminating signals in the master daemon. | ||
231 | These close the listen socket; not closing it seems to cause "Address | ||
232 | already in use" problems on some machines, which is inconvenient. */ | ||
233 | |||
234 | void sigterm_handler(int sig) | ||
235 | { | ||
236 | log("Received signal %d; terminating.", sig); | ||
237 | close(listen_sock); | ||
238 | exit(255); | ||
239 | } | ||
240 | |||
241 | /* SIGCHLD handler. This is called whenever a child dies. This will then | ||
242 | reap any zombies left by exited c. */ | ||
243 | |||
244 | void main_sigchld_handler(int sig) | ||
245 | { | ||
246 | int save_errno = errno; | ||
247 | int status; | ||
248 | wait(&status); | ||
249 | signal(SIGCHLD, main_sigchld_handler); | ||
250 | errno = save_errno; | ||
251 | } | ||
252 | |||
253 | /* Signal handler for the alarm after the login grace period has expired. */ | ||
254 | |||
255 | void grace_alarm_handler(int sig) | ||
256 | { | ||
257 | /* Close the connection. */ | ||
258 | packet_close(); | ||
259 | |||
260 | /* Log error and exit. */ | ||
261 | fatal("Timeout before authentication."); | ||
262 | } | ||
263 | |||
264 | /* Signal handler for the key regeneration alarm. Note that this | ||
265 | alarm only occurs in the daemon waiting for connections, and it does not | ||
266 | do anything with the private key or random state before forking. Thus there | ||
267 | should be no concurrency control/asynchronous execution problems. */ | ||
268 | |||
269 | void key_regeneration_alarm(int sig) | ||
270 | { | ||
271 | int save_errno = errno; | ||
272 | |||
273 | /* Check if we should generate a new key. */ | ||
274 | if (key_used) | ||
275 | { | ||
276 | /* This should really be done in the background. */ | ||
277 | log("Generating new %d bit RSA key.", options.server_key_bits); | ||
278 | |||
279 | if (sensitive_data.private_key != NULL) | ||
280 | RSA_free(sensitive_data.private_key); | ||
281 | sensitive_data.private_key = RSA_new(); | ||
282 | |||
283 | if (public_key != NULL) | ||
284 | RSA_free(public_key); | ||
285 | public_key = RSA_new(); | ||
286 | |||
287 | rsa_generate_key(sensitive_data.private_key, public_key, | ||
288 | options.server_key_bits); | ||
289 | arc4random_stir(); | ||
290 | key_used = 0; | ||
291 | log("RSA key generation complete."); | ||
292 | } | ||
293 | |||
294 | /* Reschedule the alarm. */ | ||
295 | signal(SIGALRM, key_regeneration_alarm); | ||
296 | alarm(options.key_regeneration_time); | ||
297 | errno = save_errno; | ||
298 | } | ||
299 | |||
300 | /* Main program for the daemon. */ | ||
301 | |||
302 | int | ||
303 | main(int ac, char **av) | ||
304 | { | ||
305 | extern char *optarg; | ||
306 | extern int optind; | ||
307 | int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1; | ||
308 | int remote_major, remote_minor; | ||
309 | int silentrsa = 0; | ||
310 | struct sockaddr_in sin; | ||
311 | char buf[100]; /* Must not be larger than remote_version. */ | ||
312 | char remote_version[100]; /* Must be at least as big as buf. */ | ||
313 | char *comment; | ||
314 | FILE *f; | ||
315 | struct linger linger; | ||
316 | |||
317 | /* Save argv[0]. */ | ||
318 | saved_argv = av; | ||
319 | if (strchr(av[0], '/')) | ||
320 | av0 = strrchr(av[0], '/') + 1; | ||
321 | else | ||
322 | av0 = av[0]; | ||
323 | |||
324 | /* Initialize configuration options to their default values. */ | ||
325 | initialize_server_options(&options); | ||
326 | |||
327 | /* Parse command-line arguments. */ | ||
328 | while ((opt = getopt(ac, av, "f:p:b:k:h:g:diqQ")) != EOF) | ||
329 | { | ||
330 | switch (opt) | ||
331 | { | ||
332 | case 'f': | ||
333 | config_file_name = optarg; | ||
334 | break; | ||
335 | case 'd': | ||
336 | debug_flag = 1; | ||
337 | break; | ||
338 | case 'i': | ||
339 | inetd_flag = 1; | ||
340 | break; | ||
341 | case 'Q': | ||
342 | silentrsa = 1; | ||
343 | break; | ||
344 | case 'q': | ||
345 | options.quiet_mode = 1; | ||
346 | break; | ||
347 | case 'b': | ||
348 | options.server_key_bits = atoi(optarg); | ||
349 | break; | ||
350 | case 'p': | ||
351 | options.port = atoi(optarg); | ||
352 | break; | ||
353 | case 'g': | ||
354 | options.login_grace_time = atoi(optarg); | ||
355 | break; | ||
356 | case 'k': | ||
357 | options.key_regeneration_time = atoi(optarg); | ||
358 | break; | ||
359 | case 'h': | ||
360 | options.host_key_file = optarg; | ||
361 | break; | ||
362 | case '?': | ||
363 | default: | ||
364 | fprintf(stderr, "sshd version %s\n", SSH_VERSION); | ||
365 | fprintf(stderr, "Usage: %s [options]\n", av0); | ||
366 | fprintf(stderr, "Options:\n"); | ||
367 | fprintf(stderr, " -f file Configuration file (default %s/sshd_config)\n", ETCDIR); | ||
368 | fprintf(stderr, " -d Debugging mode\n"); | ||
369 | fprintf(stderr, " -i Started from inetd\n"); | ||
370 | fprintf(stderr, " -q Quiet (no logging)\n"); | ||
371 | fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); | ||
372 | fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); | ||
373 | fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); | ||
374 | fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); | ||
375 | fprintf(stderr, " -h file File from which to read host key (default: %s)\n", | ||
376 | HOST_KEY_FILE); | ||
377 | exit(1); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | /* check if RSA support exists */ | ||
382 | if (rsa_alive() == 0) { | ||
383 | if (silentrsa == 0) | ||
384 | printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n"); | ||
385 | log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)"); | ||
386 | exit(1); | ||
387 | } | ||
388 | |||
389 | /* Read server configuration options from the configuration file. */ | ||
390 | read_server_config(&options, config_file_name); | ||
391 | |||
392 | /* Fill in default values for those options not explicitly set. */ | ||
393 | fill_default_server_options(&options); | ||
394 | |||
395 | /* Check certain values for sanity. */ | ||
396 | if (options.server_key_bits < 512 || | ||
397 | options.server_key_bits > 32768) | ||
398 | { | ||
399 | fprintf(stderr, "Bad server key size.\n"); | ||
400 | exit(1); | ||
401 | } | ||
402 | if (options.port < 1 || options.port > 65535) | ||
403 | { | ||
404 | fprintf(stderr, "Bad port number.\n"); | ||
405 | exit(1); | ||
406 | } | ||
407 | |||
408 | /* Check that there are no remaining arguments. */ | ||
409 | if (optind < ac) | ||
410 | { | ||
411 | fprintf(stderr, "Extra argument %s.\n", av[optind]); | ||
412 | exit(1); | ||
413 | } | ||
414 | |||
415 | /* Initialize the log (it is reinitialized below in case we forked). */ | ||
416 | log_init(av0, debug_flag && !inetd_flag, | ||
417 | debug_flag || options.fascist_logging, | ||
418 | options.quiet_mode, options.log_facility); | ||
419 | |||
420 | debug("sshd version %.100s", SSH_VERSION); | ||
421 | |||
422 | sensitive_data.host_key = RSA_new(); | ||
423 | /* Load the host key. It must have empty passphrase. */ | ||
424 | if (!load_private_key(options.host_key_file, "", | ||
425 | sensitive_data.host_key, &comment)) | ||
426 | { | ||
427 | if (debug_flag) | ||
428 | fprintf(stderr, "Could not load host key: %s: %s\n", | ||
429 | options.host_key_file, strerror(errno)); | ||
430 | else | ||
431 | { | ||
432 | int err = errno; | ||
433 | log_init(av0, !inetd_flag, 1, 0, options.log_facility); | ||
434 | error("Could not load host key: %.200s: %.100s", | ||
435 | options.host_key_file, strerror(err)); | ||
436 | } | ||
437 | exit(1); | ||
438 | } | ||
439 | xfree(comment); | ||
440 | |||
441 | /* If not in debugging mode, and not started from inetd, disconnect from | ||
442 | the controlling terminal, and fork. The original process exits. */ | ||
443 | if (!debug_flag && !inetd_flag) | ||
444 | { | ||
445 | #ifdef TIOCNOTTY | ||
446 | int fd; | ||
447 | #endif /* TIOCNOTTY */ | ||
448 | if (daemon(0, 0) < 0) | ||
449 | fatal("daemon() failed: %.200s", strerror(errno)); | ||
450 | |||
451 | /* Disconnect from the controlling tty. */ | ||
452 | #ifdef TIOCNOTTY | ||
453 | fd = open("/dev/tty", O_RDWR|O_NOCTTY); | ||
454 | if (fd >= 0) | ||
455 | { | ||
456 | (void)ioctl(fd, TIOCNOTTY, NULL); | ||
457 | close(fd); | ||
458 | } | ||
459 | #endif /* TIOCNOTTY */ | ||
460 | } | ||
461 | |||
462 | /* Reinitialize the log (because of the fork above). */ | ||
463 | log_init(av0, debug_flag && !inetd_flag, | ||
464 | debug_flag || options.fascist_logging, | ||
465 | options.quiet_mode, options.log_facility); | ||
466 | |||
467 | /* Check that server and host key lengths differ sufficiently. This is | ||
468 | necessary to make double encryption work with rsaref. Oh, I hate | ||
469 | software patents. I dont know if this can go? Niels */ | ||
470 | if (options.server_key_bits > | ||
471 | BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && | ||
472 | options.server_key_bits < | ||
473 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) | ||
474 | { | ||
475 | options.server_key_bits = | ||
476 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; | ||
477 | debug("Forcing server key to %d bits to make it differ from host key.", | ||
478 | options.server_key_bits); | ||
479 | } | ||
480 | |||
481 | /* Do not display messages to stdout in RSA code. */ | ||
482 | rsa_set_verbose(0); | ||
483 | |||
484 | /* Initialize the random number generator. */ | ||
485 | arc4random_stir(); | ||
486 | |||
487 | /* Chdir to the root directory so that the current disk can be unmounted | ||
488 | if desired. */ | ||
489 | chdir("/"); | ||
490 | |||
491 | /* Close connection cleanly after attack. */ | ||
492 | cipher_attack_detected = packet_disconnect; | ||
493 | |||
494 | /* Start listening for a socket, unless started from inetd. */ | ||
495 | if (inetd_flag) | ||
496 | { | ||
497 | int s1, s2; | ||
498 | s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ | ||
499 | s2 = dup(s1); | ||
500 | sock_in = dup(0); | ||
501 | sock_out = dup(1); | ||
502 | /* We intentionally do not close the descriptors 0, 1, and 2 as our | ||
503 | code for setting the descriptors won\'t work if ttyfd happens to | ||
504 | be one of those. */ | ||
505 | debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); | ||
506 | |||
507 | public_key = RSA_new(); | ||
508 | sensitive_data.private_key = RSA_new(); | ||
509 | /* Generate an rsa key. */ | ||
510 | log("Generating %d bit RSA key.", options.server_key_bits); | ||
511 | rsa_generate_key(sensitive_data.private_key, public_key, | ||
512 | options.server_key_bits); | ||
513 | arc4random_stir(); | ||
514 | log("RSA key generation complete."); | ||
515 | } | ||
516 | else | ||
517 | { | ||
518 | /* Create socket for listening. */ | ||
519 | listen_sock = socket(AF_INET, SOCK_STREAM, 0); | ||
520 | if (listen_sock < 0) | ||
521 | fatal("socket: %.100s", strerror(errno)); | ||
522 | |||
523 | /* Set socket options. We try to make the port reusable and have it | ||
524 | close as fast as possible without waiting in unnecessary wait states | ||
525 | on close. */ | ||
526 | setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, | ||
527 | sizeof(on)); | ||
528 | linger.l_onoff = 1; | ||
529 | linger.l_linger = 5; | ||
530 | setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *)&linger, | ||
531 | sizeof(linger)); | ||
532 | |||
533 | /* Initialize the socket address. */ | ||
534 | memset(&sin, 0, sizeof(sin)); | ||
535 | sin.sin_family = AF_INET; | ||
536 | sin.sin_addr = options.listen_addr; | ||
537 | sin.sin_port = htons(options.port); | ||
538 | |||
539 | /* Bind the socket to the desired port. */ | ||
540 | if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | ||
541 | { | ||
542 | error("bind: %.100s", strerror(errno)); | ||
543 | shutdown(listen_sock, SHUT_RDWR); | ||
544 | close(listen_sock); | ||
545 | fatal("Bind to port %d failed.", options.port); | ||
546 | } | ||
547 | |||
548 | if (!debug_flag) | ||
549 | { | ||
550 | /* Record our pid in /etc/sshd_pid to make it easier to kill the | ||
551 | correct sshd. We don\'t want to do this before the bind above | ||
552 | because the bind will fail if there already is a daemon, and this | ||
553 | will overwrite any old pid in the file. */ | ||
554 | f = fopen(SSH_DAEMON_PID_FILE, "w"); | ||
555 | if (f) | ||
556 | { | ||
557 | fprintf(f, "%u\n", (unsigned int)getpid()); | ||
558 | fclose(f); | ||
559 | } | ||
560 | } | ||
561 | |||
562 | /* Start listening on the port. */ | ||
563 | log("Server listening on port %d.", options.port); | ||
564 | if (listen(listen_sock, 5) < 0) | ||
565 | fatal("listen: %.100s", strerror(errno)); | ||
566 | |||
567 | public_key = RSA_new(); | ||
568 | sensitive_data.private_key = RSA_new(); | ||
569 | /* Generate an rsa key. */ | ||
570 | log("Generating %d bit RSA key.", options.server_key_bits); | ||
571 | rsa_generate_key(sensitive_data.private_key, public_key, | ||
572 | options.server_key_bits); | ||
573 | arc4random_stir(); | ||
574 | log("RSA key generation complete."); | ||
575 | |||
576 | /* Schedule server key regeneration alarm. */ | ||
577 | signal(SIGALRM, key_regeneration_alarm); | ||
578 | alarm(options.key_regeneration_time); | ||
579 | |||
580 | /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ | ||
581 | signal(SIGHUP, sighup_handler); | ||
582 | signal(SIGTERM, sigterm_handler); | ||
583 | signal(SIGQUIT, sigterm_handler); | ||
584 | |||
585 | /* Arrange SIGCHLD to be caught. */ | ||
586 | signal(SIGCHLD, main_sigchld_handler); | ||
587 | |||
588 | /* Stay listening for connections until the system crashes or the | ||
589 | daemon is killed with a signal. */ | ||
590 | for (;;) | ||
591 | { | ||
592 | if (received_sighup) | ||
593 | sighup_restart(); | ||
594 | /* Wait in accept until there is a connection. */ | ||
595 | aux = sizeof(sin); | ||
596 | newsock = accept(listen_sock, (struct sockaddr *)&sin, &aux); | ||
597 | if (received_sighup) | ||
598 | sighup_restart(); | ||
599 | if (newsock < 0) | ||
600 | { | ||
601 | if (errno == EINTR) | ||
602 | continue; | ||
603 | error("accept: %.100s", strerror(errno)); | ||
604 | continue; | ||
605 | } | ||
606 | |||
607 | /* Got connection. Fork a child to handle it, unless we are in | ||
608 | debugging mode. */ | ||
609 | if (debug_flag) | ||
610 | { | ||
611 | /* In debugging mode. Close the listening socket, and start | ||
612 | processing the connection without forking. */ | ||
613 | debug("Server will not fork when running in debugging mode."); | ||
614 | close(listen_sock); | ||
615 | sock_in = newsock; | ||
616 | sock_out = newsock; | ||
617 | pid = getpid(); | ||
618 | break; | ||
619 | } | ||
620 | else | ||
621 | { | ||
622 | /* Normal production daemon. Fork, and have the child process | ||
623 | the connection. The parent continues listening. */ | ||
624 | if ((pid = fork()) == 0) | ||
625 | { | ||
626 | /* Child. Close the listening socket, and start using | ||
627 | the accepted socket. Reinitialize logging (since our | ||
628 | pid has changed). We break out of the loop to handle | ||
629 | the connection. */ | ||
630 | close(listen_sock); | ||
631 | sock_in = newsock; | ||
632 | sock_out = newsock; | ||
633 | log_init(av0, debug_flag && !inetd_flag, | ||
634 | options.fascist_logging || debug_flag, | ||
635 | options.quiet_mode, options.log_facility); | ||
636 | break; | ||
637 | } | ||
638 | } | ||
639 | |||
640 | /* Parent. Stay in the loop. */ | ||
641 | if (pid < 0) | ||
642 | error("fork: %.100s", strerror(errno)); | ||
643 | else | ||
644 | debug("Forked child %d.", pid); | ||
645 | |||
646 | /* Mark that the key has been used (it was "given" to the child). */ | ||
647 | key_used = 1; | ||
648 | |||
649 | arc4random_stir(); | ||
650 | |||
651 | /* Close the new socket (the child is now taking care of it). */ | ||
652 | close(newsock); | ||
653 | } | ||
654 | } | ||
655 | |||
656 | /* This is the child processing a new connection. */ | ||
657 | |||
658 | /* Disable the key regeneration alarm. We will not regenerate the key | ||
659 | since we are no longer in a position to give it to anyone. We will | ||
660 | not restart on SIGHUP since it no longer makes sense. */ | ||
661 | alarm(0); | ||
662 | signal(SIGALRM, SIG_DFL); | ||
663 | signal(SIGHUP, SIG_DFL); | ||
664 | signal(SIGTERM, SIG_DFL); | ||
665 | signal(SIGQUIT, SIG_DFL); | ||
666 | signal(SIGCHLD, SIG_DFL); | ||
667 | |||
668 | /* Set socket options for the connection. We want the socket to close | ||
669 | as fast as possible without waiting for anything. If the connection | ||
670 | is not a socket, these will do nothing. */ | ||
671 | /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ | ||
672 | linger.l_onoff = 1; | ||
673 | linger.l_linger = 5; | ||
674 | setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); | ||
675 | |||
676 | /* Register our connection. This turns encryption off because we do not | ||
677 | have a key. */ | ||
678 | packet_set_connection(sock_in, sock_out); | ||
679 | |||
680 | /* Check whether logins are denied from this host. */ | ||
681 | #ifdef LIBWRAP | ||
682 | { | ||
683 | struct request_info req; | ||
684 | |||
685 | request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); | ||
686 | fromhost(&req); | ||
687 | |||
688 | if (!hosts_access(&req)) { | ||
689 | close(sock_in); | ||
690 | close(sock_out); | ||
691 | refuse(&req); | ||
692 | } | ||
693 | log("Connection from %.500s port %d", | ||
694 | eval_client(&req), get_remote_port()); | ||
695 | } | ||
696 | #else | ||
697 | /* Log the connection. */ | ||
698 | log("Connection from %.100s port %d", | ||
699 | get_remote_ipaddr(), get_remote_port()); | ||
700 | #endif /* LIBWRAP */ | ||
701 | |||
702 | /* We don\'t want to listen forever unless the other side successfully | ||
703 | authenticates itself. So we set up an alarm which is cleared after | ||
704 | successful authentication. A limit of zero indicates no limit. | ||
705 | Note that we don\'t set the alarm in debugging mode; it is just annoying | ||
706 | to have the server exit just when you are about to discover the bug. */ | ||
707 | signal(SIGALRM, grace_alarm_handler); | ||
708 | if (!debug_flag) | ||
709 | alarm(options.login_grace_time); | ||
710 | |||
711 | /* Send our protocol version identification. */ | ||
712 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", | ||
713 | PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); | ||
714 | if (write(sock_out, buf, strlen(buf)) != strlen(buf)) | ||
715 | fatal("Could not write ident string."); | ||
716 | |||
717 | /* Read other side\'s version identification. */ | ||
718 | for (i = 0; i < sizeof(buf) - 1; i++) | ||
719 | { | ||
720 | if (read(sock_in, &buf[i], 1) != 1) | ||
721 | fatal("Did not receive ident string."); | ||
722 | if (buf[i] == '\r') | ||
723 | { | ||
724 | buf[i] = '\n'; | ||
725 | buf[i + 1] = 0; | ||
726 | break; | ||
727 | } | ||
728 | if (buf[i] == '\n') | ||
729 | { | ||
730 | /* buf[i] == '\n' */ | ||
731 | buf[i + 1] = 0; | ||
732 | break; | ||
733 | } | ||
734 | } | ||
735 | buf[sizeof(buf) - 1] = 0; | ||
736 | |||
737 | /* Check that the versions match. In future this might accept several | ||
738 | versions and set appropriate flags to handle them. */ | ||
739 | if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, | ||
740 | remote_version) != 3) | ||
741 | { | ||
742 | const char *s = "Protocol mismatch.\n"; | ||
743 | (void) write(sock_out, s, strlen(s)); | ||
744 | close(sock_in); | ||
745 | close(sock_out); | ||
746 | fatal("Bad protocol version identification: %.100s", buf); | ||
747 | } | ||
748 | debug("Client protocol version %d.%d; client software version %.100s", | ||
749 | remote_major, remote_minor, remote_version); | ||
750 | if (remote_major != PROTOCOL_MAJOR) | ||
751 | { | ||
752 | const char *s = "Protocol major versions differ.\n"; | ||
753 | (void) write(sock_out, s, strlen(s)); | ||
754 | close(sock_in); | ||
755 | close(sock_out); | ||
756 | fatal("Protocol major versions differ: %d vs. %d", | ||
757 | PROTOCOL_MAJOR, remote_major); | ||
758 | } | ||
759 | |||
760 | /* Check that the client has sufficiently high software version. */ | ||
761 | if (remote_major == 1 && remote_minor < 3) | ||
762 | packet_disconnect("Your ssh version is too old and is no longer supported. Please install a newer version."); | ||
763 | |||
764 | if (remote_major == 1 && remote_minor == 3) { | ||
765 | enable_compat13(); | ||
766 | if (strcmp(remote_version, "OpenSSH-1.1") != 0) { | ||
767 | debug("Agent forwarding disabled, remote version is not compatible."); | ||
768 | no_agent_forwarding_flag = 1; | ||
769 | } | ||
770 | } | ||
771 | |||
772 | packet_set_nonblocking(); | ||
773 | |||
774 | /* Handle the connection. We pass as argument whether the connection | ||
775 | came from a privileged port. */ | ||
776 | do_connection(get_remote_port() < IPPORT_RESERVED); | ||
777 | |||
778 | #ifdef KRB4 | ||
779 | /* Cleanup user's ticket cache file. */ | ||
780 | if (options.kerberos_ticket_cleanup) | ||
781 | (void) dest_tkt(); | ||
782 | #endif /* KRB4 */ | ||
783 | |||
784 | /* Cleanup user's local Xauthority file. */ | ||
785 | if (xauthfile) unlink(xauthfile); | ||
786 | |||
787 | /* The connection has been terminated. */ | ||
788 | log("Closing connection to %.100s", inet_ntoa(sin.sin_addr)); | ||
789 | |||
790 | #ifdef HAVE_PAM | ||
791 | if (retval == PAM_SUCCESS) | ||
792 | retval = pam_close_session((pam_handle_t *)pamh, 0); | ||
793 | |||
794 | if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS) | ||
795 | log("Cannot release PAM authentication."); | ||
796 | |||
797 | fatal_remove_cleanup(&pam_cleanup_proc, NULL); | ||
798 | #endif /* HAVE_PAM */ | ||
799 | |||
800 | packet_close(); | ||
801 | |||
802 | exit(0); | ||
803 | } | ||
804 | |||
805 | /* Process an incoming connection. Protocol version identifiers have already | ||
806 | been exchanged. This sends server key and performs the key exchange. | ||
807 | Server and host keys will no longer be needed after this functions. */ | ||
808 | |||
809 | void do_connection(int privileged_port) | ||
810 | { | ||
811 | int i; | ||
812 | BIGNUM *session_key_int; | ||
813 | unsigned char session_key[SSH_SESSION_KEY_LENGTH]; | ||
814 | unsigned char check_bytes[8]; | ||
815 | char *user; | ||
816 | unsigned int cipher_type, auth_mask, protocol_flags; | ||
817 | int plen, slen; | ||
818 | u_int32_t rand = 0; | ||
819 | |||
820 | /* Generate check bytes that the client must send back in the user packet | ||
821 | in order for it to be accepted; this is used to defy ip spoofing | ||
822 | attacks. Note that this only works against somebody doing IP spoofing | ||
823 | from a remote machine; any machine on the local network can still see | ||
824 | outgoing packets and catch the random cookie. This only affects | ||
825 | rhosts authentication, and this is one of the reasons why it is | ||
826 | inherently insecure. */ | ||
827 | for (i = 0; i < 8; i++) { | ||
828 | if (i % 4 == 0) | ||
829 | rand = arc4random(); | ||
830 | check_bytes[i] = rand & 0xff; | ||
831 | rand >>= 8; | ||
832 | } | ||
833 | |||
834 | /* Send our public key. We include in the packet 64 bits of random | ||
835 | data that must be matched in the reply in order to prevent IP spoofing. */ | ||
836 | packet_start(SSH_SMSG_PUBLIC_KEY); | ||
837 | for (i = 0; i < 8; i++) | ||
838 | packet_put_char(check_bytes[i]); | ||
839 | |||
840 | /* Store our public server RSA key. */ | ||
841 | packet_put_int(BN_num_bits(public_key->n)); | ||
842 | packet_put_bignum(public_key->e); | ||
843 | packet_put_bignum(public_key->n); | ||
844 | |||
845 | /* Store our public host RSA key. */ | ||
846 | packet_put_int(BN_num_bits(sensitive_data.host_key->n)); | ||
847 | packet_put_bignum(sensitive_data.host_key->e); | ||
848 | packet_put_bignum(sensitive_data.host_key->n); | ||
849 | |||
850 | /* Put protocol flags. */ | ||
851 | packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); | ||
852 | |||
853 | /* Declare which ciphers we support. */ | ||
854 | packet_put_int(cipher_mask()); | ||
855 | |||
856 | /* Declare supported authentication types. */ | ||
857 | auth_mask = 0; | ||
858 | if (options.rhosts_authentication) | ||
859 | auth_mask |= 1 << SSH_AUTH_RHOSTS; | ||
860 | if (options.rhosts_rsa_authentication) | ||
861 | auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; | ||
862 | if (options.rsa_authentication) | ||
863 | auth_mask |= 1 << SSH_AUTH_RSA; | ||
864 | #ifdef KRB4 | ||
865 | if (options.kerberos_authentication) | ||
866 | auth_mask |= 1 << SSH_AUTH_KERBEROS; | ||
867 | #endif | ||
868 | #ifdef AFS | ||
869 | if (options.kerberos_tgt_passing) | ||
870 | auth_mask |= 1 << SSH_PASS_KERBEROS_TGT; | ||
871 | if (options.afs_token_passing) | ||
872 | auth_mask |= 1 << SSH_PASS_AFS_TOKEN; | ||
873 | #endif | ||
874 | if (options.password_authentication) | ||
875 | auth_mask |= 1 << SSH_AUTH_PASSWORD; | ||
876 | packet_put_int(auth_mask); | ||
877 | |||
878 | /* Send the packet and wait for it to be sent. */ | ||
879 | packet_send(); | ||
880 | packet_write_wait(); | ||
881 | |||
882 | debug("Sent %d bit public key and %d bit host key.", | ||
883 | BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); | ||
884 | |||
885 | /* Read clients reply (cipher type and session key). */ | ||
886 | packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); | ||
887 | |||
888 | /* Get cipher type. */ | ||
889 | cipher_type = packet_get_char(); | ||
890 | |||
891 | /* Get check bytes from the packet. These must match those we sent earlier | ||
892 | with the public key packet. */ | ||
893 | for (i = 0; i < 8; i++) | ||
894 | if (check_bytes[i] != packet_get_char()) | ||
895 | packet_disconnect("IP Spoofing check bytes do not match."); | ||
896 | |||
897 | debug("Encryption type: %.200s", cipher_name(cipher_type)); | ||
898 | |||
899 | /* Get the encrypted integer. */ | ||
900 | session_key_int = BN_new(); | ||
901 | packet_get_bignum(session_key_int, &slen); | ||
902 | |||
903 | /* Get protocol flags. */ | ||
904 | protocol_flags = packet_get_int(); | ||
905 | packet_set_protocol_flags(protocol_flags); | ||
906 | |||
907 | packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); | ||
908 | |||
909 | /* Decrypt it using our private server key and private host key (key with | ||
910 | larger modulus first). */ | ||
911 | if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) | ||
912 | { | ||
913 | /* Private key has bigger modulus. */ | ||
914 | assert(BN_num_bits(sensitive_data.private_key->n) >= | ||
915 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED); | ||
916 | rsa_private_decrypt(session_key_int, session_key_int, | ||
917 | sensitive_data.private_key); | ||
918 | rsa_private_decrypt(session_key_int, session_key_int, | ||
919 | sensitive_data.host_key); | ||
920 | } | ||
921 | else | ||
922 | { | ||
923 | /* Host key has bigger modulus (or they are equal). */ | ||
924 | assert(BN_num_bits(sensitive_data.host_key->n) >= | ||
925 | BN_num_bits(sensitive_data.private_key->n) + | ||
926 | SSH_KEY_BITS_RESERVED); | ||
927 | rsa_private_decrypt(session_key_int, session_key_int, | ||
928 | sensitive_data.host_key); | ||
929 | rsa_private_decrypt(session_key_int, session_key_int, | ||
930 | sensitive_data.private_key); | ||
931 | } | ||
932 | |||
933 | /* Compute session id for this session. */ | ||
934 | compute_session_id(session_id, check_bytes, | ||
935 | BN_num_bits(sensitive_data.host_key->n), | ||
936 | sensitive_data.host_key->n, | ||
937 | BN_num_bits(sensitive_data.private_key->n), | ||
938 | sensitive_data.private_key->n); | ||
939 | |||
940 | /* Extract session key from the decrypted integer. The key is in the | ||
941 | least significant 256 bits of the integer; the first byte of the | ||
942 | key is in the highest bits. */ | ||
943 | BN_mask_bits(session_key_int, sizeof(session_key) * 8); | ||
944 | assert(BN_num_bytes(session_key_int) == sizeof(session_key)); | ||
945 | BN_bn2bin(session_key_int, session_key); | ||
946 | |||
947 | /* Xor the first 16 bytes of the session key with the session id. */ | ||
948 | for (i = 0; i < 16; i++) | ||
949 | session_key[i] ^= session_id[i]; | ||
950 | |||
951 | /* Destroy the decrypted integer. It is no longer needed. */ | ||
952 | BN_clear_free(session_key_int); | ||
953 | |||
954 | /* Set the session key. From this on all communications will be | ||
955 | encrypted. */ | ||
956 | packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, | ||
957 | cipher_type, 0); | ||
958 | |||
959 | /* Destroy our copy of the session key. It is no longer needed. */ | ||
960 | memset(session_key, 0, sizeof(session_key)); | ||
961 | |||
962 | debug("Received session key; encryption turned on."); | ||
963 | |||
964 | /* Send an acknowledgement packet. Note that this packet is sent | ||
965 | encrypted. */ | ||
966 | packet_start(SSH_SMSG_SUCCESS); | ||
967 | packet_send(); | ||
968 | packet_write_wait(); | ||
969 | |||
970 | /* Get the name of the user that we wish to log in as. */ | ||
971 | packet_read_expect(&plen, SSH_CMSG_USER); | ||
972 | |||
973 | /* Get the user name. */ | ||
974 | { | ||
975 | int ulen; | ||
976 | user = packet_get_string(&ulen); | ||
977 | packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); | ||
978 | } | ||
979 | |||
980 | /* Destroy the private and public keys. They will no longer be needed. */ | ||
981 | RSA_free(public_key); | ||
982 | RSA_free(sensitive_data.private_key); | ||
983 | RSA_free(sensitive_data.host_key); | ||
984 | |||
985 | setproctitle("%s", user); | ||
986 | /* Do the authentication. */ | ||
987 | do_authentication(user, privileged_port); | ||
988 | } | ||
989 | |||
990 | /* Check if the user is allowed to log in via ssh. If user is listed in | ||
991 | DenyUsers or user's primary group is listed in DenyGroups, false will | ||
992 | be returned. If AllowUsers isn't empty and user isn't listed there, or | ||
993 | if AllowGroups isn't empty and user isn't listed there, false will be | ||
994 | returned. Otherwise true is returned. | ||
995 | XXX This function should also check if user has a valid shell */ | ||
996 | |||
997 | static int | ||
998 | allowed_user(struct passwd *pw) | ||
999 | { | ||
1000 | struct group *grp; | ||
1001 | int i; | ||
1002 | |||
1003 | /* Shouldn't be called if pw is NULL, but better safe than sorry... */ | ||
1004 | if (!pw) | ||
1005 | return 0; | ||
1006 | |||
1007 | /* XXX Should check for valid login shell */ | ||
1008 | |||
1009 | /* Return false if user is listed in DenyUsers */ | ||
1010 | if (options.num_deny_users > 0) | ||
1011 | { | ||
1012 | if (!pw->pw_name) | ||
1013 | return 0; | ||
1014 | for (i = 0; i < options.num_deny_users; i++) | ||
1015 | if (match_pattern(pw->pw_name, options.deny_users[i])) | ||
1016 | return 0; | ||
1017 | } | ||
1018 | |||
1019 | /* Return false if AllowUsers isn't empty and user isn't listed there */ | ||
1020 | if (options.num_allow_users > 0) | ||
1021 | { | ||
1022 | if (!pw->pw_name) | ||
1023 | return 0; | ||
1024 | for (i = 0; i < options.num_allow_users; i++) | ||
1025 | if (match_pattern(pw->pw_name, options.allow_users[i])) | ||
1026 | break; | ||
1027 | /* i < options.num_allow_users iff we break for loop */ | ||
1028 | if (i >= options.num_allow_users) | ||
1029 | return 0; | ||
1030 | } | ||
1031 | |||
1032 | /* Get the primary group name if we need it. Return false if it fails */ | ||
1033 | if (options.num_deny_groups > 0 || options.num_allow_groups > 0 ) | ||
1034 | { | ||
1035 | grp = getgrgid(pw->pw_gid); | ||
1036 | if (!grp) | ||
1037 | return 0; | ||
1038 | |||
1039 | /* Return false if user's group is listed in DenyGroups */ | ||
1040 | if (options.num_deny_groups > 0) | ||
1041 | { | ||
1042 | if (!grp->gr_name) | ||
1043 | return 0; | ||
1044 | for (i = 0; i < options.num_deny_groups; i++) | ||
1045 | if (match_pattern(grp->gr_name, options.deny_groups[i])) | ||
1046 | return 0; | ||
1047 | } | ||
1048 | |||
1049 | /* Return false if AllowGroups isn't empty and user's group isn't | ||
1050 | listed there */ | ||
1051 | if (options.num_allow_groups > 0) | ||
1052 | { | ||
1053 | if (!grp->gr_name) | ||
1054 | return 0; | ||
1055 | for (i = 0; i < options.num_allow_groups; i++) | ||
1056 | if (match_pattern(grp->gr_name, options.allow_groups[i])) | ||
1057 | break; | ||
1058 | /* i < options.num_allow_groups iff we break for loop */ | ||
1059 | if (i >= options.num_allow_groups) | ||
1060 | return 0; | ||
1061 | } | ||
1062 | } | ||
1063 | |||
1064 | /* We found no reason not to let this user try to log on... */ | ||
1065 | return 1; | ||
1066 | } | ||
1067 | |||
1068 | /* Performs authentication of an incoming connection. Session key has already | ||
1069 | been exchanged and encryption is enabled. User is the user name to log | ||
1070 | in as (received from the clinet). Privileged_port is true if the | ||
1071 | connection comes from a privileged port (used for .rhosts authentication).*/ | ||
1072 | |||
1073 | #define MAX_AUTH_FAILURES 5 | ||
1074 | |||
1075 | void | ||
1076 | do_authentication(char *user, int privileged_port) | ||
1077 | { | ||
1078 | int type; | ||
1079 | int authenticated = 0; | ||
1080 | int authentication_failures = 0; | ||
1081 | char *password; | ||
1082 | struct passwd *pw, pwcopy; | ||
1083 | char *client_user; | ||
1084 | unsigned int client_host_key_bits; | ||
1085 | BIGNUM *client_host_key_e, *client_host_key_n; | ||
1086 | #ifdef HAVE_PAM | ||
1087 | int pam_auth_ok; | ||
1088 | #endif /* HAVE_PAM */ | ||
1089 | |||
1090 | #ifdef AFS | ||
1091 | /* If machine has AFS, set process authentication group. */ | ||
1092 | if (k_hasafs()) { | ||
1093 | k_setpag(); | ||
1094 | k_unlog(); | ||
1095 | } | ||
1096 | #endif /* AFS */ | ||
1097 | |||
1098 | /* Verify that the user is a valid user. */ | ||
1099 | pw = getpwnam(user); | ||
1100 | #ifdef HAVE_PAM | ||
1101 | if ((pw != NULL) && allowed_user(pw)) | ||
1102 | { | ||
1103 | /* Initialise PAM */ | ||
1104 | retval = pam_start("ssh", pw->pw_name, &conv, (pam_handle_t **)&pamh); | ||
1105 | fatal_add_cleanup(&pam_cleanup_proc, NULL); | ||
1106 | origretval = retval; | ||
1107 | if (retval == PAM_SUCCESS) | ||
1108 | pam_auth_ok = 1; | ||
1109 | } | ||
1110 | |||
1111 | if (pam_auth_ok == 0) | ||
1112 | #else /* HAVE_PAM */ | ||
1113 | if (!pw || !allowed_user(pw)) | ||
1114 | #endif /* HAVE_PAM */ | ||
1115 | { | ||
1116 | /* The user does not exist or access is denied, | ||
1117 | but fake indication that authentication is needed. */ | ||
1118 | packet_start(SSH_SMSG_FAILURE); | ||
1119 | packet_send(); | ||
1120 | packet_write_wait(); | ||
1121 | |||
1122 | /* Keep reading packets, and always respond with a failure. This is to | ||
1123 | avoid disclosing whether such a user really exists. */ | ||
1124 | for (;;) | ||
1125 | { | ||
1126 | /* Read a packet. This will not return if the client disconnects. */ | ||
1127 | int plen; | ||
1128 | int type = packet_read(&plen); | ||
1129 | #ifdef SKEY | ||
1130 | int passw_len; | ||
1131 | char *password, *skeyinfo; | ||
1132 | if (options.password_authentication && | ||
1133 | options.skey_authentication == 1 && | ||
1134 | type == SSH_CMSG_AUTH_PASSWORD && | ||
1135 | (password = packet_get_string(&passw_len)) != NULL && | ||
1136 | passw_len == 5 && | ||
1137 | strncasecmp(password, "s/key", 5) == 0 && | ||
1138 | (skeyinfo = skey_fake_keyinfo(user)) != NULL ){ | ||
1139 | /* Send a fake s/key challenge. */ | ||
1140 | packet_send_debug(skeyinfo); | ||
1141 | } | ||
1142 | #endif | ||
1143 | /* Send failure. This should be indistinguishable from a failed | ||
1144 | authentication. */ | ||
1145 | packet_start(SSH_SMSG_FAILURE); | ||
1146 | packet_send(); | ||
1147 | packet_write_wait(); | ||
1148 | if (++authentication_failures >= MAX_AUTH_FAILURES) { | ||
1149 | packet_disconnect("Too many authentication failures for %.100s from %.200s", | ||
1150 | user, get_canonical_hostname()); | ||
1151 | } | ||
1152 | } | ||
1153 | /*NOTREACHED*/ | ||
1154 | abort(); | ||
1155 | } | ||
1156 | |||
1157 | /* Take a copy of the returned structure. */ | ||
1158 | memset(&pwcopy, 0, sizeof(pwcopy)); | ||
1159 | pwcopy.pw_name = xstrdup(pw->pw_name); | ||
1160 | pwcopy.pw_passwd = xstrdup(pw->pw_passwd); | ||
1161 | pwcopy.pw_uid = pw->pw_uid; | ||
1162 | pwcopy.pw_gid = pw->pw_gid; | ||
1163 | pwcopy.pw_dir = xstrdup(pw->pw_dir); | ||
1164 | pwcopy.pw_shell = xstrdup(pw->pw_shell); | ||
1165 | pw = &pwcopy; | ||
1166 | |||
1167 | /* If we are not running as root, the user must have the same uid as the | ||
1168 | server. */ | ||
1169 | if (getuid() != 0 && pw->pw_uid != getuid()) | ||
1170 | packet_disconnect("Cannot change user when server not running as root."); | ||
1171 | |||
1172 | debug("Attempting authentication for %.100s.", user); | ||
1173 | |||
1174 | /* If the user has no password, accept authentication immediately. */ | ||
1175 | if (options.password_authentication && | ||
1176 | #ifdef KRB4 | ||
1177 | (!options.kerberos_authentication || options.kerberos_or_local_passwd) && | ||
1178 | #endif /* KRB4 */ | ||
1179 | auth_password(pw, "")) | ||
1180 | { | ||
1181 | /* Authentication with empty password succeeded. */ | ||
1182 | debug("Login for user %.100s accepted without authentication.", user); | ||
1183 | /* authentication_type = SSH_AUTH_PASSWORD; */ | ||
1184 | authenticated = 1; | ||
1185 | /* Success packet will be sent after loop below. */ | ||
1186 | } | ||
1187 | else | ||
1188 | { | ||
1189 | /* Indicate that authentication is needed. */ | ||
1190 | packet_start(SSH_SMSG_FAILURE); | ||
1191 | packet_send(); | ||
1192 | packet_write_wait(); | ||
1193 | } | ||
1194 | |||
1195 | /* Loop until the user has been authenticated or the connection is closed. */ | ||
1196 | while (!authenticated) | ||
1197 | { | ||
1198 | int plen; | ||
1199 | /* Get a packet from the client. */ | ||
1200 | type = packet_read(&plen); | ||
1201 | |||
1202 | /* Process the packet. */ | ||
1203 | switch (type) | ||
1204 | { | ||
1205 | |||
1206 | #ifdef AFS | ||
1207 | case SSH_CMSG_HAVE_KERBEROS_TGT: | ||
1208 | if (!options.kerberos_tgt_passing) | ||
1209 | { | ||
1210 | /* packet_get_all(); */ | ||
1211 | log("Kerberos tgt passing disabled."); | ||
1212 | break; | ||
1213 | } | ||
1214 | else { | ||
1215 | /* Accept Kerberos tgt. */ | ||
1216 | int dlen; | ||
1217 | char *tgt = packet_get_string(&dlen); | ||
1218 | packet_integrity_check(plen, 4 + dlen, type); | ||
1219 | if (!auth_kerberos_tgt(pw, tgt)) | ||
1220 | debug("Kerberos tgt REFUSED for %s", user); | ||
1221 | xfree(tgt); | ||
1222 | } | ||
1223 | continue; | ||
1224 | |||
1225 | case SSH_CMSG_HAVE_AFS_TOKEN: | ||
1226 | if (!options.afs_token_passing || !k_hasafs()) { | ||
1227 | /* packet_get_all(); */ | ||
1228 | log("AFS token passing disabled."); | ||
1229 | break; | ||
1230 | } | ||
1231 | else { | ||
1232 | /* Accept AFS token. */ | ||
1233 | int dlen; | ||
1234 | char *token_string = packet_get_string(&dlen); | ||
1235 | packet_integrity_check(plen, 4 + dlen, type); | ||
1236 | if (!auth_afs_token(user, pw->pw_uid, token_string)) | ||
1237 | debug("AFS token REFUSED for %s", user); | ||
1238 | xfree(token_string); | ||
1239 | continue; | ||
1240 | } | ||
1241 | #endif /* AFS */ | ||
1242 | |||
1243 | #ifdef KRB4 | ||
1244 | case SSH_CMSG_AUTH_KERBEROS: | ||
1245 | if (!options.kerberos_authentication) | ||
1246 | { | ||
1247 | /* packet_get_all(); */ | ||
1248 | log("Kerberos authentication disabled."); | ||
1249 | break; | ||
1250 | } | ||
1251 | else { | ||
1252 | /* Try Kerberos v4 authentication. */ | ||
1253 | KTEXT_ST auth; | ||
1254 | char *tkt_user = NULL; | ||
1255 | char *kdata = packet_get_string((unsigned int *)&auth.length); | ||
1256 | packet_integrity_check(plen, 4 + auth.length, type); | ||
1257 | |||
1258 | if (auth.length < MAX_KTXT_LEN) | ||
1259 | memcpy(auth.dat, kdata, auth.length); | ||
1260 | xfree(kdata); | ||
1261 | |||
1262 | if (auth_krb4(user, &auth, &tkt_user)) { | ||
1263 | /* Client has successfully authenticated to us. */ | ||
1264 | log("Kerberos authentication accepted %s for account " | ||
1265 | "%s from %s", tkt_user, user, get_canonical_hostname()); | ||
1266 | /* authentication_type = SSH_AUTH_KERBEROS; */ | ||
1267 | authenticated = 1; | ||
1268 | xfree(tkt_user); | ||
1269 | } | ||
1270 | else { | ||
1271 | log("Kerberos authentication failed for account " | ||
1272 | "%s from %s", user, get_canonical_hostname()); | ||
1273 | } | ||
1274 | } | ||
1275 | break; | ||
1276 | #endif /* KRB4 */ | ||
1277 | |||
1278 | case SSH_CMSG_AUTH_RHOSTS: | ||
1279 | if (!options.rhosts_authentication) | ||
1280 | { | ||
1281 | log("Rhosts authentication disabled."); | ||
1282 | break; | ||
1283 | } | ||
1284 | |||
1285 | /* Rhosts authentication (also uses /etc/hosts.equiv). */ | ||
1286 | if (!privileged_port) | ||
1287 | { | ||
1288 | log("Rhosts authentication not available for connections from unprivileged port."); | ||
1289 | break; | ||
1290 | } | ||
1291 | |||
1292 | /* Get client user name. Note that we just have to trust the client; | ||
1293 | this is one reason why rhosts authentication is insecure. | ||
1294 | (Another is IP-spoofing on a local network.) */ | ||
1295 | { | ||
1296 | int dlen; | ||
1297 | client_user = packet_get_string(&dlen); | ||
1298 | packet_integrity_check(plen, 4 + dlen, type); | ||
1299 | } | ||
1300 | |||
1301 | /* Try to authenticate using /etc/hosts.equiv and .rhosts. */ | ||
1302 | if (auth_rhosts(pw, client_user, options.ignore_rhosts, | ||
1303 | options.strict_modes)) | ||
1304 | { | ||
1305 | /* Authentication accepted. */ | ||
1306 | log("Rhosts authentication accepted for %.100s, remote %.100s on %.700s.", | ||
1307 | user, client_user, get_canonical_hostname()); | ||
1308 | authenticated = 1; | ||
1309 | xfree(client_user); | ||
1310 | break; | ||
1311 | } | ||
1312 | log("Rhosts authentication failed for %.100s, remote %.100s.", | ||
1313 | user, client_user); | ||
1314 | xfree(client_user); | ||
1315 | break; | ||
1316 | |||
1317 | case SSH_CMSG_AUTH_RHOSTS_RSA: | ||
1318 | if (!options.rhosts_rsa_authentication) | ||
1319 | { | ||
1320 | log("Rhosts with RSA authentication disabled."); | ||
1321 | break; | ||
1322 | } | ||
1323 | |||
1324 | /* Rhosts authentication (also uses /etc/hosts.equiv) with RSA | ||
1325 | host authentication. */ | ||
1326 | if (!privileged_port) | ||
1327 | { | ||
1328 | log("Rhosts authentication not available for connections from unprivileged port."); | ||
1329 | break; | ||
1330 | } | ||
1331 | |||
1332 | { | ||
1333 | int ulen, elen, nlen; | ||
1334 | /* Get client user name. Note that we just have to trust | ||
1335 | the client; root on the client machine can claim to be | ||
1336 | any user. */ | ||
1337 | client_user = packet_get_string(&ulen); | ||
1338 | |||
1339 | /* Get the client host key. */ | ||
1340 | client_host_key_e = BN_new(); | ||
1341 | client_host_key_n = BN_new(); | ||
1342 | client_host_key_bits = packet_get_int(); | ||
1343 | packet_get_bignum(client_host_key_e, &elen); | ||
1344 | packet_get_bignum(client_host_key_n, &nlen); | ||
1345 | |||
1346 | packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); | ||
1347 | } | ||
1348 | |||
1349 | /* Try to authenticate using /etc/hosts.equiv and .rhosts. */ | ||
1350 | if (auth_rhosts_rsa(pw, client_user, | ||
1351 | client_host_key_bits, client_host_key_e, | ||
1352 | client_host_key_n, options.ignore_rhosts, | ||
1353 | options.strict_modes)) | ||
1354 | { | ||
1355 | /* Authentication accepted. */ | ||
1356 | authenticated = 1; | ||
1357 | xfree(client_user); | ||
1358 | BN_clear_free(client_host_key_e); | ||
1359 | BN_clear_free(client_host_key_n); | ||
1360 | break; | ||
1361 | } | ||
1362 | log("Rhosts authentication failed for %.100s, remote %.100s.", | ||
1363 | user, client_user); | ||
1364 | xfree(client_user); | ||
1365 | BN_clear_free(client_host_key_e); | ||
1366 | BN_clear_free(client_host_key_n); | ||
1367 | break; | ||
1368 | |||
1369 | case SSH_CMSG_AUTH_RSA: | ||
1370 | if (!options.rsa_authentication) | ||
1371 | { | ||
1372 | log("RSA authentication disabled."); | ||
1373 | break; | ||
1374 | } | ||
1375 | |||
1376 | /* RSA authentication requested. */ | ||
1377 | { | ||
1378 | int nlen; | ||
1379 | BIGNUM *n; | ||
1380 | n = BN_new(); | ||
1381 | packet_get_bignum(n, &nlen); | ||
1382 | |||
1383 | packet_integrity_check(plen, nlen, type); | ||
1384 | |||
1385 | if (auth_rsa(pw, n, options.strict_modes)) | ||
1386 | { | ||
1387 | /* Successful authentication. */ | ||
1388 | BN_clear_free(n); | ||
1389 | log("RSA authentication for %.100s accepted.", user); | ||
1390 | authenticated = 1; | ||
1391 | break; | ||
1392 | } | ||
1393 | BN_clear_free(n); | ||
1394 | log("RSA authentication for %.100s failed.", user); | ||
1395 | } | ||
1396 | break; | ||
1397 | |||
1398 | case SSH_CMSG_AUTH_PASSWORD: | ||
1399 | if (!options.password_authentication) | ||
1400 | { | ||
1401 | log("Password authentication disabled."); | ||
1402 | break; | ||
1403 | } | ||
1404 | |||
1405 | /* Password authentication requested. */ | ||
1406 | /* Read user password. It is in plain text, but was transmitted | ||
1407 | over the encrypted channel so it is not visible to an outside | ||
1408 | observer. */ | ||
1409 | { | ||
1410 | int passw_len; | ||
1411 | password = packet_get_string(&passw_len); | ||
1412 | packet_integrity_check(plen, 4 + passw_len, type); | ||
1413 | } | ||
1414 | |||
1415 | /* Try authentication with the password. */ | ||
1416 | if (auth_password(pw, password)) | ||
1417 | { | ||
1418 | /* Successful authentication. */ | ||
1419 | /* Clear the password from memory. */ | ||
1420 | memset(password, 0, strlen(password)); | ||
1421 | xfree(password); | ||
1422 | log("Password authentication for %.100s accepted.", user); | ||
1423 | authenticated = 1; | ||
1424 | break; | ||
1425 | } | ||
1426 | log("Password authentication for %.100s failed.", user); | ||
1427 | memset(password, 0, strlen(password)); | ||
1428 | xfree(password); | ||
1429 | break; | ||
1430 | |||
1431 | case SSH_CMSG_AUTH_TIS: | ||
1432 | /* TIS Authentication is unsupported */ | ||
1433 | log("TIS authentication disabled."); | ||
1434 | break; | ||
1435 | |||
1436 | default: | ||
1437 | /* Any unknown messages will be ignored (and failure returned) | ||
1438 | during authentication. */ | ||
1439 | log("Unknown message during authentication: type %d", type); | ||
1440 | break; /* Respond with a failure message. */ | ||
1441 | } | ||
1442 | /* If successfully authenticated, break out of loop. */ | ||
1443 | if (authenticated) | ||
1444 | break; | ||
1445 | |||
1446 | /* Send a message indicating that the authentication attempt failed. */ | ||
1447 | packet_start(SSH_SMSG_FAILURE); | ||
1448 | packet_send(); | ||
1449 | packet_write_wait(); | ||
1450 | |||
1451 | if (++authentication_failures >= MAX_AUTH_FAILURES) { | ||
1452 | packet_disconnect("Too many authentication failures for %.100s from %.200s", | ||
1453 | pw->pw_name, get_canonical_hostname()); | ||
1454 | } | ||
1455 | } | ||
1456 | |||
1457 | /* Check if the user is logging in as root and root logins are disallowed. */ | ||
1458 | if (pw->pw_uid == 0 && !options.permit_root_login) | ||
1459 | { | ||
1460 | if (forced_command) | ||
1461 | log("Root login accepted for forced command.", forced_command); | ||
1462 | else | ||
1463 | packet_disconnect("ROOT LOGIN REFUSED FROM %.200s", | ||
1464 | get_canonical_hostname()); | ||
1465 | } | ||
1466 | |||
1467 | /* The user has been authenticated and accepted. */ | ||
1468 | packet_start(SSH_SMSG_SUCCESS); | ||
1469 | packet_send(); | ||
1470 | packet_write_wait(); | ||
1471 | |||
1472 | /* Perform session preparation. */ | ||
1473 | do_authenticated(pw); | ||
1474 | } | ||
1475 | |||
1476 | /* Prepares for an interactive session. This is called after the user has | ||
1477 | been successfully authenticated. During this message exchange, pseudo | ||
1478 | terminals are allocated, X11, TCP/IP, and authentication agent forwardings | ||
1479 | are requested, etc. */ | ||
1480 | |||
1481 | void do_authenticated(struct passwd *pw) | ||
1482 | { | ||
1483 | int type; | ||
1484 | int compression_level = 0, enable_compression_after_reply = 0; | ||
1485 | int have_pty = 0, ptyfd = -1, ttyfd = -1, xauthfd = -1; | ||
1486 | int row, col, xpixel, ypixel, screen; | ||
1487 | char ttyname[64]; | ||
1488 | char *command, *term = NULL, *display = NULL, *proto = NULL, *data = NULL; | ||
1489 | struct group *grp; | ||
1490 | gid_t tty_gid; | ||
1491 | mode_t tty_mode; | ||
1492 | int n_bytes; | ||
1493 | |||
1494 | /* Cancel the alarm we set to limit the time taken for authentication. */ | ||
1495 | alarm(0); | ||
1496 | |||
1497 | /* Inform the channel mechanism that we are the server side and that | ||
1498 | the client may request to connect to any port at all. (The user could | ||
1499 | do it anyway, and we wouldn\'t know what is permitted except by the | ||
1500 | client telling us, so we can equally well trust the client not to request | ||
1501 | anything bogus.) */ | ||
1502 | channel_permit_all_opens(); | ||
1503 | |||
1504 | /* We stay in this loop until the client requests to execute a shell or a | ||
1505 | command. */ | ||
1506 | while (1) | ||
1507 | { | ||
1508 | int plen, dlen; | ||
1509 | |||
1510 | /* Get a packet from the client. */ | ||
1511 | type = packet_read(&plen); | ||
1512 | |||
1513 | /* Process the packet. */ | ||
1514 | switch (type) | ||
1515 | { | ||
1516 | case SSH_CMSG_REQUEST_COMPRESSION: | ||
1517 | packet_integrity_check(plen, 4, type); | ||
1518 | compression_level = packet_get_int(); | ||
1519 | if (compression_level < 1 || compression_level > 9) | ||
1520 | { | ||
1521 | packet_send_debug("Received illegal compression level %d.", | ||
1522 | compression_level); | ||
1523 | goto fail; | ||
1524 | } | ||
1525 | /* Enable compression after we have responded with SUCCESS. */ | ||
1526 | enable_compression_after_reply = 1; | ||
1527 | break; | ||
1528 | |||
1529 | case SSH_CMSG_REQUEST_PTY: | ||
1530 | if (no_pty_flag) | ||
1531 | { | ||
1532 | debug("Allocating a pty not permitted for this authentication."); | ||
1533 | goto fail; | ||
1534 | } | ||
1535 | if (have_pty) | ||
1536 | packet_disconnect("Protocol error: you already have a pty."); | ||
1537 | |||
1538 | debug("Allocating pty."); | ||
1539 | |||
1540 | /* Allocate a pty and open it. */ | ||
1541 | if (!pty_allocate(&ptyfd, &ttyfd, ttyname)) | ||
1542 | { | ||
1543 | error("Failed to allocate pty."); | ||
1544 | goto fail; | ||
1545 | } | ||
1546 | |||
1547 | /* Determine the group to make the owner of the tty. */ | ||
1548 | grp = getgrnam("tty"); | ||
1549 | if (grp) | ||
1550 | { | ||
1551 | tty_gid = grp->gr_gid; | ||
1552 | tty_mode = S_IRUSR|S_IWUSR|S_IWGRP; | ||
1553 | } | ||
1554 | else | ||
1555 | { | ||
1556 | tty_gid = pw->pw_gid; | ||
1557 | tty_mode = S_IRUSR|S_IWUSR|S_IWGRP|S_IWOTH; | ||
1558 | } | ||
1559 | |||
1560 | /* Change ownership of the tty. */ | ||
1561 | if (chown(ttyname, pw->pw_uid, tty_gid) < 0) | ||
1562 | fatal("chown(%.100s, %d, %d) failed: %.100s", | ||
1563 | ttyname, pw->pw_uid, tty_gid, strerror(errno)); | ||
1564 | if (chmod(ttyname, tty_mode) < 0) | ||
1565 | fatal("chmod(%.100s, 0%o) failed: %.100s", | ||
1566 | ttyname, tty_mode, strerror(errno)); | ||
1567 | |||
1568 | /* Get TERM from the packet. Note that the value may be of arbitrary | ||
1569 | length. */ | ||
1570 | |||
1571 | term = packet_get_string(&dlen); | ||
1572 | packet_integrity_check(dlen, strlen(term), type); | ||
1573 | /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */ | ||
1574 | /* Remaining bytes */ | ||
1575 | n_bytes = plen - (4 + dlen + 4*4); | ||
1576 | |||
1577 | if (strcmp(term, "") == 0) | ||
1578 | term = NULL; | ||
1579 | |||
1580 | /* Get window size from the packet. */ | ||
1581 | row = packet_get_int(); | ||
1582 | col = packet_get_int(); | ||
1583 | xpixel = packet_get_int(); | ||
1584 | ypixel = packet_get_int(); | ||
1585 | pty_change_window_size(ptyfd, row, col, xpixel, ypixel); | ||
1586 | |||
1587 | /* Get tty modes from the packet. */ | ||
1588 | tty_parse_modes(ttyfd, &n_bytes); | ||
1589 | packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); | ||
1590 | |||
1591 | /* Indicate that we now have a pty. */ | ||
1592 | have_pty = 1; | ||
1593 | break; | ||
1594 | |||
1595 | case SSH_CMSG_X11_REQUEST_FORWARDING: | ||
1596 | if (!options.x11_forwarding) | ||
1597 | { | ||
1598 | packet_send_debug("X11 forwarding disabled in server configuration file."); | ||
1599 | goto fail; | ||
1600 | } | ||
1601 | #ifdef XAUTH_PATH | ||
1602 | if (no_x11_forwarding_flag) | ||
1603 | { | ||
1604 | packet_send_debug("X11 forwarding not permitted for this authentication."); | ||
1605 | goto fail; | ||
1606 | } | ||
1607 | debug("Received request for X11 forwarding with auth spoofing."); | ||
1608 | if (display) | ||
1609 | packet_disconnect("Protocol error: X11 display already set."); | ||
1610 | { | ||
1611 | int proto_len, data_len; | ||
1612 | proto = packet_get_string(&proto_len); | ||
1613 | data = packet_get_string(&data_len); | ||
1614 | packet_integrity_check(plen, 4+proto_len + 4+data_len + 4, type); | ||
1615 | } | ||
1616 | if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER) | ||
1617 | screen = packet_get_int(); | ||
1618 | else | ||
1619 | screen = 0; | ||
1620 | display = x11_create_display_inet(screen); | ||
1621 | if (!display) | ||
1622 | goto fail; | ||
1623 | |||
1624 | /* Setup to always have a local .Xauthority. */ | ||
1625 | xauthfile = xmalloc(MAXPATHLEN); | ||
1626 | snprintf(xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX"); | ||
1627 | |||
1628 | if ((xauthfd = mkstemp(xauthfile)) != -1) { | ||
1629 | fchown(xauthfd, pw->pw_uid, pw->pw_gid); | ||
1630 | close(xauthfd); | ||
1631 | } | ||
1632 | else { | ||
1633 | xfree(xauthfile); | ||
1634 | xauthfile = NULL; | ||
1635 | } | ||
1636 | break; | ||
1637 | #else /* XAUTH_PATH */ | ||
1638 | /* No xauth program; we won't accept forwarding with spoofing. */ | ||
1639 | packet_send_debug("No xauth program; cannot forward with spoofing."); | ||
1640 | goto fail; | ||
1641 | #endif /* XAUTH_PATH */ | ||
1642 | |||
1643 | case SSH_CMSG_AGENT_REQUEST_FORWARDING: | ||
1644 | if (no_agent_forwarding_flag) | ||
1645 | { | ||
1646 | debug("Authentication agent forwarding not permitted for this authentication."); | ||
1647 | goto fail; | ||
1648 | } | ||
1649 | debug("Received authentication agent forwarding request."); | ||
1650 | auth_input_request_forwarding(pw); | ||
1651 | break; | ||
1652 | |||
1653 | case SSH_CMSG_PORT_FORWARD_REQUEST: | ||
1654 | if (no_port_forwarding_flag) | ||
1655 | { | ||
1656 | debug("Port forwarding not permitted for this authentication."); | ||
1657 | goto fail; | ||
1658 | } | ||
1659 | debug("Received TCP/IP port forwarding request."); | ||
1660 | channel_input_port_forward_request(pw->pw_uid == 0); | ||
1661 | break; | ||
1662 | |||
1663 | case SSH_CMSG_EXEC_SHELL: | ||
1664 | /* Set interactive/non-interactive mode. */ | ||
1665 | packet_set_interactive(have_pty || display != NULL, | ||
1666 | options.keepalives); | ||
1667 | |||
1668 | if (forced_command != NULL) | ||
1669 | goto do_forced_command; | ||
1670 | debug("Forking shell."); | ||
1671 | packet_integrity_check(plen, 0, type); | ||
1672 | if (have_pty) | ||
1673 | do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto, | ||
1674 | data); | ||
1675 | else | ||
1676 | do_exec_no_pty(NULL, pw, display, proto, data); | ||
1677 | return; | ||
1678 | |||
1679 | case SSH_CMSG_EXEC_CMD: | ||
1680 | /* Set interactive/non-interactive mode. */ | ||
1681 | packet_set_interactive(have_pty || display != NULL, | ||
1682 | options.keepalives); | ||
1683 | |||
1684 | if (forced_command != NULL) | ||
1685 | goto do_forced_command; | ||
1686 | /* Get command from the packet. */ | ||
1687 | { | ||
1688 | int dlen; | ||
1689 | command = packet_get_string(&dlen); | ||
1690 | debug("Executing command '%.500s'", command); | ||
1691 | packet_integrity_check(plen, 4 + dlen, type); | ||
1692 | } | ||
1693 | if (have_pty) | ||
1694 | do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display, | ||
1695 | proto, data); | ||
1696 | else | ||
1697 | do_exec_no_pty(command, pw, display, proto, data); | ||
1698 | xfree(command); | ||
1699 | return; | ||
1700 | |||
1701 | case SSH_CMSG_MAX_PACKET_SIZE: | ||
1702 | debug("The server does not support limiting packet size."); | ||
1703 | goto fail; | ||
1704 | |||
1705 | default: | ||
1706 | /* Any unknown messages in this phase are ignored, and a failure | ||
1707 | message is returned. */ | ||
1708 | log("Unknown packet type received after authentication: %d", type); | ||
1709 | goto fail; | ||
1710 | } | ||
1711 | |||
1712 | /* The request was successfully processed. */ | ||
1713 | packet_start(SSH_SMSG_SUCCESS); | ||
1714 | packet_send(); | ||
1715 | packet_write_wait(); | ||
1716 | |||
1717 | /* Enable compression now that we have replied if appropriate. */ | ||
1718 | if (enable_compression_after_reply) | ||
1719 | { | ||
1720 | enable_compression_after_reply = 0; | ||
1721 | packet_start_compression(compression_level); | ||
1722 | } | ||
1723 | |||
1724 | continue; | ||
1725 | |||
1726 | fail: | ||
1727 | /* The request failed. */ | ||
1728 | packet_start(SSH_SMSG_FAILURE); | ||
1729 | packet_send(); | ||
1730 | packet_write_wait(); | ||
1731 | continue; | ||
1732 | |||
1733 | do_forced_command: | ||
1734 | /* There is a forced command specified for this login. Execute it. */ | ||
1735 | debug("Executing forced command: %.900s", forced_command); | ||
1736 | if (have_pty) | ||
1737 | do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display, | ||
1738 | proto, data); | ||
1739 | else | ||
1740 | do_exec_no_pty(forced_command, pw, display, proto, data); | ||
1741 | return; | ||
1742 | } | ||
1743 | } | ||
1744 | |||
1745 | /* This is called to fork and execute a command when we have no tty. This | ||
1746 | will call do_child from the child, and server_loop from the parent after | ||
1747 | setting up file descriptors and such. */ | ||
1748 | |||
1749 | void do_exec_no_pty(const char *command, struct passwd *pw, | ||
1750 | const char *display, const char *auth_proto, | ||
1751 | const char *auth_data) | ||
1752 | { | ||
1753 | int pid; | ||
1754 | |||
1755 | #ifdef USE_PIPES | ||
1756 | int pin[2], pout[2], perr[2]; | ||
1757 | /* Allocate pipes for communicating with the program. */ | ||
1758 | if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) | ||
1759 | packet_disconnect("Could not create pipes: %.100s", | ||
1760 | strerror(errno)); | ||
1761 | #else /* USE_PIPES */ | ||
1762 | int inout[2], err[2]; | ||
1763 | /* Uses socket pairs to communicate with the program. */ | ||
1764 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || | ||
1765 | socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) | ||
1766 | packet_disconnect("Could not create socket pairs: %.100s", | ||
1767 | strerror(errno)); | ||
1768 | #endif /* USE_PIPES */ | ||
1769 | |||
1770 | setproctitle("%s@notty", pw->pw_name); | ||
1771 | |||
1772 | /* Fork the child. */ | ||
1773 | if ((pid = fork()) == 0) | ||
1774 | { | ||
1775 | /* Child. Reinitialize the log since the pid has changed. */ | ||
1776 | log_init(av0, debug_flag && !inetd_flag, debug_flag, | ||
1777 | options.quiet_mode, options.log_facility); | ||
1778 | |||
1779 | /* Create a new session and process group since the 4.4BSD setlogin() | ||
1780 | affects the entire process group. */ | ||
1781 | if (setsid() < 0) | ||
1782 | error("setsid failed: %.100s", strerror(errno)); | ||
1783 | |||
1784 | #ifdef USE_PIPES | ||
1785 | /* Redirect stdin. We close the parent side of the socket pair, | ||
1786 | and make the child side the standard input. */ | ||
1787 | close(pin[1]); | ||
1788 | if (dup2(pin[0], 0) < 0) | ||
1789 | perror("dup2 stdin"); | ||
1790 | close(pin[0]); | ||
1791 | |||
1792 | /* Redirect stdout. */ | ||
1793 | close(pout[0]); | ||
1794 | if (dup2(pout[1], 1) < 0) | ||
1795 | perror("dup2 stdout"); | ||
1796 | close(pout[1]); | ||
1797 | |||
1798 | /* Redirect stderr. */ | ||
1799 | close(perr[0]); | ||
1800 | if (dup2(perr[1], 2) < 0) | ||
1801 | perror("dup2 stderr"); | ||
1802 | close(perr[1]); | ||
1803 | #else /* USE_PIPES */ | ||
1804 | /* Redirect stdin, stdout, and stderr. Stdin and stdout will use the | ||
1805 | same socket, as some programs (particularly rdist) seem to depend | ||
1806 | on it. */ | ||
1807 | close(inout[1]); | ||
1808 | close(err[1]); | ||
1809 | if (dup2(inout[0], 0) < 0) /* stdin */ | ||
1810 | perror("dup2 stdin"); | ||
1811 | if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ | ||
1812 | perror("dup2 stdout"); | ||
1813 | if (dup2(err[0], 2) < 0) /* stderr */ | ||
1814 | perror("dup2 stderr"); | ||
1815 | #endif /* USE_PIPES */ | ||
1816 | |||
1817 | /* Do processing for the child (exec command etc). */ | ||
1818 | do_child(command, pw, NULL, display, auth_proto, auth_data, NULL); | ||
1819 | /*NOTREACHED*/ | ||
1820 | } | ||
1821 | if (pid < 0) | ||
1822 | packet_disconnect("fork failed: %.100s", strerror(errno)); | ||
1823 | #ifdef USE_PIPES | ||
1824 | /* We are the parent. Close the child sides of the pipes. */ | ||
1825 | close(pin[0]); | ||
1826 | close(pout[1]); | ||
1827 | close(perr[1]); | ||
1828 | |||
1829 | /* Enter the interactive session. */ | ||
1830 | server_loop(pid, pin[1], pout[0], perr[0]); | ||
1831 | /* server_loop has closed pin[1], pout[1], and perr[1]. */ | ||
1832 | #else /* USE_PIPES */ | ||
1833 | /* We are the parent. Close the child sides of the socket pairs. */ | ||
1834 | close(inout[0]); | ||
1835 | close(err[0]); | ||
1836 | |||
1837 | /* Enter the interactive session. Note: server_loop must be able to handle | ||
1838 | the case that fdin and fdout are the same. */ | ||
1839 | server_loop(pid, inout[1], inout[1], err[1]); | ||
1840 | /* server_loop has closed inout[1] and err[1]. */ | ||
1841 | #endif /* USE_PIPES */ | ||
1842 | } | ||
1843 | |||
1844 | struct pty_cleanup_context | ||
1845 | { | ||
1846 | const char *ttyname; | ||
1847 | int pid; | ||
1848 | }; | ||
1849 | |||
1850 | /* Function to perform cleanup if we get aborted abnormally (e.g., due to a | ||
1851 | dropped connection). */ | ||
1852 | |||
1853 | void pty_cleanup_proc(void *context) | ||
1854 | { | ||
1855 | struct pty_cleanup_context *cu = context; | ||
1856 | |||
1857 | debug("pty_cleanup_proc called"); | ||
1858 | |||
1859 | #if defined(KRB4) | ||
1860 | /* Destroy user's ticket cache file. */ | ||
1861 | (void) dest_tkt(); | ||
1862 | #endif /* KRB4 */ | ||
1863 | |||
1864 | /* Record that the user has logged out. */ | ||
1865 | record_logout(cu->pid, cu->ttyname); | ||
1866 | |||
1867 | /* Release the pseudo-tty. */ | ||
1868 | pty_release(cu->ttyname); | ||
1869 | } | ||
1870 | |||
1871 | /* This is called to fork and execute a command when we have a tty. This | ||
1872 | will call do_child from the child, and server_loop from the parent after | ||
1873 | setting up file descriptors, controlling tty, updating wtmp, utmp, | ||
1874 | lastlog, and other such operations. */ | ||
1875 | |||
1876 | void do_exec_pty(const char *command, int ptyfd, int ttyfd, | ||
1877 | const char *ttyname, struct passwd *pw, const char *term, | ||
1878 | const char *display, const char *auth_proto, | ||
1879 | const char *auth_data) | ||
1880 | { | ||
1881 | int pid, fdout; | ||
1882 | const char *hostname; | ||
1883 | time_t last_login_time; | ||
1884 | char buf[100], *time_string; | ||
1885 | FILE *f; | ||
1886 | char line[256]; | ||
1887 | struct stat st; | ||
1888 | int quiet_login; | ||
1889 | struct sockaddr_in from; | ||
1890 | int fromlen; | ||
1891 | struct pty_cleanup_context cleanup_context; | ||
1892 | |||
1893 | /* Get remote host name. */ | ||
1894 | hostname = get_canonical_hostname(); | ||
1895 | |||
1896 | /* Get the time when the user last logged in. Buf will be set to contain | ||
1897 | the hostname the last login was from. */ | ||
1898 | if(!options.use_login) { | ||
1899 | last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, | ||
1900 | buf, sizeof(buf)); | ||
1901 | } | ||
1902 | |||
1903 | setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1); | ||
1904 | |||
1905 | /* Fork the child. */ | ||
1906 | if ((pid = fork()) == 0) | ||
1907 | { | ||
1908 | pid = getpid(); | ||
1909 | |||
1910 | /* Child. Reinitialize the log because the pid has changed. */ | ||
1911 | log_init(av0, debug_flag && !inetd_flag, debug_flag, options.quiet_mode, | ||
1912 | options.log_facility); | ||
1913 | |||
1914 | /* Close the master side of the pseudo tty. */ | ||
1915 | close(ptyfd); | ||
1916 | |||
1917 | /* Make the pseudo tty our controlling tty. */ | ||
1918 | pty_make_controlling_tty(&ttyfd, ttyname); | ||
1919 | |||
1920 | /* Redirect stdin from the pseudo tty. */ | ||
1921 | if (dup2(ttyfd, fileno(stdin)) < 0) | ||
1922 | error("dup2 stdin failed: %.100s", strerror(errno)); | ||
1923 | |||
1924 | /* Redirect stdout to the pseudo tty. */ | ||
1925 | if (dup2(ttyfd, fileno(stdout)) < 0) | ||
1926 | error("dup2 stdin failed: %.100s", strerror(errno)); | ||
1927 | |||
1928 | /* Redirect stderr to the pseudo tty. */ | ||
1929 | if (dup2(ttyfd, fileno(stderr)) < 0) | ||
1930 | error("dup2 stdin failed: %.100s", strerror(errno)); | ||
1931 | |||
1932 | /* Close the extra descriptor for the pseudo tty. */ | ||
1933 | close(ttyfd); | ||
1934 | |||
1935 | /* Get IP address of client. This is needed because we want to record | ||
1936 | where the user logged in from. If the connection is not a socket, | ||
1937 | let the ip address be 0.0.0.0. */ | ||
1938 | memset(&from, 0, sizeof(from)); | ||
1939 | if (packet_get_connection_in() == packet_get_connection_out()) | ||
1940 | { | ||
1941 | fromlen = sizeof(from); | ||
1942 | if (getpeername(packet_get_connection_in(), | ||
1943 | (struct sockaddr *)&from, &fromlen) < 0) | ||
1944 | fatal("getpeername: %.100s", strerror(errno)); | ||
1945 | } | ||
1946 | |||
1947 | /* Record that there was a login on that terminal. */ | ||
1948 | record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname, | ||
1949 | &from); | ||
1950 | |||
1951 | /* Check if .hushlogin exists. */ | ||
1952 | snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); | ||
1953 | quiet_login = stat(line, &st) >= 0; | ||
1954 | |||
1955 | /* If the user has logged in before, display the time of last login. | ||
1956 | However, don't display anything extra if a command has been | ||
1957 | specified (so that ssh can be used to execute commands on a remote | ||
1958 | machine without users knowing they are going to another machine). | ||
1959 | Login(1) will do this for us as well, so check if login(1) is used */ | ||
1960 | if (command == NULL && last_login_time != 0 && !quiet_login && | ||
1961 | !options.use_login) | ||
1962 | { | ||
1963 | /* Convert the date to a string. */ | ||
1964 | time_string = ctime(&last_login_time); | ||
1965 | /* Remove the trailing newline. */ | ||
1966 | if (strchr(time_string, '\n')) | ||
1967 | *strchr(time_string, '\n') = 0; | ||
1968 | /* Display the last login time. Host if displayed if known. */ | ||
1969 | if (strcmp(buf, "") == 0) | ||
1970 | printf("Last login: %s\r\n", time_string); | ||
1971 | else | ||
1972 | printf("Last login: %s from %s\r\n", time_string, buf); | ||
1973 | } | ||
1974 | |||
1975 | /* Print /etc/motd unless a command was specified or printing it was | ||
1976 | disabled in server options or login(1) will be used. Note that | ||
1977 | some machines appear to print it in /etc/profile or similar. */ | ||
1978 | if (command == NULL && options.print_motd && !quiet_login && | ||
1979 | !options.use_login) | ||
1980 | { | ||
1981 | /* Print /etc/motd if it exists. */ | ||
1982 | f = fopen("/etc/motd", "r"); | ||
1983 | if (f) | ||
1984 | { | ||
1985 | while (fgets(line, sizeof(line), f)) | ||
1986 | fputs(line, stdout); | ||
1987 | fclose(f); | ||
1988 | } | ||
1989 | } | ||
1990 | |||
1991 | /* Do common processing for the child, such as execing the command. */ | ||
1992 | do_child(command, pw, term, display, auth_proto, auth_data, ttyname); | ||
1993 | /*NOTREACHED*/ | ||
1994 | } | ||
1995 | if (pid < 0) | ||
1996 | packet_disconnect("fork failed: %.100s", strerror(errno)); | ||
1997 | /* Parent. Close the slave side of the pseudo tty. */ | ||
1998 | close(ttyfd); | ||
1999 | |||
2000 | /* Create another descriptor of the pty master side for use as the standard | ||
2001 | input. We could use the original descriptor, but this simplifies code | ||
2002 | in server_loop. The descriptor is bidirectional. */ | ||
2003 | fdout = dup(ptyfd); | ||
2004 | if (fdout < 0) | ||
2005 | packet_disconnect("dup failed: %.100s", strerror(errno)); | ||
2006 | |||
2007 | /* Add a cleanup function to clear the utmp entry and record logout time | ||
2008 | in case we call fatal() (e.g., the connection gets closed). */ | ||
2009 | cleanup_context.pid = pid; | ||
2010 | cleanup_context.ttyname = ttyname; | ||
2011 | fatal_add_cleanup(pty_cleanup_proc, (void *)&cleanup_context); | ||
2012 | |||
2013 | /* Enter interactive session. */ | ||
2014 | server_loop(pid, ptyfd, fdout, -1); | ||
2015 | /* server_loop has not closed ptyfd and fdout. */ | ||
2016 | |||
2017 | /* Cancel the cleanup function. */ | ||
2018 | fatal_remove_cleanup(pty_cleanup_proc, (void *)&cleanup_context); | ||
2019 | |||
2020 | /* Record that the user has logged out. */ | ||
2021 | record_logout(pid, ttyname); | ||
2022 | |||
2023 | /* Release the pseudo-tty. */ | ||
2024 | pty_release(ttyname); | ||
2025 | |||
2026 | /* Close the server side of the socket pairs. We must do this after the | ||
2027 | pty cleanup, so that another process doesn't get this pty while we're | ||
2028 | still cleaning up. */ | ||
2029 | close(ptyfd); | ||
2030 | close(fdout); | ||
2031 | } | ||
2032 | |||
2033 | /* Sets the value of the given variable in the environment. If the variable | ||
2034 | already exists, its value is overriden. */ | ||
2035 | |||
2036 | void child_set_env(char ***envp, unsigned int *envsizep, const char *name, | ||
2037 | const char *value) | ||
2038 | { | ||
2039 | unsigned int i, namelen; | ||
2040 | char **env; | ||
2041 | |||
2042 | /* Find the slot where the value should be stored. If the variable already | ||
2043 | exists, we reuse the slot; otherwise we append a new slot at the end | ||
2044 | of the array, expanding if necessary. */ | ||
2045 | env = *envp; | ||
2046 | namelen = strlen(name); | ||
2047 | for (i = 0; env[i]; i++) | ||
2048 | if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') | ||
2049 | break; | ||
2050 | if (env[i]) | ||
2051 | { | ||
2052 | /* Name already exists. Reuse the slot. */ | ||
2053 | xfree(env[i]); | ||
2054 | } | ||
2055 | else | ||
2056 | { | ||
2057 | /* New variable. Expand the array if necessary. */ | ||
2058 | if (i >= (*envsizep) - 1) | ||
2059 | { | ||
2060 | (*envsizep) += 50; | ||
2061 | env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *)); | ||
2062 | } | ||
2063 | |||
2064 | /* Need to set the NULL pointer at end of array beyond the new | ||
2065 | slot. */ | ||
2066 | env[i + 1] = NULL; | ||
2067 | } | ||
2068 | |||
2069 | /* Allocate space and format the variable in the appropriate slot. */ | ||
2070 | env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); | ||
2071 | snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); | ||
2072 | } | ||
2073 | |||
2074 | /* Reads environment variables from the given file and adds/overrides them | ||
2075 | into the environment. If the file does not exist, this does nothing. | ||
2076 | Otherwise, it must consist of empty lines, comments (line starts with '#') | ||
2077 | and assignments of the form name=value. No other forms are allowed. */ | ||
2078 | |||
2079 | void read_environment_file(char ***env, unsigned int *envsize, | ||
2080 | const char *filename) | ||
2081 | { | ||
2082 | FILE *f; | ||
2083 | char buf[4096]; | ||
2084 | char *cp, *value; | ||
2085 | |||
2086 | /* Open the environment file. */ | ||
2087 | f = fopen(filename, "r"); | ||
2088 | if (!f) | ||
2089 | return; /* Not found. */ | ||
2090 | |||
2091 | /* Process each line. */ | ||
2092 | while (fgets(buf, sizeof(buf), f)) | ||
2093 | { | ||
2094 | /* Skip leading whitespace. */ | ||
2095 | for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) | ||
2096 | ; | ||
2097 | |||
2098 | /* Ignore empty and comment lines. */ | ||
2099 | if (!*cp || *cp == '#' || *cp == '\n') | ||
2100 | continue; | ||
2101 | |||
2102 | /* Remove newline. */ | ||
2103 | if (strchr(cp, '\n')) | ||
2104 | *strchr(cp, '\n') = '\0'; | ||
2105 | |||
2106 | /* Find the equals sign. Its lack indicates badly formatted line. */ | ||
2107 | value = strchr(cp, '='); | ||
2108 | if (value == NULL) | ||
2109 | { | ||
2110 | fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); | ||
2111 | continue; | ||
2112 | } | ||
2113 | |||
2114 | /* Replace the equals sign by nul, and advance value to the value | ||
2115 | string. */ | ||
2116 | *value = '\0'; | ||
2117 | value++; | ||
2118 | |||
2119 | /* Set the value in environment. */ | ||
2120 | child_set_env(env, envsize, cp, value); | ||
2121 | } | ||
2122 | |||
2123 | fclose(f); | ||
2124 | } | ||
2125 | |||
2126 | /* Performs common processing for the child, such as setting up the | ||
2127 | environment, closing extra file descriptors, setting the user and group | ||
2128 | ids, and executing the command or shell. */ | ||
2129 | |||
2130 | void do_child(const char *command, struct passwd *pw, const char *term, | ||
2131 | const char *display, const char *auth_proto, | ||
2132 | const char *auth_data, const char *ttyname) | ||
2133 | { | ||
2134 | const char *shell, *cp = NULL; | ||
2135 | char buf[256]; | ||
2136 | FILE *f; | ||
2137 | unsigned int envsize, i; | ||
2138 | char **env; | ||
2139 | extern char **environ; | ||
2140 | struct stat st; | ||
2141 | char *argv[10]; | ||
2142 | |||
2143 | /* Check /etc/nologin. */ | ||
2144 | f = fopen("/etc/nologin", "r"); | ||
2145 | if (f) | ||
2146 | { /* /etc/nologin exists. Print its contents and exit. */ | ||
2147 | while (fgets(buf, sizeof(buf), f)) | ||
2148 | fputs(buf, stderr); | ||
2149 | fclose(f); | ||
2150 | if (pw->pw_uid != 0) | ||
2151 | exit(254); | ||
2152 | } | ||
2153 | |||
2154 | /* Set login name in the kernel. */ | ||
2155 | if (setlogin(pw->pw_name) < 0) | ||
2156 | error("setlogin failed: %s", strerror(errno)); | ||
2157 | |||
2158 | /* Set uid, gid, and groups. */ | ||
2159 | /* Login(1) does this as well, and it needs uid 0 for the "-h" switch, | ||
2160 | so we let login(1) to this for us. */ | ||
2161 | if(!options.use_login) { | ||
2162 | if (getuid() == 0 || geteuid() == 0) | ||
2163 | { | ||
2164 | if (setgid(pw->pw_gid) < 0) | ||
2165 | { | ||
2166 | perror("setgid"); | ||
2167 | exit(1); | ||
2168 | } | ||
2169 | /* Initialize the group list. */ | ||
2170 | if (initgroups(pw->pw_name, pw->pw_gid) < 0) | ||
2171 | { | ||
2172 | perror("initgroups"); | ||
2173 | exit(1); | ||
2174 | } | ||
2175 | endgrent(); | ||
2176 | |||
2177 | /* Permanently switch to the desired uid. */ | ||
2178 | permanently_set_uid(pw->pw_uid); | ||
2179 | } | ||
2180 | |||
2181 | if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) | ||
2182 | fatal("Failed to set uids to %d.", (int)pw->pw_uid); | ||
2183 | } | ||
2184 | |||
2185 | /* Get the shell from the password data. An empty shell field is legal, | ||
2186 | and means /bin/sh. */ | ||
2187 | shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; | ||
2188 | |||
2189 | #ifdef AFS | ||
2190 | /* Try to get AFS tokens for the local cell. */ | ||
2191 | if (k_hasafs()) { | ||
2192 | char cell[64]; | ||
2193 | |||
2194 | if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) | ||
2195 | krb_afslog(cell, 0); | ||
2196 | |||
2197 | krb_afslog(0, 0); | ||
2198 | } | ||
2199 | #endif /* AFS */ | ||
2200 | |||
2201 | /* Initialize the environment. In the first part we allocate space for | ||
2202 | all environment variables. */ | ||
2203 | envsize = 100; | ||
2204 | env = xmalloc(envsize * sizeof(char *)); | ||
2205 | env[0] = NULL; | ||
2206 | |||
2207 | if(!options.use_login) { | ||
2208 | /* Set basic environment. */ | ||
2209 | child_set_env(&env, &envsize, "USER", pw->pw_name); | ||
2210 | child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); | ||
2211 | child_set_env(&env, &envsize, "HOME", pw->pw_dir); | ||
2212 | child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); | ||
2213 | |||
2214 | snprintf(buf, sizeof buf, "%.200s/%.50s", | ||
2215 | _PATH_MAILDIR, pw->pw_name); | ||
2216 | child_set_env(&env, &envsize, "MAIL", buf); | ||
2217 | |||
2218 | /* Normal systems set SHELL by default. */ | ||
2219 | child_set_env(&env, &envsize, "SHELL", shell); | ||
2220 | } | ||
2221 | |||
2222 | /* Let it inherit timezone if we have one. */ | ||
2223 | if (getenv("TZ")) | ||
2224 | child_set_env(&env, &envsize, "TZ", getenv("TZ")); | ||
2225 | |||
2226 | /* Set custom environment options from RSA authentication. */ | ||
2227 | while (custom_environment) | ||
2228 | { | ||
2229 | struct envstring *ce = custom_environment; | ||
2230 | char *s = ce->s; | ||
2231 | int i; | ||
2232 | for (i = 0; s[i] != '=' && s[i]; i++) | ||
2233 | ; | ||
2234 | if (s[i] == '=') | ||
2235 | { | ||
2236 | s[i] = 0; | ||
2237 | child_set_env(&env, &envsize, s, s + i + 1); | ||
2238 | } | ||
2239 | custom_environment = ce->next; | ||
2240 | xfree(ce->s); | ||
2241 | xfree(ce); | ||
2242 | } | ||
2243 | |||
2244 | /* Set SSH_CLIENT. */ | ||
2245 | snprintf(buf, sizeof buf, "%.50s %d %d", | ||
2246 | get_remote_ipaddr(), get_remote_port(), options.port); | ||
2247 | child_set_env(&env, &envsize, "SSH_CLIENT", buf); | ||
2248 | |||
2249 | /* Set SSH_TTY if we have a pty. */ | ||
2250 | if (ttyname) | ||
2251 | child_set_env(&env, &envsize, "SSH_TTY", ttyname); | ||
2252 | |||
2253 | /* Set TERM if we have a pty. */ | ||
2254 | if (term) | ||
2255 | child_set_env(&env, &envsize, "TERM", term); | ||
2256 | |||
2257 | /* Set DISPLAY if we have one. */ | ||
2258 | if (display) | ||
2259 | child_set_env(&env, &envsize, "DISPLAY", display); | ||
2260 | |||
2261 | #ifdef KRB4 | ||
2262 | if (ticket) | ||
2263 | child_set_env(&env, &envsize, "KRBTKFILE", ticket); | ||
2264 | #endif /* KRB4 */ | ||
2265 | |||
2266 | /* Set XAUTHORITY to always be a local file. */ | ||
2267 | if (xauthfile) | ||
2268 | child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); | ||
2269 | |||
2270 | /* Set variable for forwarded authentication connection, if we have one. */ | ||
2271 | if (auth_get_socket_name() != NULL) | ||
2272 | child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, | ||
2273 | auth_get_socket_name()); | ||
2274 | |||
2275 | /* Read $HOME/.ssh/environment. */ | ||
2276 | if(!options.use_login) { | ||
2277 | snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir); | ||
2278 | read_environment_file(&env, &envsize, buf); | ||
2279 | } | ||
2280 | |||
2281 | /* If debugging, dump the environment to stderr. */ | ||
2282 | if (debug_flag) | ||
2283 | { | ||
2284 | fprintf(stderr, "Environment:\n"); | ||
2285 | for (i = 0; env[i]; i++) | ||
2286 | fprintf(stderr, " %.200s\n", env[i]); | ||
2287 | } | ||
2288 | |||
2289 | /* Close the connection descriptors; note that this is the child, and the | ||
2290 | server will still have the socket open, and it is important that we | ||
2291 | do not shutdown it. Note that the descriptors cannot be closed before | ||
2292 | building the environment, as we call get_remote_ipaddr there. */ | ||
2293 | if (packet_get_connection_in() == packet_get_connection_out()) | ||
2294 | close(packet_get_connection_in()); | ||
2295 | else | ||
2296 | { | ||
2297 | close(packet_get_connection_in()); | ||
2298 | close(packet_get_connection_out()); | ||
2299 | } | ||
2300 | /* Close all descriptors related to channels. They will still remain | ||
2301 | open in the parent. */ | ||
2302 | channel_close_all(); | ||
2303 | |||
2304 | /* Close any extra file descriptors. Note that there may still be | ||
2305 | descriptors left by system functions. They will be closed later. */ | ||
2306 | endpwent(); | ||
2307 | endhostent(); | ||
2308 | |||
2309 | /* Close any extra open file descriptors so that we don\'t have them | ||
2310 | hanging around in clients. Note that we want to do this after | ||
2311 | initgroups, because at least on Solaris 2.3 it leaves file descriptors | ||
2312 | open. */ | ||
2313 | for (i = 3; i < 64; i++) | ||
2314 | close(i); | ||
2315 | |||
2316 | /* Change current directory to the user\'s home directory. */ | ||
2317 | if (chdir(pw->pw_dir) < 0) | ||
2318 | fprintf(stderr, "Could not chdir to home directory %s: %s\n", | ||
2319 | pw->pw_dir, strerror(errno)); | ||
2320 | |||
2321 | /* Must take new environment into use so that .ssh/rc, /etc/sshrc and | ||
2322 | xauth are run in the proper environment. */ | ||
2323 | environ = env; | ||
2324 | |||
2325 | /* Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first | ||
2326 | in this order). */ | ||
2327 | if(!options.use_login) { | ||
2328 | if (stat(SSH_USER_RC, &st) >= 0) | ||
2329 | { | ||
2330 | if (debug_flag) | ||
2331 | fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); | ||
2332 | |||
2333 | f = popen("/bin/sh " SSH_USER_RC, "w"); | ||
2334 | if (f) | ||
2335 | { | ||
2336 | if (auth_proto != NULL && auth_data != NULL) | ||
2337 | fprintf(f, "%s %s\n", auth_proto, auth_data); | ||
2338 | pclose(f); | ||
2339 | } | ||
2340 | else | ||
2341 | fprintf(stderr, "Could not run %s\n", SSH_USER_RC); | ||
2342 | } | ||
2343 | else | ||
2344 | if (stat(SSH_SYSTEM_RC, &st) >= 0) | ||
2345 | { | ||
2346 | if (debug_flag) | ||
2347 | fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC); | ||
2348 | |||
2349 | f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); | ||
2350 | if (f) | ||
2351 | { | ||
2352 | if (auth_proto != NULL && auth_data != NULL) | ||
2353 | fprintf(f, "%s %s\n", auth_proto, auth_data); | ||
2354 | pclose(f); | ||
2355 | } | ||
2356 | else | ||
2357 | fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC); | ||
2358 | } | ||
2359 | #ifdef XAUTH_PATH | ||
2360 | else | ||
2361 | { | ||
2362 | /* Add authority data to .Xauthority if appropriate. */ | ||
2363 | if (auth_proto != NULL && auth_data != NULL) | ||
2364 | { | ||
2365 | if (debug_flag) | ||
2366 | fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n", | ||
2367 | XAUTH_PATH, display, auth_proto, auth_data); | ||
2368 | |||
2369 | f = popen(XAUTH_PATH " -q -", "w"); | ||
2370 | if (f) | ||
2371 | { | ||
2372 | fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data); | ||
2373 | fclose(f); | ||
2374 | } | ||
2375 | else | ||
2376 | fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH); | ||
2377 | } | ||
2378 | } | ||
2379 | #endif /* XAUTH_PATH */ | ||
2380 | |||
2381 | /* Get the last component of the shell name. */ | ||
2382 | cp = strrchr(shell, '/'); | ||
2383 | if (cp) | ||
2384 | cp++; | ||
2385 | else | ||
2386 | cp = shell; | ||
2387 | } | ||
2388 | |||
2389 | /* If we have no command, execute the shell. In this case, the shell name | ||
2390 | to be passed in argv[0] is preceded by '-' to indicate that this is | ||
2391 | a login shell. */ | ||
2392 | if (!command) | ||
2393 | { | ||
2394 | if(!options.use_login) { | ||
2395 | char buf[256]; | ||
2396 | |||
2397 | /* Check for mail if we have a tty and it was enabled in server options. */ | ||
2398 | if (ttyname && options.check_mail) { | ||
2399 | char *mailbox; | ||
2400 | struct stat mailstat; | ||
2401 | mailbox = getenv("MAIL"); | ||
2402 | if(mailbox != NULL) { | ||
2403 | if(stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) { | ||
2404 | printf("No mail.\n"); | ||
2405 | } else if(mailstat.st_mtime < mailstat.st_atime) { | ||
2406 | printf("You have mail.\n"); | ||
2407 | } else { | ||
2408 | printf("You have new mail.\n"); | ||
2409 | } | ||
2410 | } | ||
2411 | } | ||
2412 | /* Start the shell. Set initial character to '-'. */ | ||
2413 | buf[0] = '-'; | ||
2414 | strncpy(buf + 1, cp, sizeof(buf) - 1); | ||
2415 | buf[sizeof(buf) - 1] = 0; | ||
2416 | /* Execute the shell. */ | ||
2417 | argv[0] = buf; | ||
2418 | argv[1] = NULL; | ||
2419 | execve(shell, argv, env); | ||
2420 | /* Executing the shell failed. */ | ||
2421 | perror(shell); | ||
2422 | exit(1); | ||
2423 | |||
2424 | } else { | ||
2425 | /* Launch login(1). */ | ||
2426 | |||
2427 | execl("/usr/bin/login", "login", "-h", get_remote_ipaddr(), "-p", "-f", "--", pw->pw_name, NULL); | ||
2428 | |||
2429 | /* Login couldn't be executed, die. */ | ||
2430 | |||
2431 | perror("login"); | ||
2432 | exit(1); | ||
2433 | } | ||
2434 | } | ||
2435 | |||
2436 | /* Execute the command using the user's shell. This uses the -c option | ||
2437 | to execute the command. */ | ||
2438 | argv[0] = (char *)cp; | ||
2439 | argv[1] = "-c"; | ||
2440 | argv[2] = (char *)command; | ||
2441 | argv[3] = NULL; | ||
2442 | execve(shell, argv, env); | ||
2443 | perror(shell); | ||
2444 | exit(1); | ||
2445 | } | ||
diff --git a/sshd.init b/sshd.init new file mode 100755 index 000000000..b36b57aa6 --- /dev/null +++ b/sshd.init | |||
@@ -0,0 +1,49 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | # Init file for OpenSSH sshd | ||
4 | # | ||
5 | # chkconfig: 2345 55 25 | ||
6 | # description: OpenSSH server daemon | ||
7 | # | ||
8 | # processname: sshd | ||
9 | # config: /etc/ssh/ssh_host_key | ||
10 | # config: /etc/ssh/ssh_host_key.pub | ||
11 | # config: /etc/ssh/ssh_random_seed | ||
12 | # config: /etc/ssh/sshd_config | ||
13 | # pidfile: /var/run/sshd.pid | ||
14 | |||
15 | # source function library | ||
16 | . /etc/rc.d/init.d/functions | ||
17 | |||
18 | RETVAL=0 | ||
19 | |||
20 | case "$1" in | ||
21 | start) | ||
22 | echo -n "Starting sshd: " | ||
23 | daemon /usr/sbin/sshd | ||
24 | RETVAL=$? | ||
25 | [ $RETVAL -eq 0 ] && touch /var/lock/subsys/sshd | ||
26 | echo | ||
27 | ;; | ||
28 | stop) | ||
29 | echo -n "Shutting down sshd: " | ||
30 | killproc sshd | ||
31 | RETVAL=$? | ||
32 | [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/sshd | ||
33 | echo | ||
34 | ;; | ||
35 | restart) | ||
36 | $0 stop | ||
37 | $0 start | ||
38 | RETVAL=$? | ||
39 | ;; | ||
40 | status) | ||
41 | status sshd | ||
42 | RETVAL=$? | ||
43 | ;; | ||
44 | *) | ||
45 | echo "Usage: sshd {start|stop|restart|status}" | ||
46 | exit 1 | ||
47 | esac | ||
48 | |||
49 | exit $RETVAL | ||
diff --git a/sshd_config b/sshd_config new file mode 100644 index 000000000..97f6f8e1c --- /dev/null +++ b/sshd_config | |||
@@ -0,0 +1,44 @@ | |||
1 | # This is ssh server systemwide configuration file. | ||
2 | |||
3 | Port 22 | ||
4 | ListenAddress 0.0.0.0 | ||
5 | HostKey /etc/ssh/ssh_host_key | ||
6 | ServerKeyBits 768 | ||
7 | LoginGraceTime 600 | ||
8 | KeyRegenerationInterval 3600 | ||
9 | PermitRootLogin yes | ||
10 | # | ||
11 | # Don't read ~/.rhosts and ~/.shosts files | ||
12 | IgnoreRhosts yes | ||
13 | StrictModes yes | ||
14 | QuietMode no | ||
15 | X11Forwarding yes | ||
16 | X11DisplayOffset 10 | ||
17 | FascistLogging no | ||
18 | PrintMotd yes | ||
19 | KeepAlive yes | ||
20 | SyslogFacility AUTH | ||
21 | RhostsAuthentication no | ||
22 | # | ||
23 | # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts | ||
24 | RhostsRSAAuthentication no | ||
25 | # | ||
26 | RSAAuthentication yes | ||
27 | |||
28 | # To disable tunneled clear text passwords, change to no here! | ||
29 | PasswordAuthentication yes | ||
30 | PermitEmptyPasswords no | ||
31 | # Uncomment to disable s/key passwords | ||
32 | #SkeyAuthentication no | ||
33 | |||
34 | # To change Kerberos options | ||
35 | #KerberosAuthentication no | ||
36 | #KerberosOrLocalPasswd yes | ||
37 | #AFSTokenPassing no | ||
38 | #KerberosTicketCleanup no | ||
39 | |||
40 | # Kerberos TGT Passing does only work with the AFS kaserver | ||
41 | #KerberosTgtPassing yes | ||
42 | |||
43 | #CheckMail yes | ||
44 | #UseLogin no | ||
diff --git a/strlcpy.c b/strlcpy.c new file mode 100644 index 000000000..300a28bc3 --- /dev/null +++ b/strlcpy.c | |||
@@ -0,0 +1,68 @@ | |||
1 | /* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */ | ||
2 | |||
3 | /* | ||
4 | * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Redistribution and use in source and binary forms, with or without | ||
8 | * modification, are permitted provided that the following conditions | ||
9 | * are met: | ||
10 | * 1. Redistributions of source code must retain the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer. | ||
12 | * 2. Redistributions in binary form must reproduce the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer in the | ||
14 | * documentation and/or other materials provided with the distribution. | ||
15 | * 3. The name of the author may not be used to endorse or promote products | ||
16 | * derived from this software without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
19 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | ||
20 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | ||
21 | * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
24 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
25 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
26 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||
27 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | #if defined(LIBC_SCCS) && !defined(lint) | ||
31 | static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $"; | ||
32 | #endif /* LIBC_SCCS and not lint */ | ||
33 | |||
34 | #include <sys/types.h> | ||
35 | #include <string.h> | ||
36 | |||
37 | /* | ||
38 | * Copy src to string dst of size siz. At most siz-1 characters | ||
39 | * will be copied. Always NUL terminates (unless siz == 0). | ||
40 | * Returns strlen(src); if retval >= siz, truncation occurred. | ||
41 | */ | ||
42 | size_t strlcpy(dst, src, siz) | ||
43 | char *dst; | ||
44 | const char *src; | ||
45 | size_t siz; | ||
46 | { | ||
47 | register char *d = dst; | ||
48 | register const char *s = src; | ||
49 | register size_t n = siz; | ||
50 | |||
51 | /* Copy as many bytes as will fit */ | ||
52 | if (n != 0 && --n != 0) { | ||
53 | do { | ||
54 | if ((*d++ = *s++) == 0) | ||
55 | break; | ||
56 | } while (--n != 0); | ||
57 | } | ||
58 | |||
59 | /* Not enough room in dst, add NUL and traverse rest of src */ | ||
60 | if (n == 0) { | ||
61 | if (siz != 0) | ||
62 | *d = '\0'; /* NUL-terminate dst */ | ||
63 | while (*s++) | ||
64 | ; | ||
65 | } | ||
66 | |||
67 | return(s - src - 1); /* count does not include NUL */ | ||
68 | } | ||
diff --git a/strlcpy.h b/strlcpy.h new file mode 100644 index 000000000..824df300c --- /dev/null +++ b/strlcpy.h | |||
@@ -0,0 +1,4 @@ | |||
1 | #ifndef _STRLCPY_H | ||
2 | #define _STRLCPY_H | ||
3 | size_t strlcpy(char *dst, const char *src, size_t siz); | ||
4 | #endif /* _STRLCPY_H */ | ||
diff --git a/tildexpand.c b/tildexpand.c new file mode 100644 index 000000000..e4b57091e --- /dev/null +++ b/tildexpand.c | |||
@@ -0,0 +1,70 @@ | |||
1 | /* | ||
2 | |||
3 | tildexpand.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Jul 12 01:07:36 1995 ylo | ||
11 | |||
12 | */ | ||
13 | |||
14 | #include "includes.h" | ||
15 | RCSID("$Id: tildexpand.c,v 1.1 1999/10/27 03:42:46 damien Exp $"); | ||
16 | |||
17 | #include "xmalloc.h" | ||
18 | #include "ssh.h" | ||
19 | |||
20 | /* Expands tildes in the file name. Returns data allocated by xmalloc. | ||
21 | Warning: this calls getpw*. */ | ||
22 | |||
23 | char *tilde_expand_filename(const char *filename, uid_t my_uid) | ||
24 | { | ||
25 | const char *cp; | ||
26 | unsigned int userlen; | ||
27 | char *expanded; | ||
28 | struct passwd *pw; | ||
29 | char user[100]; | ||
30 | |||
31 | /* Return immediately if no tilde. */ | ||
32 | if (filename[0] != '~') | ||
33 | return xstrdup(filename); | ||
34 | |||
35 | /* Skip the tilde. */ | ||
36 | filename++; | ||
37 | |||
38 | /* Find where the username ends. */ | ||
39 | cp = strchr(filename, '/'); | ||
40 | if (cp) | ||
41 | userlen = cp - filename; /* Have something after username. */ | ||
42 | else | ||
43 | userlen = strlen(filename); /* Nothign after username. */ | ||
44 | if (userlen == 0) | ||
45 | pw = getpwuid(my_uid); /* Own home directory. */ | ||
46 | else | ||
47 | { | ||
48 | /* Tilde refers to someone elses home directory. */ | ||
49 | if (userlen > sizeof(user) - 1) | ||
50 | fatal("User name after tilde too long."); | ||
51 | memcpy(user, filename, userlen); | ||
52 | user[userlen] = 0; | ||
53 | pw = getpwnam(user); | ||
54 | } | ||
55 | |||
56 | /* Check that we found the user. */ | ||
57 | if (!pw) | ||
58 | fatal("Unknown user %100s.", user); | ||
59 | |||
60 | /* If referring to someones home directory, return it now. */ | ||
61 | if (!cp) | ||
62 | { /* Only home directory specified */ | ||
63 | return xstrdup(pw->pw_dir); | ||
64 | } | ||
65 | |||
66 | /* Build a path combining the specified directory and path. */ | ||
67 | expanded = xmalloc(strlen(pw->pw_dir) + strlen(cp + 1) + 2); | ||
68 | sprintf(expanded, "%s/%s", pw->pw_dir, cp + 1); | ||
69 | return expanded; | ||
70 | } | ||
diff --git a/ttymodes.c b/ttymodes.c new file mode 100644 index 000000000..cbb7f2f64 --- /dev/null +++ b/ttymodes.c | |||
@@ -0,0 +1,359 @@ | |||
1 | /* | ||
2 | |||
3 | ttymodes.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Tue Mar 21 15:59:15 1995 ylo | ||
11 | |||
12 | Encoding and decoding of terminal modes in a portable way. | ||
13 | Much of the format is defined in ttymodes.h; it is included multiple times | ||
14 | into this file with the appropriate macro definitions to generate the | ||
15 | suitable code. | ||
16 | |||
17 | */ | ||
18 | |||
19 | #include "includes.h" | ||
20 | RCSID("$Id: ttymodes.c,v 1.1 1999/10/27 03:42:46 damien Exp $"); | ||
21 | |||
22 | #include "packet.h" | ||
23 | #include "ssh.h" | ||
24 | |||
25 | #define TTY_OP_END 0 | ||
26 | #define TTY_OP_ISPEED 192 /* int follows */ | ||
27 | #define TTY_OP_OSPEED 193 /* int follows */ | ||
28 | |||
29 | /* Converts POSIX speed_t to a baud rate. The values of the constants | ||
30 | for speed_t are not themselves portable. */ | ||
31 | |||
32 | static int speed_to_baud(speed_t speed) | ||
33 | { | ||
34 | switch (speed) | ||
35 | { | ||
36 | case B0: | ||
37 | return 0; | ||
38 | case B50: | ||
39 | return 50; | ||
40 | case B75: | ||
41 | return 75; | ||
42 | case B110: | ||
43 | return 110; | ||
44 | case B134: | ||
45 | return 134; | ||
46 | case B150: | ||
47 | return 150; | ||
48 | case B200: | ||
49 | return 200; | ||
50 | case B300: | ||
51 | return 300; | ||
52 | case B600: | ||
53 | return 600; | ||
54 | case B1200: | ||
55 | return 1200; | ||
56 | case B1800: | ||
57 | return 1800; | ||
58 | case B2400: | ||
59 | return 2400; | ||
60 | case B4800: | ||
61 | return 4800; | ||
62 | case B9600: | ||
63 | return 9600; | ||
64 | |||
65 | #ifdef B19200 | ||
66 | case B19200: | ||
67 | return 19200; | ||
68 | #else /* B19200 */ | ||
69 | #ifdef EXTA | ||
70 | case EXTA: | ||
71 | return 19200; | ||
72 | #endif /* EXTA */ | ||
73 | #endif /* B19200 */ | ||
74 | |||
75 | #ifdef B38400 | ||
76 | case B38400: | ||
77 | return 38400; | ||
78 | #else /* B38400 */ | ||
79 | #ifdef EXTB | ||
80 | case EXTB: | ||
81 | return 38400; | ||
82 | #endif /* EXTB */ | ||
83 | #endif /* B38400 */ | ||
84 | |||
85 | #ifdef B7200 | ||
86 | case B7200: | ||
87 | return 7200; | ||
88 | #endif /* B7200 */ | ||
89 | #ifdef B14400 | ||
90 | case B14400: | ||
91 | return 14400; | ||
92 | #endif /* B14400 */ | ||
93 | #ifdef B28800 | ||
94 | case B28800: | ||
95 | return 28800; | ||
96 | #endif /* B28800 */ | ||
97 | #ifdef B57600 | ||
98 | case B57600: | ||
99 | return 57600; | ||
100 | #endif /* B57600 */ | ||
101 | #ifdef B76800 | ||
102 | case B76800: | ||
103 | return 76800; | ||
104 | #endif /* B76800 */ | ||
105 | #ifdef B115200 | ||
106 | case B115200: | ||
107 | return 115200; | ||
108 | #endif /* B115200 */ | ||
109 | #ifdef B230400 | ||
110 | case B230400: | ||
111 | return 230400; | ||
112 | #endif /* B230400 */ | ||
113 | default: | ||
114 | return 9600; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | /* Converts a numeric baud rate to a POSIX speed_t. */ | ||
119 | |||
120 | static speed_t baud_to_speed(int baud) | ||
121 | { | ||
122 | switch (baud) | ||
123 | { | ||
124 | case 0: | ||
125 | return B0; | ||
126 | case 50: | ||
127 | return B50; | ||
128 | case 75: | ||
129 | return B75; | ||
130 | case 110: | ||
131 | return B110; | ||
132 | case 134: | ||
133 | return B134; | ||
134 | case 150: | ||
135 | return B150; | ||
136 | case 200: | ||
137 | return B200; | ||
138 | case 300: | ||
139 | return B300; | ||
140 | case 600: | ||
141 | return B600; | ||
142 | case 1200: | ||
143 | return B1200; | ||
144 | case 1800: | ||
145 | return B1800; | ||
146 | case 2400: | ||
147 | return B2400; | ||
148 | case 4800: | ||
149 | return B4800; | ||
150 | case 9600: | ||
151 | return B9600; | ||
152 | |||
153 | #ifdef B19200 | ||
154 | case 19200: | ||
155 | return B19200; | ||
156 | #else /* B19200 */ | ||
157 | #ifdef EXTA | ||
158 | case 19200: | ||
159 | return EXTA; | ||
160 | #endif /* EXTA */ | ||
161 | #endif /* B19200 */ | ||
162 | |||
163 | #ifdef B38400 | ||
164 | case 38400: | ||
165 | return B38400; | ||
166 | #else /* B38400 */ | ||
167 | #ifdef EXTB | ||
168 | case 38400: | ||
169 | return EXTB; | ||
170 | #endif /* EXTB */ | ||
171 | #endif /* B38400 */ | ||
172 | |||
173 | #ifdef B7200 | ||
174 | case 7200: | ||
175 | return B7200; | ||
176 | #endif /* B7200 */ | ||
177 | #ifdef B14400 | ||
178 | case 14400: | ||
179 | return B14400; | ||
180 | #endif /* B14400 */ | ||
181 | #ifdef B28800 | ||
182 | case 28800: | ||
183 | return B28800; | ||
184 | #endif /* B28800 */ | ||
185 | #ifdef B57600 | ||
186 | case 57600: | ||
187 | return B57600; | ||
188 | #endif /* B57600 */ | ||
189 | #ifdef B76800 | ||
190 | case 76800: | ||
191 | return B76800; | ||
192 | #endif /* B76800 */ | ||
193 | #ifdef B115200 | ||
194 | case 115200: | ||
195 | return B115200; | ||
196 | #endif /* B115200 */ | ||
197 | #ifdef B230400 | ||
198 | case 230400: | ||
199 | return B230400; | ||
200 | #endif /* B230400 */ | ||
201 | default: | ||
202 | return B9600; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | /* Encodes terminal modes for the terminal referenced by fd in a portable | ||
207 | manner, and appends the modes to a packet being constructed. */ | ||
208 | |||
209 | void tty_make_modes(int fd) | ||
210 | { | ||
211 | struct termios tio; | ||
212 | int baud; | ||
213 | |||
214 | /* Get the modes. */ | ||
215 | if (tcgetattr(fd, &tio) < 0) | ||
216 | { | ||
217 | packet_put_char(TTY_OP_END); | ||
218 | log("tcgetattr: %.100s", strerror(errno)); | ||
219 | return; | ||
220 | } | ||
221 | |||
222 | /* Store input and output baud rates. */ | ||
223 | baud = speed_to_baud(cfgetospeed(&tio)); | ||
224 | packet_put_char(TTY_OP_OSPEED); | ||
225 | packet_put_int(baud); | ||
226 | baud = speed_to_baud(cfgetispeed(&tio)); | ||
227 | packet_put_char(TTY_OP_ISPEED); | ||
228 | packet_put_int(baud); | ||
229 | |||
230 | /* Store values of mode flags. */ | ||
231 | #define TTYCHAR(NAME, OP) \ | ||
232 | packet_put_char(OP); packet_put_char(tio.c_cc[NAME]); | ||
233 | #define TTYMODE(NAME, FIELD, OP) \ | ||
234 | packet_put_char(OP); packet_put_char((tio.FIELD & NAME) != 0); | ||
235 | #define SGTTYCHAR(NAME, OP) | ||
236 | #define SGTTYMODE(NAME, FIELD, OP) | ||
237 | #define SGTTYMODEN(NAME, FIELD, OP) | ||
238 | |||
239 | #include "ttymodes.h" | ||
240 | |||
241 | #undef TTYCHAR | ||
242 | #undef TTYMODE | ||
243 | #undef SGTTYCHAR | ||
244 | #undef SGTTYMODE | ||
245 | #undef SGTTYMODEN | ||
246 | |||
247 | /* Mark end of mode data. */ | ||
248 | packet_put_char(TTY_OP_END); | ||
249 | } | ||
250 | |||
251 | /* Decodes terminal modes for the terminal referenced by fd in a portable | ||
252 | manner from a packet being read. */ | ||
253 | |||
254 | void tty_parse_modes(int fd, int *n_bytes_ptr) | ||
255 | { | ||
256 | struct termios tio; | ||
257 | int opcode, baud; | ||
258 | int n_bytes = 0; | ||
259 | int failure = 0; | ||
260 | |||
261 | /* Get old attributes for the terminal. We will modify these flags. | ||
262 | I am hoping that if there are any machine-specific modes, they will | ||
263 | initially have reasonable values. */ | ||
264 | if (tcgetattr(fd, &tio) < 0) | ||
265 | failure = -1; | ||
266 | |||
267 | for (;;) | ||
268 | { | ||
269 | n_bytes += 1; | ||
270 | opcode = packet_get_char(); | ||
271 | switch (opcode) | ||
272 | { | ||
273 | case TTY_OP_END: | ||
274 | goto set; | ||
275 | |||
276 | case TTY_OP_ISPEED: | ||
277 | n_bytes += 4; | ||
278 | baud = packet_get_int(); | ||
279 | if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0) | ||
280 | error("cfsetispeed failed for %d", baud); | ||
281 | break; | ||
282 | |||
283 | case TTY_OP_OSPEED: | ||
284 | n_bytes += 4; | ||
285 | baud = packet_get_int(); | ||
286 | if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0) | ||
287 | error("cfsetospeed failed for %d", baud); | ||
288 | break; | ||
289 | |||
290 | #define TTYCHAR(NAME, OP) \ | ||
291 | case OP: \ | ||
292 | n_bytes += 1; \ | ||
293 | tio.c_cc[NAME] = packet_get_char(); \ | ||
294 | break; | ||
295 | #define TTYMODE(NAME, FIELD, OP) \ | ||
296 | case OP: \ | ||
297 | n_bytes += 1; \ | ||
298 | if (packet_get_char()) \ | ||
299 | tio.FIELD |= NAME; \ | ||
300 | else \ | ||
301 | tio.FIELD &= ~NAME; \ | ||
302 | break; | ||
303 | #define SGTTYCHAR(NAME, OP) | ||
304 | #define SGTTYMODE(NAME, FIELD, OP) | ||
305 | #define SGTTYMODEN(NAME, FIELD, OP) | ||
306 | |||
307 | #include "ttymodes.h" | ||
308 | |||
309 | #undef TTYCHAR | ||
310 | #undef TTYMODE | ||
311 | #undef SGTTYCHAR | ||
312 | #undef SGTTYMODE | ||
313 | #undef SGTTYMODEN | ||
314 | |||
315 | default: | ||
316 | debug("Ignoring unsupported tty mode opcode %d (0x%x)", | ||
317 | opcode, opcode); | ||
318 | /* Opcodes 0 to 127 are defined to have a one-byte argument. */ | ||
319 | if (opcode >= 0 && opcode < 128) | ||
320 | { | ||
321 | n_bytes += 1; | ||
322 | (void)packet_get_char(); | ||
323 | break; | ||
324 | } | ||
325 | else | ||
326 | { | ||
327 | /* Opcodes 128 to 159 are defined to have an integer argument. */ | ||
328 | if (opcode >= 128 && opcode < 160) | ||
329 | { | ||
330 | n_bytes += 4; | ||
331 | (void)packet_get_int(); | ||
332 | break; | ||
333 | } | ||
334 | } | ||
335 | /* It is a truly undefined opcode (160 to 255). We have no idea | ||
336 | about its arguments. So we must stop parsing. Note that some | ||
337 | data may be left in the packet; hopefully there is nothing more | ||
338 | coming after the mode data. */ | ||
339 | log("parse_tty_modes: unknown opcode %d", opcode); | ||
340 | packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY); | ||
341 | goto set; | ||
342 | } | ||
343 | } | ||
344 | |||
345 | set: | ||
346 | if (*n_bytes_ptr != n_bytes) | ||
347 | { | ||
348 | *n_bytes_ptr = n_bytes; | ||
349 | return; /* Don't process bytes passed */ | ||
350 | } | ||
351 | |||
352 | if (failure == -1) | ||
353 | return; /* Packet parsed ok but tty stuff failed */ | ||
354 | |||
355 | /* Set the new modes for the terminal. */ | ||
356 | if (tcsetattr(fd, TCSANOW, &tio) < 0) | ||
357 | log("Setting tty modes failed: %.100s", strerror(errno)); | ||
358 | return; | ||
359 | } | ||
diff --git a/ttymodes.h b/ttymodes.h new file mode 100644 index 000000000..2a33eb78d --- /dev/null +++ b/ttymodes.h | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | |||
3 | ttymodes.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | SGTTY stuff contributed by Janne Snabb <snabb@niksula.hut.fi> | ||
7 | |||
8 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
9 | All rights reserved | ||
10 | |||
11 | Created: Tue Mar 21 15:42:09 1995 ylo | ||
12 | |||
13 | */ | ||
14 | |||
15 | /* RCSID("$Id: ttymodes.h,v 1.1 1999/10/27 03:42:46 damien Exp $"); */ | ||
16 | |||
17 | /* The tty mode description is a stream of bytes. The stream consists of | ||
18 | opcode-arguments pairs. It is terminated by opcode TTY_OP_END (0). | ||
19 | Opcodes 1-127 have one-byte arguments. Opcodes 128-159 have integer | ||
20 | arguments. Opcodes 160-255 are not yet defined, and cause parsing to | ||
21 | stop (they should only be used after any other data). | ||
22 | |||
23 | The client puts in the stream any modes it knows about, and the | ||
24 | server ignores any modes it does not know about. This allows some degree | ||
25 | of machine-independence, at least between systems that use a posix-like | ||
26 | tty interface. The protocol can support other systems as well, but might | ||
27 | require reimplementing as mode names would likely be different. */ | ||
28 | |||
29 | /* Some constants and prototypes are defined in packet.h; this file | ||
30 | is only intended for including from ttymodes.h. */ | ||
31 | |||
32 | /* termios macro */ /* sgtty macro */ | ||
33 | /* name, op */ | ||
34 | TTYCHAR(VINTR, 1) SGTTYCHAR(tiotc.t_intrc, 1) | ||
35 | TTYCHAR(VQUIT, 2) SGTTYCHAR(tiotc.t_quitc, 2) | ||
36 | TTYCHAR(VERASE, 3) SGTTYCHAR(tio.sg_erase, 3) | ||
37 | #if defined(VKILL) | ||
38 | TTYCHAR(VKILL, 4) SGTTYCHAR(tio.sg_kill, 4) | ||
39 | #endif /* VKILL */ | ||
40 | TTYCHAR(VEOF, 5) SGTTYCHAR(tiotc.t_eofc, 5) | ||
41 | #if defined(VEOL) | ||
42 | TTYCHAR(VEOL, 6) SGTTYCHAR(tiotc.t_brkc, 6) | ||
43 | #endif /* VEOL */ | ||
44 | #ifdef VEOL2 /* n/a */ | ||
45 | TTYCHAR(VEOL2, 7) | ||
46 | #endif /* VEOL2 */ | ||
47 | TTYCHAR(VSTART, 8) SGTTYCHAR(tiotc.t_startc, 8) | ||
48 | TTYCHAR(VSTOP, 9) SGTTYCHAR(tiotc.t_stopc, 9) | ||
49 | #if defined(VSUSP) | ||
50 | TTYCHAR(VSUSP, 10) SGTTYCHAR(tioltc.t_suspc, 10) | ||
51 | #endif /* VSUSP */ | ||
52 | #if defined(VDSUSP) | ||
53 | TTYCHAR(VDSUSP, 11) SGTTYCHAR(tioltc.t_dsuspc, 11) | ||
54 | #endif /* VDSUSP */ | ||
55 | #if defined(VREPRINT) | ||
56 | TTYCHAR(VREPRINT, 12) SGTTYCHAR(tioltc.t_rprntc, 12) | ||
57 | #endif /* VREPRINT */ | ||
58 | #if defined(VWERASE) | ||
59 | TTYCHAR(VWERASE, 13) SGTTYCHAR(tioltc.t_werasc, 13) | ||
60 | #endif /* VWERASE */ | ||
61 | #if defined(VLNEXT) | ||
62 | TTYCHAR(VLNEXT, 14) SGTTYCHAR(tioltc.t_lnextc, 14) | ||
63 | #endif /* VLNEXT */ | ||
64 | #if defined(VFLUSH) | ||
65 | TTYCHAR(VFLUSH, 15) SGTTYCHAR(tioltc.t_flushc, 15) | ||
66 | #endif /* VFLUSH */ | ||
67 | #ifdef VSWTCH | ||
68 | TTYCHAR(VSWTCH, 16) /* n/a */ | ||
69 | #endif /* VSWTCH */ | ||
70 | #if defined(VSTATUS) | ||
71 | TTYCHAR(VSTATUS, 17) SGTTYCHAR(tiots.tc_statusc, 17) | ||
72 | #endif /* VSTATUS */ | ||
73 | #ifdef VDISCARD | ||
74 | TTYCHAR(VDISCARD, 18) /* n/a */ | ||
75 | #endif /* VDISCARD */ | ||
76 | |||
77 | /* name, field, op */ | ||
78 | TTYMODE(IGNPAR, c_iflag, 30) /* n/a */ | ||
79 | TTYMODE(PARMRK, c_iflag, 31) /* n/a */ | ||
80 | TTYMODE(INPCK, c_iflag, 32) SGTTYMODEN(ANYP, tio.sg_flags, 32) | ||
81 | TTYMODE(ISTRIP, c_iflag, 33) SGTTYMODEN(LPASS8, tiolm, 33) | ||
82 | TTYMODE(INLCR, c_iflag, 34) /* n/a */ | ||
83 | TTYMODE(IGNCR, c_iflag, 35) /* n/a */ | ||
84 | TTYMODE(ICRNL, c_iflag, 36) SGTTYMODE(CRMOD, tio.sg_flags, 36) | ||
85 | #if defined(IUCLC) | ||
86 | TTYMODE(IUCLC, c_iflag, 37) SGTTYMODE(LCASE, tio.sg_flags, 37) | ||
87 | #endif | ||
88 | TTYMODE(IXON, c_iflag, 38) /* n/a */ | ||
89 | TTYMODE(IXANY, c_iflag, 39) SGTTYMODEN(LDECCTQ, tiolm, 39) | ||
90 | TTYMODE(IXOFF, c_iflag, 40) SGTTYMODE(TANDEM, tio.sg_flags, 40) | ||
91 | #ifdef IMAXBEL | ||
92 | TTYMODE(IMAXBEL,c_iflag, 41) /* n/a */ | ||
93 | #endif /* IMAXBEL */ | ||
94 | |||
95 | TTYMODE(ISIG, c_lflag, 50) /* n/a */ | ||
96 | TTYMODE(ICANON, c_lflag, 51) SGTTYMODEN(CBREAK, tio.sg_flags, 51) | ||
97 | #ifdef XCASE | ||
98 | TTYMODE(XCASE, c_lflag, 52) /* n/a */ | ||
99 | #endif | ||
100 | TTYMODE(ECHO, c_lflag, 53) SGTTYMODE(ECHO, tio.sg_flags, 53) | ||
101 | TTYMODE(ECHOE, c_lflag, 54) SGTTYMODE(LCRTERA, tiolm, 54) | ||
102 | TTYMODE(ECHOK, c_lflag, 55) SGTTYMODE(LCRTKIL, tiolm, 55) | ||
103 | TTYMODE(ECHONL, c_lflag, 56) /* n/a */ | ||
104 | TTYMODE(NOFLSH, c_lflag, 57) SGTTYMODE(LNOFLSH, tiolm, 57) | ||
105 | TTYMODE(TOSTOP, c_lflag, 58) SGTTYMODE(LTOSTOP, tiolm, 58) | ||
106 | #ifdef IEXTEN | ||
107 | TTYMODE(IEXTEN, c_lflag, 59) /* n/a */ | ||
108 | #endif /* IEXTEN */ | ||
109 | #if defined(ECHOCTL) | ||
110 | TTYMODE(ECHOCTL,c_lflag, 60) SGTTYMODE(LCTLECH, tiolm, 60) | ||
111 | #endif /* ECHOCTL */ | ||
112 | #ifdef ECHOKE | ||
113 | TTYMODE(ECHOKE, c_lflag, 61) /* n/a */ | ||
114 | #endif /* ECHOKE */ | ||
115 | #if defined(PENDIN) | ||
116 | TTYMODE(PENDIN, c_lflag, 62) SGTTYMODE(LPENDIN, tiolm, 62) | ||
117 | #endif /* PENDIN */ | ||
118 | |||
119 | TTYMODE(OPOST, c_oflag, 70) /* n/a */ | ||
120 | #if defined(OLCUC) | ||
121 | TTYMODE(OLCUC, c_oflag, 71) SGTTYMODE(LCASE, tio.sg_flags, 71) | ||
122 | #endif | ||
123 | TTYMODE(ONLCR, c_oflag, 72) SGTTYMODE(CRMOD, tio.sg_flags, 72) | ||
124 | #ifdef OCRNL | ||
125 | TTYMODE(OCRNL, c_oflag, 73) /* n/a */ | ||
126 | #endif | ||
127 | #ifdef ONOCR | ||
128 | TTYMODE(ONOCR, c_oflag, 74) /* n/a */ | ||
129 | #endif | ||
130 | #ifdef ONLRET | ||
131 | TTYMODE(ONLRET, c_oflag, 75) /* n/a */ | ||
132 | #endif | ||
133 | |||
134 | TTYMODE(CS7, c_cflag, 90) /* n/a */ | ||
135 | TTYMODE(CS8, c_cflag, 91) SGTTYMODE(LPASS8, tiolm, 91) | ||
136 | TTYMODE(PARENB, c_cflag, 92) /* n/a */ | ||
137 | TTYMODE(PARODD, c_cflag, 93) SGTTYMODE(ODDP, tio.sg_flags, 93) | ||
138 | |||
diff --git a/uidswap.c b/uidswap.c new file mode 100644 index 000000000..0eb1fd085 --- /dev/null +++ b/uidswap.c | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | |||
3 | uidswap.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Sep 9 01:56:14 1995 ylo | ||
11 | |||
12 | Code for uid-swapping. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: uidswap.c,v 1.1 1999/10/27 03:42:46 damien Exp $"); | ||
18 | |||
19 | #include "ssh.h" | ||
20 | #include "uidswap.h" | ||
21 | |||
22 | /* Note: all these functions must work in all of the following cases: | ||
23 | |||
24 | 1. euid=0, ruid=0 | ||
25 | 2. euid=0, ruid!=0 | ||
26 | 3. euid!=0, ruid!=0 | ||
27 | |||
28 | Additionally, they must work regardless of whether the system has | ||
29 | POSIX saved uids or not. */ | ||
30 | |||
31 | #ifdef _POSIX_SAVED_IDS | ||
32 | /* Lets assume that posix saved ids also work with seteuid, even though that | ||
33 | is not part of the posix specification. */ | ||
34 | #define SAVED_IDS_WORK_WITH_SETEUID | ||
35 | #endif /* _POSIX_SAVED_IDS */ | ||
36 | |||
37 | /* Saved effective uid. */ | ||
38 | static uid_t saved_euid = 0; | ||
39 | |||
40 | /* Temporarily changes to the given uid. If the effective user id is not | ||
41 | root, this does nothing. This call cannot be nested. */ | ||
42 | |||
43 | void temporarily_use_uid(uid_t uid) | ||
44 | { | ||
45 | #ifdef SAVED_IDS_WORK_WITH_SETEUID | ||
46 | |||
47 | /* Save the current euid. */ | ||
48 | saved_euid = geteuid(); | ||
49 | |||
50 | /* Set the effective uid to the given (unprivileged) uid. */ | ||
51 | if (seteuid(uid) == -1) | ||
52 | debug("seteuid %d: %.100s", (int)uid, strerror(errno)); | ||
53 | |||
54 | #else /* SAVED_IDS_WORK_WITH_SETUID */ | ||
55 | |||
56 | /* Propagate the privileged uid to all of our uids. */ | ||
57 | if (setuid(geteuid()) < 0) | ||
58 | debug("setuid %d: %.100s", (int)geteuid(), strerror(errno)); | ||
59 | |||
60 | /* Set the effective uid to the given (unprivileged) uid. */ | ||
61 | if (seteuid(uid) == -1) | ||
62 | debug("seteuid %d: %.100s", (int)uid, strerror(errno)); | ||
63 | |||
64 | #endif /* SAVED_IDS_WORK_WITH_SETEUID */ | ||
65 | |||
66 | } | ||
67 | |||
68 | /* Restores to the original uid. */ | ||
69 | |||
70 | void restore_uid() | ||
71 | { | ||
72 | #ifdef SAVED_IDS_WORK_WITH_SETEUID | ||
73 | |||
74 | /* Set the effective uid back to the saved uid. */ | ||
75 | if (seteuid(saved_euid) < 0) | ||
76 | debug("seteuid %d: %.100s", (int)saved_euid, strerror(errno)); | ||
77 | |||
78 | #else /* SAVED_IDS_WORK_WITH_SETEUID */ | ||
79 | |||
80 | /* We are unable to restore the real uid to its unprivileged value. */ | ||
81 | /* Propagate the real uid (usually more privileged) to effective uid | ||
82 | as well. */ | ||
83 | setuid(getuid()); | ||
84 | |||
85 | #endif /* SAVED_IDS_WORK_WITH_SETEUID */ | ||
86 | } | ||
87 | |||
88 | /* Permanently sets all uids to the given uid. This cannot be called while | ||
89 | temporarily_use_uid is effective. */ | ||
90 | |||
91 | void permanently_set_uid(uid_t uid) | ||
92 | { | ||
93 | if (setuid(uid) < 0) | ||
94 | debug("setuid %d: %.100s", (int)uid, strerror(errno)); | ||
95 | } | ||
diff --git a/uidswap.h b/uidswap.h new file mode 100644 index 000000000..af4f924f0 --- /dev/null +++ b/uidswap.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | |||
3 | uidswap.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Sep 9 01:43:15 1995 ylo | ||
11 | Last modified: Sat Sep 9 02:34:04 1995 ylo | ||
12 | |||
13 | */ | ||
14 | |||
15 | #ifndef UIDSWAP_H | ||
16 | #define UIDSWAP_H | ||
17 | |||
18 | /* Temporarily changes to the given uid. If the effective user id is not | ||
19 | root, this does nothing. This call cannot be nested. */ | ||
20 | void temporarily_use_uid(uid_t uid); | ||
21 | |||
22 | /* Restores the original effective user id after temporarily_use_uid(). | ||
23 | This should only be called while temporarily_use_uid is effective. */ | ||
24 | void restore_uid(); | ||
25 | |||
26 | /* Permanently sets all uids to the given uid. This cannot be called while | ||
27 | temporarily_use_uid is effective. This must also clear any saved uids. */ | ||
28 | void permanently_set_uid(uid_t uid); | ||
29 | |||
30 | #endif /* UIDSWAP_H */ | ||
diff --git a/version.h b/version.h new file mode 100644 index 000000000..8d2fc5c2c --- /dev/null +++ b/version.h | |||
@@ -0,0 +1 @@ | |||
#define SSH_VERSION "OpenSSH-1.2" | |||
diff --git a/xmalloc.c b/xmalloc.c new file mode 100644 index 000000000..b536f9d19 --- /dev/null +++ b/xmalloc.c | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | |||
3 | xmalloc.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Mar 20 21:23:10 1995 ylo | ||
11 | |||
12 | Versions of malloc and friends that check their results, and never return | ||
13 | failure (they call fatal if they encounter an error). | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: xmalloc.c,v 1.1 1999/10/27 03:42:46 damien Exp $"); | ||
19 | |||
20 | #include "ssh.h" | ||
21 | |||
22 | void *xmalloc(size_t size) | ||
23 | { | ||
24 | void *ptr = malloc(size); | ||
25 | if (ptr == NULL) | ||
26 | fatal("xmalloc: out of memory (allocating %d bytes)", (int)size); | ||
27 | return ptr; | ||
28 | } | ||
29 | |||
30 | void *xrealloc(void *ptr, size_t new_size) | ||
31 | { | ||
32 | void *new_ptr; | ||
33 | |||
34 | if (ptr == NULL) | ||
35 | fatal("xrealloc: NULL pointer given as argument"); | ||
36 | new_ptr = realloc(ptr, new_size); | ||
37 | if (new_ptr == NULL) | ||
38 | fatal("xrealloc: out of memory (new_size %d bytes)", (int)new_size); | ||
39 | return new_ptr; | ||
40 | } | ||
41 | |||
42 | void xfree(void *ptr) | ||
43 | { | ||
44 | if (ptr == NULL) | ||
45 | fatal("xfree: NULL pointer given as argument"); | ||
46 | free(ptr); | ||
47 | } | ||
48 | |||
49 | char *xstrdup(const char *str) | ||
50 | { | ||
51 | int len = strlen(str) + 1; | ||
52 | |||
53 | char *cp = xmalloc(len); | ||
54 | strlcpy(cp, str, len); | ||
55 | return cp; | ||
56 | } | ||
diff --git a/xmalloc.h b/xmalloc.h new file mode 100644 index 000000000..7a9610e7e --- /dev/null +++ b/xmalloc.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | |||
3 | xmalloc.h | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Mar 20 22:09:17 1995 ylo | ||
11 | |||
12 | Versions of malloc and friends that check their results, and never return | ||
13 | failure (they call fatal if they encounter an error). | ||
14 | |||
15 | */ | ||
16 | |||
17 | /* RCSID("$Id: xmalloc.h,v 1.1 1999/10/27 03:42:46 damien Exp $"); */ | ||
18 | |||
19 | #ifndef XMALLOC_H | ||
20 | #define XMALLOC_H | ||
21 | |||
22 | /* Like malloc, but calls fatal() if out of memory. */ | ||
23 | void *xmalloc(size_t size); | ||
24 | |||
25 | /* Like realloc, but calls fatal() if out of memory. */ | ||
26 | void *xrealloc(void *ptr, size_t new_size); | ||
27 | |||
28 | /* Frees memory allocated using xmalloc or xrealloc. */ | ||
29 | void xfree(void *ptr); | ||
30 | |||
31 | /* Allocates memory using xmalloc, and copies the string into that memory. */ | ||
32 | char *xstrdup(const char *str); | ||
33 | |||
34 | #endif /* XMALLOC_H */ | ||